5 Commits

Author SHA1 Message Date
Bartłomiej Pluta
a1bc422fba Fix accessing properties and methods in identifiers with EQUAL 2019-07-10 16:25:06 +02:00
Bartłomiej Pluta
98d710ac97 Add equal ('==') operator to strings 2019-07-10 16:02:39 +02:00
Bartłomiej Pluta
d6fb101337 Add equal ('==') operator to both identifiers and bools 2019-07-10 15:58:37 +02:00
Bartłomiej Pluta
e008be7952 Do some refactor with multiple left associative operators 2019-07-10 15:26:35 +02:00
Bartłomiej Pluta
0a7d29d4a1 Working proof of concept of multiple left associative operators 2019-07-10 15:02:17 +02:00
66 changed files with 1033 additions and 1628 deletions

56
smnp/ast/node/access.py Normal file
View File

@@ -0,0 +1,56 @@
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.none import NoneNode
from smnp.ast.parser import Parser
from smnp.error.syntax import SyntaxException
from smnp.token.type import TokenType
class AccessNode(ExpressionNode):
def __init__(self, pos):
super().__init__(pos)
self.children = [ NoneNode(), NoneNode() ]
@property
def left(self):
return self[0]
@left.setter
def left(self, value):
self[0] = value
@property
def right(self):
return self[1]
@right.setter
def right(self, value):
self[1] = value
@classmethod
def accessParser(cls):
def createNode(left, operator, right):
node = AccessNode(right.pos)
node.left = left
node.right = right
return node
return Parser.leftAssociativeOperatorParser(
cls._accessLhs(),
TokenType.DOT,
cls._accessRhs(),
createNode=createNode
)
@classmethod
def _accessLhs(cls):
raise RuntimeError(f"_accessLhs() is not implemented in {cls.__name__} class")
@staticmethod
def _accessRhs():
from smnp.ast.node.identifier import IdentifierNode
return Parser.oneOf(
IdentifierNode.functionCallParser(),
IdentifierNode.identifierParser(),
exception=lambda input: SyntaxException(f"Expected property name or method call, found '{input.current().rawValue}'", input.currentPos())
)

View File

@@ -0,0 +1,28 @@
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.none import NoneNode
class AssignmentNode(ExpressionNode):
def __init__(self, pos):
super().__init__(pos)
self.children.append(NoneNode())
@property
def target(self):
return self[0]
@target.setter
def target(self, value):
self[0] = value
@property
def value(self):
return self[1]
@value.setter
def value(self, value):
self[1] = value
@classmethod
def _parse(cls, input):
raise RuntimeError("This class is not supposed to be automatically called")

28
smnp/ast/node/asterisk.py Normal file
View File

@@ -0,0 +1,28 @@
from smnp.ast.node.none import NoneNode
from smnp.ast.node.statement import StatementNode
class AsteriskNode(StatementNode):
def __init__(self, pos):
super().__init__(pos)
self.children = [NoneNode(), NoneNode()]
@property
def iterator(self):
return self[0]
@iterator.setter
def iterator(self, value):
self[0] = value
@property
def statement(self):
return self[1]
@statement.setter
def statement(self, value):
self[1] = value
@classmethod
def _parse(cls, input):
raise RuntimeError("This class is not supposed to be automatically called")

View File

@@ -1,90 +0,0 @@
from smnp.ast.node.model import Node
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class Atom(Node):
def __init__(self, pos):
super().__init__(pos)
self.children = [None]
@property
def value(self):
return self[0]
@value.setter
def value(self, value):
self[0] = value
@classmethod
def withValue(cls, value, pos):
node = cls(pos)
node.value = value
return node
class IntegerLiteral(Atom):
pass
class StringLiteral(Atom):
pass
class NoteLiteral(Atom):
pass
class BoolLiteral(Atom):
pass
class TypeLiteral(Atom):
pass
def IntegerParser(input):
return Parser.terminal(TokenType.INTEGER, createNode=IntegerLiteral.withValue)(input)
def StringParser(input):
return Parser.terminal(TokenType.STRING, createNode=StringLiteral.withValue)(input)
def NoteParser(input):
return Parser.terminal(TokenType.NOTE, createNode=NoteLiteral.withValue)(input)
def BoolParser(input):
return Parser.terminal(TokenType.BOOL, createNode=BoolLiteral.withValue)(input)
def TypeLiteralParser(input):
return Parser.terminal(TokenType.TYPE, createNode=TypeLiteral.withValue)(input)
def LiteralParser(input):
return Parser.oneOf(
IntegerParser,
StringParser,
NoteParser,
BoolParser,
TypeLiteralParser,
name="literal"
)(input)
def AtomParser(input):
from smnp.ast.node.identifier import IdentifierParser
from smnp.ast.node.list import ListParser
from smnp.ast.node.map import MapParser
return Parser.oneOf(
LiteralParser,
IdentifierParser,
ListParser,
MapParser,
name="atom"
)(input)

View File

@@ -1,17 +1,20 @@
from smnp.ast.node.model import Node from smnp.ast.node.statement import StatementNode
from smnp.ast.node.statement import StatementParser
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class Block(Node): class BlockNode(StatementNode):
pass
@classmethod
def _parse(cls, input):
def createNode(start, items, end):
node = BlockNode(start.pos)
node.children = items
return node
def BlockParser(input): return Parser.loop(
return Parser.loop( Parser.terminalParser(TokenType.OPEN_CURLY),
Parser.terminal(TokenType.OPEN_CURLY), Parser.doAssert(StatementNode.parse, f"statement or '{TokenType.CLOSE_CURLY.key}'"),
Parser.doAssert(StatementParser, f"statement or '{TokenType.CLOSE_CURLY.key}'"), Parser.terminalParser(TokenType.CLOSE_CURLY),
Parser.terminal(TokenType.CLOSE_CURLY), createNode=createNode,
createNode=lambda open, statements, close: Block.withChildren(statements, open.pos) )(input)
)(input)

32
smnp/ast/node/bool.py Normal file
View File

@@ -0,0 +1,32 @@
from smnp.ast.node.access import AccessNode
from smnp.ast.node.literal import LiteralNode
from smnp.ast.node.relation import RelationOperatorNode
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class BoolLiteralNode(LiteralNode, AccessNode, RelationOperatorNode):
def __init__(self, pos):
super().__init__(pos)
del self.children[1]
@classmethod
def _getTokenType(cls):
return TokenType.BOOL
@classmethod
def _parse(cls, input):
x = Parser.oneOf(
cls.accessParser(),
cls.relationParser(),
cls.literalParser()
)(input)
return x
@classmethod
def _accessLhs(cls):
return cls.literalParser()
@classmethod
def _relationLhs(cls):
return cls.literalParser()

View File

@@ -1,74 +0,0 @@
from smnp.ast.node.expression import ExpressionParser
from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode
from smnp.ast.node.statement import StatementParser
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class IfElse(Node):
def __init__(self, pos):
super().__init__(pos)
self.children = [NoneNode(), NoneNode(), NoneNode()]
@property
def condition(self):
return self[0]
@condition.setter
def condition(self, value):
self[0] = value
@property
def ifNode(self):
return self[1]
@ifNode.setter
def ifNode(self, value):
self[1] = value
@property
def elseNode(self):
return self[2]
@elseNode.setter
def elseNode(self, value):
self[2] = value
@classmethod
def createNode(cls, ifNode, condition, elseNode=NoneNode()):
node = cls(ifNode.pos)
node.ifNode = ifNode
node.condition = condition
node.elseNode = elseNode
return node
def IfElseStatementParser(input):
ifStatementParser = Parser.allOf(
Parser.terminal(TokenType.IF),
Parser.terminal(TokenType.OPEN_PAREN),
ExpressionParser,
Parser.terminal(TokenType.CLOSE_PAREN),
StatementParser,
createNode=lambda _, __, condition, ___, ifStatement: IfElse.createNode(ifStatement, condition),
name="if statement"
)
ifElseStatementParser = Parser.allOf(
Parser.terminal(TokenType.IF),
Parser.terminal(TokenType.OPEN_PAREN, doAssert=True),
Parser.doAssert(ExpressionParser, "expression"),
Parser.terminal(TokenType.CLOSE_PAREN, doAssert=True),
Parser.doAssert(StatementParser, "statement"),
Parser.terminal(TokenType.ELSE),
Parser.doAssert(StatementParser, "statement"),
createNode=lambda _, __, condition, ___, ifStatement, ____, elseStatement: IfElse.createNode(ifStatement, condition, elseStatement),
name="if-else statement"
)
return Parser.oneOf(
ifElseStatementParser,
ifStatementParser,
name="if-else/if statement"
)(input)

View File

@@ -1,51 +1,66 @@
from smnp.ast.node.operator import BinaryOperator from smnp.ast.node.asterisk import AsteriskNode
from smnp.ast.node.term import TermParser from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode
from smnp.ast.node.statement import StatementNode
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class Sum(BinaryOperator): class ExpressionNode(Node):
pass def __init__(self, pos):
super().__init__(pos, [NoneNode()])
@property
def value(self):
return self[0]
class Relation(BinaryOperator): @value.setter
pass def value(self, v):
self[0] = v
class And(BinaryOperator): @classmethod
pass def withValue(cls, val, pos):
node = cls(pos)
node.value = val
return node
@classmethod
def _parse(cls, input):
return Parser.oneOf(
cls._asteriskParser(),
cls._expressionParser(),
)(input)
class Or(BinaryOperator): @classmethod
pass def _asteriskParser(cls):
def createNode(iterator, asterisk, statement):
node = AsteriskNode(asterisk.pos)
node.iterator = iterator
node.statement = statement
return node
return Parser.allOf(
cls._expressionParser(),
Parser.terminalParser(TokenType.ASTERISK),
Parser.doAssert(StatementNode.parse, 'statement'),
createNode=createNode
)
def ExpressionParser(input): @classmethod
expr1 = Parser.leftAssociativeOperatorParser( def _expressionParser(cls):
TermParser, from smnp.ast.node.bool import BoolLiteralNode
[TokenType.PLUS, TokenType.MINUS],
TermParser,
lambda left, op, right: Sum.withValues(left, op, right)
)
expr2 = Parser.leftAssociativeOperatorParser(
expr1,
[TokenType.RELATION, TokenType.OPEN_ANGLE, TokenType.CLOSE_ANGLE],
expr1,
lambda left, op, right: Relation.withValues(left, op, right)
)
expr3 = Parser.leftAssociativeOperatorParser(
expr2,
[TokenType.AND],
expr2,
lambda left, op, right: And.withValues(left, op, right)
)
return Parser.leftAssociativeOperatorParser(
expr3,
[TokenType.OR],
expr3,
lambda left, op, right: Or.withValues(left, op, right)
)(input)
from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.string import StringLiteralNode
return Parser.oneOf(
#IntegerLiteralNode.parse,
StringLiteralNode.parse,
#NoteLiteralNode.parse,
BoolLiteralNode.parse,
IdentifierNode.parse,
#MapNode.parse,
#ListNode.parse,
#TypeNode.parse,
)

View File

@@ -1,14 +1,14 @@
from smnp.ast.node.block import Block from smnp.ast.node.block import BlockNode
from smnp.ast.node.function import FunctionDefinitionParser from smnp.ast.node.function import FunctionDefinitionNode
from smnp.ast.node.identifier import IdentifierLiteralParser from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode from smnp.ast.node.none import NoneNode
from smnp.ast.node.type import TypeParser from smnp.ast.node.statement import StatementNode
from smnp.ast.node.type import TypeNode
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class Extend(Node): class ExtendNode(StatementNode):
def __init__(self, pos): def __init__(self, pos):
super().__init__(pos) super().__init__(pos)
self.children = [NoneNode(), NoneNode(), NoneNode()] self.children = [NoneNode(), NoneNode(), NoneNode()]
@@ -38,51 +38,33 @@ class Extend(Node):
self[2] = value self[2] = value
@classmethod @classmethod
def withValues(cls, pos, type, variable, methods): def _parse(cls, input):
node = cls(pos) def createNode(extend, type, asKeyword, variable, methods):
node.type = type node = ExtendNode(extend.pos)
node.variable = variable node.type = type
node.methods = methods node.variable = variable
return node node.methods = methods
return node
return Parser.allOf(
Parser.terminalParser(TokenType.EXTEND),
Parser.doAssert(TypeNode.parse, "type being extended"),
Parser.terminalParser(TokenType.AS, doAssert=True),
Parser.doAssert(IdentifierNode.identifierParser(), "variable name"),
Parser.doAssert(cls._methodsDeclarationsParser(), "methods declarations"),
createNode=createNode
)(input)
def ExtendParser(input): @classmethod
def _methodsDeclarationsParser(cls):
simpleExtend = Parser.allOf( def createNode(openBracket, items, closeBracket):
Parser.terminal(TokenType.EXTEND), node = BlockNode(openBracket.pos)
TypeParser, node.children = items
Parser.terminal(TokenType.AS), return node
IdentifierLiteralParser,
Parser.terminal(TokenType.WITH),
Parser.doAssert(Parser.wrap(FunctionDefinitionParser, lambda method: Block.withChildren([ method ], method.pos)), "method definition"),
createNode=lambda extend, type, _, variable, __, methods: Extend.withValues(extend.pos, type, variable, methods),
name="simple extend"
)
multiExtend = Parser.allOf(
Parser.terminal(TokenType.EXTEND),
Parser.doAssert(TypeParser, "type being extended"),
Parser.terminal(TokenType.AS, doAssert=True),
Parser.doAssert(IdentifierLiteralParser, "variable name"),
Parser.doAssert(MethodsDeclarationParser, f"block with methods definitions or '{TokenType.WITH.key}' keyword"),
createNode=lambda extend, type, _, variable, methods: Extend.withValues(extend.pos, type, variable, methods),
name="multiple extend"
)
return Parser.oneOf(
simpleExtend,
multiExtend,
name="extend"
)(input)
def MethodsDeclarationParser(input):
return Parser.loop(
Parser.terminal(TokenType.OPEN_CURLY),
Parser.doAssert(FunctionDefinitionParser, f"method definition or '{TokenType.CLOSE_CURLY.key}'"),
Parser.terminal(TokenType.CLOSE_CURLY),
createNode=lambda open, methods, close: Block.withChildren(methods, open.pos),
name="methods block"
)(input)
return Parser.loop(
Parser.terminalParser(TokenType.OPEN_CURLY),
Parser.doAssert(FunctionDefinitionNode.parse, f"method declaration or '{TokenType.CLOSE_CURLY.key}'"),
Parser.terminalParser(TokenType.CLOSE_CURLY),
createNode=createNode
)

View File

@@ -1,102 +0,0 @@
from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode
from smnp.ast.node.operator import BinaryOperator, Operator, UnaryOperator
from smnp.ast.node.unit import UnitParser
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class NotOperator(UnaryOperator):
pass
class Power(BinaryOperator):
pass
class Loop(BinaryOperator):
def __init__(self, pos):
super().__init__(pos)
self.children.append(NoneNode())
@property
def parameters(self):
return self[3]
@parameters.setter
def parameters(self, value):
self[3] = value
@classmethod
def loop(cls, left, parameters, operator, right):
node = cls(left.pos)
node.left = left
node.parameters = parameters
node.operator = operator
node.right = right
return node
class LoopParameters(Node):
pass
def FactorParser(input):
from smnp.ast.node.expression import ExpressionParser
from smnp.ast.node.statement import StatementParser
from smnp.ast.node.identifier import IdentifierLiteralParser
parentheses = Parser.allOf(
Parser.terminal(TokenType.OPEN_PAREN),
Parser.doAssert(ExpressionParser, "expression"),
Parser.terminal(TokenType.CLOSE_PAREN),
createNode=lambda open, expr, close: expr,
name="grouping parentheses"
)
factorOperands = Parser.oneOf(
parentheses,
UnitParser,
name="factor operands"
)
powerFactor = Parser.leftAssociativeOperatorParser(
factorOperands,
[TokenType.DOUBLE_ASTERISK],
factorOperands,
lambda left, op, right: Power.withValues(left, op, right),
name="power operator"
)
notOperator = Parser.allOf(
Parser.terminal(TokenType.NOT, Operator.withValue),
powerFactor,
createNode=NotOperator.withValues,
name="not"
)
loopParameters = Parser.allOf(
Parser.terminal(TokenType.AS),
Parser.oneOf(
Parser.wrap(IdentifierLiteralParser, lambda id: LoopParameters.withChildren([id], id.pos)),
abstractIterableParser(LoopParameters, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, IdentifierLiteralParser)
),
createNode=lambda asKeyword, parameters: parameters,
name="loop parameters"
)
loopFactor = Parser.allOf(
powerFactor,
Parser.optional(loopParameters),
Parser.terminal(TokenType.DASH, createNode=Operator.withValue),
StatementParser,
createNode=Loop.loop,
name="dash-loop"
)
return Parser.oneOf(
loopFactor,
notOperator,
powerFactor,
name="factor"
)(input)

View File

@@ -1,58 +1,83 @@
from smnp.ast.node.block import BlockParser from smnp.ast.node.block import BlockNode
from smnp.ast.node.identifier import IdentifierLiteralParser from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.iterable import abstractIterableParser from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.model import Node from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode from smnp.ast.node.none import NoneNode
from smnp.ast.node.type import TypeParser, Type from smnp.ast.node.statement import StatementNode
from smnp.ast.node.type import TypeNode, TypeSpecifier
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class ArgumentsDeclaration(Node): class ArgumentsDeclarationNode(Node):
pass
@classmethod
class Argument(Node): def _parse(cls, input):
raise RuntimeError("This class is not supposed to be automatically called")
def __init__(self, pos):
super().__init__(pos)
self.children = [NoneNode(), NoneNode(), False]
@property
def type(self):
return self[0]
@type.setter
def type(self, value):
self[0] = value
@property
def variable(self):
return self[1]
@variable.setter
def variable(self, value):
self[1] = value
@property
def vararg(self):
return self[2]
@vararg.setter
def vararg(self, value):
self[2] = value
class VarargNode(Node): class VarargNode(Node):
pass pass
class FunctionDefinition(Node): class ArgumentDefinitionNode(ExpressionNode):
def __init__(self, pos):
super().__init__(pos)
self.children.extend([NoneNode(), False])
@property
def type(self):
return self[0]
@type.setter
def type(self, value):
self[0] = value
@property
def variable(self):
return self[1]
@variable.setter
def variable(self, value):
self[1] = value
@property
def vararg(self):
return self[2]
@vararg.setter
def vararg(self, value):
self[2] = value
@classmethod
def parser(cls):
def createNode(type, variable, dots):
node = ArgumentDefinitionNode(type.pos)
node.type = type
node.variable = variable
node.vararg = isinstance(dots, VarargNode)
return node
return Parser.allOf(
Parser.optional(Parser.oneOf(
TypeNode.parse,
TypeSpecifier.parse
)),
Parser.doAssert(IdentifierNode.identifierParser(), "variable name"),
Parser.optional(Parser.terminalParser(TokenType.DOTS, lambda val, pos: VarargNode(pos))),
createNode=createNode
)
@classmethod
def _parse(cls, input):
#TODO
raise RuntimeError("Not implemented yet. There is still required work to correctly build AST related to IdentifierNode")
class FunctionDefinitionNode(StatementNode):
def __init__(self, pos): def __init__(self, pos):
super().__init__(pos) super().__init__(pos)
self.children = [NoneNode(), NoneNode(), NoneNode()] self.children = [NoneNode(), NoneNode(), NoneNode()]
@@ -82,48 +107,22 @@ class FunctionDefinition(Node):
self[2] = value self[2] = value
@classmethod @classmethod
def withValues(cls, name, arguments, body): def _parse(cls, input):
node = cls(name.pos) def createNode(function, name, arguments, body):
node.name = name node = FunctionDefinitionNode(function.pos)
node.arguments = arguments node.name = name
node.body = body node.arguments = arguments
return node node.body = body
return node
return Parser.allOf(
Parser.terminalParser(TokenType.FUNCTION),
Parser.doAssert(IdentifierNode.identifierParser(), "function name"),
Parser.doAssert(cls._argumentsDeclarationParser(), "arguments list"),
Parser.doAssert(BlockNode.parse, "function body"),
createNode=createNode
)(input)
def ArgumentParser(input): @staticmethod
def createNode(type, variable, vararg): def _argumentsDeclarationParser():
pos = type.pos if isinstance(type, Type) else variable.pos return abstractIterableParser(ArgumentsDeclarationNode, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, ArgumentDefinitionNode.parser())
node = Argument(pos)
node.type = type
node.variable = variable
node.vararg = vararg is True
return node
return Parser.allOf(
Parser.optional(TypeParser),
Parser.doAssert(IdentifierLiteralParser, "argument name"),
Parser.optional(Parser.terminal(TokenType.DOTS, lambda val, pos: True)),
createNode=createNode,
name="function argument"
)(input)
def ArgumentsDeclarationParser(input):
return abstractIterableParser(
ArgumentsDeclaration,
TokenType.OPEN_PAREN,
TokenType.CLOSE_PAREN,
Parser.doAssert(ArgumentParser, "function/method argument")
)(input)
def FunctionDefinitionParser(input):
return Parser.allOf(
Parser.terminal(TokenType.FUNCTION),
Parser.doAssert(IdentifierLiteralParser, "function/method name"),
Parser.doAssert(ArgumentsDeclarationParser, "function/method arguments"),
Parser.doAssert(BlockParser, "function/method body"),
createNode=lambda _, name, args, body: FunctionDefinition.withValues(name, args, body),
name="function definition"
)(input)

View File

@@ -1,74 +1,73 @@
from smnp.ast.node.atom import Atom from smnp.ast.node.access import AccessNode
from smnp.ast.node.expression import ExpressionParser from smnp.ast.node.assignment import AssignmentNode
from smnp.ast.node.iterable import abstractIterableParser from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.model import Node from smnp.ast.node.invocation import FunctionCallNode, ArgumentsListNode
from smnp.ast.node.none import NoneNode from smnp.ast.node.relation import RelationOperatorNode
from smnp.ast.node.operator import BinaryOperator, Operator
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class Identifier(Atom): class IdentifierNode(AccessNode, RelationOperatorNode):
pass
class FunctionCall(Node):
def __init__(self, pos): def __init__(self, pos):
super().__init__(pos) super().__init__(pos)
self.children = [NoneNode(), NoneNode()] del self.children[1]
@property
def name(self):
return self[0]
@name.setter
def name(self, value):
self[0] = value
@property
def arguments(self):
return self[1]
@arguments.setter
def arguments(self, value):
self[1] = value
@classmethod @classmethod
def withChildren(cls, name, arguments): def _parse(cls, input):
node = cls(name.pos) return Parser.oneOf(
node.name = name cls.relationParser(),
node.arguments = arguments cls.accessParser(),
return node cls.literalParser()
)(input)
@classmethod
def _accessLhs(cls):
return cls.literalParser()
class ArgumentsList(Node): @classmethod
pass def _relationLhs(cls):
return Parser.oneOf(
cls.accessParser(),
cls.literalParser()
)
@classmethod
def literalParser(cls):
return Parser.oneOf(
IdentifierNode.functionCallParser(),
IdentifierNode._assignmentParser(),
IdentifierNode.identifierParser()
)
class Assignment(BinaryOperator): @staticmethod
pass def _assignmentParser():
def createNode(target, assignment, value):
node = AssignmentNode(assignment.pos)
node.target = target
node.value = value
return node
return Parser.allOf(
IdentifierNode.identifierParser(),
Parser.terminalParser(TokenType.ASSIGN),
Parser.doAssert(ExpressionNode.parse, "expression"),
createNode=createNode
)
def IdentifierLiteralParser(input): @staticmethod
return Parser.terminal(TokenType.IDENTIFIER, createNode=Identifier.withValue)(input) def functionCallParser():
def createNode(name, arguments):
node = FunctionCallNode(name.pos)
node.name = name
node.arguments = arguments
return node
return Parser.allOf(
IdentifierNode.identifierParser(),
ArgumentsListNode.parse,
createNode=createNode
)
def IdentifierParser(input): @staticmethod
functionCallParser = Parser.allOf( def identifierParser():
IdentifierLiteralParser, return Parser.terminalParser(TokenType.IDENTIFIER, lambda val, pos: IdentifierNode.withValue(val, pos))
abstractIterableParser(ArgumentsList, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, Parser.doAssert(ExpressionParser, "expression")),
createNode=lambda name, arguments: FunctionCall.withChildren(name, arguments)
)
assignmentParser = Parser.allOf(
IdentifierLiteralParser,
Parser.terminal(TokenType.ASSIGN, createNode=Operator.withValue),
Parser.doAssert(ExpressionParser, "expression"),
createNode=lambda identifier, assign, expr: Assignment.withValues(identifier, assign, expr)
)
return Parser.oneOf(
assignmentParser,
functionCallParser,
IdentifierLiteralParser
)(input)

View File

@@ -1,13 +1,16 @@
from smnp.ast.node.atom import StringParser from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.model import Node from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode
from smnp.ast.node.string import StringLiteralNode
from smnp.ast.node.type import TypeNode
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class Import(Node): class ImportNode(Node):
def __init__(self, pos): def __init__(self, pos):
super().__init__(pos) super().__init__(pos)
self.children = [None] self.children = [NoneNode(), NoneNode(), NoneNode()]
@property @property
def source(self): def source(self):
@@ -17,17 +20,57 @@ class Import(Node):
def source(self, value): def source(self, value):
self[0] = value self[0] = value
@property
def type(self):
return self[1]
@type.setter
def type(self, value):
self[1] = value
@property
def variable(self):
return self[2]
@variable.setter
def variable(self, value):
self[2] = value
@classmethod @classmethod
def withValue(cls, value): def _parse(cls, input):
node = cls(value.pos) return Parser.oneOf(
node.source = value cls._literalImportParser(),
return node cls._fileImportParser()
)(input)
@classmethod
def _literalImportParser(cls):
def createNode(importKeyword, type, fromKeyword, source, asKeyword, variable):
node = ImportNode(importKeyword.pos)
node.source = source
node.type = type
node.variable = variable
return node
def ImportParser(input): return Parser.allOf(
return Parser.allOf( Parser.terminalParser(TokenType.IMPORT),
Parser.terminal(TokenType.IMPORT), TypeNode.parse,
Parser.doAssert(StringParser, "import source as string"), Parser.doAssert(Parser.terminalParser(TokenType.FROM), "'from <source> as <variable name>'"),
createNode=lambda imp, source: Import.withValue(source), Parser.doAssert(StringLiteralNode.literalParser(), "source as a string"),
name="import" Parser.doAssert(Parser.terminalParser(TokenType.AS), "'as <variable name>'"),
)(input) Parser.doAssert(IdentifierNode.identifierParser(), "variable name"),
createNode=createNode
)
@classmethod
def _fileImportParser(cls):
def createNode(importKeyword, source):
node = ImportNode(importKeyword.pos)
node.source = source
return node
return Parser.allOf(
Parser.terminalParser(TokenType.IMPORT),
Parser.doAssert(StringLiteralNode.literalParser(), "source as a string"),
createNode=createNode
)

37
smnp/ast/node/integer.py Normal file
View File

@@ -0,0 +1,37 @@
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class IntegerLiteralNode(ExpressionNode):
def __init__(self, pos):
super().__init__(pos)
#TODO del self.children[1]
# TODO: To Remove
@classmethod
def _parse(cls, input):
return cls._literalParser()(input)
@classmethod
def _literalParser(cls):
return Parser.oneOf(
cls._negativeIntegerParser(),
cls._positiveIntegerParser()
)
@classmethod
def _negativeIntegerParser(cls):
def createNode(minus, integer):
return IntegerLiteralNode.withValue(-integer.value, minus.pos)
return Parser.allOf(
Parser.terminalParser(TokenType.MINUS),
Parser.doAssert(cls._positiveIntegerParser(), "integer"),
createNode=createNode
)
@classmethod
def _positiveIntegerParser(cls):
return Parser.terminalParser(TokenType.INTEGER, lambda val, pos: IntegerLiteralNode.withValue(int(val), pos))

View File

@@ -0,0 +1,40 @@
from smnp.ast.node.access import AccessNode
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.model import Node
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class ArgumentsListNode(Node):
@classmethod
def _parse(cls, input):
return abstractIterableParser(ArgumentsListNode, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN,
Parser.doAssert(ExpressionNode.parse, "expression"))(input)
class FunctionCallNode(AccessNode):
def __init__(self, pos):
super().__init__(pos)
@property
def name(self):
return self[0]
@name.setter
def name(self, value):
self[0] = value
@property
def arguments(self):
return self[1]
@arguments.setter
def arguments(self, value):
self[1] = value
@classmethod
def _parse(cls, input):
raise RuntimeError("This class is not supposed to be automatically called")

View File

@@ -1,3 +1,4 @@
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.ignore import IgnoredNode from smnp.ast.node.ignore import IgnoredNode
from smnp.ast.node.model import Node, ParseResult from smnp.ast.node.model import Node, ParseResult
from smnp.ast.node.none import NoneNode from smnp.ast.node.none import NoneNode
@@ -6,19 +7,11 @@ from smnp.token.type import TokenType
def abstractIterableParser(iterableNodeType, openTokenType, closeTokenType, itemParser): def abstractIterableParser(iterableNodeType, openTokenType, closeTokenType, itemParser):
class AbstractIterableTailNode(ExpressionNode):
class AbstractIterable(Node):
def __init__(self, pos): def __init__(self, pos):
super().__init__(pos) super().__init__(pos)
self.children = [NoneNode(), NoneNode()]
@property self.children.append(NoneNode())
def value(self):
return self[0]
@value.setter
def value(self, value):
self[0] = value
@property @property
def next(self): def next(self):
@@ -28,67 +21,83 @@ def abstractIterableParser(iterableNodeType, openTokenType, closeTokenType, item
def next(self, value): def next(self, value):
self[1] = value self[1] = value
class AbstractIterableTail(AbstractIterable): @classmethod
pass def _parse(cls, input):
return Parser.oneOf(
AbstractIterableTailNode._parser1(),
AbstractIterableTailNode._parser2(),
)(input)
def abstractIterableParser(input): @staticmethod
return Parser.oneOf( def _parser1():
emptyIterable, return Parser.terminalParser(closeTokenType)
openIterable
)(input)
def emptyIterable(input): @staticmethod
def createNode(open, close): def _parser2():
node = AbstractIterable(open.pos) def createNode(comma, expr, iterableTail):
node.value = open node = AbstractIterableTailNode(expr.pos)
node.next = close node.value = expr
return node node.next = iterableTail
return node
return Parser.allOf( return Parser.allOf(
Parser.terminal(openTokenType), Parser.terminalParser(TokenType.COMMA, doAssert=True),
Parser.terminal(closeTokenType), itemParser,
createNode=createNode AbstractIterableTailNode.parse,
)(input) createNode=createNode
)
def openIterable(input): class AbstractIterableNode(ExpressionNode):
def createNode(open, item, tail): def __init__(self, pos):
node = AbstractIterable(open.pos) super().__init__(pos)
node.value = item
node.next = tail
return node
return Parser.allOf( self.children.append(NoneNode())
Parser.terminal(openTokenType),
itemParser,
abstractIterableTailParser,
createNode=createNode
)(input)
def abstractIterableTailParser(input): @property
return Parser.oneOf( def next(self):
closeIterable, return self[1]
nextItem,
)(input)
def nextItem(input): @next.setter
def createNode(comma, item, tail): def next(self, value):
node = AbstractIterableTail(item.pos) self[1] = value
node.value = item
node.next = tail
return node
return Parser.allOf( @classmethod
Parser.doAssert(Parser.terminal(TokenType.COMMA), f"'{TokenType.COMMA.key}' or '{closeTokenType.key}'"), def _parse(cls, input):
itemParser, return Parser.oneOf(
abstractIterableTailParser, AbstractIterableNode._parser1(),
createNode=createNode AbstractIterableNode._parser2()
)(input) )(input)
def closeIterable(input): @staticmethod
return Parser.terminal(closeTokenType)(input) def _parser1():
def emptyIterable(openToken, closeToken):
node = AbstractIterableNode(openToken.pos)
node.value = openToken
node.next = closeToken
return node
return Parser.allOf(
Parser.terminalParser(openTokenType),
Parser.terminalParser(closeTokenType),
createNode=emptyIterable
)
return toFlatDesiredNode(iterableNodeType, abstractIterableParser) @staticmethod
def _parser2():
def createNode(openParen, expr, iterableTail):
node = AbstractIterableNode(openParen.pos)
node.value = expr
node.next = iterableTail
return node
return Parser.allOf(
Parser.terminalParser(openTokenType, lambda val, pos: Node(pos)),
itemParser,
AbstractIterableTailNode.parse,
createNode=createNode
)
return toFlatDesiredNode(iterableNodeType, AbstractIterableNode.parse)
def toFlatDesiredNode(iterableNodeType, parser): def toFlatDesiredNode(iterableNodeType, parser):
@@ -105,7 +114,7 @@ def toFlatDesiredNode(iterableNodeType, parser):
return ParseResult.FAIL() return ParseResult.FAIL()
return Parser(parse, "flat", [parser]) return parse
def flattenList(node, output=None): def flattenList(node, output=None):

View File

@@ -1,19 +1,17 @@
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.iterable import abstractIterableParser from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.model import Node
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class List(Node): class ListNode(ExpressionNode):
pass
# TODO: To Remove
@classmethod
def _parse(cls, input):
return cls._literalParser()(input)
def ListParser(input): @classmethod
from smnp.ast.node.expression import ExpressionParser def _literalParser(cls):
return abstractIterableParser(ListNode, TokenType.OPEN_SQUARE, TokenType.CLOSE_SQUARE,
return abstractIterableParser( Parser.doAssert(ExpressionNode.parse, "expression"))
List,
TokenType.OPEN_SQUARE,
TokenType.CLOSE_SQUARE,
Parser.doAssert(ExpressionParser, "expression")
)(input)

18
smnp/ast/node/literal.py Normal file
View File

@@ -0,0 +1,18 @@
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.parser import Parser
class LiteralNode(ExpressionNode):
@classmethod
def _getTokenType(cls):
pass
@classmethod
def _processValue(cls, value):
return value
@classmethod
def literalParser(cls):
createNode = lambda val, pos: cls.withValue(cls._processValue(val), pos)
return Parser.terminalParser(cls._getTokenType(), createNode)

View File

@@ -1,12 +1,19 @@
from smnp.ast.node.atom import LiteralParser from smnp.ast.node.access import AccessNode
from smnp.ast.node.bool import BoolLiteralNode
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.integer import IntegerLiteralNode
from smnp.ast.node.iterable import abstractIterableParser from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.model import Node from smnp.ast.node.none import NoneNode
from smnp.ast.node.operator import BinaryOperator, Operator from smnp.ast.node.note import NoteLiteralNode
from smnp.ast.node.string import StringLiteralNode
from smnp.ast.node.type import TypeNode
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class MapEntry(ExpressionNode):
class MapEntry(BinaryOperator): def __init__(self, pos):
super().__init__(pos)
self.children = [NoneNode(), NoneNode()]
@property @property
def key(self): def key(self):
@@ -18,33 +25,39 @@ class MapEntry(BinaryOperator):
@property @property
def value(self): def value(self):
return self[2] return self[1]
@value.setter @value.setter
def value(self, value): def value(self, value):
self[2] = value self[1] = value
class MapNode(AccessNode):
class Map(Node): @classmethod
pass def _literalParser(cls):
return abstractIterableParser(MapNode, TokenType.OPEN_CURLY, TokenType.CLOSE_CURLY, cls._entryParser())
@classmethod
def _entryParser(cls):
def createNode(key, arrow, value):
node = MapEntry(key.pos)
node.key = key
node.value = value
return node
def MapParser(input): return Parser.allOf(
from smnp.ast.node.expression import ExpressionParser cls._keyParser(),
keyParser = LiteralParser Parser.terminalParser(TokenType.ARROW),
valueParser = ExpressionParser ExpressionNode.parse,
createNode=createNode
mapEntryParser = Parser.allOf( )
keyParser,
Parser.terminal(TokenType.ARROW, createNode=Operator.withValue, doAssert=True),
Parser.doAssert(valueParser, "expression"),
createNode=MapEntry.withValues
)
return abstractIterableParser(
Map,
TokenType.OPEN_CURLY,
TokenType.CLOSE_CURLY,
mapEntryParser
)(input)
@classmethod
def _keyParser(cls):
return Parser.oneOf(
IntegerLiteralNode._literalParser(),
StringLiteralNode._literalParser(),
NoteLiteralNode._literalParser(),
BoolLiteralNode._literalParser(),
TypeNode.parse
)

View File

@@ -28,11 +28,26 @@ class Node:
def pop(self, index): def pop(self, index):
return self.children.pop(index) return self.children.pop(index)
@classmethod
def _parse(cls, input):
pass
@classmethod
def parse(cls, input):
result = cls._parse(input)
if result is None:
return ParseResult.FAIL()
if not isinstance(result, ParseResult):
raise RuntimeError(f"_parse() method of '{cls.__name__}' class haven't returned ParseResult object")
return result
def print(self): def print(self):
self._print(first=True) self._print(first=True)
def _print(self, prefix="", last=True, first=False): def _print(self, prefix="", last=True, first=False):
print(prefix, '' if first else '└─' if last else '├─', self.__class__.__name__, f" (line {self.pos[0]+1}, col {self.pos[1]+1})", sep="") print(prefix, '' if first else '└─' if last else '├─', self.__class__.__name__, sep="")
prefix += ' ' if last else '' prefix += ' ' if last else ''
for i, child in enumerate(self.children): for i, child in enumerate(self.children):
last = i == len(self.children) - 1 last = i == len(self.children) - 1

18
smnp/ast/node/note.py Normal file
View File

@@ -0,0 +1,18 @@
from smnp.ast.node.literal import LiteralNode
from smnp.token.type import TokenType
class NoteLiteralNode(LiteralNode):
def __init__(self, pos):
super().__init__(pos)
#TODO del self.children[1]
# TODO: To Remove
@classmethod
def _parse(cls, input):
return cls.literalParser()(input)
@classmethod
def _getTokenType(cls):
return TokenType.NOTE

View File

@@ -1,75 +1,7 @@
from smnp.ast.node.model import Node from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode
class UnaryOperator(Node): class OperatorNode(Node):
def __init__(self, pos):
super().__init__(pos)
self.children=[NoneNode(), NoneNode()]
@property
def operator(self):
return self[0]
@operator.setter
def operator(self, value):
self[0] = value
@property
def value(self):
return self[1]
@value.setter
def value(self, value):
self[1] = value
@classmethod
def withValues(cls, operator, value):
node = cls(operator.pos)
node.operator = operator
node.value = value
return node
class BinaryOperator(Node):
def __init__(self, pos):
super().__init__(pos)
self.children = [NoneNode(), NoneNode(), NoneNode()]
@property
def left(self):
return self[0]
@left.setter
def left(self, value):
self[0] = value
@property
def operator(self):
return self[1]
@operator.setter
def operator(self, value):
self[1] = value
@property
def right(self):
return self[2]
@right.setter
def right(self, value):
self[2] = value
@classmethod
def withValues(cls, left, operator, right):
node = cls(operator.pos)
node.left = left
node.operator = operator
node.right = right
return node
class Operator(Node):
def __init__(self, pos): def __init__(self, pos):
super().__init__(pos) super().__init__(pos)
self.children = [None] self.children = [None]
@@ -81,9 +13,3 @@ class Operator(Node):
@value.setter @value.setter
def value(self, value): def value(self, value):
self[0] = value self[0] = value
@classmethod
def withValue(cls, value, pos):
node = cls(pos)
node.value = value
return node

View File

@@ -1,8 +1,9 @@
from smnp.ast.node.extend import ExtendParser from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.function import FunctionDefinitionParser from smnp.ast.node.extend import ExtendNode
from smnp.ast.node.imports import ImportParser from smnp.ast.node.function import FunctionDefinitionNode
from smnp.ast.node.imports import ImportNode
from smnp.ast.node.model import Node, ParseResult from smnp.ast.node.model import Node, ParseResult
from smnp.ast.node.statement import StatementParser from smnp.ast.node.statement import StatementNode
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.error.syntax import SyntaxException from smnp.error.syntax import SyntaxException
@@ -11,26 +12,21 @@ class Program(Node):
def __init__(self): def __init__(self):
super().__init__((-1, -1)) super().__init__((-1, -1))
def ProgramParser(input): @classmethod
def parse(input): def _parse(cls, input):
def parseToken(input):
return Parser.oneOf(
FunctionDefinitionNode.parse,
ExtendNode.parse,
ExpressionNode.parse,
ImportNode.parse,
StatementNode.parse,
exception = SyntaxException(f"Invalid statement: {input.currentToEndOfLine()}", input.current().pos)
)(input)
root = Program() root = Program()
# Start Symbol
startSymbolParser = Parser.oneOf(
ImportParser,
FunctionDefinitionParser,
ExtendParser,
StatementParser,
exception=lambda inp: SyntaxException(f"Invalid statement: {inp.currentToEndOfLine()}", inp.current().pos),
name="start symbol"
)
while input.hasCurrent(): while input.hasCurrent():
result = startSymbolParser(input) result = parseToken(input)
if result.result: if result.result:
root.append(result.node) root.append(result.node)
return ParseResult.OK(root) return ParseResult.OK(root)
return Parser(parse, name="program")(input)

68
smnp/ast/node/relation.py Normal file
View File

@@ -0,0 +1,68 @@
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.none import NoneNode
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class RelationOperatorNode(ExpressionNode):
def __init__(self, pos):
super().__init__(pos)
self.children = [ NoneNode(), NoneNode(), NoneNode()]
@property
def left(self):
return self[0]
@left.setter
def left(self, value):
self[0] = value
@property
def operator(self):
return self[1]
@operator.setter
def operator(self, value):
self[1] = value
@property
def right(self):
return self[2]
@right.setter
def right(self, value):
self[2] = value
@classmethod
def relationParser(cls):
def createNode(left, operator, right):
node = RelationOperatorNode(right.pos)
node.left = left
node.operator = operator
node.right = right
return node
return Parser.leftAssociativeOperatorParser(
cls._relationLhs(),
TokenType.EQUAL,
cls._relationRhs(),
createNode=createNode
)
@classmethod
def _relationLhs(cls):
raise RuntimeError(f"_relationLhs() is not implemented in {cls.__name__} class")
@staticmethod
def _relationRhs():
from smnp.ast.node.bool import BoolLiteralNode
from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.string import StringLiteralNode
return Parser.doAssert(Parser.oneOf(
BoolLiteralNode.accessParser(),
BoolLiteralNode.literalParser(),
IdentifierNode.parse,
StringLiteralNode.accessParser(),
StringLiteralNode.literalParser()
), "expression")

View File

@@ -1,17 +1,32 @@
from smnp.ast.node.expression import ExpressionParser from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.valuable import Valuable from smnp.ast.node.none import NoneNode
from smnp.ast.node.statement import StatementNode
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class Return(Valuable): class ReturnNode(StatementNode):
pass def __init__(self, pos):
super().__init__(pos)
self.children.append(NoneNode())
@property
def value(self):
return self[0]
def ReturnParser(input): @value.setter
return Parser.allOf( def value(self, value):
Parser.terminal(TokenType.RETURN), self[0] = value
Parser.optional(ExpressionParser),
createNode=lambda ret, val: Return.withValue(val, ret.pos), @classmethod
name="return" def _parse(cls, input):
)(input) def createNode(ret, value):
node = ReturnNode(ret.pos)
node.value = value
return node
return Parser.allOf(
Parser.terminalParser(TokenType.RETURN),
Parser.doAssert(ExpressionNode.parse, "expression"),
createNode=createNode
)(input)

View File

@@ -2,20 +2,16 @@ from smnp.ast.node.model import Node
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
class Statement(Node): class StatementNode(Node):
pass
@classmethod
def _parse(cls, input):
from smnp.ast.node.block import BlockNode
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.ret import ReturnNode
def StatementParser(input): return Parser.oneOf(
from smnp.ast.node.block import BlockParser ExpressionNode.parse,
from smnp.ast.node.condition import IfElseStatementParser BlockNode.parse,
from smnp.ast.node.expression import ExpressionParser ReturnNode.parse,
)(input)
from smnp.ast.node.ret import ReturnParser
return Parser.oneOf(
IfElseStatementParser,
ExpressionParser,
BlockParser,
ReturnParser,
name="statement"
)(input)

31
smnp/ast/node/string.py Normal file
View File

@@ -0,0 +1,31 @@
from smnp.ast.node.access import AccessNode
from smnp.ast.node.literal import LiteralNode
from smnp.ast.node.relation import RelationOperatorNode
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class StringLiteralNode(AccessNode, RelationOperatorNode, LiteralNode):
def __init__(self, pos):
super().__init__(pos)
del self.children[1]
@classmethod
def _parse(cls, input):
return Parser.oneOf(
cls.accessParser(),
cls.relationParser(),
cls.literalParser()
)(input)
@classmethod
def _accessLhs(cls):
return cls.literalParser()
@classmethod
def _relationLhs(cls):
return cls.literalParser()
@classmethod
def _getTokenType(cls):
return TokenType.STRING

View File

@@ -1,17 +0,0 @@
from smnp.ast.node.factor import FactorParser
from smnp.ast.node.operator import BinaryOperator
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class Product(BinaryOperator):
pass
def TermParser(input):
return Parser.leftAssociativeOperatorParser(
FactorParser,
[TokenType.ASTERISK, TokenType.SLASH],
FactorParser,
lambda left, op, right: Product.withValues(left, op, right)
)(input)

View File

@@ -1,15 +1,32 @@
from smnp.ast.node.atom import TypeLiteralParser from smnp.ast.node.access import AccessNode
from smnp.ast.node.iterable import abstractIterableParser from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.model import Node from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
from smnp.type.model import Type
class Type(Node): class TypeSpecifier(Node):
@classmethod
def _parse(cls, input):
return abstractIterableParser(TypeSpecifier, TokenType.OPEN_ANGLE, TokenType.CLOSE_ANGLE,
Parser.doAssert(cls._specifierItem(), "type"))(input)
@classmethod
def _specifierItem(cls):
return Parser.oneOf(
TypeNode.parse,
cls.parse
)
class TypeSpecifiers(Node):
pass
class TypeNode(AccessNode):
def __init__(self, pos): def __init__(self, pos):
super().__init__(pos) super().__init__(pos)
self.children = [NoneNode(), NoneNode()]
@property @property
def type(self): def type(self):
@@ -28,40 +45,19 @@ class Type(Node):
self[1] = value self[1] = value
@classmethod @classmethod
def withValues(cls, pos, type, specifiers=NoneNode()): def _parse(cls, input):
node = cls(pos) def createNode(type, specifiers):
node.type = type node = TypeNode(type.pos)
node.specifiers = specifiers node.type = Type[type.value.upper()]
return node node.specifiers = specifiers
return node
return Parser.allOf(
cls._rawTypeParser(),
Parser.many(TypeSpecifier.parse, lambda specifiers, pos: TypeSpecifiers.withChildren(specifiers, pos)),
createNode=createNode
)(input)
class TypesList(Node): @classmethod
pass def _rawTypeParser(cls):
return Parser.terminalParser(TokenType.TYPE, lambda val, pos: TypeNode.withValue(val, pos))
def TypesListParser(input):
return abstractIterableParser(
TypesList,
TokenType.OPEN_ANGLE,
TokenType.CLOSE_ANGLE,
TypeParser
)(input)
class TypeSpecifiers(Node):
pass
def TypeParser(input):
typeWithSpecifier = Parser.allOf(
TypeLiteralParser,
Parser.many(TypesListParser, createNode=TypeSpecifiers.withChildren),
createNode=lambda type, specifiers: Type.withValues(type.pos, type, specifiers),
name="type with specifiers?"
)
return Parser.oneOf(
typeWithSpecifier,
TypesListParser,
name="mult. types or type with specifier"
)(input)

View File

@@ -1,36 +0,0 @@
from smnp.ast.node.atom import AtomParser
from smnp.ast.node.operator import BinaryOperator, UnaryOperator, Operator
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class MinusOperator(UnaryOperator):
pass
class Access(BinaryOperator):
pass
def UnitParser(input):
minusOperator = Parser.allOf(
Parser.terminal(TokenType.MINUS, createNode=Operator.withValue),
Parser.doAssert(AtomParser, "atom"),
createNode=MinusOperator.withValues,
name="minus"
)
atom2 = Parser.oneOf(
minusOperator,
AtomParser,
name="atom2"
)
return Parser.leftAssociativeOperatorParser(
atom2,
[TokenType.DOT],
Parser.doAssert(atom2, "atom"),
createNode=lambda left, op, right: Access.withValues(left, op, right),
name="unit"
)(input)

View File

@@ -1,22 +0,0 @@
from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode
class Valuable(Node):
def __init__(self, pos):
super().__init__(pos)
self.children = [NoneNode()]
@property
def value(self):
return self[0]
@value.setter
def value(self, value):
self[0] = value
@classmethod
def withValue(cls, value, pos=None):
node = cls(value.pos if pos is None else pos)
node.value = value
return node

View File

@@ -1,47 +1,20 @@
from smnp.ast.node.ignore import IgnoredNode from smnp.ast.node.ignore import IgnoredNode
from smnp.ast.node.model import ParseResult, Node from smnp.ast.node.model import ParseResult, Node
from smnp.ast.node.none import NoneNode from smnp.ast.node.none import NoneNode
from smnp.ast.node.operator import OperatorNode
from smnp.error.syntax import SyntaxException from smnp.error.syntax import SyntaxException
def parse(input): def parse(input):
from smnp.ast.node.program import ProgramParser from smnp.ast.node.program import Program
return ProgramParser(input).node return Program.parse(input).node
class Parser: class Parser:
def __init__(self, parse, name=None, parsers=None):
if parsers is None:
parsers = []
self.parsers = parsers
self._parse = parse
if name is None:
name = parse.__name__
self.name = name
def parse(self, input):
result = self._parse(input)
if result is None:
return ParseResult.FAIL()
if not isinstance(result, ParseResult):
raise RuntimeError(f"_parse() method of '{self.__class__.__name__}' class haven't returned ParseResult object")
return result
def __call__(self, input):
return self.parse(input)
def __str__(self):
return self.name
def __repr__(self):
return self.__str__()
# a -> A # a -> A
@staticmethod @staticmethod
def terminal(expectedType, createNode=None, doAssert=False): def terminalParser(expectedType, createNode=None, doAssert=False):
def provideNode(value, pos): def provideNode(value, pos):
if createNode is None: if createNode is None:
return IgnoredNode(pos) return IgnoredNode(pos)
@@ -58,11 +31,11 @@ class Parser:
return ParseResult.FAIL() return ParseResult.FAIL()
return Parser(parse, name=expectedType.name.lower()) return parse
# oneOf -> a | b | c | ... # oneOf -> a | b | c | ...
@staticmethod @staticmethod
def oneOf(*parsers, assertExpected=None, exception=None, name="or"): def oneOf(*parsers, exception=None):
def combinedParser(input): def combinedParser(input):
snap = input.snapshot() snap = input.snapshot()
for parser in parsers: for parser in parsers:
@@ -71,24 +44,21 @@ class Parser:
return value return value
input.reset(snap) input.reset(snap)
if assertExpected is not None:
found = f", found '{input.current().rawValue}'" if input.hasCurrent() else ""
raise SyntaxException(f"Expected {assertExpected}{found}", input.currentPos())
if exception is not None: if exception is not None:
if callable(exception): if callable(exception):
raise exception(input) raise exception(input)
else: else:
raise exception raise exception
input.reset(snap) input.reset(snap)
return ParseResult.FAIL() return ParseResult.FAIL()
return Parser(combinedParser, name=name, parsers=parsers) return combinedParser
# allOf -> a b c ... # allOf -> a b c ...
@staticmethod @staticmethod
def allOf(*parsers, createNode, exception=None, name="all"): def allOf(*parsers, createNode, exception=None):
if len(parsers) == 0: if len(parsers) == 0:
raise RuntimeError("Pass one parser at least") raise RuntimeError("Pass one parser at least")
@@ -119,36 +89,33 @@ class Parser:
return ParseResult.OK(node) return ParseResult.OK(node)
return Parser(extendedParser, name=name, parsers=parsers)
return extendedParser
# leftAssociative -> left | left OP right # leftAssociative -> left | left OP right
@staticmethod @staticmethod
def leftAssociativeOperatorParser(leftParser, operatorTokenTypes, rightParser, createNode, name="leftAssoc"): def leftAssociativeOperatorParser(leftParser, operatorTokenType, rightParser, createNode):
from smnp.ast.node.operator import Operator
def parse(input): def parse(input):
operatorParser = Parser.oneOfTerminals(*operatorTokenTypes, createNode=lambda val, pos: Operator.withChildren([val], pos))
left = leftParser(input) left = leftParser(input)
oneAtLeast = False
if left.result: if left.result:
operator = operatorParser(input) operator = Parser.terminalParser(operatorTokenType, lambda val, pos: OperatorNode.withChildren([val], pos))(input)
while operator.result: while operator.result:
oneAtLeast = True
right = rightParser(input) right = rightParser(input)
left = ParseResult.OK(createNode(left.node, operator.node, right.node)) left = ParseResult.OK(createNode(left.node, operator.node, right.node))
operator = operatorParser(input) operator = Parser.terminalParser(operatorTokenType, lambda val, pos: OperatorNode.withChildren([val], pos))(input)
return left if oneAtLeast:
return left
return ParseResult.FAIL() return ParseResult.FAIL()
return Parser(parse, name=name, parsers=[leftParser, '|'.join([t.value for t in operatorTokenTypes]), rightParser]) return parse
@staticmethod
def oneOfTerminals(*tokenTypes, createNode=None):
return Parser.oneOf(*[Parser.terminal(expectedType, createNode=createNode) for expectedType in tokenTypes], name='|'.join([t.value for t in tokenTypes]))
# loop -> start item* end # loop -> start item* end
@staticmethod @staticmethod
def loop(startParser, itemParser, endParser, createNode, name="loop"): def loop(startParser, itemParser, endParser, createNode):
def parse(input): def parse(input):
items = [] items = []
start = startParser(input) start = startParser(input)
@@ -164,10 +131,10 @@ class Parser:
return ParseResult.FAIL() return ParseResult.FAIL()
return Parser(parse, name, parsers=[startParser, itemParser, endParser]) return parse
@staticmethod @staticmethod
def doAssert(parser, expected, name="!!"): def doAssert(parser, expected):
def parse(input): def parse(input):
result = parser(input) result = parser(input)
@@ -178,10 +145,10 @@ class Parser:
return result return result
return Parser(parse, name, parsers=parser) return parse
@staticmethod @staticmethod
def optional(parser, name="??"): def optional(parser):
def parse(input): def parse(input):
result = parser(input) result = parser(input)
if result.result: if result.result:
@@ -189,14 +156,10 @@ class Parser:
return ParseResult.OK(NoneNode()) return ParseResult.OK(NoneNode())
return Parser(parse, name, parsers=[parser]) return parse
@staticmethod @staticmethod
def epsilon(): def many(parser, createNode):
return lambda *args: ParseResult.OK(NoneNode())
@staticmethod
def many(parser, createNode, name="*"):
def parse(input): def parse(input):
results = [] results = []
snap = input.snapshot() snap = input.snapshot()
@@ -210,15 +173,4 @@ class Parser:
input.reset(snap) input.reset(snap)
return ParseResult.OK(createNode(results, pos) if len(results) > 0 else NoneNode()) return ParseResult.OK(createNode(results, pos) if len(results) > 0 else NoneNode())
return Parser(parse, name, parsers=[parser])
@staticmethod
def wrap(parser, createNode):
def parse(input):
result = parser(input)
if result.result:
return ParseResult.OK(createNode(result.node))
return result
return parse return parse

View File

@@ -10,9 +10,6 @@ class Sound:
def play(self): def play(self):
sd.play(self.data, self.fs, blocking=True) sd.play(self.data, self.fs, blocking=True)
def __eq__(self, other):
return self.file == other.file and self.data == other.data
def __str__(self): def __str__(self):
return f"sound[{self.file}]" return f"sound[{self.file}]"

View File

@@ -1,66 +0,0 @@
from smnp.ast.node.model import Node
from smnp.ast.parser import Parser
from smnp.token.tokenizer import tokenize
from smnp.token.type import TokenType
class Atom(Node):
def __init__(self, value, pos):
super().__init__(pos)
self.children = [value]
@property
def value(self):
return self[0]
class Operation(Node):
def __init__(self, left, op, right, pos):
super().__init__(pos)
self.children = [left, op, right]
@property
def left(self):
return self[0]
@property
def operator(self):
return self[1]
@property
def right(self):
return self[2]
def atom():
return Parser.oneOfTerminals(TokenType.INTEGER, TokenType.NOTE, TokenType.STRING, createNode=lambda val, pos: Atom(val, pos))
def chain():
return Parser.leftAssociativeOperatorParser(atom(), [TokenType.DOT], atom(), lambda left, op, right: Operation(left, op, right, op.pos), name="chain")
def factor():
return Parser.leftAssociativeOperatorParser(chain(), [TokenType.DOUBLE_ASTERISK], chain(), lambda left, op, right: Operation(left, op, right, op.pos), name="factor")
def term():
return Parser.leftAssociativeOperatorParser(factor(), [TokenType.ASTERISK, TokenType.SLASH], factor(), lambda left, op, right: Operation(left, op, right, op.pos), name="term")
def expr():
return Parser.leftAssociativeOperatorParser(term(), [TokenType.PLUS, TokenType.MINUS], term(), lambda left, op, right: Operation(left, op, right, op.pos), name="expr")
#
def evaluate(node):
if type(node) == Atom:
return node.value
lhs = evaluate(node.left)
rhs = evaluate(node.right)
return {
"+": int(lhs) + int(rhs),
"*": int(lhs) * int(rhs),
"-": int(lhs) - int(rhs),
"/": int(lhs) / int(rhs),
"**": int(lhs) ** int(rhs)
}[node.operator.value]
def draft():
tokens = tokenize(['"fesf fe" + "fsefsef" + "fsefs"'])
e = expr()
node = e(tokens).node
node.print()

View File

@@ -5,5 +5,7 @@ from smnp.program.interpreter import Interpreter
def loadStandardLibrary(): def loadStandardLibrary():
mainSource = resource_string('smnp.library.code', 'main.mus').decode("utf-8") mainSource = resource_string('smnp.library.code', 'main.mus').decode("utf-8")
boolSource = resource_string('smnp.library.code', 'bool.mus').decode("utf-8")
env = Interpreter.interpretString(mainSource) env = Interpreter.interpretString(mainSource)
return env return Interpreter.interpretString(boolSource, baseEnvironment=env)

View File

@@ -1,17 +1,13 @@
import sys import sys
from smnp.error.base import SmnpException from smnp.error.base import SmnpException
from smnp.library.loader import loadStandardLibrary
from smnp.program.interpreter import Interpreter from smnp.program.interpreter import Interpreter
def main(): def main():
try: try:
stdLibraryEnv = loadStandardLibrary() #stdLibraryEnv = loadStandardLibrary()
Interpreter.interpretFile(sys.argv[1], printTokens=False, printAst=False, execute=True, baseEnvironment=stdLibraryEnv) Interpreter.interpretFile(sys.argv[1], printTokens=False, printAst=True, baseEnvironment=None)
#draft()
#tokens = tokenize(['function a(b...) { x+y}'])
#FunctionDefinitionParser(tokens).node.print()
except SmnpException as e: except SmnpException as e:
print(e.message()) print(e.message())

View File

@@ -1,4 +1,4 @@
from smnp.module.iterable.function import combine, map, range, get from smnp.module.iterable.function import combine, flat, map, range, get
functions = [ combine.function, map.function, range.function ] functions = [ combine.function, flat.function, map.function, range.function ]
methods = [ get.function ] methods = [ get.function ]

View File

@@ -0,0 +1,23 @@
from smnp.function.model import Function
from smnp.function.signature import varargSignature
from smnp.type.model import Type
from smnp.type.signature.matcher.type import allTypes
_signature = varargSignature(allTypes())
def _function(env, vararg):
return Type.list(doFlat(vararg, [])).decompose()
def doFlat(input, output=None):
if output is None:
output = []
for item in input:
if item.type == Type.LIST:
doFlat(item.value, output)
else:
output.append(item)
return output
function = Function(_signature, _function, 'flat')

View File

@@ -6,17 +6,17 @@ from smnp.type.signature.matcher.type import ofType
_signature1 = signature(ofType(Type.INTEGER)) _signature1 = signature(ofType(Type.INTEGER))
def _function1(env, upper): def _function1(env, upper):
return Type.list([ Type.integer(i) for i in range(upper.value + 1)]) return Type.list(list(range(upper.value + 1)))
_signature2 = signature(ofType(Type.INTEGER), ofType(Type.INTEGER)) _signature2 = signature(ofType(Type.INTEGER), ofType(Type.INTEGER))
def _function2(env, lower, upper): def _function2(env, lower, upper):
return Type.list([ Type.integer(i) for i in range(lower.value, upper.value + 1)]) return Type.list(list(range(lower.value, upper.value + 1)))
_signature3 = signature(ofType(Type.INTEGER), ofType(Type.INTEGER), ofType(Type.INTEGER)) _signature3 = signature(ofType(Type.INTEGER), ofType(Type.INTEGER), ofType(Type.INTEGER))
def _function3(env, lower, upper, step): def _function3(env, lower, upper, step):
return Type.list([ Type.integer(i) for i in range(lower.value, upper.value + 1, step.value)]) return Type.list(list(range(lower.value, upper.value + 1, step.value)))
_signature4 = signature(ofType(Type.NOTE), ofType(Type.NOTE)) _signature4 = signature(ofType(Type.NOTE), ofType(Type.NOTE))

View File

@@ -9,15 +9,15 @@ from smnp.token.tokenizer import tokenize
class Interpreter: class Interpreter:
@staticmethod @staticmethod
def interpretString(string, printTokens=False, printAst=False, execute=True, baseEnvironment=None): def interpretString(string, printTokens=False, printAst=False, baseEnvironment=None):
return Interpreter._interpret(string.splitlines(), printTokens, printAst, execute, baseEnvironment) return Interpreter._interpret(string.splitlines(), printTokens, printAst, baseEnvironment)
@staticmethod @staticmethod
def interpretFile(file, printTokens=False, printAst=False, execute=True, baseEnvironment=None): def interpretFile(file, printTokens=False, printAst=False, baseEnvironment=None):
return Interpreter._interpret(readLines(file), printTokens, printAst, execute, baseEnvironment) return Interpreter._interpret(readLines(file), printTokens, printAst, baseEnvironment)
@staticmethod @staticmethod
def _interpret(lines, printTokens=False, printAst=False, execute=True, baseEnvironment=None): def _interpret(lines, printTokens=False, printAst=False, baseEnvironment=None):
environment = createEnvironment() environment = createEnvironment()
if baseEnvironment is not None: if baseEnvironment is not None:
environment.extend(baseEnvironment) environment.extend(baseEnvironment)
@@ -31,8 +31,7 @@ class Interpreter:
if printAst: if printAst:
ast.print() ast.print()
if execute: evaluate(ast, environment)
evaluate(ast, environment)
return environment return environment
except RuntimeException as e: except RuntimeException as e:

View File

@@ -1,10 +1,4 @@
from smnp.ast.node.block import Block
from smnp.ast.node.condition import IfElse
from smnp.ast.node.extend import Extend
from smnp.ast.node.function import FunctionDefinition
from smnp.ast.node.imports import Import
from smnp.ast.node.program import Program from smnp.ast.node.program import Program
from smnp.ast.node.ret import Return
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.type.model import Type from smnp.type.model import Type
@@ -69,22 +63,10 @@ class EvaluationResult():
def evaluate(node, environment): def evaluate(node, environment):
from smnp.runtime.evaluators.program import ProgramEvaluator from smnp.runtime.evaluators.program import ProgramEvaluator
from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.condition import IfElseStatementEvaluator
from smnp.runtime.evaluators.block import BlockEvaluator
from smnp.runtime.evaluators.imports import ImportEvaluator
from smnp.runtime.evaluators.function import FunctionDefinitionEvaluator
from smnp.runtime.evaluators.function import ReturnEvaluator
from smnp.runtime.evaluators.extend import ExtendEvaluator
result = Evaluator.oneOf( result = Evaluator.oneOf(
Evaluator.forNodes(ProgramEvaluator.evaluate, Program), Evaluator.forNodes(ProgramEvaluator.evaluate, Program),
Evaluator.forNodes(IfElseStatementEvaluator.evaluate, IfElse),
Evaluator.forNodes(BlockEvaluator.evaluate, Block),
Evaluator.forNodes(ImportEvaluator.evaluate, Import),
Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinition),
Evaluator.forNodes(ReturnEvaluator.evaluate, Return),
Evaluator.forNodes(ExtendEvaluator.evaluate, Extend),
#Evaluator.forNodes(ImportEvaluator.evaluate, ImportNode), #Evaluator.forNodes(ImportEvaluator.evaluate, ImportNode),
#Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinitionNode), #Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinitionNode),
#Evaluator.forNodes(ExtendEvaluator.evaluate, ExtendNode), #Evaluator.forNodes(ExtendEvaluator.evaluate, ExtendNode),

View File

@@ -1,4 +1,5 @@
from smnp.ast.node.identifier import Identifier, FunctionCall from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.invocation import FunctionCallNode
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.expression import expressionEvaluator
@@ -13,15 +14,15 @@ class AccessEvaluator(Evaluator):
left = expressionEvaluator(doAssert=True)(node.left, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult left = expressionEvaluator(doAssert=True)(node.left, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
right = node.right right = node.right
if type(right) == Identifier: if type(right) == IdentifierNode:
try: try:
return left.properties[right.value] return left.properties[right.value]
except KeyError: except KeyError:
raise RuntimeException(f"Unknown property '{right.value}' of type '{left.type.name.lower()}'", right.pos) raise RuntimeException(f"Unknown property '{right.value}' of type '{left.type.name.lower()}'", right.pos)
if type(right) == FunctionCall: if type(right) == FunctionCallNode:
try: try:
arguments = abstractIterableEvaluator(expressionEvaluator(doAssert=True))(right.arguments, environment) arguments = abstractIterableEvaluator(expressionEvaluator(True))(right.arguments, environment)
return environment.invokeMethod(left, right.name.value, arguments) return environment.invokeMethod(left, right.name.value, arguments)
except RuntimeException as e: except RuntimeException as e:
raise updatePos(e, right) raise updatePos(e, right)

View File

@@ -1,3 +1,4 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.expression import expressionEvaluator
@@ -6,8 +7,11 @@ class AssignmentEvaluator(Evaluator):
@classmethod @classmethod
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
target = node.left.value target = node.target.value
value = expressionEvaluator(doAssert=True)(node.right, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult if target.startswith("_"):
raise RuntimeException("Declaration and assignation variables with names starting with '_' is not allowed", node.target.pos)
value = expressionEvaluator(doAssert=True)(node.value, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
scopeOfExistingVariable = environment.findVariableScope(target) scopeOfExistingVariable = environment.findVariableScope(target)
if scopeOfExistingVariable is None: if scopeOfExistingVariable is None:
environment.scopes[-1][target] = value environment.scopes[-1][target] = value

View File

@@ -1,4 +1,4 @@
from smnp.ast.node.identifier import Identifier from smnp.ast.node.identifier import IdentifierNode
from smnp.runtime.evaluator import evaluate, Evaluator, EvaluationResult from smnp.runtime.evaluator import evaluate, Evaluator, EvaluationResult
from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type from smnp.type.model import Type
@@ -40,7 +40,7 @@ class AsteriskEvaluator(Evaluator):
@classmethod @classmethod
def _automaticNamedVariable(cls, iteratorNode, environment, prefix=''): def _automaticNamedVariable(cls, iteratorNode, environment, prefix=''):
if type(iteratorNode) == Identifier: if type(iteratorNode) == IdentifierNode:
return cls._automaticVariableName(environment, prefix, iteratorNode.value, False) return cls._automaticVariableName(environment, prefix, iteratorNode.value, False)
else: else:
return cls._automaticVariableName(environment, prefix, '', True) return cls._automaticVariableName(environment, prefix, '', True)

View File

@@ -1,94 +0,0 @@
from smnp.ast.node.atom import StringLiteral, IntegerLiteral, NoteLiteral, BoolLiteral, TypeLiteral
from smnp.ast.node.identifier import Identifier
from smnp.ast.node.list import List
from smnp.ast.node.map import Map
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
from smnp.runtime.tools.error import updatePos
from smnp.type.model import Type
class IntegerEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Type.integer(node.value)
class StringEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Type.string(node.value)
class NoteEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Type.note(node.value)
class BoolEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Type.bool(node.value)
class TypeEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Type.type(node.value)
class ListEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
list = abstractIterableEvaluator(expressionEvaluator(doAssert=True))(node, environment)
return Type.list(list)
class MapEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
map = {}
exprEvaluator = expressionEvaluator(doAssert=True)
for entry in node.children:
key = exprEvaluator(entry.key, environment).value
if key in map:
raise RuntimeException(f"Duplicated key '{key.stringify()}' found in map", entry.pos)
map[key] = exprEvaluator(entry.value, environment).value
return Type.map(map)
class IdentifierEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
try:
return environment.findVariable(node.value)
except RuntimeException as e:
raise updatePos(e, node)
class AtomEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Evaluator.oneOf(
Evaluator.forNodes(StringEvaluator.evaluate, StringLiteral),
Evaluator.forNodes(IntegerEvaluator.evaluate, IntegerLiteral),
Evaluator.forNodes(NoteEvaluator.evaluate, NoteLiteral),
Evaluator.forNodes(BoolEvaluator.evaluate, BoolLiteral),
Evaluator.forNodes(TypeEvaluator.evaluate, TypeLiteral),
Evaluator.forNodes(IdentifierEvaluator.evaluate, Identifier),
Evaluator.forNodes(ListEvaluator.evaluate, List),
Evaluator.forNodes(MapEvaluator.evaluate, Map)
)(node, environment).value

View File

@@ -1,35 +0,0 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator, evaluate
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class IfElseEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
condition = expressionEvaluator(doAssert=True)(node.condition, environment).value
if condition.type != Type.BOOL:
raise RuntimeException(f"Only {Type.BOOL.name.lower()} types can be used as conditions in conditional expression", node.condition.pos)
if condition.value:
return expressionEvaluator(doAssert=True)(node.ifNode, environment).value
else:
return expressionEvaluator(doAssert=True)(node.elseNode, environment).value
class IfElseStatementEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
condition = expressionEvaluator(doAssert=True)(node.condition, environment).value
if condition.type != Type.BOOL:
raise RuntimeException(
f"Only {Type.BOOL.name.lower()} types can be used as conditions in conditional expression", node.condition.pos)
if condition.value:
evaluate(node.ifNode, environment)
else:
evaluate(node.elseNode, environment)

View File

@@ -1,9 +1,15 @@
from smnp.ast.node.condition import IfElse from smnp.ast.node.access import AccessNode
from smnp.ast.node.expression import Sum, Relation from smnp.ast.node.assignment import AssignmentNode
from smnp.ast.node.factor import NotOperator, Power, Loop from smnp.ast.node.asterisk import AsteriskNode
from smnp.ast.node.identifier import FunctionCall, Assignment from smnp.ast.node.bool import BoolLiteralNode
from smnp.ast.node.term import Product from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.unit import MinusOperator, Access from smnp.ast.node.integer import IntegerLiteralNode
from smnp.ast.node.invocation import FunctionCallNode
from smnp.ast.node.list import ListNode
from smnp.ast.node.map import MapNode
from smnp.ast.node.note import NoteLiteralNode
from smnp.ast.node.string import StringLiteralNode
from smnp.ast.node.type import TypeNode
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator from smnp.runtime.evaluator import Evaluator
from smnp.type.model import Type from smnp.type.model import Type
@@ -11,38 +17,38 @@ from smnp.type.model import Type
def expressionEvaluator(doAssert=False): def expressionEvaluator(doAssert=False):
def evaluateExpression(node, environment): def evaluateExpression(node, environment):
from smnp.runtime.evaluators.function import FunctionCallEvaluator from smnp.runtime.evaluators.string import StringEvaluator
from smnp.runtime.evaluators.minus import MinusEvaluator from smnp.runtime.evaluators.integer import IntegerEvaluator
from smnp.runtime.evaluators.atom import AtomEvaluator from smnp.runtime.evaluators.note import NoteEvaluator
from smnp.runtime.evaluators.access import AccessEvaluator from smnp.runtime.evaluators.identifier import IdentifierEvaluator
from smnp.runtime.evaluators.negation import NotEvaluator from smnp.runtime.evaluators.list import ListEvaluator
from smnp.runtime.evaluators.power import PowerEvaluator from smnp.runtime.evaluators.function import FunctionCallEvaluator
from smnp.runtime.evaluators.loop import LoopEvaluator
from smnp.runtime.evaluators.assignment import AssignmentEvaluator
from smnp.runtime.evaluators.product import ProductEvaluator
from smnp.runtime.evaluators.sum import SumEvaluator from smnp.runtime.evaluators.access import AccessEvaluator
from smnp.runtime.evaluators.relation import RelationEvaluator from smnp.runtime.evaluators.assignment import AssignmentEvaluator
from smnp.runtime.evaluators.condition import IfElseEvaluator from smnp.runtime.evaluators.asterisk import AsteriskEvaluator
result = Evaluator.oneOf( from smnp.runtime.evaluators.map import MapEvaluator
Evaluator.forNodes(FunctionCallEvaluator.evaluate, FunctionCall), from smnp.runtime.evaluators.type import TypeEvaluator
Evaluator.forNodes(MinusEvaluator.evaluate, MinusOperator), from smnp.runtime.evaluators.bool import BoolEvaluator
Evaluator.forNodes(AccessEvaluator.evaluate, Access), result = Evaluator.oneOf(
Evaluator.forNodes(NotEvaluator.evaluate, NotOperator), Evaluator.forNodes(FunctionCallEvaluator.evaluate, FunctionCallNode),
Evaluator.forNodes(PowerEvaluator.evaluate, Power), Evaluator.forNodes(StringEvaluator.evaluate, StringLiteralNode),
Evaluator.forNodes(LoopEvaluator.evaluate, Loop), Evaluator.forNodes(IntegerEvaluator.evaluate, IntegerLiteralNode),
Evaluator.forNodes(AssignmentEvaluator.evaluate, Assignment), Evaluator.forNodes(NoteEvaluator.evaluate, NoteLiteralNode),
Evaluator.forNodes(ProductEvaluator.evaluate, Product), Evaluator.forNodes(BoolEvaluator.evaluate, BoolLiteralNode),
Evaluator.forNodes(SumEvaluator.evaluate, Sum), Evaluator.forNodes(TypeEvaluator.evaluate, TypeNode),
Evaluator.forNodes(RelationEvaluator.evaluate, Relation), Evaluator.forNodes(IdentifierEvaluator.evaluate, IdentifierNode),
Evaluator.forNodes(IfElseEvaluator.evaluate, IfElse), Evaluator.forNodes(ListEvaluator.evaluate, ListNode),
AtomEvaluator.evaluate Evaluator.forNodes(AccessEvaluator.evaluate, AccessNode),
)(node, environment) Evaluator.forNodes(AssignmentEvaluator.evaluate, AssignmentNode),
Evaluator.forNodes(AsteriskEvaluator.evaluate, AsteriskNode),
Evaluator.forNodes(MapEvaluator.evaluate, MapNode)
)(node, environment)
if doAssert and result.result and result.value.type == Type.VOID: if doAssert and result.result and result.value.type == Type.VOID:
raise RuntimeException(f"Expected expression", node.pos) raise RuntimeException(f"Expected expression", node.pos)
return result
return result
return evaluateExpression return evaluateExpression

View File

@@ -17,11 +17,11 @@ class ExtendEvaluator(Evaluator):
@classmethod @classmethod
def _typeToMethodSignature(cls, node): def _typeToMethodSignature(cls, node):
if type(node.specifiers) == NoneNode: if type(node.specifiers) == NoneNode:
return signature(ofType(node.type.value)) return signature(ofType(node.type))
elif node.type.value == Type.LIST: elif node.type == Type.LIST:
return signature(listSpecifier(node.specifiers[0])) return signature(listSpecifier(node.specifiers[0]))
elif node.type.value == Type.MAP: elif node.type == Type.MAP:
return signature(mapSpecifier(node.specifiers[0], node.specifiers[1])) return signature(mapSpecifier(node.specifiers[0], node.specifiers[1]))
@classmethod @classmethod

View File

@@ -1,3 +1,4 @@
from smnp.ast.node.none import NoneNode
from smnp.program.interpreter import Interpreter from smnp.program.interpreter import Interpreter
from smnp.runtime.evaluator import Evaluator from smnp.runtime.evaluator import Evaluator
@@ -6,6 +7,13 @@ class ImportEvaluator(Evaluator):
@classmethod @classmethod
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
if type(node.type) == NoneNode:
cls._evaluateCodeImport(node, environment)
else:
raise RuntimeError("Importing types is not implemented yet")
@classmethod
def _evaluateCodeImport(cls, node, environment):
source = node.source source = node.source
newEnvironment = Interpreter.interpretFile(source.value, baseEnvironment=environment) newEnvironment = Interpreter.interpretFile(source.value)
environment.extend(newEnvironment) environment.extend(newEnvironment)

View File

@@ -1,104 +0,0 @@
from smnp.ast.node.none import NoneNode
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator, evaluate
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class LoopEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
iterator = expressionEvaluator(doAssert=True)(node.left, environment).value
parameters = [ identifier.value for identifier in node.parameters ] if type(node.parameters) != NoneNode() else []
try:
environment.scopes.append({})
output = {
Type.INTEGER: cls.numberEvaluator,
Type.BOOL: cls.boolEvaluator,
Type.LIST: cls.listEvaluator,
Type.MAP: cls.mapEvaluator
}[iterator.type](node, environment, iterator, parameters)
environment.scopes.pop(-1)
except KeyError:
raise RuntimeException(f"The {iterator.type.name.lower()} type cannot stand as an iterator for loop statement", node.left.pos)
return Type.list(output)
@classmethod
def numberEvaluator(cls, node, environment, evaluatedIterator, parameters):
output = []
if len(parameters) > 1:
raise RuntimeException(f"Loop with numeric iterator can handle only one parameter", node.parameters.pos)
for i in range(evaluatedIterator.value):
if len(parameters) > 0:
environment.scopes[-1][parameters[0]] = Type.integer(i)
output.append(evaluate(node.right, environment).value)
return output
@classmethod
def boolEvaluator(cls, node, environment, evaluatedIterator, parameters):
output = []
if len(parameters) > 0:
raise RuntimeException(f"Loop with logic iterator can't' handle any parameters", node.parameters.pos)
condition = evaluatedIterator
while condition.value:
output.append(evaluate(node.right, environment).value)
condition = expressionEvaluator(doAssert=True)(node.left, environment).value
return output
@classmethod
def listEvaluator(cls, node, environment, evaluatedIterator, parameters):
output = []
if len(parameters) > 2:
raise RuntimeException(f"Loop with list iterator can handle only two parameters", node.parameters.pos)
for i, value in enumerate(evaluatedIterator.value):
if len(parameters) == 1:
environment.scopes[-1][parameters[0]] = value
if len(parameters) == 2:
environment.scopes[-1][parameters[0]] = Type.integer(i)
environment.scopes[-1][parameters[1]] = value
output.append(evaluate(node.right, environment).value)
return output
@classmethod
def mapEvaluator(cls, node, environment, evaluatedIterator, parameters):
output = []
if len(parameters) > 3:
raise RuntimeException(f"Loop with map iterator can handle only three parameters", node.parameters.pos)
i = 0
for key, value in evaluatedIterator.value.items():
if len(parameters) == 1:
environment.scopes[-1][parameters[0]] = value
if len(parameters) == 2:
environment.scopes[-1][parameters[0]] = key
environment.scopes[-1][parameters[1]] = value
if len(parameters) == 3:
environment.scopes[-1][parameters[0]] = Type.integer(i)
environment.scopes[-1][parameters[1]] = key
environment.scopes[-1][parameters[2]] = value
i += 1
output.append(evaluate(node.right, environment).value)
return output

View File

@@ -1,31 +0,0 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator, evaluate
from smnp.type.model import Type
class MinusEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
value = evaluate(node.value, environment).value
try:
return {
Type.INTEGER: cls.evaluateForInteger,
Type.STRING: cls.evaluateForString,
Type.LIST: cls.evaluateForList
}[value.type](value.value)
except KeyError:
raise RuntimeException(f"Type {value.type.name.lower()} does not support '{node.operator.value}' operator", node.pos)
@classmethod
def evaluateForInteger(cls, value):
return Type.integer(-value)
@classmethod
def evaluateForString(cls, value):
return Type.string(value[::-1])
@classmethod
def evaluateForList(cls, value):
return Type.list(value[::-1])

View File

@@ -1,16 +0,0 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class NotEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
value = expressionEvaluator(doAssert=True)(node.value, environment).value
if value.type != Type.BOOL:
raise RuntimeException(f"Operator '{node.operator.value}' is supported only by {Type.BOOL.name.lower()} type", node.value.pos)
return Type.bool(not value.value)

View File

@@ -1,20 +0,0 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class PowerEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, environment).value
if left.type != Type.INTEGER:
raise RuntimeException( f"Operator '{node.operator.value}' is supported only by {Type.INTEGER.name.lower()} type", node.left.pos)
if right.type != Type.INTEGER:
raise RuntimeException( f"Operator '{node.operator.value}' is supported only by {Type.INTEGER.name.lower()} type", node.right.pos)
return Type.integer(int(left.value ** right.value))

View File

@@ -1,31 +0,0 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class ProductEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, environment).value
if left.type != Type.INTEGER:
raise RuntimeException(
f"Operator '{node.operator.value}' is supported only by {Type.INTEGER.name.lower()} type", node.left.pos)
if right.type != Type.INTEGER:
raise RuntimeException(
f"Operator '{node.operator.value}' is supported only by {Type.INTEGER.name.lower()} type", node.right.pos)
if node.operator.value == "*":
return Type.integer(int(left.value * right.value))
if node.operator.value == "/":
if right.value == 0:
raise RuntimeException("Attempt to divide by 0", node.right.pos)
return Type.integer(int(left.value / right.value))
raise RuntimeError("This line should never be reached")

View File

@@ -1,46 +0,0 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class RelationEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, environment).value
if node.operator.value == "==":
return cls.equalOperatorEvaluator(left, node.operator, right)
if node.operator.value == "!=":
return cls.notEqualOperatorEvaluator(left, node.operator, right)
return cls.otherRelationOperatorsEvaluator(left, node.operator, right)
@classmethod
def equalOperatorEvaluator(cls, left, operator, right):
return Type.bool(left.value == right.value)
@classmethod
def notEqualOperatorEvaluator(cls, left, operator, right):
return Type.bool(left.value != right.value)
@classmethod
def otherRelationOperatorsEvaluator(cls, left, operator, right):
if left.type == right.type == Type.INTEGER:
if operator.value == ">":
return Type.bool(left.value > right.value)
if operator.value == ">=":
return Type.bool(left.value >= right.value)
if operator.value == "<":
return Type.bool(left.value < right.value)
if operator.value == "<=":
return Type.bool(left.value < right.value)
raise RuntimeException(f"Operator {operator.value} is not supported by {left.type.name.lower()} and {right.type.name.lower()} types", operator.pos)

View File

@@ -1,66 +0,0 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class SumEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, environment).value
if left.type == right.type == Type.INTEGER:
return cls.integerEvaluator(left, node.operator, right)
if left.type == right.type == Type.STRING:
return cls.stringEvaluator(left, node.operator, right)
if left.type == right.type == Type.LIST:
return cls.listEvaluator(left, node.operator, right)
if left.type == right.type == Type.MAP:
return cls.mapEvaluator(left, node.operator, right)
raise RuntimeException(f"Operator {node.operator.value} is not supported by {left.type.name.lower()} and {right.type.name.lower()} types", node.operator.pos)
@classmethod
def integerEvaluator(cls, left, operator, right):
if operator.value == "+":
return Type.integer(left.value + right.value)
if operator.value == "-":
return Type.integer(left.value - right.value)
raise RuntimeError("This line should never be reached")
@classmethod
def stringEvaluator(cls, left, operator, right):
if operator.value == "+":
return Type.string(left.value + right.value)
if operator.value == "-":
raise RuntimeException(f"Operator {operator.value} is not supported by string types", operator.pos)
raise RuntimeError("This line should never be reached")
@classmethod
def listEvaluator(cls, left, operator, right):
if operator.value == "+":
return Type.list(left.value + right.value)
if operator.value == "-":
raise RuntimeException(f"Operator {operator.value} is not supported by list types", operator.pos)
raise RuntimeError("This line should never be reached")
@classmethod
def mapEvaluator(cls, left, operator, right):
if operator.value == "+":
return Type.map({**left.value, **right.value})
if operator.value == "-":
raise RuntimeException(f"Operator {operator.value} is not supported by map types", operator.pos)
raise RuntimeError("This line should never be reached")

View File

@@ -1,34 +0,0 @@
from smnp.ast.node.identifier import Identifier, FunctionCall
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
from smnp.runtime.tools.error import updatePos
class UnitEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return
class AccessEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
right = node.right
if type(right) == Identifier:
try:
return left.properties[right.value]
except KeyError:
raise RuntimeException(f"Unknown property '{right.value}' of type '{left.type.name.lower()}'", right.pos)
if type(right) == FunctionCall:
try:
arguments = abstractIterableEvaluator(expressionEvaluator(doAssert=True))(right.arguments, environment)
return environment.invokeMethod(left, right.name.value, arguments)
except RuntimeException as e:
raise updatePos(e, right)

View File

@@ -1,6 +1,5 @@
from smnp.ast.node import type as ast
from smnp.ast.node.none import NoneNode from smnp.ast.node.none import NoneNode
from smnp.ast.node.type import TypesList from smnp.ast.node.type import TypeNode, TypeSpecifier
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.function.signature import varargSignature, signature from smnp.function.signature import varargSignature, signature
from smnp.runtime.tools.error import updatePos from smnp.runtime.tools.error import updatePos
@@ -17,9 +16,9 @@ def argumentsNodeToMethodSignature(node):
argumentsCount = len(node.children) argumentsCount = len(node.children)
for i, child in enumerate(node.children): for i, child in enumerate(node.children):
matchers = { matchers = {
ast.Type: (lambda c: c.type, typeMatcher), TypeNode: (lambda c: c.type, typeMatcher),
NoneNode: (lambda c: c.type, lambda c: allTypes()), NoneNode: (lambda c: c.type, lambda c: allTypes()),
TypesList: (lambda c: c, multipleTypeMatcher) TypeSpecifier: (lambda c: c, multipleTypeMatcher)
} }
evaluatedMatcher = matchers[type(child.type)][1](matchers[type(child.type)][0](child)) evaluatedMatcher = matchers[type(child.type)][1](matchers[type(child.type)][0](child))
if child.vararg: if child.vararg:
@@ -50,10 +49,10 @@ def multipleTypeMatcher(typeNode):
def typeMatcher(typeNode): def typeMatcher(typeNode):
if type(typeNode.specifiers) == NoneNode: if type(typeNode.specifiers) == NoneNode:
return ofType(typeNode.type.value) return ofType(typeNode.type)
elif typeNode.type.value == Type.LIST and len(typeNode.specifiers) == 1: elif typeNode.type == Type.LIST and len(typeNode.specifiers) == 1:
return listSpecifier(typeNode.specifiers[0]) return listSpecifier(typeNode.specifiers[0])
elif typeNode.type.value == Type.MAP and len(typeNode.specifiers) == 2: elif typeNode.type == Type.MAP and len(typeNode.specifiers) == 2:
return mapSpecifier(typeNode.specifiers[0], typeNode.specifiers[1]) return mapSpecifier(typeNode.specifiers[0], typeNode.specifiers[1])
raise RuntimeException("Unknown type", typeNode.pos) # Todo: Improve pointing position raise RuntimeException("Unknown type", typeNode.pos) # Todo: Improve pointing position

View File

@@ -5,18 +5,13 @@ from smnp.token.tokenizers.comment import commentTokenizer
from smnp.token.tokenizers.identifier import identifierTokenizer from smnp.token.tokenizers.identifier import identifierTokenizer
from smnp.token.tokenizers.keyword import typeTokenizer from smnp.token.tokenizers.keyword import typeTokenizer
from smnp.token.tokenizers.note import noteTokenizer from smnp.token.tokenizers.note import noteTokenizer
from smnp.token.tokenizers.relation import relationOperatorTokenizer
from smnp.token.tokenizers.string import stringTokenizer from smnp.token.tokenizers.string import stringTokenizer
from smnp.token.tokenizers.whitespace import whitespacesTokenizer from smnp.token.tokenizers.whitespace import whitespacesTokenizer
from smnp.token.tools import defaultTokenizer, separated, regexPatternTokenizer, mapValue from smnp.token.tools import defaultTokenizer, separated, regexPatternTokenizer
from smnp.token.type import TokenType from smnp.token.type import TokenType
tokenizers = ( tokenizers = (
defaultTokenizer(TokenType.ARROW), defaultTokenizer(TokenType.EQUAL),
# Double-character operators
relationOperatorTokenizer,
defaultTokenizer(TokenType.DOUBLE_ASTERISK),
# Characters # Characters
defaultTokenizer(TokenType.OPEN_CURLY), defaultTokenizer(TokenType.OPEN_CURLY),
@@ -29,17 +24,15 @@ tokenizers = (
defaultTokenizer(TokenType.CLOSE_ANGLE), defaultTokenizer(TokenType.CLOSE_ANGLE),
defaultTokenizer(TokenType.ASTERISK), defaultTokenizer(TokenType.ASTERISK),
defaultTokenizer(TokenType.ASSIGN), defaultTokenizer(TokenType.ASSIGN),
defaultTokenizer(TokenType.ARROW),
defaultTokenizer(TokenType.COMMA), defaultTokenizer(TokenType.COMMA),
defaultTokenizer(TokenType.SLASH),
defaultTokenizer(TokenType.MINUS), defaultTokenizer(TokenType.MINUS),
defaultTokenizer(TokenType.PLUS),
defaultTokenizer(TokenType.DASH),
defaultTokenizer(TokenType.DOTS), defaultTokenizer(TokenType.DOTS),
defaultTokenizer(TokenType.AMP), defaultTokenizer(TokenType.AMP),
defaultTokenizer(TokenType.DOT), defaultTokenizer(TokenType.DOT),
# Types # Types
mapValue(separated(regexPatternTokenizer(TokenType.INTEGER, r'\d')), int), separated(regexPatternTokenizer(TokenType.INTEGER, r'\d')),
stringTokenizer, stringTokenizer,
noteTokenizer, noteTokenizer,
boolTokenizer, boolTokenizer,
@@ -51,13 +44,7 @@ tokenizers = (
separated(defaultTokenizer(TokenType.EXTEND)), separated(defaultTokenizer(TokenType.EXTEND)),
separated(defaultTokenizer(TokenType.IMPORT)), separated(defaultTokenizer(TokenType.IMPORT)),
separated(defaultTokenizer(TokenType.FROM)), separated(defaultTokenizer(TokenType.FROM)),
separated(defaultTokenizer(TokenType.WITH)),
separated(defaultTokenizer(TokenType.ELSE)),
separated(defaultTokenizer(TokenType.AND)),
separated(defaultTokenizer(TokenType.NOT)),
separated(defaultTokenizer(TokenType.AS)), separated(defaultTokenizer(TokenType.AS)),
separated(defaultTokenizer(TokenType.IF)),
separated(defaultTokenizer(TokenType.OR)),
# Identifier (couldn't be before keywords!) # Identifier (couldn't be before keywords!)
identifierTokenizer, identifierTokenizer,

View File

@@ -3,7 +3,7 @@ from smnp.token.type import TokenType
from smnp.type.model import Type from smnp.type.model import Type
typeTokenizer = separated(keywordsTokenizer(TokenType.TYPE, *[type.name.lower() for type in Type], mapKeyword=lambda value: Type[value.upper()])) typeTokenizer = separated(keywordsTokenizer(TokenType.TYPE, *[type.name.lower() for type in Type]))

View File

@@ -14,7 +14,7 @@ def noteTokenizer(input, current, line):
rawValue = '' rawValue = ''
if input[current] == '@': if input[current] == '@':
rawValue += input[current+consumedChars] rawValue += input[current+consumedChars]
consumedChars += 1 # TODO: Check if next item does even exist consumedChars += 1
if input[current+consumedChars] in ('C', 'c', 'D', 'd', 'E', 'e', 'F', 'f', 'G', 'g', 'A', 'a', 'H', 'h', 'B', 'b'): if input[current+consumedChars] in ('C', 'c', 'D', 'd', 'E', 'e', 'F', 'f', 'G', 'g', 'A', 'a', 'H', 'h', 'B', 'b'):
rawValue += input[current + consumedChars] rawValue += input[current + consumedChars]
notePitch = input[current+consumedChars] notePitch = input[current+consumedChars]

View File

@@ -1,6 +0,0 @@
from smnp.token.tools import keywordsTokenizer
from smnp.token.type import TokenType
def relationOperatorTokenizer(input, current, line):
return keywordsTokenizer(TokenType.RELATION, "==", "!=", ">=", "<=")(input, current, line)

View File

@@ -13,5 +13,5 @@ def stringTokenizer(input, current, line):
char = input[current + consumedChars] char = input[current + consumedChars]
value += char value += char
consumedChars += 1 consumedChars += 1
return (consumedChars, Token(TokenType.STRING, value[1:len(value)-1], (line, current), value)) return (consumedChars, Token(TokenType.STRING, value[1:len(value)-1], (line, current)))
return (0, None) return (0, None)

View File

@@ -16,10 +16,10 @@ def regexPatternTokenizer(type, pattern):
return tokenizer return tokenizer
def keywordsTokenizer(type, *keywords, mapKeyword=lambda x: x): def keywordsTokenizer(type, *keywords):
def tokenizer(input, current, line): def tokenizer(input, current, line):
for keyword in keywords: for keyword in keywords:
result = keywordTokenizer(type, keyword, mapKeyword)(input, current, line) result = keywordTokenizer(type, keyword)(input, current, line)
if result[0] > 0: if result[0] > 0:
return result return result
return (0, None) return (0, None)
@@ -27,10 +27,10 @@ def keywordsTokenizer(type, *keywords, mapKeyword=lambda x: x):
return tokenizer return tokenizer
def keywordTokenizer(type, keyword, mapKeyword=lambda x: x): def keywordTokenizer(type, keyword):
def tokenizer(input, current, line): def tokenizer(input, current, line):
if len(input) >= current+len(keyword) and input[current:current+len(keyword)] == keyword: if len(input) >= current+len(keyword) and input[current:current+len(keyword)] == keyword:
return (len(keyword), Token(type, mapKeyword(keyword), (line, current))) return (len(keyword), Token(type, keyword, (line, current)))
return (0, None) return (0, None)
return tokenizer return tokenizer
@@ -50,14 +50,3 @@ def separated(tokenizer, end=r"\W"):
return (0, None) return (0, None)
return separated return separated
def mapValue(tokenizer, mapper):
def tokenize(input, current, line):
consumedChars, token = tokenizer(input, current, line)
if consumedChars > 0:
return (consumedChars, Token(token.type, mapper(token.value), token.pos))
return (0, None)
return tokenize

View File

@@ -2,8 +2,7 @@ from enum import Enum
class TokenType(Enum): class TokenType(Enum):
RELATION = '==, !=, >=, <=' EQUAL = '=='
DOUBLE_ASTERISK = '**'
OPEN_CURLY = '{' OPEN_CURLY = '{'
CLOSE_CURLY = '}' CLOSE_CURLY = '}'
OPEN_PAREN = '(' OPEN_PAREN = '('
@@ -16,16 +15,10 @@ class TokenType(Enum):
ASSIGN = '=' ASSIGN = '='
ARROW = '->' ARROW = '->'
COMMA = ',' COMMA = ','
SLASH = '/'
MINUS = '-' MINUS = '-'
PLUS = '+'
DASH = '^'
DOTS = '...' DOTS = '...'
AMP = '&' AMP = '&'
DOT = '.' DOT = '.'
AND = 'and'
OR = 'or'
NOT = 'not'
INTEGER = 'integer' INTEGER = 'integer'
STRING = 'string' STRING = 'string'
NOTE = 'note' NOTE = 'note'
@@ -36,9 +29,6 @@ class TokenType(Enum):
EXTEND = 'extend' EXTEND = 'extend'
IMPORT = 'import' IMPORT = 'import'
FROM = 'from' FROM = 'from'
WITH = 'with'
ELSE = 'else'
IF = 'if'
AS = 'as' AS = 'as'
IDENTIFIER = 'identifier' IDENTIFIER = 'identifier'
COMMENT = 'comment' COMMENT = 'comment'

View File

@@ -73,7 +73,6 @@ class Type(Enum):
def void(): def void():
return Value(Type.VOID, None) return Value(Type.VOID, None)
def _failStringify(t): def _failStringify(t):
raise RuntimeException(f"Not able to interpret {t.name}'", None) raise RuntimeException(f"Not able to interpret {t.name}'", None)