Merge branch 'left-associative-ops'

This commit is contained in:
Bartłomiej Pluta
2019-07-12 23:27:32 +02:00
70 changed files with 1671 additions and 877 deletions

View File

@@ -9,7 +9,8 @@ PITCH_MODIFIER = 'b' | '#'
<string> ::= '"' CHAR* '"' <string> ::= '"' CHAR* '"'
<note> ::= '@' PITCH PITCH_MODIFIER? DIGIT? [':' DIGIT+ 'd'?]? <note> ::= '@' PITCH PITCH_MODIFIER? DIGIT? [':' DIGIT+ 'd'?]?
<identifier> ::= ID [ID|DIGIT]* <identifier> ::= ID [ID|DIGIT]*
<type> ::= 'integer' | 'string' | 'note' | 'list' | 'void' <bool> ::= 'true' | 'false'
<type> ::= 'integer' | 'string' | 'note' | 'list' | 'map' | 'sound' | 'bool' | 'void'
# Parser # Parser
<expr> ::= <integer> <access> | <integer> <expr> ::= <integer> <access> | <integer>
@@ -18,9 +19,10 @@ PITCH_MODIFIER = 'b' | '#'
<expr> ::= <identifier> <access> | <identifier> '=' <expr> | <functionCall> | <identifier> <expr> ::= <identifier> <access> | <identifier> '=' <expr> | <functionCall> | <identifier>
<expr> ::= <list> <access> | <list> <expr> ::= <list> <access> | <list>
<expr> ::= <map> <access> | <map> <expr> ::= <map> <access> | <map>
<expr> ::= <bool> <access> | <bool>
<expr> ::= <expr> '.' <identifier> | <expr> '.' <functionCall> <expr> ::= <expr> '.' <identifier> | <expr> '.' <functionCall>
<expr> ::= <asteriskExpr> '*' <expr> <expr> ::= <asteriskExpr> '*' <expr>
<asteriskExpr> ::= <integer> | <string> | <note> | <identifier> | <list> <asteriskExpr> ::= <integer> | <string> | <note> | <identifier> | <list> | <map>
<functionCall> ::= <identifier> <arglist> <functionCall> ::= <identifier> <arglist>

View File

@@ -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())
)

View File

@@ -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")

View File

@@ -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
View 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)

View File

@@ -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.ast.parser import Parser
from smnp.token.type import TokenType 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( def BlockParser(input):
Parser.terminalParser(TokenType.OPEN_CURLY), return Parser.loop(
Parser.doAssert(StatementNode.parse, f"statement or '{TokenType.CLOSE_CURLY.key}'"), Parser.terminal(TokenType.OPEN_CURLY),
Parser.terminalParser(TokenType.CLOSE_CURLY), Parser.doAssert(StatementParser, f"statement or '{TokenType.CLOSE_CURLY.key}'"),
createNode=createNode, Parser.terminal(TokenType.CLOSE_CURLY),
)(input) createNode=lambda open, statements, close: Block.withChildren(statements, open.pos)
)(input)

View 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)

View File

@@ -1,69 +1,51 @@
from smnp.ast.node.asterisk import AsteriskNode from smnp.ast.node.operator import BinaryOperator
from smnp.ast.node.model import Node from smnp.ast.node.term import TermParser
from smnp.ast.node.none import NoneNode
from smnp.ast.node.statement import StatementNode
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class ExpressionNode(Node): class Sum(BinaryOperator):
def __init__(self, pos): pass
super().__init__(pos, [NoneNode()])
@property
def value(self):
return self[0]
@value.setter class Relation(BinaryOperator):
def value(self, v): pass
self[0] = v
@classmethod class And(BinaryOperator):
def withValue(cls, val, pos): pass
node = cls(pos)
node.value = val
return node
@classmethod
def _parse(cls, input):
return Parser.oneOf(
cls._asteriskParser(),
cls._expressionParser(),
)(input)
@classmethod class Or(BinaryOperator):
def _asteriskParser(cls): pass
def createNode(iterator, asterisk, statement):
node = AsteriskNode(asterisk.pos)
node.iterator = iterator
node.statement = statement
return node
return Parser.allOf(
cls._expressionParser(),
Parser.terminalParser(TokenType.ASTERISK),
Parser.doAssert(StatementNode.parse, 'statement'),
createNode=createNode
)
@classmethod def ExpressionParser(input):
def _expressionParser(cls): expr1 = Parser.leftAssociativeOperatorParser(
from smnp.ast.node.integer import IntegerLiteralNode TermParser,
from smnp.ast.node.string import StringLiteralNode [TokenType.PLUS, TokenType.MINUS],
from smnp.ast.node.note import NoteLiteralNode TermParser,
from smnp.ast.node.identifier import IdentifierNode lambda left, op, right: Sum.withValues(left, op, right)
from smnp.ast.node.list import ListNode )
from smnp.ast.node.map import MapNode
from smnp.ast.node.type import TypeNode 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,
)

View File

@@ -1,14 +1,14 @@
from smnp.ast.node.block import BlockNode from smnp.ast.node.block import Block
from smnp.ast.node.function import FunctionDefinitionNode from smnp.ast.node.function import FunctionDefinitionParser
from smnp.ast.node.identifier import IdentifierNode 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.none import NoneNode
from smnp.ast.node.statement import StatementNode from smnp.ast.node.type import TypeParser
from smnp.ast.node.type import TypeNode
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class ExtendNode(StatementNode): class Extend(Node):
def __init__(self, pos): def __init__(self, pos):
super().__init__(pos) super().__init__(pos)
self.children = [NoneNode(), NoneNode(), NoneNode()] self.children = [NoneNode(), NoneNode(), NoneNode()]
@@ -38,33 +38,51 @@ class ExtendNode(StatementNode):
self[2] = value self[2] = value
@classmethod @classmethod
def _parse(cls, input): def withValues(cls, pos, type, variable, methods):
def createNode(extend, type, asKeyword, variable, methods): node = cls(pos)
node = ExtendNode(extend.pos) node.type = type
node.type = type node.variable = variable
node.variable = variable node.methods = methods
node.methods = methods return node
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 ExtendParser(input):
def _methodsDeclarationsParser(cls):
def createNode(openBracket, items, closeBracket): simpleExtend = Parser.allOf(
node = BlockNode(openBracket.pos) Parser.terminal(TokenType.EXTEND),
node.children = items TypeParser,
return node 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
View 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)

View File

@@ -1,83 +1,58 @@
from smnp.ast.node.block import BlockNode from smnp.ast.node.block import BlockParser
from smnp.ast.node.expression import ExpressionNode from smnp.ast.node.identifier import IdentifierLiteralParser
from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.iterable import abstractIterableParser from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.model import Node from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode from smnp.ast.node.none import NoneNode
from smnp.ast.node.statement import StatementNode from smnp.ast.node.type import TypeParser, Type
from smnp.ast.node.type import TypeNode, TypeSpecifier
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class ArgumentsDeclarationNode(Node): class ArgumentsDeclaration(Node):
@classmethod
def _parse(cls, input):
raise RuntimeError("This class is not supposed to be automatically called")
class VarargNode(Node):
pass pass
class ArgumentDefinitionNode(ExpressionNode): class Argument(Node):
def __init__(self, pos): def __init__(self, pos):
super().__init__(pos) super().__init__(pos)
self.children.extend([NoneNode(), False]) self.children = [NoneNode(), NoneNode(), False]
@property @property
def type(self): def type(self):
return self[0] return self[0]
@type.setter @type.setter
def type(self, value): def type(self, value):
self[0] = value self[0] = value
@property @property
def variable(self): def variable(self):
return self[1] return self[1]
@variable.setter @variable.setter
def variable(self, value): def variable(self, value):
self[1] = value self[1] = value
@property @property
def vararg(self): def vararg(self):
return self[2] return self[2]
@vararg.setter @vararg.setter
def vararg(self, value): def vararg(self, value):
self[2] = value self[2] = value
@classmethod class VarargNode(Node):
def parser(cls): pass
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 FunctionDefinition(Node):
class FunctionDefinitionNode(StatementNode):
def __init__(self, pos): def __init__(self, pos):
super().__init__(pos) super().__init__(pos)
self.children = [NoneNode(), NoneNode(), NoneNode()] self.children = [NoneNode(), NoneNode(), NoneNode()]
@@ -107,22 +82,48 @@ class FunctionDefinitionNode(StatementNode):
self[2] = value self[2] = value
@classmethod @classmethod
def _parse(cls, input): def withValues(cls, name, arguments, body):
def createNode(function, name, arguments, body): node = cls(name.pos)
node = FunctionDefinitionNode(function.pos) node.name = name
node.name = name node.arguments = arguments
node.arguments = arguments node.body = body
node.body = body return node
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 ArgumentParser(input):
def _argumentsDeclarationParser(): def createNode(type, variable, vararg):
return abstractIterableParser(ArgumentsDeclarationNode, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, ArgumentDefinitionNode.parser()) 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)

View File

@@ -1,53 +1,74 @@
from smnp.ast.node.access import AccessNode from smnp.ast.node.atom import Atom
from smnp.ast.node.assignment import AssignmentNode from smnp.ast.node.expression import ExpressionParser
from smnp.ast.node.expression import ExpressionNode from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.invocation import FunctionCallNode, ArgumentsListNode 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.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class IdentifierNode(AccessNode): class Identifier(Atom):
pass
class FunctionCall(Node):
def __init__(self, pos): def __init__(self, pos):
super().__init__(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 @classmethod
def _literalParser(cls): def withChildren(cls, name, arguments):
return Parser.oneOf( node = cls(name.pos)
IdentifierNode._functionCallParser(), node.name = name
IdentifierNode._assignmentParser(), node.arguments = arguments
IdentifierNode.identifierParser() 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( class ArgumentsList(Node):
IdentifierNode.identifierParser(), pass
Parser.terminalParser(TokenType.ASSIGN),
Parser.doAssert(ExpressionNode.parse, "expression"),
createNode=createNode
)
@staticmethod
def _functionCallParser():
def createNode(name, arguments):
node = FunctionCallNode(name.pos)
node.name = name
node.arguments = arguments
return node
return Parser.allOf( class Assignment(BinaryOperator):
IdentifierNode.identifierParser(), pass
ArgumentsListNode.parse,
createNode=createNode
)
@staticmethod
def identifierParser(): def IdentifierLiteralParser(input):
return Parser.terminalParser(TokenType.IDENTIFIER, lambda val, pos: IdentifierNode.withValue(val, pos)) 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)

View File

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

View File

@@ -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))

View File

@@ -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")

View File

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

View File

@@ -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.iterable import abstractIterableParser
from smnp.ast.node.model import Node
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class ListNode(AccessNode): class List(Node):
pass
@classmethod
def _literalParser(cls): def ListParser(input):
return abstractIterableParser(ListNode, TokenType.OPEN_SQUARE, TokenType.CLOSE_SQUARE, from smnp.ast.node.expression import ExpressionParser
Parser.doAssert(ExpressionNode.parse, "expression"))
return abstractIterableParser(
List,
TokenType.OPEN_SQUARE,
TokenType.CLOSE_SQUARE,
Parser.doAssert(ExpressionParser, "expression")
)(input)

View File

@@ -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)

View File

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

View File

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

View File

@@ -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
View 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

View File

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

View File

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

View File

@@ -2,16 +2,20 @@ from smnp.ast.node.model import Node
from smnp.ast.parser import Parser 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( def StatementParser(input):
ExpressionNode.parse, from smnp.ast.node.block import BlockParser
BlockNode.parse, from smnp.ast.node.condition import IfElseStatementParser
ReturnNode.parse, from smnp.ast.node.expression import ExpressionParser
)(input)
from smnp.ast.node.ret import ReturnParser
return Parser.oneOf(
IfElseStatementParser,
ExpressionParser,
BlockParser,
ReturnParser,
name="statement"
)(input)

View File

@@ -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
View 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)

View File

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

36
smnp/ast/node/unit.py Normal file
View 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
View 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

View File

@@ -5,15 +5,43 @@ from smnp.error.syntax import SyntaxException
def parse(input): def parse(input):
from smnp.ast.node.program import Program from smnp.ast.node.program import ProgramParser
return Program.parse(input).node return ProgramParser(input).node
class Parser: class Parser:
def __init__(self, parse, name=None, parsers=None):
if parsers is None:
parsers = []
self.parsers = parsers
self._parse = parse
if name is None:
name = parse.__name__
self.name = name
def parse(self, input):
result = self._parse(input)
if result is None:
return ParseResult.FAIL()
if not isinstance(result, ParseResult):
raise RuntimeError(f"_parse() method of '{self.__class__.__name__}' class haven't returned ParseResult object")
return result
def __call__(self, input):
return self.parse(input)
def __str__(self):
return self.name
def __repr__(self):
return self.__str__()
# a -> A # a -> A
@staticmethod @staticmethod
def terminalParser(expectedType, createNode=None, doAssert=False): def terminal(expectedType, createNode=None, doAssert=False):
def provideNode(value, pos): def provideNode(value, pos):
if createNode is None: if createNode is None:
return IgnoredNode(pos) return IgnoredNode(pos)
@@ -30,17 +58,22 @@ class Parser:
return ParseResult.FAIL() return ParseResult.FAIL()
return parse return Parser(parse, name=expectedType.name.lower())
# oneOf -> a | b | c | ... # oneOf -> a | b | c | ...
@staticmethod @staticmethod
def oneOf(*parsers, exception=None): def oneOf(*parsers, assertExpected=None, exception=None, name="or"):
def combinedParser(input): def combinedParser(input):
snap = input.snapshot() snap = input.snapshot()
for parser in parsers: for parser in parsers:
value = parser(input) value = parser(input)
if value.result: if value.result:
return value 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 exception is not None:
if callable(exception): if callable(exception):
@@ -48,15 +81,14 @@ class Parser:
else: else:
raise exception raise exception
input.reset(snap) input.reset(snap)
return ParseResult.FAIL() return ParseResult.FAIL()
return combinedParser return Parser(combinedParser, name=name, parsers=parsers)
# allOf -> a b c ... # allOf -> a b c ...
@staticmethod @staticmethod
def allOf(*parsers, createNode, exception=None): def allOf(*parsers, createNode, exception=None, name="all"):
if len(parsers) == 0: if len(parsers) == 0:
raise RuntimeError("Pass one parser at least") raise RuntimeError("Pass one parser at least")
@@ -87,29 +119,36 @@ class Parser:
return ParseResult.OK(node) return ParseResult.OK(node)
return Parser(extendedParser, name=name, parsers=parsers)
return extendedParser
# leftAssociative -> left | left OP right # leftAssociative -> left | left OP right
@staticmethod @staticmethod
def leftAssociativeOperatorParser(leftParser, operatorTokenType, rightParser, createNode): def leftAssociativeOperatorParser(leftParser, operatorTokenTypes, rightParser, createNode, name="leftAssoc"):
from smnp.ast.node.operator import Operator
def parse(input): def parse(input):
operatorParser = Parser.oneOfTerminals(*operatorTokenTypes, createNode=lambda val, pos: Operator.withChildren([val], pos))
left = leftParser(input) left = leftParser(input)
if left.result: if left.result:
while Parser.terminalParser(operatorTokenType)(input).result: operator = operatorParser(input)
while operator.result:
right = rightParser(input) 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 left
return ParseResult.FAIL() 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 # loop -> start item* end
@staticmethod @staticmethod
def loop(startParser, itemParser, endParser, createNode): def loop(startParser, itemParser, endParser, createNode, name="loop"):
def parse(input): def parse(input):
items = [] items = []
start = startParser(input) start = startParser(input)
@@ -125,10 +164,10 @@ class Parser:
return ParseResult.FAIL() return ParseResult.FAIL()
return parse return Parser(parse, name, parsers=[startParser, itemParser, endParser])
@staticmethod @staticmethod
def doAssert(parser, expected): def doAssert(parser, expected, name="!!"):
def parse(input): def parse(input):
result = parser(input) result = parser(input)
@@ -139,10 +178,10 @@ class Parser:
return result return result
return parse return Parser(parse, name, parsers=parser)
@staticmethod @staticmethod
def optional(parser): def optional(parser, name="??"):
def parse(input): def parse(input):
result = parser(input) result = parser(input)
if result.result: if result.result:
@@ -150,10 +189,14 @@ class Parser:
return ParseResult.OK(NoneNode()) return ParseResult.OK(NoneNode())
return parse return Parser(parse, name, parsers=[parser])
@staticmethod @staticmethod
def many(parser, createNode): def epsilon():
return lambda *args: ParseResult.OK(NoneNode())
@staticmethod
def many(parser, createNode, name="*"):
def parse(input): def parse(input):
results = [] results = []
snap = input.snapshot() snap = input.snapshot()
@@ -167,4 +210,15 @@ class Parser:
input.reset(snap) input.reset(snap)
return ParseResult.OK(createNode(results, pos) if len(results) > 0 else NoneNode()) return ParseResult.OK(createNode(results, pos) if len(results) > 0 else NoneNode())
return Parser(parse, name, parsers=[parser])
@staticmethod
def wrap(parser, createNode):
def parse(input):
result = parser(input)
if result.result:
return ParseResult.OK(createNode(result.node))
return result
return parse return parse

View File

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

66
smnp/calc.py Normal file
View 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()

View File

@@ -4,6 +4,6 @@ from smnp.program.interpreter import Interpreter
def loadStandardLibrary(): def loadStandardLibrary():
source = resource_string('smnp.library.code', 'main.mus').decode("utf-8") mainSource = resource_string('smnp.library.code', 'main.mus').decode("utf-8")
return Interpreter.interpretString(source) env = Interpreter.interpretString(mainSource)
return env

View File

@@ -8,7 +8,10 @@ from smnp.program.interpreter import Interpreter
def main(): def main():
try: try:
stdLibraryEnv = loadStandardLibrary() 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: except SmnpException as e:
print(e.message()) print(e.message())

View File

@@ -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 ] methods = [ get.function ]

View File

@@ -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')

View File

@@ -12,7 +12,7 @@ def _function1(env, list, index):
raise RuntimeException(f"Attempt to access item which is outside the list", None) 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): def _function2(env, map, key):
try: try:
return map.value[key] return map.value[key]

View File

@@ -4,7 +4,7 @@ from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOfMatchers, listMatches from smnp.type.signature.matcher.list import listOfMatchers, listMatches
from smnp.type.signature.matcher.type import allTypes, ofTypes 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): def _function1(env, vararg):
map = {} map = {}
for entry in vararg: for entry in vararg:
@@ -14,7 +14,7 @@ def _function1(env, vararg):
return Type.map(map) 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): def _function2(env, list):
map = {} map = {}
for entry in list.value: for entry in list.value:

View File

@@ -6,17 +6,17 @@ from smnp.type.signature.matcher.type import ofType
_signature1 = signature(ofType(Type.INTEGER)) _signature1 = signature(ofType(Type.INTEGER))
def _function1(env, upper): def _function1(env, upper):
return Type.list(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)) _signature2 = signature(ofType(Type.INTEGER), ofType(Type.INTEGER))
def _function2(env, lower, upper): 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)) _signature3 = signature(ofType(Type.INTEGER), ofType(Type.INTEGER), ofType(Type.INTEGER))
def _function3(env, lower, upper, step): def _function3(env, lower, upper, step):
return Type.list(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)) _signature4 = signature(ofType(Type.NOTE), ofType(Type.NOTE))

View File

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

View File

@@ -1,9 +1,10 @@
from smnp.ast.node.block import BlockNode from smnp.ast.node.block import Block
from smnp.ast.node.extend import ExtendNode from smnp.ast.node.condition import IfElse
from smnp.ast.node.function import FunctionDefinitionNode from smnp.ast.node.extend import Extend
from smnp.ast.node.imports import ImportNode from smnp.ast.node.function import FunctionDefinition
from smnp.ast.node.imports import Import
from smnp.ast.node.program import Program from smnp.ast.node.program import Program
from smnp.ast.node.ret import ReturnNode from smnp.ast.node.ret import Return
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.type.model import Type from smnp.type.model import Type
@@ -68,20 +69,27 @@ class EvaluationResult():
def evaluate(node, environment): def evaluate(node, environment):
from smnp.runtime.evaluators.program import ProgramEvaluator from smnp.runtime.evaluators.program import ProgramEvaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.function import FunctionDefinitionEvaluator from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.extend import ExtendEvaluator from smnp.runtime.evaluators.condition import IfElseStatementEvaluator
from smnp.runtime.evaluators.block import BlockEvaluator from smnp.runtime.evaluators.block import BlockEvaluator
from smnp.runtime.evaluators.imports import ImportEvaluator 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.function import ReturnEvaluator
from smnp.runtime.evaluators.extend import ExtendEvaluator
result = Evaluator.oneOf( result = Evaluator.oneOf(
Evaluator.forNodes(ProgramEvaluator.evaluate, Program), Evaluator.forNodes(ProgramEvaluator.evaluate, Program),
Evaluator.forNodes(ImportEvaluator.evaluate, ImportNode), Evaluator.forNodes(IfElseStatementEvaluator.evaluate, IfElse),
Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinitionNode), Evaluator.forNodes(BlockEvaluator.evaluate, Block),
Evaluator.forNodes(ExtendEvaluator.evaluate, ExtendNode), Evaluator.forNodes(ImportEvaluator.evaluate, Import),
Evaluator.forNodes(BlockEvaluator.evaluate, BlockNode), Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinition),
Evaluator.forNodes(ReturnEvaluator.evaluate, ReturnNode), 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() expressionEvaluator()
)(node, environment) )(node, environment)

View File

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

View File

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

View File

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

View File

@@ -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

View 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)

View 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)

View File

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

View File

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

View File

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

View 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

View 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])

View 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)

View 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))

View 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")

View 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)

View 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")

View 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)

View File

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

View File

@@ -8,7 +8,7 @@ class Token:
self.rawValue = rawValue self.rawValue = rawValue
def __str__(self): 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): def __repr__(self):
return self.__str__() return self.__str__()

View File

@@ -1,15 +1,23 @@
from smnp.error.syntax import SyntaxException from smnp.error.syntax import SyntaxException
from smnp.token.model import TokenList from smnp.token.model import TokenList
from smnp.token.tokenizers.bool import boolTokenizer
from smnp.token.tokenizers.comment import commentTokenizer from smnp.token.tokenizers.comment import commentTokenizer
from smnp.token.tokenizers.identifier import identifierTokenizer from smnp.token.tokenizers.identifier import identifierTokenizer
from smnp.token.tokenizers.keyword import typeTokenizer from smnp.token.tokenizers.keyword import typeTokenizer
from smnp.token.tokenizers.note import noteTokenizer from smnp.token.tokenizers.note import noteTokenizer
from smnp.token.tokenizers.relation import relationOperatorTokenizer
from smnp.token.tokenizers.string import stringTokenizer from smnp.token.tokenizers.string import stringTokenizer
from smnp.token.tokenizers.whitespace import whitespacesTokenizer from smnp.token.tokenizers.whitespace import whitespacesTokenizer
from smnp.token.tools import defaultTokenizer, separated, regexPatternTokenizer from smnp.token.tools import defaultTokenizer, separated, regexPatternTokenizer, mapValue
from smnp.token.type import TokenType from smnp.token.type import TokenType
tokenizers = ( tokenizers = (
defaultTokenizer(TokenType.ARROW),
# Double-character operators
relationOperatorTokenizer,
defaultTokenizer(TokenType.DOUBLE_ASTERISK),
# Characters # Characters
defaultTokenizer(TokenType.OPEN_CURLY), defaultTokenizer(TokenType.OPEN_CURLY),
defaultTokenizer(TokenType.CLOSE_CURLY), defaultTokenizer(TokenType.CLOSE_CURLY),
@@ -21,18 +29,21 @@ tokenizers = (
defaultTokenizer(TokenType.CLOSE_ANGLE), defaultTokenizer(TokenType.CLOSE_ANGLE),
defaultTokenizer(TokenType.ASTERISK), defaultTokenizer(TokenType.ASTERISK),
defaultTokenizer(TokenType.ASSIGN), defaultTokenizer(TokenType.ASSIGN),
defaultTokenizer(TokenType.ARROW),
defaultTokenizer(TokenType.COMMA), defaultTokenizer(TokenType.COMMA),
defaultTokenizer(TokenType.SLASH),
defaultTokenizer(TokenType.MINUS), defaultTokenizer(TokenType.MINUS),
defaultTokenizer(TokenType.PLUS),
defaultTokenizer(TokenType.DASH),
defaultTokenizer(TokenType.DOTS), defaultTokenizer(TokenType.DOTS),
defaultTokenizer(TokenType.AMP), defaultTokenizer(TokenType.AMP),
defaultTokenizer(TokenType.DOT), defaultTokenizer(TokenType.DOT),
# Types # Types
separated(regexPatternTokenizer(TokenType.INTEGER, r'\d')), mapValue(separated(regexPatternTokenizer(TokenType.INTEGER, r'\d')), int),
stringTokenizer, stringTokenizer,
typeTokenizer,
noteTokenizer, noteTokenizer,
boolTokenizer,
typeTokenizer,
# Keywords # Keywords
separated(defaultTokenizer(TokenType.FUNCTION)), separated(defaultTokenizer(TokenType.FUNCTION)),
@@ -40,7 +51,13 @@ tokenizers = (
separated(defaultTokenizer(TokenType.EXTEND)), separated(defaultTokenizer(TokenType.EXTEND)),
separated(defaultTokenizer(TokenType.IMPORT)), separated(defaultTokenizer(TokenType.IMPORT)),
separated(defaultTokenizer(TokenType.FROM)), separated(defaultTokenizer(TokenType.FROM)),
separated(defaultTokenizer(TokenType.WITH)),
separated(defaultTokenizer(TokenType.ELSE)),
separated(defaultTokenizer(TokenType.AND)),
separated(defaultTokenizer(TokenType.NOT)),
separated(defaultTokenizer(TokenType.AS)), separated(defaultTokenizer(TokenType.AS)),
separated(defaultTokenizer(TokenType.IF)),
separated(defaultTokenizer(TokenType.OR)),
# Identifier (couldn't be before keywords!) # Identifier (couldn't be before keywords!)
identifierTokenizer, identifierTokenizer,
@@ -50,7 +67,6 @@ tokenizers = (
commentTokenizer, commentTokenizer,
) )
filters = [ filters = [
lambda token: token.type is not None, lambda token: token.type is not None,
lambda token: token.type != TokenType.COMMENT lambda token: token.type != TokenType.COMMENT

View 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)

View File

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

View File

@@ -14,7 +14,7 @@ def noteTokenizer(input, current, line):
rawValue = '' rawValue = ''
if input[current] == '@': if input[current] == '@':
rawValue += input[current+consumedChars] rawValue += input[current+consumedChars]
consumedChars += 1 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'): if input[current+consumedChars] in ('C', 'c', 'D', 'd', 'E', 'e', 'F', 'f', 'G', 'g', 'A', 'a', 'H', 'h', 'B', 'b'):
rawValue += input[current + consumedChars] rawValue += input[current + consumedChars]
notePitch = input[current+consumedChars] notePitch = input[current+consumedChars]

View File

@@ -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)

View File

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

View File

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

View File

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

View File

@@ -13,6 +13,7 @@ class Type(Enum):
MAP = (dict, lambda x: '{' + ', '.join(f"'{k.stringify()}' -> '{v.stringify()}'" for k, v in x.items()) + '}') MAP = (dict, lambda x: '{' + ', '.join(f"'{k.stringify()}' -> '{v.stringify()}'" for k, v in x.items()) + '}')
PERCENT = (float, lambda x: f"{int(x * 100)}%") PERCENT = (float, lambda x: f"{int(x * 100)}%")
NOTE = (Note, lambda x: x.note.name) NOTE = (Note, lambda x: x.note.name)
BOOL = (bool, lambda x: str(x).lower())
SOUND = (Sound, lambda x: x.file) SOUND = (Sound, lambda x: x.file)
TYPE = (None, lambda x: x.name.lower()) TYPE = (None, lambda x: x.name.lower())
VOID = (type(None), lambda x: _failStringify(Type.VOID)) VOID = (type(None), lambda x: _failStringify(Type.VOID))
@@ -53,6 +54,10 @@ class Type(Enum):
"dot": Type.string('.' if value.dot else '') "dot": Type.string('.' if value.dot else '')
}) })
@staticmethod
def bool(value):
return Value(Type.BOOL, value, {})
@staticmethod @staticmethod
def sound(value): def sound(value):
return Value(Type.SOUND, value, { return Value(Type.SOUND, value, {
@@ -68,6 +73,7 @@ class Type(Enum):
def void(): def void():
return Value(Type.VOID, None) return Value(Type.VOID, None)
def _failStringify(t): def _failStringify(t):
raise RuntimeException(f"Not able to interpret {t.name}'", None) raise RuntimeException(f"Not able to interpret {t.name}'", None)