Merge branch 'left-associative-ops'
This commit is contained in:
6
grammar
6
grammar
@@ -9,7 +9,8 @@ PITCH_MODIFIER = 'b' | '#'
|
||||
<string> ::= '"' CHAR* '"'
|
||||
<note> ::= '@' PITCH PITCH_MODIFIER? DIGIT? [':' DIGIT+ 'd'?]?
|
||||
<identifier> ::= ID [ID|DIGIT]*
|
||||
<type> ::= 'integer' | 'string' | 'note' | 'list' | 'void'
|
||||
<bool> ::= 'true' | 'false'
|
||||
<type> ::= 'integer' | 'string' | 'note' | 'list' | 'map' | 'sound' | 'bool' | 'void'
|
||||
|
||||
# Parser
|
||||
<expr> ::= <integer> <access> | <integer>
|
||||
@@ -18,9 +19,10 @@ PITCH_MODIFIER = 'b' | '#'
|
||||
<expr> ::= <identifier> <access> | <identifier> '=' <expr> | <functionCall> | <identifier>
|
||||
<expr> ::= <list> <access> | <list>
|
||||
<expr> ::= <map> <access> | <map>
|
||||
<expr> ::= <bool> <access> | <bool>
|
||||
<expr> ::= <expr> '.' <identifier> | <expr> '.' <functionCall>
|
||||
<expr> ::= <asteriskExpr> '*' <expr>
|
||||
<asteriskExpr> ::= <integer> | <string> | <note> | <identifier> | <list>
|
||||
<asteriskExpr> ::= <integer> | <string> | <note> | <identifier> | <list> | <map>
|
||||
|
||||
<functionCall> ::= <identifier> <arglist>
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
from smnp.ast.node.expression import ExpressionNode
|
||||
from smnp.ast.node.ignore import IgnoredNode
|
||||
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.append(IgnoredNode(pos))
|
||||
|
||||
@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 _parse(cls, input):
|
||||
def createNode(left, right):
|
||||
node = AccessNode(right.pos)
|
||||
node.left = left
|
||||
node.right = right
|
||||
return node
|
||||
|
||||
return Parser.leftAssociativeOperatorParser(
|
||||
cls._literalParser(),
|
||||
TokenType.DOT,
|
||||
cls._parseAccessingProperty(),
|
||||
createNode=createNode
|
||||
)(input)
|
||||
|
||||
@classmethod
|
||||
def _literalParser(cls):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _parseAccessingProperty():
|
||||
from smnp.ast.node.identifier import IdentifierNode
|
||||
|
||||
return Parser.oneOf(
|
||||
IdentifierNode._literalParser(),
|
||||
IdentifierNode._functionCallParser(),
|
||||
exception=lambda input: SyntaxException(f"Expected property name or method call, found '{input.current().rawValue}'", input.currentPos())
|
||||
)
|
||||
@@ -1,28 +0,0 @@
|
||||
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")
|
||||
@@ -1,28 +0,0 @@
|
||||
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")
|
||||
90
smnp/ast/node/atom.py
Normal file
90
smnp/ast/node/atom.py
Normal file
@@ -0,0 +1,90 @@
|
||||
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)
|
||||
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
from smnp.ast.node.statement import StatementNode
|
||||
from smnp.ast.node.model import Node
|
||||
from smnp.ast.node.statement import StatementParser
|
||||
from smnp.ast.parser import Parser
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
|
||||
class BlockNode(StatementNode):
|
||||
class Block(Node):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def _parse(cls, input):
|
||||
def createNode(start, items, end):
|
||||
node = BlockNode(start.pos)
|
||||
node.children = items
|
||||
return node
|
||||
|
||||
return Parser.loop(
|
||||
Parser.terminalParser(TokenType.OPEN_CURLY),
|
||||
Parser.doAssert(StatementNode.parse, f"statement or '{TokenType.CLOSE_CURLY.key}'"),
|
||||
Parser.terminalParser(TokenType.CLOSE_CURLY),
|
||||
createNode=createNode,
|
||||
)(input)
|
||||
def BlockParser(input):
|
||||
return Parser.loop(
|
||||
Parser.terminal(TokenType.OPEN_CURLY),
|
||||
Parser.doAssert(StatementParser, f"statement or '{TokenType.CLOSE_CURLY.key}'"),
|
||||
Parser.terminal(TokenType.CLOSE_CURLY),
|
||||
createNode=lambda open, statements, close: Block.withChildren(statements, open.pos)
|
||||
)(input)
|
||||
|
||||
74
smnp/ast/node/condition.py
Normal file
74
smnp/ast/node/condition.py
Normal file
@@ -0,0 +1,74 @@
|
||||
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)
|
||||
@@ -1,69 +1,51 @@
|
||||
from smnp.ast.node.asterisk import AsteriskNode
|
||||
from smnp.ast.node.model import Node
|
||||
from smnp.ast.node.none import NoneNode
|
||||
from smnp.ast.node.statement import StatementNode
|
||||
from smnp.ast.node.operator import BinaryOperator
|
||||
from smnp.ast.node.term import TermParser
|
||||
from smnp.ast.parser import Parser
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
|
||||
class ExpressionNode(Node):
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos, [NoneNode()])
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self[0]
|
||||
class Sum(BinaryOperator):
|
||||
pass
|
||||
|
||||
|
||||
@value.setter
|
||||
def value(self, v):
|
||||
self[0] = v
|
||||
class Relation(BinaryOperator):
|
||||
pass
|
||||
|
||||
|
||||
@classmethod
|
||||
def withValue(cls, val, pos):
|
||||
node = cls(pos)
|
||||
node.value = val
|
||||
return node
|
||||
class And(BinaryOperator):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def _parse(cls, input):
|
||||
return Parser.oneOf(
|
||||
cls._asteriskParser(),
|
||||
cls._expressionParser(),
|
||||
)(input)
|
||||
|
||||
@classmethod
|
||||
def _asteriskParser(cls):
|
||||
def createNode(iterator, asterisk, statement):
|
||||
node = AsteriskNode(asterisk.pos)
|
||||
node.iterator = iterator
|
||||
node.statement = statement
|
||||
return node
|
||||
class Or(BinaryOperator):
|
||||
pass
|
||||
|
||||
return Parser.allOf(
|
||||
cls._expressionParser(),
|
||||
Parser.terminalParser(TokenType.ASTERISK),
|
||||
Parser.doAssert(StatementNode.parse, 'statement'),
|
||||
createNode=createNode
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _expressionParser(cls):
|
||||
from smnp.ast.node.integer import IntegerLiteralNode
|
||||
from smnp.ast.node.string import StringLiteralNode
|
||||
from smnp.ast.node.note import NoteLiteralNode
|
||||
from smnp.ast.node.identifier import IdentifierNode
|
||||
from smnp.ast.node.list import ListNode
|
||||
from smnp.ast.node.map import MapNode
|
||||
from smnp.ast.node.type import TypeNode
|
||||
def ExpressionParser(input):
|
||||
expr1 = Parser.leftAssociativeOperatorParser(
|
||||
TermParser,
|
||||
[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)
|
||||
|
||||
return Parser.oneOf(
|
||||
IntegerLiteralNode.parse,
|
||||
StringLiteralNode.parse,
|
||||
NoteLiteralNode.parse,
|
||||
IdentifierNode.parse,
|
||||
MapNode.parse,
|
||||
ListNode.parse,
|
||||
TypeNode.parse,
|
||||
)
|
||||
@@ -1,14 +1,14 @@
|
||||
from smnp.ast.node.block import BlockNode
|
||||
from smnp.ast.node.function import FunctionDefinitionNode
|
||||
from smnp.ast.node.identifier import IdentifierNode
|
||||
from smnp.ast.node.block import Block
|
||||
from smnp.ast.node.function import FunctionDefinitionParser
|
||||
from smnp.ast.node.identifier import IdentifierLiteralParser
|
||||
from smnp.ast.node.model import Node
|
||||
from smnp.ast.node.none import NoneNode
|
||||
from smnp.ast.node.statement import StatementNode
|
||||
from smnp.ast.node.type import TypeNode
|
||||
from smnp.ast.node.type import TypeParser
|
||||
from smnp.ast.parser import Parser
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
|
||||
class ExtendNode(StatementNode):
|
||||
class Extend(Node):
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
self.children = [NoneNode(), NoneNode(), NoneNode()]
|
||||
@@ -38,33 +38,51 @@ class ExtendNode(StatementNode):
|
||||
self[2] = value
|
||||
|
||||
@classmethod
|
||||
def _parse(cls, input):
|
||||
def createNode(extend, type, asKeyword, variable, methods):
|
||||
node = ExtendNode(extend.pos)
|
||||
node.type = type
|
||||
node.variable = variable
|
||||
node.methods = methods
|
||||
return node
|
||||
def withValues(cls, pos, type, variable, methods):
|
||||
node = cls(pos)
|
||||
node.type = type
|
||||
node.variable = variable
|
||||
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)
|
||||
|
||||
@classmethod
|
||||
def _methodsDeclarationsParser(cls):
|
||||
def createNode(openBracket, items, closeBracket):
|
||||
node = BlockNode(openBracket.pos)
|
||||
node.children = items
|
||||
return node
|
||||
def ExtendParser(input):
|
||||
|
||||
simpleExtend = Parser.allOf(
|
||||
Parser.terminal(TokenType.EXTEND),
|
||||
TypeParser,
|
||||
Parser.terminal(TokenType.AS),
|
||||
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
|
||||
)
|
||||
102
smnp/ast/node/factor.py
Normal file
102
smnp/ast/node/factor.py
Normal file
@@ -0,0 +1,102 @@
|
||||
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)
|
||||
@@ -1,83 +1,58 @@
|
||||
from smnp.ast.node.block import BlockNode
|
||||
from smnp.ast.node.expression import ExpressionNode
|
||||
from smnp.ast.node.identifier import IdentifierNode
|
||||
from smnp.ast.node.block import BlockParser
|
||||
from smnp.ast.node.identifier import IdentifierLiteralParser
|
||||
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.statement import StatementNode
|
||||
from smnp.ast.node.type import TypeNode, TypeSpecifier
|
||||
from smnp.ast.node.type import TypeParser, Type
|
||||
from smnp.ast.parser import Parser
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
|
||||
class ArgumentsDeclarationNode(Node):
|
||||
|
||||
@classmethod
|
||||
def _parse(cls, input):
|
||||
raise RuntimeError("This class is not supposed to be automatically called")
|
||||
|
||||
|
||||
class VarargNode(Node):
|
||||
class ArgumentsDeclaration(Node):
|
||||
pass
|
||||
|
||||
|
||||
class ArgumentDefinitionNode(ExpressionNode):
|
||||
class Argument(Node):
|
||||
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
self.children.extend([NoneNode(), False])
|
||||
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
|
||||
|
||||
|
||||
@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 VarargNode(Node):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class FunctionDefinitionNode(StatementNode):
|
||||
class FunctionDefinition(Node):
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
self.children = [NoneNode(), NoneNode(), NoneNode()]
|
||||
@@ -107,22 +82,48 @@ class FunctionDefinitionNode(StatementNode):
|
||||
self[2] = value
|
||||
|
||||
@classmethod
|
||||
def _parse(cls, input):
|
||||
def createNode(function, name, arguments, body):
|
||||
node = FunctionDefinitionNode(function.pos)
|
||||
node.name = name
|
||||
node.arguments = arguments
|
||||
node.body = body
|
||||
return node
|
||||
def withValues(cls, name, arguments, body):
|
||||
node = cls(name.pos)
|
||||
node.name = name
|
||||
node.arguments = arguments
|
||||
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)
|
||||
|
||||
@staticmethod
|
||||
def _argumentsDeclarationParser():
|
||||
return abstractIterableParser(ArgumentsDeclarationNode, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, ArgumentDefinitionNode.parser())
|
||||
def ArgumentParser(input):
|
||||
def createNode(type, variable, vararg):
|
||||
pos = type.pos if isinstance(type, Type) else variable.pos
|
||||
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)
|
||||
|
||||
|
||||
@@ -1,53 +1,74 @@
|
||||
from smnp.ast.node.access import AccessNode
|
||||
from smnp.ast.node.assignment import AssignmentNode
|
||||
from smnp.ast.node.expression import ExpressionNode
|
||||
from smnp.ast.node.invocation import FunctionCallNode, ArgumentsListNode
|
||||
from smnp.ast.node.atom import Atom
|
||||
from smnp.ast.node.expression import ExpressionParser
|
||||
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.parser import Parser
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
|
||||
class IdentifierNode(AccessNode):
|
||||
class Identifier(Atom):
|
||||
pass
|
||||
|
||||
|
||||
class FunctionCall(Node):
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
del self.children[1]
|
||||
self.children = [NoneNode(), NoneNode()]
|
||||
|
||||
@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 _literalParser(cls):
|
||||
return Parser.oneOf(
|
||||
IdentifierNode._functionCallParser(),
|
||||
IdentifierNode._assignmentParser(),
|
||||
IdentifierNode.identifierParser()
|
||||
)
|
||||
def withChildren(cls, name, arguments):
|
||||
node = cls(name.pos)
|
||||
node.name = name
|
||||
node.arguments = arguments
|
||||
return node
|
||||
|
||||
@staticmethod
|
||||
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
|
||||
)
|
||||
class ArgumentsList(Node):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
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
|
||||
)
|
||||
class Assignment(BinaryOperator):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def identifierParser():
|
||||
return Parser.terminalParser(TokenType.IDENTIFIER, lambda val, pos: IdentifierNode.withValue(val, pos))
|
||||
|
||||
def IdentifierLiteralParser(input):
|
||||
return Parser.terminal(TokenType.IDENTIFIER, createNode=Identifier.withValue)(input)
|
||||
|
||||
|
||||
def IdentifierParser(input):
|
||||
functionCallParser = Parser.allOf(
|
||||
IdentifierLiteralParser,
|
||||
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)
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
from smnp.ast.node.identifier import IdentifierNode
|
||||
from smnp.ast.node.atom import StringParser
|
||||
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.token.type import TokenType
|
||||
|
||||
|
||||
class ImportNode(Node):
|
||||
class Import(Node):
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
self.children = [NoneNode(), NoneNode(), NoneNode()]
|
||||
self.children = [None]
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
@@ -20,57 +17,17 @@ class ImportNode(Node):
|
||||
def source(self, 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
|
||||
def _parse(cls, input):
|
||||
return Parser.oneOf(
|
||||
cls._literalImportParser(),
|
||||
cls._fileImportParser()
|
||||
)(input)
|
||||
def withValue(cls, value):
|
||||
node = cls(value.pos)
|
||||
node.source = value
|
||||
return node
|
||||
|
||||
@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
|
||||
|
||||
return Parser.allOf(
|
||||
Parser.terminalParser(TokenType.IMPORT),
|
||||
TypeNode.parse,
|
||||
Parser.doAssert(Parser.terminalParser(TokenType.FROM), "'from <source> as <variable name>'"),
|
||||
Parser.doAssert(StringLiteralNode._literalParser(), "source as a string"),
|
||||
Parser.doAssert(Parser.terminalParser(TokenType.AS), "'as <variable name>'"),
|
||||
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
|
||||
)
|
||||
def ImportParser(input):
|
||||
return Parser.allOf(
|
||||
Parser.terminal(TokenType.IMPORT),
|
||||
Parser.doAssert(StringParser, "import source as string"),
|
||||
createNode=lambda imp, source: Import.withValue(source),
|
||||
name="import"
|
||||
)(input)
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
from smnp.ast.node.access import AccessNode
|
||||
from smnp.ast.parser import Parser
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
|
||||
class IntegerLiteralNode(AccessNode):
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
del self.children[1]
|
||||
|
||||
@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))
|
||||
@@ -1,40 +0,0 @@
|
||||
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")
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from smnp.ast.node.expression import ExpressionNode
|
||||
from smnp.ast.node.ignore import IgnoredNode
|
||||
from smnp.ast.node.model import Node, ParseResult
|
||||
from smnp.ast.node.none import NoneNode
|
||||
@@ -7,11 +6,19 @@ from smnp.token.type import TokenType
|
||||
|
||||
|
||||
def abstractIterableParser(iterableNodeType, openTokenType, closeTokenType, itemParser):
|
||||
class AbstractIterableTailNode(ExpressionNode):
|
||||
|
||||
class AbstractIterable(Node):
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
self.children = [NoneNode(), NoneNode()]
|
||||
|
||||
self.children.append(NoneNode())
|
||||
@property
|
||||
def value(self):
|
||||
return self[0]
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
self[0] = value
|
||||
|
||||
@property
|
||||
def next(self):
|
||||
@@ -21,83 +28,67 @@ def abstractIterableParser(iterableNodeType, openTokenType, closeTokenType, item
|
||||
def next(self, value):
|
||||
self[1] = value
|
||||
|
||||
@classmethod
|
||||
def _parse(cls, input):
|
||||
return Parser.oneOf(
|
||||
AbstractIterableTailNode._parser1(),
|
||||
AbstractIterableTailNode._parser2(),
|
||||
)(input)
|
||||
class AbstractIterableTail(AbstractIterable):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _parser1():
|
||||
return Parser.terminalParser(closeTokenType)
|
||||
def abstractIterableParser(input):
|
||||
return Parser.oneOf(
|
||||
emptyIterable,
|
||||
openIterable
|
||||
)(input)
|
||||
|
||||
@staticmethod
|
||||
def _parser2():
|
||||
def createNode(comma, expr, iterableTail):
|
||||
node = AbstractIterableTailNode(expr.pos)
|
||||
node.value = expr
|
||||
node.next = iterableTail
|
||||
return node
|
||||
def emptyIterable(input):
|
||||
def createNode(open, close):
|
||||
node = AbstractIterable(open.pos)
|
||||
node.value = open
|
||||
node.next = close
|
||||
return node
|
||||
|
||||
return Parser.allOf(
|
||||
Parser.terminalParser(TokenType.COMMA, doAssert=True),
|
||||
itemParser,
|
||||
AbstractIterableTailNode.parse,
|
||||
createNode=createNode
|
||||
)
|
||||
return Parser.allOf(
|
||||
Parser.terminal(openTokenType),
|
||||
Parser.terminal(closeTokenType),
|
||||
createNode=createNode
|
||||
)(input)
|
||||
|
||||
class AbstractIterableNode(ExpressionNode):
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
def openIterable(input):
|
||||
def createNode(open, item, tail):
|
||||
node = AbstractIterable(open.pos)
|
||||
node.value = item
|
||||
node.next = tail
|
||||
return node
|
||||
|
||||
self.children.append(NoneNode())
|
||||
return Parser.allOf(
|
||||
Parser.terminal(openTokenType),
|
||||
itemParser,
|
||||
abstractIterableTailParser,
|
||||
createNode=createNode
|
||||
)(input)
|
||||
|
||||
@property
|
||||
def next(self):
|
||||
return self[1]
|
||||
def abstractIterableTailParser(input):
|
||||
return Parser.oneOf(
|
||||
closeIterable,
|
||||
nextItem,
|
||||
)(input)
|
||||
|
||||
@next.setter
|
||||
def next(self, value):
|
||||
self[1] = value
|
||||
def nextItem(input):
|
||||
def createNode(comma, item, tail):
|
||||
node = AbstractIterableTail(item.pos)
|
||||
node.value = item
|
||||
node.next = tail
|
||||
return node
|
||||
|
||||
@classmethod
|
||||
def _parse(cls, input):
|
||||
return Parser.oneOf(
|
||||
AbstractIterableNode._parser1(),
|
||||
AbstractIterableNode._parser2()
|
||||
)(input)
|
||||
return Parser.allOf(
|
||||
Parser.doAssert(Parser.terminal(TokenType.COMMA), f"'{TokenType.COMMA.key}' or '{closeTokenType.key}'"),
|
||||
itemParser,
|
||||
abstractIterableTailParser,
|
||||
createNode=createNode
|
||||
)(input)
|
||||
|
||||
@staticmethod
|
||||
def _parser1():
|
||||
def emptyIterable(openToken, closeToken):
|
||||
node = AbstractIterableNode(openToken.pos)
|
||||
node.value = openToken
|
||||
node.next = closeToken
|
||||
return node
|
||||
def closeIterable(input):
|
||||
return Parser.terminal(closeTokenType)(input)
|
||||
|
||||
return Parser.allOf(
|
||||
Parser.terminalParser(openTokenType),
|
||||
Parser.terminalParser(closeTokenType),
|
||||
createNode=emptyIterable
|
||||
)
|
||||
|
||||
@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)
|
||||
return toFlatDesiredNode(iterableNodeType, abstractIterableParser)
|
||||
|
||||
|
||||
def toFlatDesiredNode(iterableNodeType, parser):
|
||||
@@ -114,7 +105,7 @@ def toFlatDesiredNode(iterableNodeType, parser):
|
||||
|
||||
return ParseResult.FAIL()
|
||||
|
||||
return parse
|
||||
return Parser(parse, "flat", [parser])
|
||||
|
||||
|
||||
def flattenList(node, output=None):
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
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 ListNode(AccessNode):
|
||||
class List(Node):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def _literalParser(cls):
|
||||
return abstractIterableParser(ListNode, TokenType.OPEN_SQUARE, TokenType.CLOSE_SQUARE,
|
||||
Parser.doAssert(ExpressionNode.parse, "expression"))
|
||||
|
||||
def ListParser(input):
|
||||
from smnp.ast.node.expression import ExpressionParser
|
||||
|
||||
return abstractIterableParser(
|
||||
List,
|
||||
TokenType.OPEN_SQUARE,
|
||||
TokenType.CLOSE_SQUARE,
|
||||
Parser.doAssert(ExpressionParser, "expression")
|
||||
)(input)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
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)
|
||||
@@ -1,18 +1,12 @@
|
||||
from smnp.ast.node.access import AccessNode
|
||||
from smnp.ast.node.expression import ExpressionNode
|
||||
from smnp.ast.node.integer import IntegerLiteralNode
|
||||
from smnp.ast.node.atom import LiteralParser
|
||||
from smnp.ast.node.iterable import abstractIterableParser
|
||||
from smnp.ast.node.none import NoneNode
|
||||
from smnp.ast.node.note import NoteLiteralNode
|
||||
from smnp.ast.node.string import StringLiteralNode
|
||||
from smnp.ast.node.type import TypeNode
|
||||
from smnp.ast.node.model import Node
|
||||
from smnp.ast.node.operator import BinaryOperator, Operator
|
||||
from smnp.ast.parser import Parser
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
class MapEntry(ExpressionNode):
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
self.children = [NoneNode(), NoneNode()]
|
||||
|
||||
class MapEntry(BinaryOperator):
|
||||
|
||||
@property
|
||||
def key(self):
|
||||
@@ -24,38 +18,33 @@ class MapEntry(ExpressionNode):
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self[1]
|
||||
return self[2]
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
self[1] = value
|
||||
self[2] = value
|
||||
|
||||
class MapNode(AccessNode):
|
||||
|
||||
@classmethod
|
||||
def _literalParser(cls):
|
||||
return abstractIterableParser(MapNode, TokenType.OPEN_CURLY, TokenType.CLOSE_CURLY, cls._entryParser())
|
||||
class Map(Node):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def _entryParser(cls):
|
||||
def createNode(key, arrow, value):
|
||||
node = MapEntry(key.pos)
|
||||
node.key = key
|
||||
node.value = value
|
||||
return node
|
||||
|
||||
return Parser.allOf(
|
||||
cls._keyParser(),
|
||||
Parser.terminalParser(TokenType.ARROW),
|
||||
ExpressionNode.parse,
|
||||
createNode=createNode
|
||||
)
|
||||
def MapParser(input):
|
||||
from smnp.ast.node.expression import ExpressionParser
|
||||
keyParser = LiteralParser
|
||||
valueParser = ExpressionParser
|
||||
|
||||
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(),
|
||||
TypeNode.parse
|
||||
)
|
||||
@@ -28,26 +28,11 @@ class Node:
|
||||
def pop(self, 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):
|
||||
self._print(first=True)
|
||||
|
||||
def _print(self, prefix="", last=True, first=False):
|
||||
print(prefix, '' if first else '└─' if last else '├─', self.__class__.__name__, sep="")
|
||||
print(prefix, '' if first else '└─' if last else '├─', self.__class__.__name__, f" (line {self.pos[0]+1}, col {self.pos[1]+1})", sep="")
|
||||
prefix += ' ' if last else '│ '
|
||||
for i, child in enumerate(self.children):
|
||||
last = i == len(self.children) - 1
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
from smnp.ast.node.access import AccessNode
|
||||
from smnp.ast.node.literal import LiteralNode
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
|
||||
class NoteLiteralNode(LiteralNode, AccessNode):
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
del self.children[1]
|
||||
|
||||
@classmethod
|
||||
def _getTokenType(cls):
|
||||
return TokenType.NOTE
|
||||
89
smnp/ast/node/operator.py
Normal file
89
smnp/ast/node/operator.py
Normal file
@@ -0,0 +1,89 @@
|
||||
from smnp.ast.node.model import Node
|
||||
from smnp.ast.node.none import NoneNode
|
||||
|
||||
|
||||
class UnaryOperator(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):
|
||||
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
|
||||
@@ -1,9 +1,8 @@
|
||||
from smnp.ast.node.expression import ExpressionNode
|
||||
from smnp.ast.node.extend import ExtendNode
|
||||
from smnp.ast.node.function import FunctionDefinitionNode
|
||||
from smnp.ast.node.imports import ImportNode
|
||||
from smnp.ast.node.extend import ExtendParser
|
||||
from smnp.ast.node.function import FunctionDefinitionParser
|
||||
from smnp.ast.node.imports import ImportParser
|
||||
from smnp.ast.node.model import Node, ParseResult
|
||||
from smnp.ast.node.statement import StatementNode
|
||||
from smnp.ast.node.statement import StatementParser
|
||||
from smnp.ast.parser import Parser
|
||||
from smnp.error.syntax import SyntaxException
|
||||
|
||||
@@ -12,21 +11,26 @@ class Program(Node):
|
||||
def __init__(self):
|
||||
super().__init__((-1, -1))
|
||||
|
||||
@classmethod
|
||||
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)
|
||||
|
||||
def ProgramParser(input):
|
||||
def parse(input):
|
||||
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():
|
||||
result = parseToken(input)
|
||||
result = startSymbolParser(input)
|
||||
|
||||
if result.result:
|
||||
root.append(result.node)
|
||||
return ParseResult.OK(root)
|
||||
|
||||
return ParseResult.OK(root)
|
||||
|
||||
return Parser(parse, name="program")(input)
|
||||
|
||||
@@ -1,32 +1,17 @@
|
||||
from smnp.ast.node.expression import ExpressionNode
|
||||
from smnp.ast.node.none import NoneNode
|
||||
from smnp.ast.node.statement import StatementNode
|
||||
from smnp.ast.node.expression import ExpressionParser
|
||||
from smnp.ast.node.valuable import Valuable
|
||||
from smnp.ast.parser import Parser
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
|
||||
class ReturnNode(StatementNode):
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
self.children.append(NoneNode())
|
||||
class Return(Valuable):
|
||||
pass
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self[0]
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
self[0] = value
|
||||
|
||||
@classmethod
|
||||
def _parse(cls, 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)
|
||||
def ReturnParser(input):
|
||||
return Parser.allOf(
|
||||
Parser.terminal(TokenType.RETURN),
|
||||
Parser.optional(ExpressionParser),
|
||||
createNode=lambda ret, val: Return.withValue(val, ret.pos),
|
||||
name="return"
|
||||
)(input)
|
||||
@@ -2,16 +2,20 @@ from smnp.ast.node.model import Node
|
||||
from smnp.ast.parser import Parser
|
||||
|
||||
|
||||
class StatementNode(Node):
|
||||
class Statement(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
|
||||
|
||||
return Parser.oneOf(
|
||||
ExpressionNode.parse,
|
||||
BlockNode.parse,
|
||||
ReturnNode.parse,
|
||||
)(input)
|
||||
def StatementParser(input):
|
||||
from smnp.ast.node.block import BlockParser
|
||||
from smnp.ast.node.condition import IfElseStatementParser
|
||||
from smnp.ast.node.expression import ExpressionParser
|
||||
|
||||
from smnp.ast.node.ret import ReturnParser
|
||||
return Parser.oneOf(
|
||||
IfElseStatementParser,
|
||||
ExpressionParser,
|
||||
BlockParser,
|
||||
ReturnParser,
|
||||
name="statement"
|
||||
)(input)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
from smnp.ast.node.access import AccessNode
|
||||
from smnp.ast.node.literal import LiteralNode
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
|
||||
class StringLiteralNode(LiteralNode, AccessNode):
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
del self.children[1]
|
||||
|
||||
@classmethod
|
||||
def _getTokenType(cls):
|
||||
return TokenType.STRING
|
||||
17
smnp/ast/node/term.py
Normal file
17
smnp/ast/node/term.py
Normal file
@@ -0,0 +1,17 @@
|
||||
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)
|
||||
@@ -1,32 +1,15 @@
|
||||
from smnp.ast.node.access import AccessNode
|
||||
from smnp.ast.node.atom import TypeLiteralParser
|
||||
from smnp.ast.node.iterable import abstractIterableParser
|
||||
from smnp.ast.node.model import Node
|
||||
from smnp.ast.node.none import NoneNode
|
||||
from smnp.ast.parser import Parser
|
||||
from smnp.token.type import TokenType
|
||||
from smnp.type.model import Type
|
||||
|
||||
|
||||
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):
|
||||
class Type(Node):
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
self.children = [NoneNode(), NoneNode()]
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
@@ -45,19 +28,40 @@ class TypeNode(AccessNode):
|
||||
self[1] = value
|
||||
|
||||
@classmethod
|
||||
def _parse(cls, input):
|
||||
def createNode(type, specifiers):
|
||||
node = TypeNode(type.pos)
|
||||
node.type = Type[type.value.upper()]
|
||||
node.specifiers = specifiers
|
||||
return node
|
||||
def withValues(cls, pos, type, specifiers=NoneNode()):
|
||||
node = cls(pos)
|
||||
node.type = type
|
||||
node.specifiers = specifiers
|
||||
return node
|
||||
|
||||
return Parser.allOf(
|
||||
cls._rawTypeParser(),
|
||||
Parser.many(TypeSpecifier.parse, lambda specifiers, pos: TypeSpecifiers.withChildren(specifiers, pos)),
|
||||
createNode=createNode
|
||||
)(input)
|
||||
|
||||
@classmethod
|
||||
def _rawTypeParser(cls):
|
||||
return Parser.terminalParser(TokenType.TYPE, lambda val, pos: TypeNode.withValue(val, pos))
|
||||
class TypesList(Node):
|
||||
pass
|
||||
|
||||
|
||||
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)
|
||||
|
||||
36
smnp/ast/node/unit.py
Normal file
36
smnp/ast/node/unit.py
Normal file
@@ -0,0 +1,36 @@
|
||||
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)
|
||||
|
||||
22
smnp/ast/node/valuable.py
Normal file
22
smnp/ast/node/valuable.py
Normal file
@@ -0,0 +1,22 @@
|
||||
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
|
||||
@@ -5,15 +5,43 @@ from smnp.error.syntax import SyntaxException
|
||||
|
||||
|
||||
def parse(input):
|
||||
from smnp.ast.node.program import Program
|
||||
return Program.parse(input).node
|
||||
from smnp.ast.node.program import ProgramParser
|
||||
return ProgramParser(input).node
|
||||
|
||||
|
||||
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
|
||||
@staticmethod
|
||||
def terminalParser(expectedType, createNode=None, doAssert=False):
|
||||
def terminal(expectedType, createNode=None, doAssert=False):
|
||||
def provideNode(value, pos):
|
||||
if createNode is None:
|
||||
return IgnoredNode(pos)
|
||||
@@ -30,17 +58,22 @@ class Parser:
|
||||
|
||||
return ParseResult.FAIL()
|
||||
|
||||
return parse
|
||||
return Parser(parse, name=expectedType.name.lower())
|
||||
|
||||
# oneOf -> a | b | c | ...
|
||||
@staticmethod
|
||||
def oneOf(*parsers, exception=None):
|
||||
def oneOf(*parsers, assertExpected=None, exception=None, name="or"):
|
||||
def combinedParser(input):
|
||||
snap = input.snapshot()
|
||||
for parser in parsers:
|
||||
value = parser(input)
|
||||
if value.result:
|
||||
return value
|
||||
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 callable(exception):
|
||||
@@ -48,15 +81,14 @@ class Parser:
|
||||
else:
|
||||
raise exception
|
||||
|
||||
|
||||
input.reset(snap)
|
||||
return ParseResult.FAIL()
|
||||
|
||||
return combinedParser
|
||||
return Parser(combinedParser, name=name, parsers=parsers)
|
||||
|
||||
# allOf -> a b c ...
|
||||
@staticmethod
|
||||
def allOf(*parsers, createNode, exception=None):
|
||||
def allOf(*parsers, createNode, exception=None, name="all"):
|
||||
if len(parsers) == 0:
|
||||
raise RuntimeError("Pass one parser at least")
|
||||
|
||||
@@ -87,29 +119,36 @@ class Parser:
|
||||
return ParseResult.OK(node)
|
||||
|
||||
|
||||
|
||||
return extendedParser
|
||||
return Parser(extendedParser, name=name, parsers=parsers)
|
||||
|
||||
|
||||
# leftAssociative -> left | left OP right
|
||||
@staticmethod
|
||||
def leftAssociativeOperatorParser(leftParser, operatorTokenType, rightParser, createNode):
|
||||
def leftAssociativeOperatorParser(leftParser, operatorTokenTypes, rightParser, createNode, name="leftAssoc"):
|
||||
from smnp.ast.node.operator import Operator
|
||||
|
||||
def parse(input):
|
||||
operatorParser = Parser.oneOfTerminals(*operatorTokenTypes, createNode=lambda val, pos: Operator.withChildren([val], pos))
|
||||
left = leftParser(input)
|
||||
if left.result:
|
||||
while Parser.terminalParser(operatorTokenType)(input).result:
|
||||
operator = operatorParser(input)
|
||||
while operator.result:
|
||||
right = rightParser(input)
|
||||
left = ParseResult.OK(createNode(left.node, right.node))
|
||||
|
||||
left = ParseResult.OK(createNode(left.node, operator.node, right.node))
|
||||
operator = operatorParser(input)
|
||||
return left
|
||||
|
||||
return ParseResult.FAIL()
|
||||
|
||||
return parse
|
||||
return Parser(parse, name=name, parsers=[leftParser, '|'.join([t.value for t in operatorTokenTypes]), rightParser])
|
||||
|
||||
@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
|
||||
@staticmethod
|
||||
def loop(startParser, itemParser, endParser, createNode):
|
||||
def loop(startParser, itemParser, endParser, createNode, name="loop"):
|
||||
def parse(input):
|
||||
items = []
|
||||
start = startParser(input)
|
||||
@@ -125,10 +164,10 @@ class Parser:
|
||||
|
||||
return ParseResult.FAIL()
|
||||
|
||||
return parse
|
||||
return Parser(parse, name, parsers=[startParser, itemParser, endParser])
|
||||
|
||||
@staticmethod
|
||||
def doAssert(parser, expected):
|
||||
def doAssert(parser, expected, name="!!"):
|
||||
def parse(input):
|
||||
result = parser(input)
|
||||
|
||||
@@ -139,10 +178,10 @@ class Parser:
|
||||
|
||||
return result
|
||||
|
||||
return parse
|
||||
return Parser(parse, name, parsers=parser)
|
||||
|
||||
@staticmethod
|
||||
def optional(parser):
|
||||
def optional(parser, name="??"):
|
||||
def parse(input):
|
||||
result = parser(input)
|
||||
if result.result:
|
||||
@@ -150,10 +189,14 @@ class Parser:
|
||||
|
||||
return ParseResult.OK(NoneNode())
|
||||
|
||||
return parse
|
||||
return Parser(parse, name, parsers=[parser])
|
||||
|
||||
@staticmethod
|
||||
def many(parser, createNode):
|
||||
def epsilon():
|
||||
return lambda *args: ParseResult.OK(NoneNode())
|
||||
|
||||
@staticmethod
|
||||
def many(parser, createNode, name="*"):
|
||||
def parse(input):
|
||||
results = []
|
||||
snap = input.snapshot()
|
||||
@@ -167,4 +210,15 @@ class Parser:
|
||||
input.reset(snap)
|
||||
return ParseResult.OK(createNode(results, pos) if len(results) > 0 else NoneNode())
|
||||
|
||||
return parse
|
||||
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
|
||||
@@ -10,6 +10,9 @@ class Sound:
|
||||
def play(self):
|
||||
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):
|
||||
return f"sound[{self.file}]"
|
||||
|
||||
|
||||
66
smnp/calc.py
Normal file
66
smnp/calc.py
Normal file
@@ -0,0 +1,66 @@
|
||||
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()
|
||||
@@ -4,6 +4,6 @@ from smnp.program.interpreter import Interpreter
|
||||
|
||||
|
||||
def loadStandardLibrary():
|
||||
source = resource_string('smnp.library.code', 'main.mus').decode("utf-8")
|
||||
return Interpreter.interpretString(source)
|
||||
|
||||
mainSource = resource_string('smnp.library.code', 'main.mus').decode("utf-8")
|
||||
env = Interpreter.interpretString(mainSource)
|
||||
return env
|
||||
|
||||
@@ -8,7 +8,10 @@ from smnp.program.interpreter import Interpreter
|
||||
def main():
|
||||
try:
|
||||
stdLibraryEnv = loadStandardLibrary()
|
||||
Interpreter.interpretFile(sys.argv[1], printAst=True, baseEnvironment=stdLibraryEnv)
|
||||
Interpreter.interpretFile(sys.argv[1], printTokens=False, printAst=False, execute=True, baseEnvironment=stdLibraryEnv)
|
||||
#draft()
|
||||
#tokens = tokenize(['function a(b...) { x+y}'])
|
||||
#FunctionDefinitionParser(tokens).node.print()
|
||||
|
||||
except SmnpException as e:
|
||||
print(e.message())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from smnp.module.iterable.function import combine, flat, map, range, get
|
||||
from smnp.module.iterable.function import combine, map, range, get
|
||||
|
||||
functions = [ combine.function, flat.function, map.function, range.function ]
|
||||
functions = [ combine.function, map.function, range.function ]
|
||||
methods = [ get.function ]
|
||||
@@ -1,23 +0,0 @@
|
||||
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')
|
||||
@@ -12,7 +12,7 @@ def _function1(env, list, index):
|
||||
raise RuntimeException(f"Attempt to access item which is outside the list", None)
|
||||
|
||||
|
||||
_signature2 = signature(ofType(Type.MAP), ofTypes(Type.INTEGER, Type.STRING, Type.NOTE, Type.TYPE))
|
||||
_signature2 = signature(ofType(Type.MAP), ofTypes(Type.INTEGER, Type.STRING, Type.NOTE, Type.BOOL, Type.TYPE))
|
||||
def _function2(env, map, key):
|
||||
try:
|
||||
return map.value[key]
|
||||
|
||||
@@ -4,7 +4,7 @@ from smnp.type.model import Type
|
||||
from smnp.type.signature.matcher.list import listOfMatchers, listMatches
|
||||
from smnp.type.signature.matcher.type import allTypes, ofTypes
|
||||
|
||||
_signature1 = varargSignature(listMatches(ofTypes(Type.INTEGER, Type.STRING, Type.NOTE, Type.TYPE), allTypes()))
|
||||
_signature1 = varargSignature(listMatches(ofTypes(Type.INTEGER, Type.STRING, Type.NOTE, Type.BOOL, Type.TYPE), allTypes()))
|
||||
def _function1(env, vararg):
|
||||
map = {}
|
||||
for entry in vararg:
|
||||
@@ -14,7 +14,7 @@ def _function1(env, vararg):
|
||||
return Type.map(map)
|
||||
|
||||
|
||||
_signature2 = signature(listOfMatchers(listMatches(ofTypes(Type.INTEGER, Type.STRING, Type.NOTE, Type.TYPE), allTypes())))
|
||||
_signature2 = signature(listOfMatchers(listMatches(ofTypes(Type.INTEGER, Type.STRING, Type.NOTE, Type.BOOL, Type.TYPE), allTypes())))
|
||||
def _function2(env, list):
|
||||
map = {}
|
||||
for entry in list.value:
|
||||
|
||||
@@ -6,17 +6,17 @@ from smnp.type.signature.matcher.type import ofType
|
||||
|
||||
_signature1 = signature(ofType(Type.INTEGER))
|
||||
def _function1(env, upper):
|
||||
return Type.list(list(range(upper.value + 1)))
|
||||
return Type.list([ Type.integer(i) for i in range(upper.value + 1)])
|
||||
|
||||
|
||||
_signature2 = signature(ofType(Type.INTEGER), ofType(Type.INTEGER))
|
||||
def _function2(env, lower, upper):
|
||||
return Type.list(list(range(lower.value, upper.value + 1)))
|
||||
return Type.list([ Type.integer(i) for i in range(lower.value, upper.value + 1)])
|
||||
|
||||
|
||||
_signature3 = signature(ofType(Type.INTEGER), ofType(Type.INTEGER), ofType(Type.INTEGER))
|
||||
def _function3(env, lower, upper, step):
|
||||
return Type.list(list(range(lower.value, upper.value + 1, step.value)))
|
||||
return Type.list([ Type.integer(i) for i in range(lower.value, upper.value + 1, step.value)])
|
||||
|
||||
|
||||
_signature4 = signature(ofType(Type.NOTE), ofType(Type.NOTE))
|
||||
|
||||
@@ -9,15 +9,15 @@ from smnp.token.tokenizer import tokenize
|
||||
class Interpreter:
|
||||
|
||||
@staticmethod
|
||||
def interpretString(string, printTokens=False, printAst=False, baseEnvironment=None):
|
||||
return Interpreter._interpret(string.splitlines(), printTokens, printAst, baseEnvironment)
|
||||
def interpretString(string, printTokens=False, printAst=False, execute=True, baseEnvironment=None):
|
||||
return Interpreter._interpret(string.splitlines(), printTokens, printAst, execute, baseEnvironment)
|
||||
|
||||
@staticmethod
|
||||
def interpretFile(file, printTokens=False, printAst=False, baseEnvironment=None):
|
||||
return Interpreter._interpret(readLines(file), printTokens, printAst, baseEnvironment)
|
||||
def interpretFile(file, printTokens=False, printAst=False, execute=True, baseEnvironment=None):
|
||||
return Interpreter._interpret(readLines(file), printTokens, printAst, execute, baseEnvironment)
|
||||
|
||||
@staticmethod
|
||||
def _interpret(lines, printTokens=False, printAst=False, baseEnvironment=None):
|
||||
def _interpret(lines, printTokens=False, printAst=False, execute=True, baseEnvironment=None):
|
||||
environment = createEnvironment()
|
||||
if baseEnvironment is not None:
|
||||
environment.extend(baseEnvironment)
|
||||
@@ -31,7 +31,8 @@ class Interpreter:
|
||||
if printAst:
|
||||
ast.print()
|
||||
|
||||
evaluate(ast, environment)
|
||||
if execute:
|
||||
evaluate(ast, environment)
|
||||
|
||||
return environment
|
||||
except RuntimeException as e:
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from smnp.ast.node.block import BlockNode
|
||||
from smnp.ast.node.extend import ExtendNode
|
||||
from smnp.ast.node.function import FunctionDefinitionNode
|
||||
from smnp.ast.node.imports import ImportNode
|
||||
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.ret import ReturnNode
|
||||
from smnp.ast.node.ret import Return
|
||||
from smnp.error.runtime import RuntimeException
|
||||
from smnp.type.model import Type
|
||||
|
||||
@@ -68,20 +69,27 @@ class EvaluationResult():
|
||||
|
||||
def evaluate(node, environment):
|
||||
from smnp.runtime.evaluators.program import ProgramEvaluator
|
||||
from smnp.runtime.evaluators.expression import expressionEvaluator
|
||||
|
||||
from smnp.runtime.evaluators.function import FunctionDefinitionEvaluator
|
||||
from smnp.runtime.evaluators.extend import ExtendEvaluator
|
||||
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(
|
||||
Evaluator.forNodes(ProgramEvaluator.evaluate, Program),
|
||||
Evaluator.forNodes(ImportEvaluator.evaluate, ImportNode),
|
||||
Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinitionNode),
|
||||
Evaluator.forNodes(ExtendEvaluator.evaluate, ExtendNode),
|
||||
Evaluator.forNodes(BlockEvaluator.evaluate, BlockNode),
|
||||
Evaluator.forNodes(ReturnEvaluator.evaluate, ReturnNode),
|
||||
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(FunctionDefinitionEvaluator.evaluate, FunctionDefinitionNode),
|
||||
#Evaluator.forNodes(ExtendEvaluator.evaluate, ExtendNode),
|
||||
#Evaluator.forNodes(BlockEvaluator.evaluate, BlockNode),
|
||||
#Evaluator.forNodes(ReturnEvaluator.evaluate, ReturnNode),
|
||||
expressionEvaluator()
|
||||
)(node, environment)
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from smnp.ast.node.identifier import IdentifierNode
|
||||
from smnp.ast.node.invocation import FunctionCallNode
|
||||
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
|
||||
@@ -14,15 +13,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
|
||||
right = node.right
|
||||
|
||||
if type(right) == IdentifierNode:
|
||||
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) == FunctionCallNode:
|
||||
if type(right) == FunctionCall:
|
||||
try:
|
||||
arguments = abstractIterableEvaluator(expressionEvaluator(True))(right.arguments, environment)
|
||||
arguments = abstractIterableEvaluator(expressionEvaluator(doAssert=True))(right.arguments, environment)
|
||||
return environment.invokeMethod(left, right.name.value, arguments)
|
||||
except RuntimeException as e:
|
||||
raise updatePos(e, right)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from smnp.error.runtime import RuntimeException
|
||||
from smnp.runtime.evaluator import Evaluator
|
||||
from smnp.runtime.evaluators.expression import expressionEvaluator
|
||||
|
||||
@@ -7,11 +6,8 @@ class AssignmentEvaluator(Evaluator):
|
||||
|
||||
@classmethod
|
||||
def evaluator(cls, node, environment):
|
||||
target = node.target.value
|
||||
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
|
||||
target = node.left.value
|
||||
value = expressionEvaluator(doAssert=True)(node.right, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
|
||||
scopeOfExistingVariable = environment.findVariableScope(target)
|
||||
if scopeOfExistingVariable is None:
|
||||
environment.scopes[-1][target] = value
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from smnp.ast.node.identifier import IdentifierNode
|
||||
from smnp.ast.node.identifier import Identifier
|
||||
from smnp.runtime.evaluator import evaluate, Evaluator, EvaluationResult
|
||||
from smnp.runtime.evaluators.expression import expressionEvaluator
|
||||
from smnp.type.model import Type
|
||||
@@ -40,7 +40,7 @@ class AsteriskEvaluator(Evaluator):
|
||||
|
||||
@classmethod
|
||||
def _automaticNamedVariable(cls, iteratorNode, environment, prefix=''):
|
||||
if type(iteratorNode) == IdentifierNode:
|
||||
if type(iteratorNode) == Identifier:
|
||||
return cls._automaticVariableName(environment, prefix, iteratorNode.value, False)
|
||||
else:
|
||||
return cls._automaticVariableName(environment, prefix, '', True)
|
||||
|
||||
94
smnp/runtime/evaluators/atom.py
Normal file
94
smnp/runtime/evaluators/atom.py
Normal file
@@ -0,0 +1,94 @@
|
||||
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
|
||||
9
smnp/runtime/evaluators/bool.py
Normal file
9
smnp/runtime/evaluators/bool.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from smnp.runtime.evaluator import Evaluator
|
||||
from smnp.type.model import Type
|
||||
|
||||
|
||||
class BoolEvaluator(Evaluator):
|
||||
|
||||
@classmethod
|
||||
def evaluator(cls, node, environment):
|
||||
return Type.bool(node.value)
|
||||
35
smnp/runtime/evaluators/condition.py
Normal file
35
smnp/runtime/evaluators/condition.py
Normal file
@@ -0,0 +1,35 @@
|
||||
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)
|
||||
@@ -1,14 +1,9 @@
|
||||
from smnp.ast.node.access import AccessNode
|
||||
from smnp.ast.node.assignment import AssignmentNode
|
||||
from smnp.ast.node.asterisk import AsteriskNode
|
||||
from smnp.ast.node.identifier import IdentifierNode
|
||||
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.ast.node.condition import IfElse
|
||||
from smnp.ast.node.expression import Sum, Relation
|
||||
from smnp.ast.node.factor import NotOperator, Power, Loop
|
||||
from smnp.ast.node.identifier import FunctionCall, Assignment
|
||||
from smnp.ast.node.term import Product
|
||||
from smnp.ast.node.unit import MinusOperator, Access
|
||||
from smnp.error.runtime import RuntimeException
|
||||
from smnp.runtime.evaluator import Evaluator
|
||||
from smnp.type.model import Type
|
||||
@@ -16,36 +11,38 @@ from smnp.type.model import Type
|
||||
|
||||
def expressionEvaluator(doAssert=False):
|
||||
def evaluateExpression(node, environment):
|
||||
from smnp.runtime.evaluators.string import StringEvaluator
|
||||
from smnp.runtime.evaluators.integer import IntegerEvaluator
|
||||
from smnp.runtime.evaluators.note import NoteEvaluator
|
||||
from smnp.runtime.evaluators.identifier import IdentifierEvaluator
|
||||
from smnp.runtime.evaluators.list import ListEvaluator
|
||||
from smnp.runtime.evaluators.function import FunctionCallEvaluator
|
||||
from smnp.runtime.evaluators.function import FunctionCallEvaluator
|
||||
from smnp.runtime.evaluators.minus import MinusEvaluator
|
||||
from smnp.runtime.evaluators.atom import AtomEvaluator
|
||||
from smnp.runtime.evaluators.access import AccessEvaluator
|
||||
from smnp.runtime.evaluators.negation import NotEvaluator
|
||||
from smnp.runtime.evaluators.power import PowerEvaluator
|
||||
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.access import AccessEvaluator
|
||||
from smnp.runtime.evaluators.assignment import AssignmentEvaluator
|
||||
from smnp.runtime.evaluators.asterisk import AsteriskEvaluator
|
||||
from smnp.runtime.evaluators.map import MapEvaluator
|
||||
from smnp.runtime.evaluators.type import TypeEvaluator
|
||||
result = Evaluator.oneOf(
|
||||
Evaluator.forNodes(FunctionCallEvaluator.evaluate, FunctionCallNode),
|
||||
Evaluator.forNodes(StringEvaluator.evaluate, StringLiteralNode),
|
||||
Evaluator.forNodes(IntegerEvaluator.evaluate, IntegerLiteralNode),
|
||||
Evaluator.forNodes(NoteEvaluator.evaluate, NoteLiteralNode),
|
||||
Evaluator.forNodes(TypeEvaluator.evaluate, TypeNode),
|
||||
Evaluator.forNodes(IdentifierEvaluator.evaluate, IdentifierNode),
|
||||
Evaluator.forNodes(ListEvaluator.evaluate, ListNode),
|
||||
Evaluator.forNodes(AccessEvaluator.evaluate, AccessNode),
|
||||
Evaluator.forNodes(AssignmentEvaluator.evaluate, AssignmentNode),
|
||||
Evaluator.forNodes(AsteriskEvaluator.evaluate, AsteriskNode),
|
||||
Evaluator.forNodes(MapEvaluator.evaluate, MapNode)
|
||||
)(node, environment)
|
||||
from smnp.runtime.evaluators.sum import SumEvaluator
|
||||
from smnp.runtime.evaluators.relation import RelationEvaluator
|
||||
from smnp.runtime.evaluators.condition import IfElseEvaluator
|
||||
result = Evaluator.oneOf(
|
||||
Evaluator.forNodes(FunctionCallEvaluator.evaluate, FunctionCall),
|
||||
Evaluator.forNodes(MinusEvaluator.evaluate, MinusOperator),
|
||||
Evaluator.forNodes(AccessEvaluator.evaluate, Access),
|
||||
Evaluator.forNodes(NotEvaluator.evaluate, NotOperator),
|
||||
Evaluator.forNodes(PowerEvaluator.evaluate, Power),
|
||||
Evaluator.forNodes(LoopEvaluator.evaluate, Loop),
|
||||
Evaluator.forNodes(AssignmentEvaluator.evaluate, Assignment),
|
||||
Evaluator.forNodes(ProductEvaluator.evaluate, Product),
|
||||
Evaluator.forNodes(SumEvaluator.evaluate, Sum),
|
||||
Evaluator.forNodes(RelationEvaluator.evaluate, Relation),
|
||||
Evaluator.forNodes(IfElseEvaluator.evaluate, IfElse),
|
||||
AtomEvaluator.evaluate
|
||||
)(node, environment)
|
||||
|
||||
if doAssert and result.result and result.value.type == Type.VOID:
|
||||
raise RuntimeException(f"Expected expression", node.pos)
|
||||
if doAssert and result.result and result.value.type == Type.VOID:
|
||||
raise RuntimeException(f"Expected expression", node.pos)
|
||||
|
||||
return result
|
||||
|
||||
return result
|
||||
|
||||
return evaluateExpression
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@ class ExtendEvaluator(Evaluator):
|
||||
@classmethod
|
||||
def _typeToMethodSignature(cls, node):
|
||||
if type(node.specifiers) == NoneNode:
|
||||
return signature(ofType(node.type))
|
||||
return signature(ofType(node.type.value))
|
||||
|
||||
elif node.type == Type.LIST:
|
||||
elif node.type.value == Type.LIST:
|
||||
return signature(listSpecifier(node.specifiers[0]))
|
||||
elif node.type == Type.MAP:
|
||||
elif node.type.value == Type.MAP:
|
||||
return signature(mapSpecifier(node.specifiers[0], node.specifiers[1]))
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from smnp.ast.node.none import NoneNode
|
||||
from smnp.program.interpreter import Interpreter
|
||||
from smnp.runtime.evaluator import Evaluator
|
||||
|
||||
@@ -7,13 +6,6 @@ class ImportEvaluator(Evaluator):
|
||||
|
||||
@classmethod
|
||||
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
|
||||
newEnvironment = Interpreter.interpretFile(source.value)
|
||||
newEnvironment = Interpreter.interpretFile(source.value, baseEnvironment=environment)
|
||||
environment.extend(newEnvironment)
|
||||
104
smnp/runtime/evaluators/loop.py
Normal file
104
smnp/runtime/evaluators/loop.py
Normal file
@@ -0,0 +1,104 @@
|
||||
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
|
||||
31
smnp/runtime/evaluators/minus.py
Normal file
31
smnp/runtime/evaluators/minus.py
Normal file
@@ -0,0 +1,31 @@
|
||||
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])
|
||||
16
smnp/runtime/evaluators/negation.py
Normal file
16
smnp/runtime/evaluators/negation.py
Normal file
@@ -0,0 +1,16 @@
|
||||
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)
|
||||
20
smnp/runtime/evaluators/power.py
Normal file
20
smnp/runtime/evaluators/power.py
Normal file
@@ -0,0 +1,20 @@
|
||||
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))
|
||||
31
smnp/runtime/evaluators/product.py
Normal file
31
smnp/runtime/evaluators/product.py
Normal file
@@ -0,0 +1,31 @@
|
||||
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")
|
||||
|
||||
46
smnp/runtime/evaluators/relation.py
Normal file
46
smnp/runtime/evaluators/relation.py
Normal file
@@ -0,0 +1,46 @@
|
||||
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)
|
||||
|
||||
66
smnp/runtime/evaluators/sum.py
Normal file
66
smnp/runtime/evaluators/sum.py
Normal file
@@ -0,0 +1,66 @@
|
||||
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")
|
||||
34
smnp/runtime/evaluators/unit.py
Normal file
34
smnp/runtime/evaluators/unit.py
Normal file
@@ -0,0 +1,34 @@
|
||||
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)
|
||||
@@ -1,5 +1,6 @@
|
||||
from smnp.ast.node import type as ast
|
||||
from smnp.ast.node.none import NoneNode
|
||||
from smnp.ast.node.type import TypeNode, TypeSpecifier
|
||||
from smnp.ast.node.type import TypesList
|
||||
from smnp.error.runtime import RuntimeException
|
||||
from smnp.function.signature import varargSignature, signature
|
||||
from smnp.runtime.tools.error import updatePos
|
||||
@@ -16,9 +17,9 @@ def argumentsNodeToMethodSignature(node):
|
||||
argumentsCount = len(node.children)
|
||||
for i, child in enumerate(node.children):
|
||||
matchers = {
|
||||
TypeNode: (lambda c: c.type, typeMatcher),
|
||||
ast.Type: (lambda c: c.type, typeMatcher),
|
||||
NoneNode: (lambda c: c.type, lambda c: allTypes()),
|
||||
TypeSpecifier: (lambda c: c, multipleTypeMatcher)
|
||||
TypesList: (lambda c: c, multipleTypeMatcher)
|
||||
}
|
||||
evaluatedMatcher = matchers[type(child.type)][1](matchers[type(child.type)][0](child))
|
||||
if child.vararg:
|
||||
@@ -49,10 +50,10 @@ def multipleTypeMatcher(typeNode):
|
||||
|
||||
def typeMatcher(typeNode):
|
||||
if type(typeNode.specifiers) == NoneNode:
|
||||
return ofType(typeNode.type)
|
||||
elif typeNode.type == Type.LIST and len(typeNode.specifiers) == 1:
|
||||
return ofType(typeNode.type.value)
|
||||
elif typeNode.type.value == Type.LIST and len(typeNode.specifiers) == 1:
|
||||
return listSpecifier(typeNode.specifiers[0])
|
||||
elif typeNode.type == Type.MAP and len(typeNode.specifiers) == 2:
|
||||
elif typeNode.type.value == Type.MAP and len(typeNode.specifiers) == 2:
|
||||
return mapSpecifier(typeNode.specifiers[0], typeNode.specifiers[1])
|
||||
|
||||
raise RuntimeException("Unknown type", typeNode.pos) # Todo: Improve pointing position
|
||||
|
||||
@@ -8,7 +8,7 @@ class Token:
|
||||
self.rawValue = rawValue
|
||||
|
||||
def __str__(self):
|
||||
return "Token(" + str(self.type) + ", '" + str(self.value) + "', " + str(self.pos) + ")"
|
||||
return "{" + str(self.type.name) + ", '" + str(self.value) + "', " + str(self.pos) + "}"
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
from smnp.error.syntax import SyntaxException
|
||||
from smnp.token.model import TokenList
|
||||
from smnp.token.tokenizers.bool import boolTokenizer
|
||||
from smnp.token.tokenizers.comment import commentTokenizer
|
||||
from smnp.token.tokenizers.identifier import identifierTokenizer
|
||||
from smnp.token.tokenizers.keyword import typeTokenizer
|
||||
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.whitespace import whitespacesTokenizer
|
||||
from smnp.token.tools import defaultTokenizer, separated, regexPatternTokenizer
|
||||
from smnp.token.tools import defaultTokenizer, separated, regexPatternTokenizer, mapValue
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
tokenizers = (
|
||||
defaultTokenizer(TokenType.ARROW),
|
||||
|
||||
# Double-character operators
|
||||
relationOperatorTokenizer,
|
||||
defaultTokenizer(TokenType.DOUBLE_ASTERISK),
|
||||
|
||||
# Characters
|
||||
defaultTokenizer(TokenType.OPEN_CURLY),
|
||||
defaultTokenizer(TokenType.CLOSE_CURLY),
|
||||
@@ -21,18 +29,21 @@ tokenizers = (
|
||||
defaultTokenizer(TokenType.CLOSE_ANGLE),
|
||||
defaultTokenizer(TokenType.ASTERISK),
|
||||
defaultTokenizer(TokenType.ASSIGN),
|
||||
defaultTokenizer(TokenType.ARROW),
|
||||
defaultTokenizer(TokenType.COMMA),
|
||||
defaultTokenizer(TokenType.SLASH),
|
||||
defaultTokenizer(TokenType.MINUS),
|
||||
defaultTokenizer(TokenType.PLUS),
|
||||
defaultTokenizer(TokenType.DASH),
|
||||
defaultTokenizer(TokenType.DOTS),
|
||||
defaultTokenizer(TokenType.AMP),
|
||||
defaultTokenizer(TokenType.DOT),
|
||||
|
||||
# Types
|
||||
separated(regexPatternTokenizer(TokenType.INTEGER, r'\d')),
|
||||
mapValue(separated(regexPatternTokenizer(TokenType.INTEGER, r'\d')), int),
|
||||
stringTokenizer,
|
||||
typeTokenizer,
|
||||
noteTokenizer,
|
||||
boolTokenizer,
|
||||
typeTokenizer,
|
||||
|
||||
# Keywords
|
||||
separated(defaultTokenizer(TokenType.FUNCTION)),
|
||||
@@ -40,7 +51,13 @@ tokenizers = (
|
||||
separated(defaultTokenizer(TokenType.EXTEND)),
|
||||
separated(defaultTokenizer(TokenType.IMPORT)),
|
||||
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.IF)),
|
||||
separated(defaultTokenizer(TokenType.OR)),
|
||||
|
||||
# Identifier (couldn't be before keywords!)
|
||||
identifierTokenizer,
|
||||
@@ -50,7 +67,6 @@ tokenizers = (
|
||||
commentTokenizer,
|
||||
)
|
||||
|
||||
|
||||
filters = [
|
||||
lambda token: token.type is not None,
|
||||
lambda token: token.type != TokenType.COMMENT
|
||||
@@ -58,33 +74,33 @@ filters = [
|
||||
|
||||
|
||||
def tokenize(lines):
|
||||
tokens = []
|
||||
for lineNumber, line in enumerate(lines):
|
||||
tokens = []
|
||||
for lineNumber, line in enumerate(lines):
|
||||
current = 0
|
||||
while current < len(line):
|
||||
consumedChars, token = combinedTokenizer(line, current, lineNumber)
|
||||
|
||||
consumedChars, token = combinedTokenizer(line, current, lineNumber)
|
||||
|
||||
if consumedChars == 0:
|
||||
raise SyntaxException(f"Unknown symbol '{line[current]}'", (lineNumber, current))
|
||||
|
||||
|
||||
current += consumedChars
|
||||
tokens.append(token)
|
||||
|
||||
|
||||
return TokenList(filterTokens(filters, tokens), lines)
|
||||
|
||||
|
||||
def combinedTokenizer(line, current, lineNumber):
|
||||
for tokenizer in tokenizers:
|
||||
consumedChars, token = tokenizer(line, current, lineNumber)
|
||||
if consumedChars > 0:
|
||||
if consumedChars > 0:
|
||||
return (consumedChars, token)
|
||||
return (0, None)
|
||||
|
||||
|
||||
def filterTokens(filters, tokens):
|
||||
def filterTokens(filters, tokens):
|
||||
if not filters:
|
||||
return tokens
|
||||
|
||||
|
||||
return list(filterTokens(filters[1:], (token for token in tokens if filters[0](token))))
|
||||
|
||||
|
||||
|
||||
11
smnp/token/tokenizers/bool.py
Normal file
11
smnp/token/tokenizers/bool.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from smnp.token.tools import keywordsTokenizer, separated
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
|
||||
def boolTokenizer(input, current, line):
|
||||
consumedChars, token = separated(keywordsTokenizer(TokenType.BOOL, "true", "false"))(input, current, line)
|
||||
if consumedChars > 0:
|
||||
token.value = token.value == "true"
|
||||
return (consumedChars, token)
|
||||
|
||||
return (0, None)
|
||||
@@ -3,7 +3,7 @@ from smnp.token.type import TokenType
|
||||
from smnp.type.model import Type
|
||||
|
||||
|
||||
typeTokenizer = separated(keywordsTokenizer(TokenType.TYPE, *[type.name.lower() for type in Type]))
|
||||
typeTokenizer = separated(keywordsTokenizer(TokenType.TYPE, *[type.name.lower() for type in Type], mapKeyword=lambda value: Type[value.upper()]))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ def noteTokenizer(input, current, line):
|
||||
rawValue = ''
|
||||
if input[current] == '@':
|
||||
rawValue += input[current+consumedChars]
|
||||
consumedChars += 1
|
||||
consumedChars += 1 # TODO: Check if next item does even exist
|
||||
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]
|
||||
notePitch = input[current+consumedChars]
|
||||
|
||||
6
smnp/token/tokenizers/relation.py
Normal file
6
smnp/token/tokenizers/relation.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from smnp.token.tools import keywordsTokenizer
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
|
||||
def relationOperatorTokenizer(input, current, line):
|
||||
return keywordsTokenizer(TokenType.RELATION, "==", "!=", ">=", "<=")(input, current, line)
|
||||
@@ -13,5 +13,5 @@ def stringTokenizer(input, current, line):
|
||||
char = input[current + consumedChars]
|
||||
value += char
|
||||
consumedChars += 1
|
||||
return (consumedChars, Token(TokenType.STRING, value[1:len(value)-1], (line, current)))
|
||||
return (consumedChars, Token(TokenType.STRING, value[1:len(value)-1], (line, current), value))
|
||||
return (0, None)
|
||||
|
||||
@@ -16,10 +16,10 @@ def regexPatternTokenizer(type, pattern):
|
||||
return tokenizer
|
||||
|
||||
|
||||
def keywordsTokenizer(type, *keywords):
|
||||
def keywordsTokenizer(type, *keywords, mapKeyword=lambda x: x):
|
||||
def tokenizer(input, current, line):
|
||||
for keyword in keywords:
|
||||
result = keywordTokenizer(type, keyword)(input, current, line)
|
||||
result = keywordTokenizer(type, keyword, mapKeyword)(input, current, line)
|
||||
if result[0] > 0:
|
||||
return result
|
||||
return (0, None)
|
||||
@@ -27,10 +27,10 @@ def keywordsTokenizer(type, *keywords):
|
||||
return tokenizer
|
||||
|
||||
|
||||
def keywordTokenizer(type, keyword):
|
||||
def keywordTokenizer(type, keyword, mapKeyword=lambda x: x):
|
||||
def tokenizer(input, current, line):
|
||||
if len(input) >= current+len(keyword) and input[current:current+len(keyword)] == keyword:
|
||||
return (len(keyword), Token(type, keyword, (line, current)))
|
||||
return (len(keyword), Token(type, mapKeyword(keyword), (line, current)))
|
||||
return (0, None)
|
||||
return tokenizer
|
||||
|
||||
@@ -50,3 +50,14 @@ def separated(tokenizer, end=r"\W"):
|
||||
return (0, None)
|
||||
|
||||
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
|
||||
|
||||
@@ -2,6 +2,8 @@ from enum import Enum
|
||||
|
||||
|
||||
class TokenType(Enum):
|
||||
RELATION = '==, !=, >=, <='
|
||||
DOUBLE_ASTERISK = '**'
|
||||
OPEN_CURLY = '{'
|
||||
CLOSE_CURLY = '}'
|
||||
OPEN_PAREN = '('
|
||||
@@ -14,19 +16,29 @@ class TokenType(Enum):
|
||||
ASSIGN = '='
|
||||
ARROW = '->'
|
||||
COMMA = ','
|
||||
SLASH = '/'
|
||||
MINUS = '-'
|
||||
PLUS = '+'
|
||||
DASH = '^'
|
||||
DOTS = '...'
|
||||
AMP = '&'
|
||||
DOT = '.'
|
||||
AND = 'and'
|
||||
OR = 'or'
|
||||
NOT = 'not'
|
||||
INTEGER = 'integer'
|
||||
STRING = 'string'
|
||||
NOTE = 'note'
|
||||
BOOL = 'bool'
|
||||
TYPE = 'type'
|
||||
FUNCTION = 'function'
|
||||
RETURN = 'return'
|
||||
EXTEND = 'extend'
|
||||
IMPORT = 'import'
|
||||
FROM = 'from'
|
||||
WITH = 'with'
|
||||
ELSE = 'else'
|
||||
IF = 'if'
|
||||
AS = 'as'
|
||||
IDENTIFIER = 'identifier'
|
||||
COMMENT = 'comment'
|
||||
|
||||
@@ -13,6 +13,7 @@ class Type(Enum):
|
||||
MAP = (dict, lambda x: '{' + ', '.join(f"'{k.stringify()}' -> '{v.stringify()}'" for k, v in x.items()) + '}')
|
||||
PERCENT = (float, lambda x: f"{int(x * 100)}%")
|
||||
NOTE = (Note, lambda x: x.note.name)
|
||||
BOOL = (bool, lambda x: str(x).lower())
|
||||
SOUND = (Sound, lambda x: x.file)
|
||||
TYPE = (None, lambda x: x.name.lower())
|
||||
VOID = (type(None), lambda x: _failStringify(Type.VOID))
|
||||
@@ -53,6 +54,10 @@ class Type(Enum):
|
||||
"dot": Type.string('.' if value.dot else '')
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def bool(value):
|
||||
return Value(Type.BOOL, value, {})
|
||||
|
||||
@staticmethod
|
||||
def sound(value):
|
||||
return Value(Type.SOUND, value, {
|
||||
@@ -68,6 +73,7 @@ class Type(Enum):
|
||||
def void():
|
||||
return Value(Type.VOID, None)
|
||||
|
||||
|
||||
def _failStringify(t):
|
||||
raise RuntimeException(f"Not able to interpret {t.name}'", None)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user