4 Commits

Author SHA1 Message Date
Bartłomiej Pluta
49e4c4487e --wip-- [skip ci] 2019-07-11 19:36:25 +02:00
Bartłomiej Pluta
e7674a4834 Create almost working proof of concept with new parsers 2019-07-11 19:10:13 +02:00
Bartłomiej Pluta
ed2c8dc6dd Create base for future parsers 2019-07-11 18:24:05 +02:00
Bartłomiej Pluta
f91e2a75de Create oneOf and allOf parsers 2019-07-11 17:23:39 +02:00
85 changed files with 1323 additions and 1725 deletions

View File

@@ -1,6 +1,7 @@
from smnp.ast.node.model import Node from smnp.ast.node.model import Node
from smnp.ast.parser import Parser from smnp.ast.parser import Parsers
from smnp.token.type import TokenType from smnp.token.type import TokenType
from smnp.util.singleton import SingletonParser
class Atom(Node): class Atom(Node):
@@ -43,48 +44,60 @@ class TypeLiteral(Atom):
pass pass
def IntegerParser(input): @SingletonParser
return Parser.terminal(TokenType.INTEGER, createNode=IntegerLiteral.withValue)(input) def IntegerParser():
return Parsers.oneOf(
Parsers.terminal(TokenType.INTEGER, lambda val, pos: IntegerLiteral.withValue(int(val), pos)),
Parsers.allOf(
Parsers.terminal(TokenType.MINUS),
Parsers.terminal(TokenType.INTEGER, lambda val, pos: IntegerLiteral.withValue(int(val), pos)),
createNode=lambda minus, integer: IntegerLiteral.withValue(-integer.value, minus.pos),
name="negativeInteger"
),
name="int"
)
def StringParser(input): @SingletonParser
return Parser.terminal(TokenType.STRING, createNode=StringLiteral.withValue)(input) def StringParser():
return Parsers.terminal(TokenType.STRING, createNode=StringLiteral.withValue)
def NoteParser(input): @SingletonParser
return Parser.terminal(TokenType.NOTE, createNode=NoteLiteral.withValue)(input) def NoteParser():
return Parsers.terminal(TokenType.NOTE, createNode=NoteLiteral.withValue)
def BoolParser(input): @SingletonParser
return Parser.terminal(TokenType.BOOL, createNode=BoolLiteral.withValue)(input) def BoolParser():
return Parsers.terminal(TokenType.BOOL, createNode=BoolLiteral.withValue)
def TypeLiteralParser(input): @SingletonParser
return Parser.terminal(TokenType.TYPE, createNode=TypeLiteral.withValue)(input) def TypeParser():
return Parsers.terminal(TokenType.TYPE, createNode=TypeLiteral.withValue)
def LiteralParser(input): @SingletonParser
return Parser.oneOf( def LiteralParser():
IntegerParser, return Parsers.oneOf(
StringParser, IntegerParser(),
NoteParser, StringParser(),
BoolParser, NoteParser(),
TypeLiteralParser, BoolParser(),
TypeParser(),
name="literal" name="literal"
)(input) )
def AtomParser(input): @SingletonParser
def AtomParser():
from smnp.ast.node.identifier import IdentifierParser from smnp.ast.node.identifier import IdentifierParser
from smnp.ast.node.list import ListParser
from smnp.ast.node.map import MapParser
return Parser.oneOf( return Parsers.oneOf(
LiteralParser, LiteralParser(),
IdentifierParser, IdentifierParser(),
ListParser,
MapParser,
name="atom" name="atom"
)(input) )

View File

@@ -9,9 +9,11 @@ class Block(Node):
def BlockParser(input): def BlockParser(input):
return Parser.loop( parser = Parser.loop(
Parser.terminal(TokenType.OPEN_CURLY), Parser.terminalParser(TokenType.OPEN_CURLY),
Parser.doAssert(StatementParser, f"statement or '{TokenType.CLOSE_CURLY.key}'"), Parser.doAssert(StatementParser, f"statement or '{TokenType.CLOSE_CURLY.key}'"),
Parser.terminal(TokenType.CLOSE_CURLY), Parser.terminalParser(TokenType.CLOSE_CURLY),
createNode=lambda open, statements, close: Block.withChildren(statements, open.pos) createNode=lambda open, statements, close: Block.withChildren(statements, open.pos)
)(input) )
return Parser(parser, "block", [parser])(input)

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

@@ -0,0 +1,32 @@
from smnp.ast.node.list import ListParser
from smnp.ast.node.operator import BinaryOperator
from smnp.ast.node.valuable import Valuable
from smnp.ast.parser import Parsers
from smnp.token.type import TokenType
from smnp.util.singleton import SingletonParser
class Chain(Valuable):
pass
@SingletonParser
def ChainParser():
from smnp.ast.node.atom import AtomParser
itemParser = Parsers.oneOf(
ListParser,
#MapParser,
AtomParser,
name="chainItem"
)
return Parsers.leftAssociativeOperatorParser(
itemParser,
[TokenType.DOT],
itemParser,
lambda left, op, right: Chain.withValue(BinaryOperator.withValues(left, op, right)),
name="chain"
)

View File

@@ -1,4 +1,4 @@
from smnp.ast.node.expression import ExpressionParser from smnp.ast.node.expression import MaxPrecedenceExpressionParser
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 StatementParser from smnp.ast.node.statement import StatementParser
@@ -46,23 +46,23 @@ class IfElse(Node):
def IfElseStatementParser(input): def IfElseStatementParser(input):
ifStatementParser = Parser.allOf( ifStatementParser = Parser.allOf(
Parser.terminal(TokenType.IF), Parser.terminalParser(TokenType.IF),
Parser.terminal(TokenType.OPEN_PAREN), Parser.terminalParser(TokenType.OPEN_PAREN),
ExpressionParser, MaxPrecedenceExpressionParser,
Parser.terminal(TokenType.CLOSE_PAREN), Parser.terminalParser(TokenType.CLOSE_PAREN),
StatementParser, StatementParser,
createNode=lambda _, __, condition, ___, ifStatement: IfElse.createNode(ifStatement, condition), createNode=lambda _, __, condition, ___, ifStatement: IfElse.createNode(ifStatement, condition),
name="if statement" name="if statement"
) )
ifElseStatementParser = Parser.allOf( ifElseStatementParser = Parser.allOf(
Parser.terminal(TokenType.IF), Parser.terminalParser(TokenType.IF),
Parser.terminal(TokenType.OPEN_PAREN, doAssert=True), Parser.terminalParser(TokenType.OPEN_PAREN),
Parser.doAssert(ExpressionParser, "expression"), MaxPrecedenceExpressionParser,
Parser.terminal(TokenType.CLOSE_PAREN, doAssert=True), Parser.terminalParser(TokenType.CLOSE_PAREN),
Parser.doAssert(StatementParser, "statement"), StatementParser,
Parser.terminal(TokenType.ELSE), Parser.terminalParser(TokenType.ELSE),
Parser.doAssert(StatementParser, "statement"), StatementParser,
createNode=lambda _, __, condition, ___, ifStatement, ____, elseStatement: IfElse.createNode(ifStatement, condition, elseStatement), createNode=lambda _, __, condition, ___, ifStatement, ____, elseStatement: IfElse.createNode(ifStatement, condition, elseStatement),
name="if-else statement" name="if-else statement"
) )

View File

@@ -1,111 +1,63 @@
from smnp.ast.node.model import Node from smnp.ast.node.operator import BinaryOperator
from smnp.ast.node.none import NoneNode
from smnp.ast.node.operator import BinaryOperator, Operator
from smnp.ast.node.term import TermParser from smnp.ast.node.term import TermParser
from smnp.ast.node.valuable import Valuable
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
class Sum(BinaryOperator): class Expression(Valuable):
pass pass
class Relation(BinaryOperator):
pass
class And(BinaryOperator):
pass
class Or(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 ExpressionWithoutLoopParser(input):
expr1 = Parser.leftAssociativeOperatorParser(
TermParser,
[TokenType.PLUS, TokenType.MINUS],
TermParser,
lambda left, op, right: Sum.withValues(left, op, right)
)
expr2 = Parser.leftAssociativeOperatorParser(
expr1,
[TokenType.RELATION, TokenType.OPEN_ANGLE, TokenType.CLOSE_ANGLE],
expr1,
lambda left, op, right: Relation.withValues(left, op, right)
)
expr3 = Parser.leftAssociativeOperatorParser(
expr2,
[TokenType.AND],
expr2,
lambda left, op, right: And.withValues(left, op, right)
)
return Parser.leftAssociativeOperatorParser(
expr3,
[TokenType.OR],
expr3,
lambda left, op, right: Or.withValues(left, op, right)
)(input)
def LoopParser(input):
from smnp.ast.node.identifier import IdentifierLiteralParser
from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.statement import StatementParser
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"
)
return Parser.allOf(
ExpressionWithoutLoopParser,
Parser.optional(loopParameters),
Parser.terminal(TokenType.DASH, createNode=Operator.withValue),
StatementParser,
createNode=Loop.loop,
name="dash-loop"
)(input)
def ExpressionParser(input): def ExpressionParser(input):
return Parser.oneOf( return Parser.leftAssociativeOperatorParser(
LoopParser, TermParser,
ExpressionWithoutLoopParser [TokenType.PLUS, TokenType.MINUS],
TermParser,
lambda left, op, right: Expression.withValue(BinaryOperator.withValues(left, op, right))
)(input) )(input)
def Expression2Parser(input):
return Parser.leftAssociativeOperatorParser(
ExpressionParser,
[TokenType.RELATION],
ExpressionParser,
lambda left, op, right: Expression.withValue(BinaryOperator.withValues(left, op, right))
)(input)
def Expression3Parser(input):
return Parser.leftAssociativeOperatorParser(
Expression2Parser,
[TokenType.AND],
Expression2Parser,
lambda left, op, right: Expression.withValue(BinaryOperator.withValues(left, op, right))
)(input)
def Expression4Parser(input):
from smnp.ast.node.condition import IfElse
exprParser = Parser.leftAssociativeOperatorParser(
Expression3Parser,
[TokenType.OR],
Expression3Parser,
lambda left, op, right: Expression.withValue(BinaryOperator.withValues(left, op, right))
)
ifElseExpression = Parser.allOf(
exprParser,
Parser.terminalParser(TokenType.IF),
Expression4Parser,
Parser.terminalParser(TokenType.ELSE),
Expression4Parser,
createNode=lambda ifNode, _, condition, __, elseNode: IfElse.createNode(ifNode, condition, elseNode)
)
return Parser.oneOf(
ifElseExpression,
exprParser,
)(input)
MaxPrecedenceExpressionParser = Expression4Parser

View File

@@ -1,88 +1,70 @@
from smnp.ast.node.block import Block # from smnp.ast.node.block import BlockNode
from smnp.ast.node.function import FunctionDefinitionParser # from smnp.ast.node.function import FunctionDefinitionNode
from smnp.ast.node.identifier import IdentifierLiteralParser # from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.model import Node # from smnp.ast.node.none import NoneNode
from smnp.ast.node.none import NoneNode # from smnp.ast.node.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 Extend(Node): # class ExtendNode(StatementNode):
def __init__(self, pos): # def __init__(self, pos):
super().__init__(pos) # super().__init__(pos)
self.children = [NoneNode(), NoneNode(), NoneNode()] # self.children = [NoneNode(), NoneNode(), NoneNode()]
#
@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 methods(self): # def methods(self):
return self[2] # return self[2]
#
@methods.setter # @methods.setter
def methods(self, value): # def methods(self, value):
self[2] = value # self[2] = value
#
@classmethod # @classmethod
def withValues(cls, pos, type, variable, methods): # def _parse(cls, input):
node = cls(pos) # def createNode(extend, type, asKeyword, variable, methods):
node.type = type # node = ExtendNode(extend.pos)
node.variable = variable # node.type = type
node.methods = methods # node.variable = variable
return node # node.methods = methods
# return node
#
def ExtendParser(input): # return Parser.allOf(
# Parser.terminalParser(TokenType.EXTEND),
simpleExtend = Parser.allOf( # Parser.doAssert(TypeNode.parse, "type being extended"),
Parser.terminal(TokenType.EXTEND), # Parser.terminalParser(TokenType.AS, doAssert=True),
TypeParser, # Parser.doAssert(IdentifierNode.identifierParser(), "variable name"),
Parser.terminal(TokenType.AS), # Parser.doAssert(cls._methodsDeclarationsParser(), "methods declarations"),
IdentifierLiteralParser, # createNode=createNode
Parser.terminal(TokenType.WITH), # )(input)
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), # @classmethod
name="simple extend" # def _methodsDeclarationsParser(cls):
) # def createNode(openBracket, items, closeBracket):
# node = BlockNode(openBracket.pos)
multiExtend = Parser.allOf( # node.children = items
Parser.terminal(TokenType.EXTEND), # return node
Parser.doAssert(TypeParser, "type being extended"), #
Parser.terminal(TokenType.AS, doAssert=True), # return Parser.loop(
Parser.doAssert(IdentifierLiteralParser, "variable name"), # Parser.terminalParser(TokenType.OPEN_CURLY),
Parser.doAssert(MethodsDeclarationParser, f"block with methods definitions or '{TokenType.WITH.key}' keyword"), # Parser.doAssert(FunctionDefinitionNode.parse, f"method declaration or '{TokenType.CLOSE_CURLY.key}'"),
createNode=lambda extend, type, _, variable, methods: Extend.withValues(extend.pos, type, variable, methods), # Parser.terminalParser(TokenType.CLOSE_CURLY),
name="multiple extend" # createNode=createNode
) # )
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)

View File

@@ -1,49 +1,64 @@
from smnp.ast.node.chain import ChainParser
from smnp.ast.node.operator import BinaryOperator, Operator, UnaryOperator from smnp.ast.node.operator import BinaryOperator, Operator, UnaryOperator
from smnp.ast.node.unit import UnitParser from smnp.ast.node.valuable import Valuable
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 Factor(Valuable):
pass
class NotOperator(UnaryOperator): class NotOperator(UnaryOperator):
pass pass
class Loop(BinaryOperator):
class Power(BinaryOperator):
pass pass
def FactorParser(input):
from smnp.ast.node.expression import ExpressionParser
parentheses = Parser.allOf( def FactorParser(input):
Parser.terminal(TokenType.OPEN_PAREN), from smnp.ast.node.expression import MaxPrecedenceExpressionParser
Parser.doAssert(ExpressionParser, "expression"), from smnp.ast.node.statement import StatementParser
Parser.terminal(TokenType.CLOSE_PAREN),
powerFactor = Parser.leftAssociativeOperatorParser(
ChainParser,
[TokenType.DOUBLE_ASTERISK],
ChainParser,
lambda left, op, right: Factor.withValue(BinaryOperator.withValues(left, op, right)),
name="power operator"
)
exprFactor = Parser.allOf(
Parser.terminalParser(TokenType.OPEN_PAREN),
MaxPrecedenceExpressionParser,
Parser.terminalParser(TokenType.CLOSE_PAREN),
createNode=lambda open, expr, close: expr, createNode=lambda open, expr, close: expr,
name="grouping parentheses" name="grouping parentheses"
) )
factorOperands = Parser.oneOf( factorParser = Parser.oneOf(
parentheses, powerFactor,
UnitParser, exprFactor,
name="factor operands" name="basic factor"
)
powerFactor = Parser.leftAssociativeOperatorParser(
factorOperands,
[TokenType.DOUBLE_ASTERISK],
factorOperands,
lambda left, op, right: Power.withValues(left, op, right),
name="power operator"
) )
notOperator = Parser.allOf( notOperator = Parser.allOf(
Parser.terminal(TokenType.NOT, Operator.withValue), Parser.terminalParser(TokenType.NOT, Operator.withValue),
powerFactor, factorParser,
createNode=NotOperator.withValues, createNode=NotOperator.withValues,
name="not" name="not"
) )
loopFactor = Parser.allOf(
factorParser,
Parser.terminalParser(TokenType.DASH, createNode=Operator.withValue),
StatementParser,
createNode=Loop.withValues,
name="dash-loop"
)
return Parser.oneOf( return Parser.oneOf(
loopFactor,
notOperator, notOperator,
powerFactor, factorParser
name="factor"
)(input) )(input)

View File

@@ -1,165 +1,128 @@
from smnp.ast.node.block import BlockParser # from smnp.ast.node.block import BlockNode
from smnp.ast.node.expression import ExpressionParser # 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.type import TypeParser, Type # from smnp.ast.node.statement import StatementNode
from smnp.ast.parser import Parser # from smnp.ast.node.type import TypeNode, TypeSpecifier
from smnp.token.type import TokenType # from smnp.ast.parser import Parser
# from smnp.token.type import TokenType
#
class ArgumentsDeclaration(Node): #
pass # class ArgumentsDeclarationNode(Node):
#
# @classmethod
class Argument(Node): # def _parse(cls, input):
# raise RuntimeError("This class is not supposed to be automatically called")
def __init__(self, pos): #
super().__init__(pos) #
self.children = [NoneNode(), NoneNode(), False, NoneNode()] # class VarargNode(Node):
# pass
@property #
def type(self): #
return self[0] # class ArgumentDefinitionNode(ExpressionNode):
# def __init__(self, pos):
# super().__init__(pos)
@type.setter # self.children.extend([NoneNode(), False])
def type(self, value): #
self[0] = value # @property
# def type(self):
# return self[0]
@property #
def variable(self): # @type.setter
return self[1] # def type(self, value):
# self[0] = value
#
@variable.setter # @property
def variable(self, value): # def variable(self):
self[1] = value # return self[1]
#
# @variable.setter
@property # def variable(self, value):
def vararg(self): # self[1] = value
return self[2] #
# @property
# def vararg(self):
@vararg.setter # return self[2]
def vararg(self, value): #
self[2] = value # @vararg.setter
# def vararg(self, value):
# self[2] = value
@property #
def optionalValue(self): #
return self[3] # @classmethod
# def parser(cls):
@optionalValue.setter # def createNode(type, variable, dots):
def optionalValue(self, value): # node = ArgumentDefinitionNode(type.pos)
self[3] = value # node.type = type
# node.variable = variable
class VarargNode(Node): # node.vararg = isinstance(dots, VarargNode)
pass # return node
#
# return Parser.allOf(
class FunctionDefinition(Node): # Parser.optional(Parser.oneOf(
def __init__(self, pos): # TypeNode.parse,
super().__init__(pos) # TypeSpecifier.parse
self.children = [NoneNode(), NoneNode(), NoneNode()] # )),
# Parser.doAssert(IdentifierNode.identifierParser(), "variable name"),
@property # Parser.optional(Parser.terminalParser(TokenType.DOTS, lambda val, pos: VarargNode(pos))),
def name(self): # createNode=createNode
return self[0] # )
#
@name.setter # @classmethod
def name(self, value): # def _parse(cls, input):
self[0] = value # #TODO
# raise RuntimeError("Not implemented yet. There is still required work to correctly build AST related to IdentifierNode")
@property #
def arguments(self): #
return self[1] #
# class FunctionDefinitionNode(StatementNode):
@arguments.setter # def __init__(self, pos):
def arguments(self, value): # super().__init__(pos)
self[1] = value # self.children = [NoneNode(), NoneNode(), NoneNode()]
#
@property # @property
def body(self): # def name(self):
return self[2] # return self[0]
#
@body.setter # @name.setter
def body(self, value): # def name(self, value):
self[2] = value # self[0] = value
#
@classmethod # @property
def withValues(cls, name, arguments, body): # def arguments(self):
node = cls(name.pos) # return self[1]
node.name = name #
node.arguments = arguments # @arguments.setter
node.body = body # def arguments(self, value):
return node # self[1] = value
#
# @property
def RegularArgumentParser(input): # def body(self):
def createNode(type, variable, vararg): # return self[2]
pos = type.pos if isinstance(type, Type) else variable.pos #
node = Argument(pos) # @body.setter
node.type = type # def body(self, value):
node.variable = variable # self[2] = value
node.vararg = vararg is True #
return node # @classmethod
# def _parse(cls, input):
return Parser.allOf( # def createNode(function, name, arguments, body):
Parser.optional(TypeParser), # node = FunctionDefinitionNode(function.pos)
Parser.doAssert(IdentifierLiteralParser, "argument name"), # node.name = name
Parser.optional(Parser.terminal(TokenType.DOTS, lambda val, pos: True)), # node.arguments = arguments
createNode=createNode, # node.body = body
name="regular function argument" # return node
)(input) #
# return Parser.allOf(
# Parser.terminalParser(TokenType.FUNCTION),
def OptionalArgumentParser(input): # Parser.doAssert(IdentifierNode.identifierParser(), "function name"),
def createNode(type, variable, _, optional): # Parser.doAssert(cls._argumentsDeclarationParser(), "arguments list"),
pos = type.pos if isinstance(type, Type) else variable.pos # Parser.doAssert(BlockNode.parse, "function body"),
node = Argument(pos) # createNode=createNode
node.type = type # )(input)
node.variable = variable #
node.optionalValue = optional # @staticmethod
return node # def _argumentsDeclarationParser():
# return abstractIterableParser(ArgumentsDeclarationNode, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, ArgumentDefinitionNode.parser())
return Parser.allOf(
Parser.optional(TypeParser),
Parser.doAssert(IdentifierLiteralParser, "argument name"),
Parser.terminal(TokenType.ASSIGN),
Parser.doAssert(ExpressionParser, "expression"),
createNode=createNode,
name="optional function argument"
)(input)
def ArgumentParser(input):
return Parser.oneOf(
OptionalArgumentParser,
RegularArgumentParser,
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,11 +1,10 @@
from smnp.ast.node.atom import Atom from smnp.ast.node.atom import Atom
from smnp.ast.node.expression import ExpressionParser
from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.model import Node from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode from smnp.ast.node.none import NoneNode
from smnp.ast.node.operator import BinaryOperator, Operator from smnp.ast.node.operator import BinaryOperator, Operator
from smnp.ast.parser import Parser from smnp.ast.parser import Parsers
from smnp.token.type import TokenType from smnp.token.type import TokenType
from smnp.util.singleton import SingletonParser
class Identifier(Atom): class Identifier(Atom):
@@ -49,26 +48,28 @@ class Assignment(BinaryOperator):
pass pass
def IdentifierLiteralParser(input): @SingletonParser
return Parser.terminal(TokenType.IDENTIFIER, createNode=Identifier.withValue)(input) def IdentifierParser():
identifierLiteralParser = Parsers.terminal(TokenType.IDENTIFIER, createNode=Identifier.withValue)
functionCallParser = Parsers.allOf(
def IdentifierParser(input): identifierLiteralParser,
functionCallParser = Parser.allOf( #abstractIterableParser(ArgumentsList, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, MaxPrecedenceExpressionParser),
IdentifierLiteralParser, createNode=lambda name, arguments: FunctionCall.withChildren(name, arguments),
abstractIterableParser(ArgumentsList, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, Parser.doAssert(ExpressionParser, "expression")), name="functionCall"
createNode=lambda name, arguments: FunctionCall.withChildren(name, arguments)
) )
assignmentParser = Parser.allOf( assignmentParser = Parsers.allOf(
IdentifierLiteralParser, identifierLiteralParser,
Parser.terminal(TokenType.ASSIGN, createNode=Operator.withValue), Parsers.terminal(TokenType.ASSIGN, createNode=Operator.withValue),
Parser.doAssert(ExpressionParser, "expression"), #MaxPrecedenceExpressionParser,
createNode=lambda identifier, assign, expr: Assignment.withValues(identifier, assign, expr) createNode=lambda identifier, assign, expr: Assignment.withValues(identifier, assign, expr),
name="assignment"
) )
return Parser.oneOf( return Parsers.oneOf(
assignmentParser, assignmentParser,
functionCallParser, functionCallParser,
IdentifierLiteralParser identifierLiteralParser,
)(input) name="idExpr"
)

View File

@@ -1,3 +1,11 @@
# from smnp.ast.node.identifier import Identifier
# from smnp.ast.node.model import Node
# from smnp.ast.node.none import NoneNode
# from smnp.ast.node.string import StringLiteralNode
# from smnp.ast.node.type import TypeNode
# from smnp.ast.parser import Parser
# from smnp.token.type import TokenType
#
from smnp.ast.node.atom import StringParser from smnp.ast.node.atom import StringParser
from smnp.ast.node.model import Node from smnp.ast.node.model import Node
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
@@ -26,8 +34,8 @@ class Import(Node):
def ImportParser(input): def ImportParser(input):
return Parser.allOf( return Parser.allOf(
Parser.terminal(TokenType.IMPORT), Parser.terminalParser(TokenType.IMPORT),
Parser.doAssert(StringParser, "import source as string"), StringParser,
createNode=lambda imp, source: Import.withValue(source), createNode=lambda imp, source: Import.withValue(source),
name="import" name="import"
)(input) )(input)

View File

@@ -1,11 +1,11 @@
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
from smnp.ast.parser import Parser from smnp.ast.parser import Parsers, DecoratorParser
from smnp.token.type import TokenType from smnp.token.type import TokenType
def abstractIterableParser(iterableNodeType, openTokenType, closeTokenType, itemParser): def abstractIterableParser(iterableNodeType, openTokenType, closeTokenType, itemParser, name):
class AbstractIterable(Node): class AbstractIterable(Node):
def __init__(self, pos): def __init__(self, pos):
@@ -31,70 +31,74 @@ def abstractIterableParser(iterableNodeType, openTokenType, closeTokenType, item
class AbstractIterableTail(AbstractIterable): class AbstractIterableTail(AbstractIterable):
pass pass
def abstractIterableParser(input): def abstractIterableParser():
return Parser.oneOf( return Parsers.oneOf(
emptyIterable, emptyIterable(),
openIterable openIterable(),
)(input) name=name
)
def emptyIterable(input): def emptyIterable():
def createNode(open, close): def createNode(open, close):
node = AbstractIterable(open.pos) node = AbstractIterable(open.pos)
node.value = open node.value = open
node.next = close node.next = close
return node return node
return Parser.allOf( return Parsers.allOf(
Parser.terminal(openTokenType), Parsers.terminal(openTokenType),
Parser.terminal(closeTokenType), Parsers.terminal(closeTokenType),
createNode=createNode createNode=createNode,
)(input) name=name+"Empty"
)
def openIterable(input): def openIterable():
def createNode(open, item, tail): def createNode(open, item, tail):
node = AbstractIterable(open.pos) node = AbstractIterable(open.pos)
node.value = item node.value = item
node.next = tail node.next = tail
return node return node
return Parser.allOf( return Parsers.allOf(
Parser.terminal(openTokenType), Parsers.terminal(openTokenType),
itemParser, itemParser,
abstractIterableTailParser, abstractIterableTailParser(),
createNode=createNode createNode=createNode,
)(input) name=name+"Open"
)
def abstractIterableTailParser(input): def abstractIterableTailParser():
return Parser.oneOf( return Parsers.oneOf(
closeIterable, closeIterable(),
nextItem, nextItem(),
)(input) name=name+"Tail"
)
def nextItem(input): def nextItem():
def createNode(comma, item, tail): def createNode(comma, item, tail):
node = AbstractIterableTail(item.pos) node = AbstractIterableTail(item.pos)
node.value = item node.value = item
node.next = tail node.next = tail
return node return node
return Parser.allOf( return Parsers.allOf(
Parser.doAssert(Parser.terminal(TokenType.COMMA), f"'{TokenType.COMMA.key}' or '{closeTokenType.key}'"), Parsers.terminal(TokenType.COMMA, doAssert=True),
itemParser, itemParser,
abstractIterableTailParser, abstractIterableTailParser(),
name=name+"NextItem",
createNode=createNode createNode=createNode
)(input) )
def closeIterable(input): def closeIterable():
return Parser.terminal(closeTokenType)(input) return Parsers.terminal(closeTokenType)
return toFlatDesiredNode(iterableNodeType, abstractIterableParser) return abstractIterableParser()
#return toFlatDesiredNode(iterableNodeType, abstractIterableParser())
def toFlatDesiredNode(iterableNodeType, parser): def toFlatDesiredNode(iterableNodeType, parser):
def parse(input): def wrapper(result):
result = parser(input)
if result.result: if result.result:
value = flattenList(result.node) value = flattenList(result.node)
node = iterableNodeType(result.node.pos) node = iterableNodeType(result.node.pos)
@@ -105,7 +109,7 @@ def toFlatDesiredNode(iterableNodeType, parser):
return ParseResult.FAIL() return ParseResult.FAIL()
return Parser(parse, "flat", [parser]) return DecoratorParser(wrapper, parser)
def flattenList(node, output=None): def flattenList(node, output=None):

View File

@@ -1,6 +1,6 @@
from smnp.ast.node.atom import AtomParser
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.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
@@ -8,12 +8,6 @@ class List(Node):
pass pass
def ListParser(input): def ListParser():
from smnp.ast.node.expression import ExpressionParser return abstractIterableParser(List, TokenType.OPEN_SQUARE, TokenType.CLOSE_SQUARE, AtomParser(), name="list")
#MaxPrecedenceExpressionParser)(input)
return abstractIterableParser(
List,
TokenType.OPEN_SQUARE,
TokenType.CLOSE_SQUARE,
Parser.doAssert(ExpressionParser, "expression")
)(input)

View File

@@ -1,8 +1,7 @@
from smnp.ast.node.atom import LiteralParser
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.operator import BinaryOperator, Operator from smnp.ast.node.operator import BinaryOperator, Operator
from smnp.ast.parser import Parser from smnp.ast.parser import Parsers
from smnp.token.type import TokenType from smnp.token.type import TokenType
@@ -30,21 +29,19 @@ class Map(Node):
def MapParser(input): def MapParser(input):
from smnp.ast.node.expression import ExpressionParser from smnp.ast.node.atom import LiteralParser
#from smnp.ast.node.expression import MaxPrecedenceExpressionParser
keyParser = LiteralParser keyParser = LiteralParser
valueParser = ExpressionParser #valueParser = MaxPrecedenceExpressionParser
mapEntryParser = Parser.allOf( mapEntryParser = Parsers.allOf(
keyParser, keyParser,
Parser.terminal(TokenType.ARROW, createNode=Operator.withValue, doAssert=True), Parsers.terminal(TokenType.ARROW, createNode=Operator.withValue),
Parser.doAssert(valueParser, "expression"), #valueParser,
createNode=MapEntry.withValues createNode=MapEntry.withValues,
name="mapEntry"
) )
return abstractIterableParser( return abstractIterableParser(Map, TokenType.OPEN_CURLY, TokenType.CLOSE_CURLY, mapEntryParser)
Map,
TokenType.OPEN_CURLY,
TokenType.CLOSE_CURLY,
mapEntryParser
)(input)

View File

@@ -28,6 +28,21 @@ 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)

View File

@@ -68,7 +68,68 @@ class BinaryOperator(Node):
node.right = right node.right = right
return node return node
#
# class LeftAssociativeOperatorNode(ExpressionNode):
# def __init__(self, pos):
# super().__init__(pos)
# self.children = [NoneNode(), NoneNode(), NoneNode()]
#
# @property
# def left(self):
# return self[0]
#
# @left.setter
# def left(self, value):
# self[0] = value
#
# @property
# def operator(self):
# return self[1]
#
# @operator.setter
# def operator(self, value):
# self[1] = value
#
# @property
# def right(self):
# return self[2]
#
# @right.setter
# def right(self, value):
# self[2] = value
#
# @classmethod
# def _parse(cls, input):
# def createNode(left, operator, right):
# node = LeftAssociativeOperatorNode(right.pos)
# node.left = left
# node.operator = operator
# node.right = right
# return node
#
# return Parser.leftAssociativeOperatorParser(
# cls._lhsParser(),
# TokenType.DOT,
# cls._rhsParser(),
# createNode=createNode
# )(input)
#
# @classmethod
# def _lhsParser(cls):
# raise RuntimeError(f"LHS parser is not implemented in {cls.__name__}")
#
# @staticmethod
# def _rhsParser():
# from smnp.ast.node.identifier import IdentifierNode
#
# return Parser.oneOf(
# # TODO!!!
# IdentifierNode._lhsParser(),
# IdentifierNode._functionCallParser(),
# exception=lambda input: SyntaxException(f"Expected property name or method call, found '{input.current().rawValue}'", input.currentPos())
# )
#
#
class Operator(Node): class Operator(Node):
def __init__(self, pos): def __init__(self, pos):
super().__init__(pos) super().__init__(pos)

View File

@@ -1,36 +1,44 @@
from smnp.ast.node.extend import ExtendParser
from smnp.ast.node.function import FunctionDefinitionParser
from smnp.ast.node.imports import ImportParser from smnp.ast.node.imports import ImportParser
from smnp.ast.node.model import Node, ParseResult from smnp.ast.node.model import Node, ParseResult
from smnp.ast.node.statement import StatementParser from smnp.ast.node.statement import StatementParser
from smnp.ast.parser import Parser from smnp.ast.parser import Parser
from smnp.error.syntax import SyntaxException
class Program(Node): class Program(Node):
def __init__(self): def __init__(self):
super().__init__((-1, -1)) super().__init__((-1, -1))
def ProgramParser(input): def parse(input):
def parse(input):
root = Program() root = Program()
# Start Symbol
startSymbolParser = Parser.oneOf(
ImportParser,
FunctionDefinitionParser,
ExtendParser,
StatementParser,
exception=lambda inp: SyntaxException(f"Invalid statement: {inp.currentToEndOfLine()}", inp.current().pos),
name="start symbol"
)
while input.hasCurrent(): while input.hasCurrent():
result = startSymbolParser(input) result = Parser.oneOf(
# Start Symbol
ImportParser,
StatementParser,
exception=RuntimeError("Nie znam tego wyrazenia")
)(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) ProgramParser = Parser(parse, name="program")
# @classmethod
# def _parse(cls, input):
# def parseToken(input):
# return Parser.oneOf(
# FunctionDefinitionNode.parse,
# ExtendNode.parse,
# ExpressionNode.parse,
# ImportNode.parse,
# StatementNode.parse,
# exception = SyntaxException(f"Invalid statement: {input.currentToEndOfLine()}", input.current().pos)
# )(input)
#
# root = Program()
# while input.hasCurrent():
# result = parseToken(input)
# if result.result:
# root.append(result.node)
# return ParseResult.OK(root)

View File

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

View File

@@ -1,6 +1,6 @@
from smnp.ast.node.expression import MaxPrecedenceExpressionParser
from smnp.ast.node.model import Node 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
class Statement(Node): class Statement(Node):
@@ -10,28 +10,25 @@ class Statement(Node):
def StatementParser(input): def StatementParser(input):
from smnp.ast.node.block import BlockParser from smnp.ast.node.block import BlockParser
from smnp.ast.node.condition import IfElseStatementParser from smnp.ast.node.condition import IfElseStatementParser
from smnp.ast.node.expression import ExpressionParser
from smnp.ast.node.ret import ReturnParser
from smnp.ast.node.throw import ThrowParser
return withSemicolon( parser = Parser.oneOf(
Parser.oneOf(
IfElseStatementParser, IfElseStatementParser,
ExpressionParser, # Must be above BlockParser because of Map's syntax with curly braces
BlockParser, BlockParser,
ReturnParser, MaxPrecedenceExpressionParser
ThrowParser,
name="statement"
), optional=True)(input)
def withSemicolon(parser, optional=False, doAssert=False):
semicolonParser = Parser.optional(Parser.terminal(TokenType.SEMICOLON)) if optional else Parser.terminal(
TokenType.SEMICOLON, doAssert=doAssert)
return Parser.allOf(
parser,
semicolonParser,
createNode=lambda stmt, semicolon: stmt,
name="semicolon" + "?" if optional else ""
) )
return Parser(parser, "statement", parser)(input)
# class StatementNode(Node):
#
# @classmethod
# def _parse(cls, input):
# from smnp.ast.node.block import BlockNode
# from smnp.ast.node.expression import ExpressionNode
# from smnp.ast.node.ret import ReturnNode
#
# return Parser.oneOf(
# ExpressionNode.parse,
# BlockNode.parse,
# ReturnNode.parse,
# )(input)

View File

@@ -1,17 +1,12 @@
from smnp.ast.node.factor import FactorParser from smnp.ast.node.factor import FactorParser
from smnp.ast.node.operator import BinaryOperator from smnp.ast.node.operator import BinaryOperator
from smnp.ast.node.valuable import Valuable
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 Product(BinaryOperator): class Term(Valuable):
pass pass
TermParser = Parser.leftAssociativeOperatorParser(FactorParser, [TokenType.ASTERISK, TokenType.SLASH], FactorParser,
def TermParser(input): lambda left, op, right: Term.withValue(BinaryOperator.withValues(left, op, right)))
return Parser.leftAssociativeOperatorParser(
FactorParser,
[TokenType.ASTERISK, TokenType.SLASH],
FactorParser,
lambda left, op, right: Product.withValues(left, op, right)
)(input)

View File

@@ -1,17 +0,0 @@
from smnp.ast.node.expression import ExpressionParser
from smnp.ast.node.valuable import Valuable
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class Throw(Valuable):
pass
def ThrowParser(input):
return Parser.allOf(
Parser.terminal(TokenType.THROW),
Parser.doAssert(ExpressionParser, "error message as string"),
createNode=lambda throw, message: Throw.withValue(message, throw.pos),
name="throw"
)(input)

View File

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

View File

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

View File

@@ -16,7 +16,7 @@ class Valuable(Node):
self[0] = value self[0] = value
@classmethod @classmethod
def withValue(cls, value, pos=None): def withValue(cls, value):
node = cls(value.pos if pos is None else pos) node = cls(value.pos)
node.value = value node.value = value
return node return node

View File

@@ -9,15 +9,8 @@ def parse(input):
return ProgramParser(input).node return ProgramParser(input).node
class Parser: class Parser(object):
def __init__(self, parse, name=None, parsers=None): def __init__(self, name):
if parsers is None:
parsers = []
self.parsers = parsers
self._parse = parse
if name is None:
name = parse.__name__
self.name = name self.name = name
def parse(self, input): def parse(self, input):
@@ -26,10 +19,27 @@ class Parser:
return ParseResult.FAIL() return ParseResult.FAIL()
if not isinstance(result, ParseResult): if not isinstance(result, ParseResult):
raise RuntimeError(f"_parse() method of '{self.__class__.__name__}' class haven't returned ParseResult object") raise RuntimeError(
f"_parse() method of '{self.__class__.__name__}' class haven't returned ParseResult object")
return result return result
def _parse(self, input):
raise RuntimeError(f"_name method of '{self.__class__.__name__}' class is not implemented")
def grammar(self):
rules = []
self._grammarRules(rules)
return "\n".join(self._uniq(rules))
def _uniq(self, seq):
seen = set()
seen_add = seen.add
return [x for x in seq if not (x in seen or seen_add(x))]
def _grammarRules(self, output):
output.append(f"class '{self.__class__.__name__}' does not implement _grammarRules() method")
def __call__(self, input): def __call__(self, input):
return self.parse(input) return self.parse(input)
@@ -39,186 +49,242 @@ class Parser:
def __repr__(self): def __repr__(self):
return self.__str__() return self.__str__()
# a -> A
@staticmethod
def terminal(expectedType, createNode=None, doAssert=False):
def provideNode(value, pos):
if createNode is None:
return IgnoredNode(pos)
return createNode(value, pos)
def parse(input): class Parsers:
if input.hasCurrent() and input.current().type == expectedType:
@staticmethod
def terminal(expectedType, createNode=lambda val, pos: IgnoredNode(pos), doAssert=False):
return TerminalParser(expectedType, createNode, doAssert)
@staticmethod
def oneOf(*parsers, name, exception=None):
return OneOfParser(*parsers, name=name, exception=exception)
@staticmethod
def allOf(*parsers, createNode, exception=None, name):
return AllOfParser(*parsers, createNode=createNode, exception=exception, name=name)
@staticmethod
def leftAssociativeOperatorParser(leftParser, operatorTokenTypes, rightParser, createNode, name):
return LeftAssociativeOperatorParser(leftParser, operatorTokenTypes, rightParser, createNode, name)
@staticmethod
def many(itemParser, createNode, name):
return ManyParser(itemParser, createNode, name)
@staticmethod
def optional(parser, name):
return OptionalParser(parser, name)
@staticmethod
def loop(startParser, itemParser, endParser, createNode, name):
return LoopParser(startParser, itemParser, endParser, createNode, name)
class DecoratorParser(Parser):
def __init__(self, wrapper, parser):
super().__init__(parser.name)
self.wrapper = wrapper
self.parser = parser
self._grammarRules = parser._grammarRules
def _parse(self, input):
result = self.parser.parse(input)
return self.wrapper(result)
class TerminalParser(Parser):
def __init__(self, expectedType, createNode=lambda val, pos: IgnoredNode(pos), doAssert=False):
super().__init__(expectedType.name.lower())
self.expectedType = expectedType
self.createNode = createNode
self.doAssert = doAssert
def _grammarRules(self, output):
output.append(f"{self.name} -> '{self.expectedType.value}'")
def _parse(self, input):
if input.isCurrent(self.expectedType):
token = input.current() token = input.current()
input.ahead() input.ahead()
return ParseResult.OK(provideNode(token.value, token.pos)) return ParseResult.OK(self.createNode(token.value, token.pos))
elif doAssert: elif self.doAssert:
found = f", found '{input.current().rawValue}'" if input.hasCurrent() else "" found = f", found '{input.current().rawValue}'" if input.hasCurrent() else ""
raise SyntaxException(f"Expected '{expectedType.key}'{found}", input.currentPos()) raise SyntaxException(f"Expected '{self.expectedType.key}'{found}", input.currentPos())
return ParseResult.FAIL() return ParseResult.FAIL()
return Parser(parse, name=expectedType.name.lower())
# oneOf -> a | b | c | ...
@staticmethod class OneOfParser(Parser):
def oneOf(*parsers, assertExpected=None, exception=None, name="or"): def __init__(self, *parsers, name, exception=None):
def combinedParser(input): super().__init__(name)
self.parsers = parsers
self.exception = exception
def _parse(self, input):
snap = input.snapshot() snap = input.snapshot()
for parser in parsers: for parser in self.parsers:
value = parser(input) value = parser.parse(input)
if value.result: if value.result:
return value return value
input.reset(snap) input.reset(snap) # TODO sprawdzic, czy koneiczne !!!!!
if assertExpected is not None: if self.exception is not None:
found = f", found '{input.current().rawValue}'" if input.hasCurrent() else "" if callable(self.exception):
raise SyntaxException(f"Expected {assertExpected}{found}", input.currentPos()) raise self.exception(input)
if exception is not None:
if callable(exception):
raise exception(input)
else: else:
raise exception raise self.exception
input.reset(snap)
return ParseResult.FAIL() return ParseResult.FAIL()
return Parser(combinedParser, name=name, parsers=parsers) def _grammarRules(self, output):
output.extend([ f"{self.name} -> {parser.name}" for parser in self.parsers ])
[ parser._grammarRules(output) for parser in self.parsers ]
class AllOfParser(Parser):
def __init__(self, *parsers, createNode, exception=None, name):
super().__init__(name)
# allOf -> a b c ...
@staticmethod
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")
def extendedParser(input): self.parsers = parsers
self.createNode = createNode
self.exception = exception
def _parse(self, input):
snap = input.snapshot() snap = input.snapshot()
parsedItems = []
results = [] for parser in self.parsers:
result = parser.parse(input)
for parser in parsers:
result = parser(input)
if not result.result: if not result.result:
if exception is not None: if self.exception is not None:
if callable(exception): if callable(self.exception):
raise exception(input) raise self.exception(input)
else: else:
raise exception raise self.exception
input.reset(snap) input.reset(snap)
return ParseResult.FAIL() return ParseResult.FAIL()
results.append(result.node) parsedItems.append(result.node)
node = createNode(*results) node = self.createNode(*parsedItems)
if not isinstance(node, Node): if not isinstance(node, Node):
raise RuntimeError("Function 'createNode' haven't returned a Node object. Probably forget to pass 'return'") raise RuntimeError(f"Method 'createNode' of class '{self.__class__.__name__}' haven't returned a Node object. Probably forget to pass 'return'")
return ParseResult.OK(node) return ParseResult.OK(node)
def _grammarRules(self, output):
return Parser(extendedParser, name=name, parsers=parsers) output.append(self.name + ' -> ' + ' '.join([ parser.name for parser in self.parsers ]))
[ parser._grammarRules(output) for parser in self.parsers ]
# leftAssociative -> left | left OP right class LeftAssociativeOperatorParser(Parser):
@staticmethod def __init__(self, leftParser, operatorTokenTypes, rightParser, createNode, name):
def leftAssociativeOperatorParser(leftParser, operatorTokenTypes, rightParser, createNode, name="leftAssoc"):
from smnp.ast.node.operator import Operator from smnp.ast.node.operator import Operator
def parse(input): super().__init__(name)
operatorParser = Parser.oneOfTerminals(*operatorTokenTypes, createNode=lambda val, pos: Operator.withChildren([val], pos)) self.leftParser = leftParser
left = leftParser(input) self.rightParser = rightParser
self.createNode = createNode
self.operators = operatorTokenTypes
operatorParsers = [ TerminalParser(expectedType, createNode=lambda val, pos: Operator.withValue(val, pos)) for expectedType in operatorTokenTypes ]
self.operatorParser = OneOfParser(*operatorParsers, name="not important")
def _parse(self, input):
snap = input.snapshot()
left = self.leftParser.parse(input)
if left.result: if left.result:
operator = operatorParser(input) operator = self.operatorParser.parse(input)
while operator.result: while operator.result:
right = rightParser(input) right = self.rightParser.parse(input)
left = ParseResult.OK(createNode(left.node, operator.node, right.node)) left = ParseResult.OK(self.createNode(left.node, operator.node, right.node))
operator = operatorParser(input) operator = self.operatorParser.parse(input)
return left return left
return ParseResult.FAIL() return ParseResult.FAIL()
return Parser(parse, name=name, parsers=[leftParser, '|'.join([t.value for t in operatorTokenTypes]), rightParser])
@staticmethod def _grammarRules(self, output):
def oneOfTerminals(*tokenTypes, createNode=None): output.append('\n'.join([f"{self.name} -> {self.leftParser.name} {operator.name.lower()} {self.rightParser.name} | {self.leftParser.name}" for operator in self.operators]))
return Parser.oneOf(*[Parser.terminal(expectedType, createNode=createNode) for expectedType in tokenTypes], name='|'.join([t.value for t in tokenTypes])) self.leftParser._grammarRules(output)
self.rightParser._grammarRules(output)
# loop -> start item* end
@staticmethod class ManyParser(Parser):
def loop(startParser, itemParser, endParser, createNode, name="loop"): def __init__(self, itemParser, createNode, name):
def parse(input): super().__init__(name)
self.itemParser = itemParser
self.createNode = createNode
def _parse(self, input):
snap = input.snapshot()
parsedItems = []
pos = input.currentPos()
while True:
result = self.itemParser.parse(input)
if result.result:
parsedItems.append(result.node)
snap = input.snapshot()
else:
input.reset(snap)
return ParseResult.OK(self.createNode(parsedItems, pos) if len(parsedItems) > 0 else NoneNode())
def _grammarRules(self, output):
output.append(f"{self.name} -> {self.itemParser.name}*")
self.itemParser._grammarRules(output)
class OptionalParser(Parser):
def __init__(self, parser, name):
super().__init__(name)
self.parser = parser
def _parse(self, input):
result = self.parser.parse(input)
if result.result:
return result
return ParseResult.OK(NoneNode())
def _grammarRules(self, output):
output.append(f"{self.name} -> {self.parser.name}?")
class LoopParser(Parser):
def __init__(self, startParser, itemParser, endParser, createNode, name):
super().__init__(name)
self.startParser = startParser
self.itemParser = itemParser
self.endParser = endParser
self.createNode = createNode
def _parse(self, input):
items = [] items = []
start = startParser(input) start = self.startParser.parse(input)
if start.result: if start.result:
while True: while True:
end = endParser(input) end = self.endParser.parse(input)
if end.result: if end.result:
return ParseResult.OK(createNode(start.node, items, end.node)) return ParseResult.OK(self.createNode(start.node, items, end.node))
item = itemParser(input) item = self.itemParser.parse(input)
if not item.result: if not item.result:
return ParseResult.FAIL() return ParseResult.FAIL()
items.append(item.node) items.append(item.node)
return ParseResult.FAIL() return ParseResult.FAIL()
return Parser(parse, name, parsers=[startParser, itemParser, endParser]) def _grammarRules(self, output):
output.append(f"{self.name} -> {self.startParser.name} {self.itemParser.name}* {self.endParser.name}")
self.startParser._grammarRules(output)
self.itemParser._grammarRules(output)
self.endParser._grammarRules(output)
@staticmethod
def doAssert(parser, expected, name="!!"):
def parse(input):
result = parser(input)
if not result.result:
found = f", found '{input.current().rawValue}'" if input.hasCurrent() else ''
raise SyntaxException(f"Expected {expected}{found}", input.currentPos())
return result
return Parser(parse, name, parsers=parser)
@staticmethod
def optional(parser, name="??"):
def parse(input):
result = parser(input)
if result.result:
return result
return ParseResult.OK(NoneNode())
return Parser(parse, name, parsers=[parser])
@staticmethod
def epsilon():
return lambda *args: ParseResult.OK(NoneNode())
@staticmethod
def many(parser, createNode, name="*"):
def parse(input):
results = []
snap = input.snapshot()
pos = input.currentPos()
while True:
result = parser(input)
if result.result:
results.append(result.node)
snap = input.snapshot()
else:
input.reset(snap)
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

View File

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

View File

@@ -1,19 +1,17 @@
from smnp.error.function import FunctionNotFoundException, MethodNotFoundException, IllegalFunctionInvocationException from smnp.error.function import FunctionNotFoundException, MethodNotFoundException, IllegalFunctionInvocationException
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.function.tools import argsTypesToString from smnp.function.tools import argsTypesToString
from smnp.runtime.evaluators.function import BodyEvaluator, Return from smnp.runtime.evaluators.function import BodyEvaluator
from smnp.type.model import Type
class Environment(): class Environment():
def __init__(self, scopes, functions, methods, source): def __init__(self, scopes, functions, methods):
self.scopes = scopes self.scopes = scopes
self.functions = functions self.functions = functions
self.methods = methods self.methods = methods
self.customFunctions = [] self.customFunctions = []
self.customMethods = [] self.customMethods = []
self.callStack = [] self.callStack = []
self.source = source
def invokeMethod(self, object, name, args): def invokeMethod(self, object, name, args):
builtinMethodResult = self._invokeBuiltinMethod(object, name, args) builtinMethodResult = self._invokeBuiltinMethod(object, name, args)
@@ -40,15 +38,10 @@ class Environment():
if method.typeSignature.check([object])[0] and method.name == name: #Todo sprawdzic sygnature typu if method.typeSignature.check([object])[0] and method.name == name: #Todo sprawdzic sygnature typu
signatureCheckresult = method.signature.check(args) signatureCheckresult = method.signature.check(args)
if signatureCheckresult[0]: if signatureCheckresult[0]:
self.scopes.append(method.defaultArgs) self.scopes.append({argName: argValue for argName, argValue in zip(method.arguments, list(signatureCheckresult[1:]))})
self.scopes[-1].update({argName: argValue for argName, argValue in zip(method.arguments, list(signatureCheckresult[1:]))})
self.scopes[-1][method.alias] = object self.scopes[-1][method.alias] = object
self.callStack.append(CallStackItem(name)) self.callStack.append(CallStackItem(name))
result = Type.void() result = BodyEvaluator.evaluate(method.body, self).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
try:
BodyEvaluator.evaluate(method.body, self).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
except Return as r:
result = r.value
self.callStack.pop(-1) self.callStack.pop(-1)
self.scopes.pop(-1) self.scopes.pop(-1)
return (True, result) return (True, result)
@@ -81,25 +74,20 @@ class Environment():
if function.name == name: if function.name == name:
signatureCheckresult = function.signature.check(args) signatureCheckresult = function.signature.check(args)
if signatureCheckresult[0]: if signatureCheckresult[0]:
self.scopes.append(function.defaultArgs) self.scopes.append({ argName: argValue for argName, argValue in zip(function.arguments, list(signatureCheckresult[1:])) })
self.scopes[-1].update({ argName: argValue for argName, argValue in zip(function.arguments, list(signatureCheckresult[1:])) })
self.callStack.append(CallStackItem(name)) self.callStack.append(CallStackItem(name))
result = Type.void() result = BodyEvaluator.evaluate(function.body, self).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
try:
BodyEvaluator.evaluate(function.body, self).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
except Return as r:
result = r.value
self.callStack.pop(-1) self.callStack.pop(-1)
self.scopes.pop(-1) self.scopes.pop(-1)
return (True, result) return (True, result)
raise IllegalFunctionInvocationException(f"{function.name}{function.signature.string}", f"{name}{argsTypesToString(args)}") raise IllegalFunctionInvocationException(f"{function.name}{function.signature.string}", f"{name}{argsTypesToString(args)}")
return (False, None) return (False, None)
def addCustomFunction(self, name, signature, arguments, body, defaultArguments): def addCustomFunction(self, name, signature, arguments, body):
if len([fun for fun in self.functions + self.customFunctions if fun.name == name]) > 0: if len([fun for fun in self.functions + self.customFunctions if fun.name == name]) > 0:
raise RuntimeException(f"Cannot redeclare function '{name}'", None) raise RuntimeException(f"Cannot redeclare function '{name}'", None)
self.customFunctions.append(CustomFunction(name, signature, arguments, body, defaultArguments)) self.customFunctions.append(CustomFunction(name, signature, arguments, body))
# TODO: # TODO:
# There is still problem with checking existing of generic types, like lists: # There is still problem with checking existing of generic types, like lists:
@@ -110,14 +98,14 @@ class Environment():
# function foo() { return 2 } # function foo() { return 2 }
# } # }
# Then calling [1, 2, 3, 4].foo() will produce 1, when the second method is more suitable # Then calling [1, 2, 3, 4].foo() will produce 1, when the second method is more suitable
def addCustomMethod(self, typeSignature, alias, name, signature, arguments, body, defaultArguments): def addCustomMethod(self, typeSignature, alias, name, signature, arguments, body):
if len([m for m in self.methods if m.name == name and m.signature.matchers[0] == typeSignature.matchers[0]]) > 0: if len([m for m in self.methods if m.name == name and m.signature.matchers[0] == typeSignature.matchers[0]]) > 0:
raise RuntimeException(f"Cannot redeclare method '{name}' for type '{typeSignature.matchers[0]}'", None) raise RuntimeException(f"Cannot redeclare method '{name}' for type '{typeSignature.matchers[0]}'", None)
if len([m for m in self.customMethods if m.name == name and m.typeSignature.matchers[0] == typeSignature.matchers[0]]) > 0: if len([m for m in self.customMethods if m.name == name and m.typeSignature.matchers[0] == typeSignature.matchers[0]]) > 0:
raise RuntimeException(f"Cannot redeclare method '{name}' for type '{typeSignature.matchers[0]}'", None) raise RuntimeException(f"Cannot redeclare method '{name}' for type '{typeSignature.matchers[0]}'", None)
self.customMethods.append(CustomMethod(typeSignature, alias, name, signature, arguments, body, defaultArguments)) self.customMethods.append(CustomMethod(typeSignature, alias, name, signature, arguments, body))
def findVariable(self, name, type=None, pos=None): def findVariable(self, name, type=None, pos=None):
for scope in reversed(self.scopes): for scope in reversed(self.scopes):
@@ -174,23 +162,22 @@ class Environment():
class CallStackItem: class CallStackItem:
def __init__(self, function): def __init__(self, function):
self.function = function self.function = function
self.value = None
class CustomFunction: class CustomFunction:
def __init__(self, name, signature, arguments, body, defaultArgs): def __init__(self, name, signature, arguments, body):
self.name = name self.name = name
self.signature = signature self.signature = signature
self.arguments = arguments self.arguments = arguments
self.body = body self.body = body
self.defaultArgs = defaultArgs
class CustomMethod: class CustomMethod:
def __init__(self, typeSignature, alias, name, signature, arguments, body, defaultArgs): def __init__(self, typeSignature, alias, name, signature, arguments, body):
self.typeSignature = typeSignature self.typeSignature = typeSignature
self.alias = alias self.alias = alias
self.name = name self.name = name
self.signature = signature self.signature = signature
self.arguments = arguments self.arguments = arguments
self.body = body self.body = body
self.defaultArgs = defaultArgs

View File

@@ -1,3 +1,7 @@
def createEnvironment(): from smnp.environment.environment import Environment
return from smnp.module import functions, methods
def createEnvironment():
return Environment([{}], functions, methods)

View File

@@ -2,7 +2,6 @@ class SmnpException(Exception):
def __init__(self, msg, pos): def __init__(self, msg, pos):
self.msg = msg self.msg = msg
self.pos = pos self.pos = pos
self.file = None
def _title(self): def _title(self):
pass pass
@@ -11,10 +10,7 @@ class SmnpException(Exception):
return "" return ""
def _position(self): def _position(self):
return "" if self.pos is None else f"[line {self.pos[0]+1}, col {self.pos[1]+1}]" return "" if self.pos is None else f" [line {self.pos[0]+1}, col {self.pos[1]+1}]"
def _file(self):
return "" if self.file is None else f"File: {self.file}"
def message(self): def message(self):
return f"{self._title()}\n{self._file()} {self._position()}\n\n{self.msg}\n{self._postMessage()}" return f"{self._title()}{self._position()}:\n{self.msg}\n{self._postMessage()}"

View File

@@ -1,12 +0,0 @@
from smnp.error.runtime import RuntimeException
class CustomException(RuntimeException):
def __init__(self, message, pos):
super().__init__(message, pos)
def _title(self):
return "Execution Error"
def _postMessage(self):
return "\n" + self.environment.callStackToString() if len(self.environment.callStack) > 0 else ""

View File

@@ -12,9 +12,6 @@ class Signature:
def varargSignature(varargMatcher, *basicSignature, wrapVarargInValue=False): def varargSignature(varargMatcher, *basicSignature, wrapVarargInValue=False):
def check(args): def check(args):
if any([ matcher.optional for matcher in [ varargMatcher, *basicSignature ]]):
raise RuntimeError("Vararg signature can't have optional arguments")
if len(basicSignature) > len(args): if len(basicSignature) > len(args):
return doesNotMatchVararg(basicSignature) return doesNotMatchVararg(basicSignature)
@@ -41,7 +38,7 @@ def doesNotMatchVararg(basicSignature):
def signature(*signature): def signature(*signature):
def check(args): def check(args):
if len(args) > len(signature) or len(args) < len([ matcher for matcher in signature if not matcher.optional ]): if len(signature) != len(args):
return doesNotMatch(signature) return doesNotMatch(signature)
for s, a in zip(signature, args): for s, a in zip(signature, args):
@@ -55,12 +52,6 @@ def signature(*signature):
return Signature(check, string, signature) return Signature(check, string, signature)
def optional(matcher):
matcher.optional = True
matcher.string += "?"
return matcher
def doesNotMatch(sign): def doesNotMatch(sign):
return (False, *[None for n in sign]) return (False, *[None for n in sign])

View File

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

View File

@@ -1,14 +1,32 @@
import sys from smnp.ast.node.atom import AtomParser
from smnp.ast.node.chain import ChainParser
from smnp.ast.node.list import ListParser
from smnp.ast.node.model import Node
from smnp.error.base import SmnpException from smnp.error.base import SmnpException
from smnp.library.loader import loadStandardLibrary from smnp.token.tokenizer import tokenize
from smnp.program.interpreter import Interpreter
def main(): def main():
try: try:
stdLibraryEnv = loadStandardLibrary() # stdLibraryEnv = loadStandardLibrary()
Interpreter.interpretFile(sys.argv[1], printTokens=False, printAst=False, execute=True, baseEnvironment=stdLibraryEnv) # Interpreter.interpretFile(sys.argv[1], printTokens=True, printAst=True, execute=False, baseEnvironment=None)
# draft()
class TestNode(Node):
def __init__(self, children):
super().__init__((-1, -1))
self.children = children
tokens = tokenize(['[1, 2]'])
parser = ListParser()
#print(parser.grammar())
res = parser.parse(tokens)
print()
if res.result:
res.node.print()
else:
print("nie sparsowano")
except SmnpException as e: except SmnpException as e:
print(e.message()) print(e.message())

View File

@@ -1,4 +1,4 @@
from smnp.module import system, mic, note, iterable, sound, synth, string, util from smnp.module import system, mic, note, iterable, sound, synth, string, util
functions = [ *system.functions, *mic.functions, *note.functions, *iterable.functions, *sound.functions, *synth.functions, *string.functions, *util.functions ] functions = [ *system.functions, *mic.functions, *note.functions, *iterable.functions, *sound.functions, *synth.functions, *string.functions, *util.functions ]
methods = [ *system.methods, *mic.methods, *note.methods, *iterable.methods, *sound.methods, *synth.methods, *string.methods, *util.methods ] methods = [ *system.methods, *mic.methods, *note.methods, *iterable.methods, *sound.methods, *synth.methods, *string.functions, *util.methods ]

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
from smnp.module.note.function import note from smnp.module.note.function import tuplet, transpose, semitones, octave, duration, interval
functions = [ note.function ] functions = [ semitones.function, interval.function, transpose.function, tuplet.function ]
methods = [] methods = [ duration.function, octave.function ]

View File

@@ -0,0 +1,11 @@
from smnp.function.model import Function
from smnp.function.signature import signature
from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType
_signature = signature(ofType(Type.NOTE), ofType(Type.INTEGER))
def _function(env, note, duration):
return Type.note(note.value.withDuration(duration.value))
function = Function(_signature, _function, 'withDuration')

View File

@@ -0,0 +1,27 @@
from smnp.function.model import Function, CombinedFunction
from smnp.function.signature import varargSignature
from smnp.note.interval import intervalToString
from smnp.note.model import Note
from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOf
from smnp.type.signature.matcher.type import ofTypes
_signature1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
def _function1(env, vararg):
withoutPauses = [note.value for note in vararg if note.type == Type.NOTE]
if len(withoutPauses) < 2:
return Type.list([])
semitones = [Note.checkInterval(withoutPauses[i-1], withoutPauses[i]) for i in range(1, len(withoutPauses))]
return Type.list([Type.string(intervalToString(s)) for s in semitones]).decompose()
_signature2 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
def _function2(env, vararg):
return Type.list([_function1(env, arg.value) for arg in vararg]).decompose()
function = CombinedFunction(
'interval',
Function(_signature1, _function1),
Function(_signature2, _function2)
)

View File

@@ -1,11 +0,0 @@
from smnp.function.model import Function
from smnp.function.signature import signature
from smnp.note.model import Note
from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType
_signature = signature(ofType(Type.STRING), ofType(Type.INTEGER), ofType(Type.INTEGER), ofType(Type.BOOL))
def _function(env, note, octave, duration, dot):
return Type.note(Note(note.value, octave.value, duration.value, dot.value))
function = Function(_signature, _function, 'Note')

View File

@@ -0,0 +1,11 @@
from smnp.function.model import Function
from smnp.function.signature import signature
from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType
_signature = signature(ofType(Type.NOTE), ofType(Type.INTEGER))
def _function(env, note, octave):
return Type.note(note.value.withOctave(octave.value))
function = Function(_signature, _function, 'withOctave')

View File

@@ -0,0 +1,25 @@
from smnp.function.model import Function, CombinedFunction
from smnp.function.signature import varargSignature
from smnp.note.model import Note
from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOf
from smnp.type.signature.matcher.type import ofTypes
_signature1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
def _function1(env, vararg):
withoutPauses = [note.value for note in vararg if note.type == Type.NOTE]
if len(withoutPauses) < 2:
return Type.list([])
return Type.list([Type.integer(Note.checkInterval(withoutPauses[i-1], withoutPauses[i])) for i in range(1, len(withoutPauses))]).decompose()
_signature2 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
def _function2(env, vararg):
return Type.list([_function1(env, arg.value) for arg in vararg]).decompose()
function = CombinedFunction(
"semitones",
Function(_signature1, _function1),
Function(_signature2, _function2),
)

View File

@@ -0,0 +1,22 @@
from smnp.function.model import CombinedFunction, Function
from smnp.function.signature import varargSignature
from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOf
from smnp.type.signature.matcher.type import ofTypes
_signature1 = varargSignature(ofTypes(Type.INTEGER, Type.NOTE), ofTypes(Type.INTEGER))
def _function1(env, value, vararg):
transposed = [Type.note(arg.value.transpose(value.value)) if arg.type == Type.NOTE else arg for arg in vararg]
return Type.list(transposed).decompose()
_signature2 = varargSignature(listOf(Type.INTEGER, Type.NOTE), ofTypes(Type.INTEGER))
def _function2(env, value, vararg):
return Type.list([_function1(env, value, arg.value) for arg in vararg]).decompose()
function = CombinedFunction(
'transpose',
Function(_signature1, _function1),
Function(_signature2, _function2)
)

View File

@@ -0,0 +1,23 @@
from smnp.function.model import CombinedFunction, Function
from smnp.function.signature import signature, varargSignature
from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOf
from smnp.type.signature.matcher.type import ofTypes
_signature1 = varargSignature(ofTypes(Type.NOTE), ofTypes(Type.INTEGER), ofTypes(Type.INTEGER))
def _function1(env, n, m, vararg):
t = [Type.note(arg.value.withDuration(int(arg.value.duration * n.value / m.value))) for arg in vararg]
return Type.list(t).decompose()
_signature2 = signature(ofTypes(Type.INTEGER), ofTypes(Type.INTEGER), listOf(Type.NOTE))
def _function2(env, n, m, notes):
return _function1(env, n, m, notes.value)
function = CombinedFunction(
'tuplet',
Function(_signature1, _function1),
Function(_signature2, _function2)
)

View File

@@ -1,4 +1,4 @@
from smnp.module.string.function import concat, stringify from smnp.module.string.function import concat
functions = [ concat.function ] functions = [ concat.function ]
methods = [ stringify.function ] methods = []

View File

@@ -1,10 +0,0 @@
from smnp.function.model import Function
from smnp.function.signature import signature
from smnp.type.model import Type
from smnp.type.signature.matcher.type import allTypes
_signature = signature(allTypes())
def _function(env, object):
return Type.string(object.stringify())
function = Function(_signature, _function, 'toString')

View File

@@ -1,13 +1,25 @@
from smnp.function.model import Function from smnp.function.model import CombinedFunction, Function
from smnp.function.signature import signature from smnp.function.signature import varargSignature
from smnp.module.synth.lib.player import play from smnp.module.synth.lib.player import playNotes
from smnp.type.model import Type from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType from smnp.type.signature.matcher.list import listOf
from smnp.type.signature.matcher.type import ofTypes
_signature = signature(ofType(Type.NOTE)) _signature1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
def _function(env, note): def _function1(env, vararg):
notes = [arg.value for arg in vararg]
bpm = env.findVariable('bpm') bpm = env.findVariable('bpm')
play(note.value, bpm.value) playNotes(notes, bpm.value)
function = Function(_signature, _function, 'synthNote') _signature2 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
def _function2(env, vararg):
for arg in vararg:
_function1(env, arg.value)
function = CombinedFunction(
'synth',
Function(_signature1, _function1),
Function(_signature2, _function2)
)

View File

@@ -1,4 +1,4 @@
from smnp.module.system.function import sleep, display, displayln, debug, exit, type from smnp.module.system.function import sleep, display, debug, exit, type
functions = [ debug.function, display.function, displayln.function, exit.function, sleep.function, type.function ] functions = [ debug.function, display.function, exit.function, sleep.function, type.function ]
methods = [] methods = []

View File

@@ -4,7 +4,7 @@ from smnp.type.signature.matcher.type import allTypes
_signature = varargSignature(allTypes()) _signature = varargSignature(allTypes())
def _function(env, vararg): def _function(env, vararg):
print("".join([arg.stringify() for arg in vararg]), end="") print("".join([arg.stringify() for arg in vararg]))
function = Function(_signature, _function, 'print') function = Function(_signature, _function, 'print')

View File

@@ -1,10 +0,0 @@
from smnp.function.model import Function
from smnp.function.signature import varargSignature
from smnp.type.signature.matcher.type import allTypes
_signature = varargSignature(allTypes())
def _function(env, vararg):
print("".join([arg.stringify() for arg in vararg]))
function = Function(_signature, _function, 'println')

View File

@@ -1,12 +1,29 @@
import random import random as r
from smnp.function.model import Function from smnp.error.function import IllegalArgumentException
from smnp.function.signature import signature from smnp.function.model import Function, CombinedFunction
from smnp.function.signature import varargSignature
from smnp.type.model import Type from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType from smnp.type.signature.matcher.list import listMatches
from smnp.type.signature.matcher.type import ofTypes
_signature = signature(ofType(Type.INTEGER), ofType(Type.INTEGER))
def _function(env, min, max):
return Type.integer(random.randint(min.value, max.value))
function = Function(_signature, _function, 'rand') def forType(t):
_signature = varargSignature(listMatches(ofTypes(Type.PERCENT), ofTypes(t)))
def _function(env, vararg):
choice = r.random()
acc = 0
if sum(arg.value[0].value for arg in vararg) != 1.0:
raise IllegalArgumentException("Sum of all percentage values must be equal 100%")
for arg in vararg:
percent, item = arg.value
acc += percent.value
if choice <= acc:
return item
return Function(_signature, _function)
function = CombinedFunction('random', *[ forType(t) for t in Type if t != Type.VOID ])
#TODO: sample

View File

@@ -41,15 +41,11 @@ class NotePitch(Enum):
@staticmethod @staticmethod
def toPitch(string): def toPitch(string):
if string.lower() in stringToPitch: try:
return stringToPitch[string.lower()] return stringToPitch[string.lower()]
except KeyError as e:
if string.upper() in NotePitch.__members__:
return NotePitch[string.upper()]
raise NoteException(f"Note '{string}' does not exist") raise NoteException(f"Note '{string}' does not exist")
stringToPitch = { stringToPitch = {
'cb': NotePitch.H, 'cb': NotePitch.H,
'c': NotePitch.C, 'c': NotePitch.C,

View File

@@ -1,42 +1,25 @@
from smnp.ast.parser import parse from smnp.ast.parser import parse
from smnp.environment.environment import Environment #from smnp.environment.factory import createEnvironment
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.module import functions, methods
from smnp.program.FileReader import readLines from smnp.program.FileReader import readLines
from smnp.runtime.evaluator import evaluate
from smnp.token.tokenizer import tokenize from smnp.token.tokenizer import tokenize
class Interpreter: class Interpreter:
@staticmethod @staticmethod
def interpretString(string, source, printTokens=False, printAst=False, execute=True, baseEnvironment=None): def interpretString(string, printTokens=False, printAst=False, execute=True, baseEnvironment=None):
return Interpreter._interpret( return Interpreter._interpret(string.splitlines(), printTokens, printAst, execute, baseEnvironment)
string.splitlines(),
source,
printTokens,
printAst,
execute,
baseEnvironment,
)
@staticmethod @staticmethod
def interpretFile(file, printTokens=False, printAst=False, execute=True, baseEnvironment=None, source=None): def interpretFile(file, printTokens=False, printAst=False, execute=True, baseEnvironment=None):
return Interpreter._interpret( return Interpreter._interpret(readLines(file), printTokens, printAst, execute, baseEnvironment)
readLines(file),
source if source is not None else file,
printTokens,
printAst,
execute,
baseEnvironment,
)
@staticmethod @staticmethod
def _interpret(lines, source, printTokens=False, printAst=False, execute=True, baseEnvironment=None): def _interpret(lines, printTokens=False, printAst=False, execute=True, baseEnvironment=None):
environment = Environment([{}], functions, methods, source=source) #environment = createEnvironment()
#if baseEnvironment is not None:
if baseEnvironment is not None: # environment.extend(baseEnvironment)
environment.extend(baseEnvironment)
try: try:
tokens = tokenize(lines) tokens = tokenize(lines)
@@ -47,11 +30,10 @@ class Interpreter:
if printAst: if printAst:
ast.print() ast.print()
if execute: #if execute:
evaluate(ast, environment) # evaluate(ast, environment)
return environment #return environment
except RuntimeException as e: except RuntimeException as e:
e.environment = environment #e.environment = environment
e.file = environment.source
raise e raise e

View File

@@ -1,11 +1,9 @@
from smnp.ast.node.block import Block from smnp.ast.node.block import BlockNode
from smnp.ast.node.condition import IfElse from smnp.ast.node.extend import ExtendNode
from smnp.ast.node.extend import Extend from smnp.ast.node.function import FunctionDefinitionNode
from smnp.ast.node.function import FunctionDefinition from smnp.ast.node.imports import ImportNode
from smnp.ast.node.imports import Import
from smnp.ast.node.program import Program from smnp.ast.node.program import Program
from smnp.ast.node.ret import Return from smnp.ast.node.ret import ReturnNode
from smnp.ast.node.throw import Throw
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.type.model import Type from smnp.type.model import Type
@@ -71,28 +69,19 @@ class EvaluationResult():
def evaluate(node, environment): def evaluate(node, environment):
from smnp.runtime.evaluators.program import ProgramEvaluator from smnp.runtime.evaluators.program import ProgramEvaluator
from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.condition import IfElseStatementEvaluator
from smnp.runtime.evaluators.function import FunctionDefinitionEvaluator
from smnp.runtime.evaluators.extend import ExtendEvaluator
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
from smnp.runtime.evaluators.throw import ThrowEvaluator
result = Evaluator.oneOf( result = Evaluator.oneOf(
Evaluator.forNodes(ProgramEvaluator.evaluate, Program), Evaluator.forNodes(ProgramEvaluator.evaluate, Program),
Evaluator.forNodes(IfElseStatementEvaluator.evaluate, IfElse), Evaluator.forNodes(ImportEvaluator.evaluate, ImportNode),
Evaluator.forNodes(BlockEvaluator.evaluate, Block), Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinitionNode),
Evaluator.forNodes(ImportEvaluator.evaluate, Import), Evaluator.forNodes(ExtendEvaluator.evaluate, ExtendNode),
Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinition), Evaluator.forNodes(BlockEvaluator.evaluate, BlockNode),
Evaluator.forNodes(ReturnEvaluator.evaluate, Return), Evaluator.forNodes(ReturnEvaluator.evaluate, ReturnNode),
Evaluator.forNodes(ExtendEvaluator.evaluate, Extend),
Evaluator.forNodes(ThrowEvaluator.evaluate, Throw),
#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,4 +1,5 @@
from smnp.ast.node.identifier import Identifier, FunctionCall from smnp.ast.node.identifier import Identifier
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
@@ -19,9 +20,9 @@ class AccessEvaluator(Evaluator):
except KeyError: except KeyError:
raise RuntimeException(f"Unknown property '{right.value}' of type '{left.type.name.lower()}'", right.pos) raise RuntimeException(f"Unknown property '{right.value}' of type '{left.type.name.lower()}'", right.pos)
if type(right) == FunctionCall: if type(right) == FunctionCallNode:
try: try:
arguments = abstractIterableEvaluator(expressionEvaluator(doAssert=True))(right.arguments, environment) arguments = abstractIterableEvaluator(expressionEvaluator(True))(right.arguments, environment)
return environment.invokeMethod(left, right.name.value, arguments) return environment.invokeMethod(left, right.name.value, arguments)
except RuntimeException as e: except RuntimeException as e:
raise updatePos(e, right) raise updatePos(e, right)

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,15 @@
from smnp.ast.node.condition import IfElse from smnp.ast.node.assignment import AssignmentNode
from smnp.ast.node.expression import Sum, Relation, And, Or, Loop from smnp.ast.node.asterisk import AsteriskNode
from smnp.ast.node.factor import NotOperator, Power from smnp.ast.node.bool import BoolLiteralNode
from smnp.ast.node.identifier import FunctionCall, Assignment from smnp.ast.node.identifier import Identifier
from smnp.ast.node.term import Product from smnp.ast.node.integer import IntegerLiteralNode
from smnp.ast.node.unit import MinusOperator, Access from smnp.ast.node.invocation import FunctionCallNode
from smnp.ast.node.list import List
from smnp.ast.node.map import MapNode
from smnp.ast.node.note import NoteLiteralNode
from smnp.ast.node.operator import LeftAssociativeOperatorNode
from smnp.ast.node.string import StringLiteralNode
from smnp.ast.node.type import TypeNode
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator from smnp.runtime.evaluator import Evaluator
from smnp.type.model import Type from smnp.type.model import Type
@@ -11,36 +17,32 @@ 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.integer import IntegerEvaluator
from smnp.runtime.evaluators.note import NoteEvaluator
from smnp.runtime.evaluators.identifier import IdentifierEvaluator
from smnp.runtime.evaluators.list import ListEvaluator
from smnp.runtime.evaluators.function import FunctionCallEvaluator from smnp.runtime.evaluators.function import FunctionCallEvaluator
from smnp.runtime.evaluators.minus import MinusEvaluator
from smnp.runtime.evaluators.atom import AtomEvaluator
from smnp.runtime.evaluators.access import AccessEvaluator
from smnp.runtime.evaluators.negation import NotEvaluator
from smnp.runtime.evaluators.power import PowerEvaluator
from smnp.runtime.evaluators.loop import LoopEvaluator
from smnp.runtime.evaluators.assignment import AssignmentEvaluator
from smnp.runtime.evaluators.product import ProductEvaluator
from smnp.runtime.evaluators.sum import SumEvaluator from smnp.runtime.evaluators.access import AccessEvaluator
from smnp.runtime.evaluators.relation import RelationEvaluator from smnp.runtime.evaluators.assignment import AssignmentEvaluator
from smnp.runtime.evaluators.condition import IfElseEvaluator from smnp.runtime.evaluators.asterisk import AsteriskEvaluator
from smnp.runtime.evaluators.logic import AndEvaluator from smnp.runtime.evaluators.map import MapEvaluator
from smnp.runtime.evaluators.logic import OrEvaluator from smnp.runtime.evaluators.type import TypeEvaluator
from smnp.runtime.evaluators.bool import BoolEvaluator
result = Evaluator.oneOf( result = Evaluator.oneOf(
Evaluator.forNodes(FunctionCallEvaluator.evaluate, FunctionCall), Evaluator.forNodes(FunctionCallEvaluator.evaluate, FunctionCallNode),
Evaluator.forNodes(MinusEvaluator.evaluate, MinusOperator), Evaluator.forNodes(StringEvaluator.evaluate, StringLiteralNode),
Evaluator.forNodes(AccessEvaluator.evaluate, Access), Evaluator.forNodes(IntegerEvaluator.evaluate, IntegerLiteralNode),
Evaluator.forNodes(NotEvaluator.evaluate, NotOperator), Evaluator.forNodes(NoteEvaluator.evaluate, NoteLiteralNode),
Evaluator.forNodes(PowerEvaluator.evaluate, Power), Evaluator.forNodes(BoolEvaluator.evaluate, BoolLiteralNode),
Evaluator.forNodes(LoopEvaluator.evaluate, Loop), Evaluator.forNodes(TypeEvaluator.evaluate, TypeNode),
Evaluator.forNodes(AssignmentEvaluator.evaluate, Assignment), Evaluator.forNodes(IdentifierEvaluator.evaluate, Identifier),
Evaluator.forNodes(ProductEvaluator.evaluate, Product), Evaluator.forNodes(ListEvaluator.evaluate, List),
Evaluator.forNodes(SumEvaluator.evaluate, Sum), Evaluator.forNodes(AccessEvaluator.evaluate, LeftAssociativeOperatorNode),
Evaluator.forNodes(RelationEvaluator.evaluate, Relation), Evaluator.forNodes(AssignmentEvaluator.evaluate, AssignmentNode),
Evaluator.forNodes(IfElseEvaluator.evaluate, IfElse), Evaluator.forNodes(AsteriskEvaluator.evaluate, AsteriskNode),
Evaluator.forNodes(AndEvaluator.evaluate, And), Evaluator.forNodes(MapEvaluator.evaluate, MapNode)
Evaluator.forNodes(OrEvaluator.evaluate, Or),
AtomEvaluator.evaluate
)(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:
@@ -48,17 +50,5 @@ def expressionEvaluator(doAssert=False):
return result return result
return evaluateExpression return evaluateExpression
def expressionEvaluatorWithMatcher(matcher, exceptionProvider, doAssert=True):
def evaluate(node, environment):
value = expressionEvaluator(doAssert=doAssert)(node, environment).value
if not matcher.match(value):
raise exceptionProvider(value)
return value
return evaluate

View File

@@ -17,11 +17,11 @@ class ExtendEvaluator(Evaluator):
@classmethod @classmethod
def _typeToMethodSignature(cls, node): def _typeToMethodSignature(cls, node):
if type(node.specifiers) == NoneNode: if type(node.specifiers) == NoneNode:
return signature(ofType(node.type.value)) return signature(ofType(node.type))
elif node.type.value == Type.LIST: elif node.type == Type.LIST:
return signature(listSpecifier(node.specifiers[0])) return signature(listSpecifier(node.specifiers[0]))
elif node.type.value == Type.MAP: elif node.type == Type.MAP:
return signature(mapSpecifier(node.specifiers[0], node.specifiers[1])) return signature(mapSpecifier(node.specifiers[0], node.specifiers[1]))
@classmethod @classmethod
@@ -32,8 +32,8 @@ class ExtendEvaluator(Evaluator):
@classmethod @classmethod
def _evaluateMethodDefinition(cls, node, environment, type, variable): def _evaluateMethodDefinition(cls, node, environment, type, variable):
name = node.name.value name = node.name.value
defaultArguments, signature = argumentsNodeToMethodSignature(node.arguments, environment) signature = argumentsNodeToMethodSignature(node.arguments)
arguments = [arg.variable.value for arg in node.arguments] arguments = [arg.variable.value for arg in node.arguments]
body = node.body body = node.body
environment.addCustomMethod(type, variable, name, signature, arguments, body, defaultArguments) environment.addCustomMethod(type, variable, name, signature, arguments, body)

View File

@@ -4,7 +4,6 @@ from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.iterable import abstractIterableEvaluator from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
from smnp.runtime.tools.error import updatePos from smnp.runtime.tools.error import updatePos
from smnp.runtime.tools.signature import argumentsNodeToMethodSignature from smnp.runtime.tools.signature import argumentsNodeToMethodSignature
from smnp.type.model import Type
class FunctionCallEvaluator(Evaluator): class FunctionCallEvaluator(Evaluator):
@@ -25,10 +24,10 @@ class FunctionDefinitionEvaluator(Evaluator):
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
try: try:
name = node.name.value name = node.name.value
defaultArguments, signature = argumentsNodeToMethodSignature(node.arguments, environment) signature = argumentsNodeToMethodSignature(node.arguments)
arguments = [ arg.variable.value for arg in node.arguments ] arguments = [ arg.variable.value for arg in node.arguments ]
body = node.body body = node.body
environment.addCustomFunction(name, signature, arguments, body, defaultArguments) environment.addCustomFunction(name, signature, arguments, body)
except RuntimeException as e: except RuntimeException as e:
raise updatePos(e, node) raise updatePos(e, node)
@@ -39,6 +38,8 @@ class BodyEvaluator(Evaluator):
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
for child in node.children: for child in node.children:
evaluate(child, environment) evaluate(child, environment)
if environment.callStack[-1].value is not None:
return environment.callStack[-1].value
class ReturnEvaluator(Evaluator): class ReturnEvaluator(Evaluator):
@@ -46,21 +47,7 @@ class ReturnEvaluator(Evaluator):
@classmethod @classmethod
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
if len(environment.callStack) > 0: if len(environment.callStack) > 0:
returnValue = expressionEvaluator()(node.value, environment).value returnValue = expressionEvaluator(doAssert=True)(node.value, environment)
raise Return(returnValue) environment.callStack[-1].value = returnValue.value
# Disclaimer
# Exception system usage to control program execution flow is really bad idea.
# However because of lack of 'goto' instruction equivalent in Python
# there is to need to use some mechanism to break function execution on 'return' statement
# and immediately go to Environment's method 'invokeFunction()' or 'invokeMethod()',
# which can handle value that came with exception and return it to code being executed.
else: else:
raise RuntimeException("Cannot use 'return' statement outside a function or method", node.pos, environment) raise RuntimeException("Cannot use 'return' statement outside a function or method", node.pos, environment)
class Return(Exception):
def __init__(self, value):
if value is None:
value = Type.void()
self.value = value

View File

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

View File

@@ -1,21 +0,0 @@
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.type.model import Type
class AndEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, environment).value
return Type.bool(left.value and right.value)
class OrEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, environment).value
return Type.bool(left.value or right.value)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,17 +0,0 @@
from smnp.error.custom import CustomException
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 ThrowEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
string = expressionEvaluator(doAssert=True)(node.value, environment).value
if string.type != Type.STRING:
raise RuntimeException(f"Only {Type.STRING.name.lower()} types can be thrown", node.value.pos)
raise CustomException(string.value, node.pos)

View File

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

View File

@@ -1,9 +1,7 @@
from smnp.ast.node import type as ast
from smnp.ast.node.none import NoneNode from smnp.ast.node.none import NoneNode
from smnp.ast.node.type import TypesList from smnp.ast.node.type import TypeNode, TypeSpecifier
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.function.signature import varargSignature, signature, optional from smnp.function.signature import varargSignature, signature
from smnp.runtime.evaluators.expression import expressionEvaluator, expressionEvaluatorWithMatcher
from smnp.runtime.tools.error import updatePos from smnp.runtime.tools.error import updatePos
from smnp.type.model import Type from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOfMatchers from smnp.type.signature.matcher.list import listOfMatchers
@@ -11,54 +9,31 @@ from smnp.type.signature.matcher.map import mapOfMatchers
from smnp.type.signature.matcher.type import allTypes, oneOf, ofType from smnp.type.signature.matcher.type import allTypes, oneOf, ofType
def evaluateDefaultArguments(node, environment): def argumentsNodeToMethodSignature(node):
defaultValues = { arg.variable.value: expressionEvaluator(doAssert=True)(arg.optionalValue, environment).value for arg in node.children if type(arg.optionalValue) != NoneNode }
return defaultValues
def argumentsNodeToMethodSignature(node, environment):
try: try:
sign = [] sign = []
vararg = None vararg = None
argumentsCount = len(node.children) argumentsCount = len(node.children)
checkPositionOfOptionalArguments(node)
defaultArgs = {}
for i, child in enumerate(node.children): for i, child in enumerate(node.children):
matchers = { matchers = {
ast.Type: (lambda c: c.type, typeMatcher), TypeNode: (lambda c: c.type, typeMatcher),
NoneNode: (lambda c: c.type, lambda c: allTypes()), NoneNode: (lambda c: c.type, lambda c: allTypes()),
TypesList: (lambda c: c, multipleTypeMatcher) TypeSpecifier: (lambda c: c, multipleTypeMatcher)
} }
evaluatedMatcher = matchers[type(child.type)][1](matchers[type(child.type)][0](child)) evaluatedMatcher = matchers[type(child.type)][1](matchers[type(child.type)][0](child))
if child.vararg: if child.vararg:
if i != argumentsCount - 1: if i != argumentsCount - 1:
raise RuntimeException("Vararg must be the last argument in signature", child.pos) raise RuntimeException("Vararg must be the last argument in signature", child.pos)
vararg = evaluatedMatcher vararg = evaluatedMatcher
else: else:
if type(child.optionalValue) != NoneNode:
defaultArgs[child.variable.value] = expressionEvaluatorWithMatcher(
evaluatedMatcher,
exceptionProvider=lambda value: RuntimeException(
f"Value '{value.stringify()}' doesn't match declared type: {evaluatedMatcher.string}", child.optionalValue.pos)
)(child.optionalValue, environment)
evaluatedMatcher = optional(evaluatedMatcher)
sign.append(evaluatedMatcher) sign.append(evaluatedMatcher)
return defaultArgs, (varargSignature(vararg, *sign, wrapVarargInValue=True) if vararg is not None else signature(*sign))
return varargSignature(vararg, *sign, wrapVarargInValue=True) if vararg is not None else signature(*sign)
except RuntimeException as e: except RuntimeException as e:
raise updatePos(e, node) raise updatePos(e, node)
def checkPositionOfOptionalArguments(node):
firstOptional = next((i for i, v in enumerate(node.children) if type(v.optionalValue) != NoneNode), None) #next(filter(lambda arg: type(arg.optionalValue) != NoneNode, node.children), None)
if firstOptional is not None:
regularAfterOptional = next((i for i, v in enumerate(node.children[firstOptional:]) if type(v.optionalValue) == NoneNode), None)
if regularAfterOptional is not None:
raise RuntimeException(f"Optional arguments should be declared at the end of the arguments list", node.children[regularAfterOptional].pos)
def multipleTypeMatcher(typeNode): def multipleTypeMatcher(typeNode):
subSignature = [] subSignature = []
@@ -74,10 +49,10 @@ def multipleTypeMatcher(typeNode):
def typeMatcher(typeNode): def typeMatcher(typeNode):
if type(typeNode.specifiers) == NoneNode: if type(typeNode.specifiers) == NoneNode:
return ofType(typeNode.type.value) return ofType(typeNode.type)
elif typeNode.type.value == Type.LIST and len(typeNode.specifiers) == 1: elif typeNode.type == Type.LIST and len(typeNode.specifiers) == 1:
return listSpecifier(typeNode.specifiers[0]) return listSpecifier(typeNode.specifiers[0])
elif typeNode.type.value == Type.MAP and len(typeNode.specifiers) == 2: elif typeNode.type == Type.MAP and len(typeNode.specifiers) == 2:
return mapSpecifier(typeNode.specifiers[0], typeNode.specifiers[1]) return mapSpecifier(typeNode.specifiers[0], typeNode.specifiers[1])
raise RuntimeException("Unknown type", typeNode.pos) # Todo: Improve pointing position raise RuntimeException("Unknown type", typeNode.pos) # Todo: Improve pointing position

View File

@@ -8,7 +8,7 @@ from smnp.token.tokenizers.note import noteTokenizer
from smnp.token.tokenizers.relation import relationOperatorTokenizer from smnp.token.tokenizers.relation import relationOperatorTokenizer
from smnp.token.tokenizers.string import stringTokenizer from smnp.token.tokenizers.string import stringTokenizer
from smnp.token.tokenizers.whitespace import whitespacesTokenizer from smnp.token.tokenizers.whitespace import whitespacesTokenizer
from smnp.token.tools import defaultTokenizer, separated, regexPatternTokenizer, mapValue from smnp.token.tools import defaultTokenizer, separated, regexPatternTokenizer
from smnp.token.type import TokenType from smnp.token.type import TokenType
tokenizers = ( tokenizers = (
@@ -27,7 +27,6 @@ tokenizers = (
defaultTokenizer(TokenType.CLOSE_SQUARE), defaultTokenizer(TokenType.CLOSE_SQUARE),
defaultTokenizer(TokenType.OPEN_ANGLE), defaultTokenizer(TokenType.OPEN_ANGLE),
defaultTokenizer(TokenType.CLOSE_ANGLE), defaultTokenizer(TokenType.CLOSE_ANGLE),
defaultTokenizer(TokenType.SEMICOLON),
defaultTokenizer(TokenType.ASTERISK), defaultTokenizer(TokenType.ASTERISK),
defaultTokenizer(TokenType.ASSIGN), defaultTokenizer(TokenType.ASSIGN),
defaultTokenizer(TokenType.COMMA), defaultTokenizer(TokenType.COMMA),
@@ -40,7 +39,7 @@ tokenizers = (
defaultTokenizer(TokenType.DOT), defaultTokenizer(TokenType.DOT),
# Types # Types
mapValue(separated(regexPatternTokenizer(TokenType.INTEGER, r'\d')), int), separated(regexPatternTokenizer(TokenType.INTEGER, r'\d')),
stringTokenizer, stringTokenizer,
noteTokenizer, noteTokenizer,
boolTokenizer, boolTokenizer,
@@ -51,9 +50,7 @@ tokenizers = (
separated(defaultTokenizer(TokenType.RETURN)), separated(defaultTokenizer(TokenType.RETURN)),
separated(defaultTokenizer(TokenType.EXTEND)), separated(defaultTokenizer(TokenType.EXTEND)),
separated(defaultTokenizer(TokenType.IMPORT)), separated(defaultTokenizer(TokenType.IMPORT)),
separated(defaultTokenizer(TokenType.THROW)),
separated(defaultTokenizer(TokenType.FROM)), separated(defaultTokenizer(TokenType.FROM)),
separated(defaultTokenizer(TokenType.WITH)),
separated(defaultTokenizer(TokenType.ELSE)), separated(defaultTokenizer(TokenType.ELSE)),
separated(defaultTokenizer(TokenType.AND)), separated(defaultTokenizer(TokenType.AND)),
separated(defaultTokenizer(TokenType.NOT)), separated(defaultTokenizer(TokenType.NOT)),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@ from enum import Enum
class TokenType(Enum): class TokenType(Enum):
RELATION = '==, !=, >=, <=' RELATION = 'relation'
DOUBLE_ASTERISK = '**' DOUBLE_ASTERISK = '**'
OPEN_CURLY = '{' OPEN_CURLY = '{'
CLOSE_CURLY = '}' CLOSE_CURLY = '}'
@@ -12,7 +12,6 @@ class TokenType(Enum):
CLOSE_SQUARE = ']' CLOSE_SQUARE = ']'
OPEN_ANGLE = '<' OPEN_ANGLE = '<'
CLOSE_ANGLE = '>' CLOSE_ANGLE = '>'
SEMICOLON = ';'
ASTERISK = '*' ASTERISK = '*'
ASSIGN = '=' ASSIGN = '='
ARROW = '->' ARROW = '->'
@@ -36,9 +35,7 @@ class TokenType(Enum):
RETURN = 'return' RETURN = 'return'
EXTEND = 'extend' EXTEND = 'extend'
IMPORT = 'import' IMPORT = 'import'
THROW = 'throw'
FROM = 'from' FROM = 'from'
WITH = 'with'
ELSE = 'else' ELSE = 'else'
IF = 'if' IF = 'if'
AS = 'as' AS = 'as'

View File

@@ -51,7 +51,7 @@ class Type(Enum):
"pitch": Type.string(str(value.note)), "pitch": Type.string(str(value.note)),
"octave": Type.integer(value.octave), "octave": Type.integer(value.octave),
"duration": Type.integer(value.duration), "duration": Type.integer(value.duration),
"dot": Type.bool(value.dot) "dot": Type.string('.' if value.dot else '')
}) })
@staticmethod @staticmethod
@@ -73,7 +73,6 @@ class Type(Enum):
def void(): def void():
return Value(Type.VOID, None) return Value(Type.VOID, None)
def _failStringify(t): def _failStringify(t):
raise RuntimeException(f"Not able to interpret {t.name}'", None) raise RuntimeException(f"Not able to interpret {t.name}'", None)

View File

@@ -3,7 +3,6 @@ class Matcher:
self.type = objectType self.type = objectType
self.matcher = matcher self.matcher = matcher
self.string = string self.string = string
self.optional = False
def match(self, value): def match(self, value):
if self.type is not None and self.type != value.type: if self.type is not None and self.type != value.type:

0
smnp/util/__init__.py Normal file
View File

14
smnp/util/singleton.py Normal file
View File

@@ -0,0 +1,14 @@
from smnp.ast.parser import Parser
def SingletonParser(function):
def wrapper(*args, **kwargs):
if not hasattr(function, 'instance'):
function.instance = function(*args, **kwargs)
if not isinstance(function.instance, Parser):
raise RuntimeError(f"Function {function.__name__} haven't returned Parser object")
return function.instance
return wrapper