Remove old parser and move new parser to 'ast' package

This commit is contained in:
Bartłomiej Pluta
2019-07-06 15:56:28 +02:00
parent 9a42bbbb2d
commit f81279094f
64 changed files with 573 additions and 1229 deletions

View File

@@ -1,10 +1,54 @@
from smnp.ast.node.model import Node
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.ignore import IgnoredNode
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class AccessNode(Node):
def __init__(self, element, property, parent, pos):
Node.__init__(self, parent, pos)
self.children.extend([element, property])
class AccessNode(ExpressionNode):
def __init__(self, pos):
super().__init__(pos)
self.children.append(IgnoredNode(pos))
self.element = self.children[0]
self.property = self.children[1]
@property
def left(self):
return self[0]
@left.setter
def left(self, value):
self[0] = value
@property
def right(self):
return self[1]
@right.setter
def right(self, value):
self[1] = value
@classmethod
def _parse(cls, input):
def createNode(left, right):
node = AccessNode(right.pos)
node.left = left
node.right = right
return node
return Parser.leftAssociativeOperatorParser(
cls._literalParser(),
TokenType.DOT,
cls._parseAccessingProperty(),
createNode=createNode
)(input)
@classmethod
def _literalParser(cls):
pass
@staticmethod
def _parseAccessingProperty():
from smnp.ast.node.identifier import IdentifierNode
return Parser.oneOf(
IdentifierNode._literalParser(),
IdentifierNode._functionCallParser()
)

View File

@@ -1,6 +1,6 @@
from smnp.newast.node.expression import ExpressionNode
from smnp.newast.node.iterable import abstractIterableParser
from smnp.newast.node.model import Node
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.model import Node
from smnp.token.type import TokenType

View File

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

View File

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

View File

@@ -1,17 +1,20 @@
from smnp.ast.node.model import Node
from smnp.ast.node.statement import StatementNode
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class BlockNode(Node):
pass
class BlockNode(StatementNode):
@classmethod
def _parse(cls, input):
def createNode(start, items, end):
node = BlockNode(start.pos)
node.children = items
return node
class BlockItemNode(Node):
def __init__(self, statement, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(statement)
self.statement = self.children[0]
class CloseBlockNode(Node):
pass
return Parser.loop(
Parser.terminalParser(TokenType.OPEN_BRACKET),
StatementNode.parse,
Parser.terminalParser(TokenType.CLOSE_BRACKET),
createNode=createNode
)(input)

View File

@@ -1,10 +0,0 @@
from smnp.ast.node.model import Node
class ColonNode(Node):
def __init__(self, a, b, parent, pos):
Node.__init__(self, parent, pos)
self.children.extend([a, b])
self.a = self.children[0]
self.b = self.children[1]

View File

@@ -1,8 +1,8 @@
from smnp.newast.node.asterisk import AsteriskNode
from smnp.newast.node.model import Node
from smnp.newast.node.none import NoneNode
from smnp.newast.node.statement import StatementNode
from smnp.newast.parser import Parser
from smnp.ast.node.asterisk import AsteriskNode
from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode
from smnp.ast.node.statement import StatementNode
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
@@ -50,11 +50,11 @@ class ExpressionNode(Node):
@classmethod
def _expressionParser(cls):
from smnp.newast.node.integer import IntegerLiteralNode
from smnp.newast.node.string import StringLiteralNode
from smnp.newast.node.note import NoteLiteralNode
from smnp.newast.node.identifier import IdentifierNode
from smnp.newast.node.list import ListNode
from smnp.ast.node.integer import IntegerLiteralNode
from smnp.ast.node.string import StringLiteralNode
from smnp.ast.node.note import NoteLiteralNode
from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.list import ListNode
return Parser.oneOf(
IntegerLiteralNode.parse,

View File

@@ -1,10 +1,10 @@
from smnp.newast.node.block import BlockNode
from smnp.newast.node.function import FunctionDefinitionNode
from smnp.newast.node.identifier import IdentifierNode
from smnp.newast.node.none import NoneNode
from smnp.newast.node.statement import StatementNode
from smnp.newast.node.type import TypeNode
from smnp.newast.parser import Parser
from smnp.ast.node.block import BlockNode
from smnp.ast.node.function import FunctionDefinitionNode
from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.none import NoneNode
from smnp.ast.node.statement import StatementNode
from smnp.ast.node.type import TypeNode
from smnp.ast.parser import Parser
from smnp.token.type import TokenType

View File

@@ -1,20 +1,67 @@
from smnp.ast.node.block import BlockNode
from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.iterable import abstractIterableParser
from smnp.ast.node.model import Node
from smnp.ast.node.none import NoneNode
from smnp.ast.node.statement import StatementNode
from smnp.ast.node.variable import TypedVariableNode
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class FunctionCallNode(Node):
def __init__(self, identifier, arguments, parent, pos):
Node.__init__(self, parent, pos)
self.children.extend([identifier, arguments])
class ArgumentsDeclarationNode(Node):
self.identifier = self.children[0]
self.arguments = self.children[1]
@classmethod
def _parse(cls, input):
raise RuntimeError("This class is not supposed to be automatically called")
class FunctionDefinitionNode(Node):
def __init__(self, name, parameters, body, parent, pos):
Node.__init__(self, parent, pos)
self.children.extend([name, parameters, body])
class FunctionDefinitionNode(StatementNode):
def __init__(self, pos):
super().__init__(pos)
self.children = [NoneNode(), NoneNode(), NoneNode()]
self.name = self.children[0]
self.parameters = self.children[1]
self.body = self.children[2]
@property
def name(self):
return self[0]
@name.setter
def name(self, value):
self[0] = value
@property
def arguments(self):
return self[1]
@arguments.setter
def arguments(self, value):
self[1] = value
@property
def body(self):
return self[2]
@body.setter
def body(self, value):
self[2] = value
@classmethod
def _parse(cls, input):
def createNode(function, name, arguments, body):
node = FunctionDefinitionNode(function.pos)
node.name = name
node.arguments = arguments
node.body = body
return node
return Parser.allOf(
Parser.terminalParser(TokenType.FUNCTION),
IdentifierNode.identifierParser(),
cls._argumentsDeclarationParser(),
BlockNode.parse,
createNode=createNode
)(input)
@staticmethod
def _argumentsDeclarationParser():
return abstractIterableParser(ArgumentsDeclarationNode, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, TypedVariableNode.parser())

View File

@@ -1,9 +1,54 @@
from smnp.ast.node.model import Node
from smnp.ast.node.access import AccessNode
from smnp.ast.node.args import ArgumentsListNode
from smnp.ast.node.assignment import AssignmentNode
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.invocation import FunctionCall
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class IdentifierNode(Node):
def __init__(self, identifier, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(identifier)
class IdentifierNode(AccessNode):
def __init__(self, pos):
super().__init__(pos)
del self.children[1]
self.identifier = self.children[0]
@classmethod
def _literalParser(cls):
return Parser.oneOf(
IdentifierNode._functionCallParser(),
IdentifierNode._assignmentParser(),
IdentifierNode.identifierParser()
)
@staticmethod
def _assignmentParser():
def createNode(target, assignment, value):
node = AssignmentNode(assignment.pos)
node.target = target
node.value = value
return node
return Parser.allOf(
IdentifierNode.identifierParser(),
Parser.terminalParser(TokenType.ASSIGN),
ExpressionNode.parse,
createNode=createNode
)
@staticmethod
def _functionCallParser():
def createNode(name, arguments):
node = FunctionCall(name.pos)
node.name = name
node.arguments = arguments
return node
return Parser.allOf(
IdentifierNode.identifierParser(),
ArgumentsListNode.parse,
createNode=createNode
)
@staticmethod
def identifierParser():
return Parser.terminalParser(TokenType.IDENTIFIER, lambda val, pos: IdentifierNode.withValue(val, pos))

View File

@@ -1,4 +1,4 @@
from smnp.newast.node.model import Node
from smnp.ast.node.model import Node
class IgnoredNode(Node):

View File

@@ -1,9 +1,9 @@
from smnp.newast.node.identifier import IdentifierNode
from smnp.newast.node.model import Node
from smnp.newast.node.none import NoneNode
from smnp.newast.node.string import StringLiteralNode
from smnp.newast.node.type import TypeNode
from smnp.newast.parser import Parser
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.string import StringLiteralNode
from smnp.ast.node.type import TypeNode
from smnp.ast.parser import Parser
from smnp.token.type import TokenType

View File

@@ -1,9 +1,31 @@
from smnp.ast.node.model import Node
from smnp.ast.node.access import AccessNode
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class IntegerLiteralNode(Node):
def __init__(self, value, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(value)
class IntegerLiteralNode(AccessNode):
def __init__(self, pos):
super().__init__(pos)
del self.children[1]
self.value = self.children[0]
@classmethod
def _literalParser(cls):
return Parser.oneOf(
cls._negativeIntegerParser(),
cls._positiveIntegerParser()
)
@classmethod
def _negativeIntegerParser(cls):
def createNode(minus, integer):
return IntegerLiteralNode.withValue(-integer.value, minus.pos)
return Parser.allOf(
Parser.terminalParser(TokenType.MINUS),
cls._positiveIntegerParser(),
createNode=createNode
)
@classmethod
def _positiveIntegerParser(cls):
return Parser.terminalParser(TokenType.INTEGER, lambda val, pos: IntegerLiteralNode.withValue(int(val), pos))

View File

@@ -1,4 +1,4 @@
from smnp.newast.node.access import AccessNode
from smnp.ast.node.access import AccessNode
class FunctionCall(AccessNode):

View File

@@ -1,8 +1,8 @@
from smnp.newast.node.expression import ExpressionNode
from smnp.newast.node.ignore import IgnoredNode
from smnp.newast.node.model import Node, ParseResult
from smnp.newast.node.none import NoneNode
from smnp.newast.parser import Parser
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.ignore import IgnoredNode
from smnp.ast.node.model import Node, ParseResult
from smnp.ast.node.none import NoneNode
from smnp.ast.parser import Parser
from smnp.token.type import TokenType

View File

@@ -1,17 +1,11 @@
from smnp.ast.node.model import Node
from smnp.ast.node.access import AccessNode
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.iterable import abstractIterableParser
from smnp.token.type import TokenType
class ListNode(Node):
pass
class ListNode(AccessNode):
class ListItemNode(Node):
def __init__(self, value, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(value)
self.value = self.children[0]
class CloseListNode(Node):
pass
@classmethod
def _literalParser(cls):
return abstractIterableParser(ListNode, TokenType.OPEN_SQUARE, TokenType.CLOSE_SQUARE, ExpressionNode.parse)

View File

@@ -1,5 +1,5 @@
from smnp.newast.node.expression import ExpressionNode
from smnp.newast.parser import Parser
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.parser import Parser
class LiteralNode(ExpressionNode):

View File

@@ -1,44 +1,81 @@
from smnp.note.model import Note
class Node:
def __init__(self, parent, pos):
self.children = []
self.parent = parent
def __init__(self, pos, children=None):
if children is None:
children = []
self.children = children
self.pos = pos
self.parent = None
for child in self.children:
if isinstance(child, Node):
child.parent = self
def __repr__(self):
return self.__str__()
def __len__(self):
return len(self.children)
def __getitem__(self, index):
return self.children[index]
def __getitem__(self, key):
return self.children[key]
def __setitem__(self, key, value):
if isinstance(value, Node):
value.parent = self
self.children[key] = value
def append(self, node):
if isinstance(node, Node):
node.parent = self
self.children.append(node)
def pop(self, index):
return self.children.pop(index)
def print(self):
print(self._print(0))
@classmethod
def _parse(cls, input):
pass
def _print(self, level):
string = f"{pad(level)}{self.__class__.__name__}({self.parent.__class__.__name__}):\n"
for child in self.children:
if isinstance(child, str) or isinstance(child, int) or isinstance(child, Note):
string += pad(level + 1) + f"'{child}'\n"
@classmethod
def parse(cls, input):
result = cls._parse(input)
if result is None:
return ParseResult.FAIL()
if not isinstance(result, ParseResult):
raise RuntimeError(f"_parse() method of '{cls.__name__}' class haven't returned ParseResult object")
return result
def print(self):
self._print(first=True)
def _print(self, prefix="", last=True, first=False):
print(prefix, '' if first else '└─' if last else '├─', self.__class__.__name__, sep="")
prefix += ' ' if last else ''
for i, child in enumerate(self.children):
last = i == len(self.children) - 1
if isinstance(child, Node):
child._print(prefix, last)
else:
string += child._print(level + 1)
return string
print(prefix, '' if last else '', f"'{str(child)}'", sep="")
def __str__(self):
return self._print(0)
return self.__class__.__name__
class ParseResult():
def __init__(self, result, node):
if result and node is None:
raise RuntimeError("Node musn't be None if result is set to True for ParseResult")
self.result = result
self.node = node
@staticmethod
def FAIL():
return ParseResult(False, None)
@staticmethod
def OK(node):
return ParseResult(True, node)
def __str__(self):
return f"{'OK' if self.result else 'FAILED'}[{self.node}]"
def pad(level):
return " " * level

View File

@@ -1,4 +1,4 @@
from smnp.newast.node.model import Node
from smnp.ast.node.model import Node
class NoneNode(Node):

View File

@@ -1,9 +1,13 @@
from smnp.ast.node.model import Node
from smnp.ast.node.access import AccessNode
from smnp.ast.node.literal import LiteralNode
from smnp.token.type import TokenType
class NoteLiteralNode(Node):
def __init__(self, value, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(value)
class NoteLiteralNode(LiteralNode, AccessNode):
def __init__(self, pos):
super().__init__(pos)
del self.children[1]
self.value = self.children[0]
@classmethod
def _getTokenType(cls):
return TokenType.NOTE

View File

@@ -1,9 +0,0 @@
from smnp.ast.node.model import Node
class PercentNode(Node):
def __init__(self, value, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(value)
self.value = self.children[0]

View File

@@ -1,6 +1,32 @@
from smnp.ast.node.model import Node
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.extend import ExtendNode
from smnp.ast.node.function import FunctionDefinitionNode
from smnp.ast.node.imports import ImportNode
from smnp.ast.node.model import Node, ParseResult
from smnp.ast.node.statement import StatementNode
from smnp.ast.parser import Parser
from smnp.error.syntax import SyntaxException
class Program(Node):
def __init__(self):
Node.__init__(self, None, (-1, -1))
super().__init__((-1, -1))
@classmethod
def _parse(cls, input):
def parseToken(input):
return Parser.oneOf(
FunctionDefinitionNode.parse,
ExtendNode.parse,
ExpressionNode.parse,
ImportNode.parse,
StatementNode.parse,
exception = SyntaxException(f"Unknown statement: {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,9 +1,32 @@
from smnp.ast.node.model import Node
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.none import NoneNode
from smnp.ast.node.statement import StatementNode
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class ReturnNode(Node):
def __init__(self, value, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(value)
class ReturnNode(StatementNode):
def __init__(self, pos):
super().__init__(pos)
self.children.append(NoneNode())
self.value = self.children[0]
@property
def value(self):
return self[0]
@value.setter
def value(self, value):
self[0] = value
@classmethod
def _parse(cls, input):
def createNode(ret, value):
node = ReturnNode(ret.pos)
node.value = value
return node
return Parser.allOf(
Parser.terminalParser(TokenType.RETURN),
ExpressionNode.parse,
createNode=createNode
)(input)

View File

@@ -0,0 +1,17 @@
from smnp.ast.node.model import Node
from smnp.ast.parser import Parser
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(
BlockNode.parse,
ReturnNode.parse,
ExpressionNode.parse
)(input)

View File

@@ -1,9 +1,13 @@
from smnp.ast.node.model import Node
from smnp.ast.node.access import AccessNode
from smnp.ast.node.literal import LiteralNode
from smnp.token.type import TokenType
class StringLiteralNode(Node):
def __init__(self, value, parent, pos):
Node.__init__(self, parent, pos)
self.children.append(value)
class StringLiteralNode(LiteralNode, AccessNode):
def __init__(self, pos):
super().__init__(pos)
del self.children[1]
self.value = self.children[0]
@classmethod
def _getTokenType(cls):
return TokenType.STRING

View File

@@ -1,5 +1,5 @@
from smnp.newast.node.access import AccessNode
from smnp.newast.node.literal import LiteralNode
from smnp.ast.node.access import AccessNode
from smnp.ast.node.literal import LiteralNode
from smnp.token.type import TokenType

View File

@@ -1,8 +1,8 @@
from smnp.newast.node.expression import ExpressionNode
from smnp.newast.node.identifier import IdentifierNode
from smnp.newast.node.none import NoneNode
from smnp.newast.node.type import TypeNode
from smnp.newast.parser import Parser
from smnp.ast.node.expression import ExpressionNode
from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.none import NoneNode
from smnp.ast.node.type import TypeNode
from smnp.ast.parser import Parser
from smnp.token.type import TokenType
class TypedVariableNode(ExpressionNode):

View File

@@ -1,12 +1,115 @@
from smnp.ast.node.ignore import IgnoredNode
from smnp.ast.node.model import ParseResult, Node
from smnp.ast.node.program import Program
from smnp.ast.parsers.token import parseToken
def parse(input):
root = Program()
while input.hasCurrent():
root.append(parseToken(input, root))
return root
return Program.parse(input)
class Parser:
# a -> A
@staticmethod
def terminalParser(expectedType, createNode=None):
def provideNode(value, pos):
if createNode is None:
return IgnoredNode(pos)
return createNode(value, pos)
def parse(input):
if input.hasCurrent() and input.current().type == expectedType:
token = input.current()
input.ahead()
return ParseResult.OK(provideNode(token.value, token.pos))
return ParseResult.FAIL()
return parse
# oneOf -> a | b | c | ...
@staticmethod
def oneOf(*parsers, exception=None):
def combinedParser(input):
snap = input.snapshot()
for parser in parsers:
value = parser(input)
if value.result:
return value
if exception is not None:
raise exception
input.reset(snap)
return ParseResult.FAIL()
return combinedParser
# allOf -> a b c ...
@staticmethod
def allOf(*parsers, createNode, exception=None):
if len(parsers) == 0:
raise RuntimeError("Pass one parser at least")
def extendedParser(input):
snap = input.snapshot()
results = []
for parser in parsers:
result = parser(input)
if not result.result:
if exception is not None:
raise exception
input.reset(snap)
return ParseResult.FAIL()
results.append(result.node)
node = createNode(*results)
if not isinstance(node, Node):
raise RuntimeError("Function 'createNode' haven't returned a Node object. Probably forget to pass 'return'")
return ParseResult.OK(node)
__all__ = ["parse"]
return extendedParser
# leftAssociative -> left | left OP right
@staticmethod
def leftAssociativeOperatorParser(leftParser, operatorTokenType, rightParser, createNode):
def parse(input):
left = leftParser(input)
if left.result:
while Parser.terminalParser(operatorTokenType)(input).result:
right = rightParser(input)
left = ParseResult.OK(createNode(left.node, right.node))
return left
return ParseResult.FAIL()
return parse
# loop -> start item* end
@staticmethod
def loop(startParser, itemParser, endParser, createNode):
def parse(input):
items = []
start = startParser(input)
if start.result:
while True:
end = endParser(input)
if end.result:
return ParseResult.OK(createNode(start.node, items, end.node))
item = itemParser(input)
if not item.result:
return ParseResult.FAIL()
items.append(item.node)
return ParseResult.FAIL()
return parse

View File

@@ -1,24 +0,0 @@
from smnp.ast.node.access import AccessNode
from smnp.ast.parsers.expression import parseExpression
from smnp.token.type import TokenType
# access -> expr '.' expr
# TODO: dodać dziedziczenie wszystkich expressions po jednym typie ExpressionNode
# i potem sprawdzać przy wszystkich parent.pop(-1) czy pobrany z parenta element
# jest rzeczywiście wyrażeniem, bo teraz możliwe jest np. {}.fun()
def parseAccess(input, parent):
if input.isCurrent(TokenType.DOT):
token = input.current()
input.ahead()
element = parent.pop(-1)
property = parseExpression(input, parent)
node = AccessNode(element, property, parent, token.pos)
element.parent = node
property.parent = node
return node
return None

View File

@@ -1,18 +0,0 @@
from smnp.ast.node.asterisk import AsteriskNode
from smnp.ast.parsers.statement import parseStatement
from smnp.token.type import TokenType
# asterisk -> expr '*' stmt
def parseAsterisk(expr, input, parent):
if input.hasMore() and input.isCurrent(TokenType.ASTERISK):
token = input.current()
input.ahead()
stmt = parseStatement(input, parent)
asterisk = AsteriskNode(expr, stmt, parent, token.pos)
expr.parent = asterisk
stmt.parent = asterisk
return asterisk
return None

View File

@@ -1,47 +0,0 @@
from smnp.ast.node.block import BlockNode, CloseBlockNode, BlockItemNode
from smnp.ast.parsers.statement import parseStatement
from smnp.token.type import TokenType
def parseBlock(input, parent):
# '{'
if input.current().type == TokenType.OPEN_BRACKET:
token = input.current()
input.ahead()
node = BlockNode(parent, token.pos)
# '}'
if input.isCurrent(TokenType.CLOSE_BRACKET):
input.ahead()
return node
# blockItem
if input.hasCurrent():
item = parseBlockItem(input, node)
node.append(item)
return node
return None
# blockItem -> stmt | '}'
def parseBlockItem(input, parent):
# '}'
if input.isCurrent(TokenType.CLOSE_BRACKET):
close = CloseBlockNode(parent, input.current().pos)
input.ahead()
return close
if input.hasCurrent():
stmt = parseStatement(input, parent)
if stmt is not None:
item = BlockItemNode(stmt, parent, stmt.pos)
stmt.parent = item
nextBlockItem = parseBlockItem(input, item)
if nextBlockItem != None:
item.append(nextBlockItem)
return item
return None

View File

@@ -1,20 +0,0 @@
from smnp.ast.node.colon import ColonNode
from smnp.ast.parsers.expression import parseExpression
from smnp.error.syntax import SyntaxException
from smnp.token.type import TokenType
# colon -> expr ':' expr
def parseColon(expr1, input, parent):
if input.isCurrent(TokenType.COLON):
token = input.current()
input.ahead()
expr2 = parseExpression(input, parent)
if expr2 is None:
raise SyntaxException(f"Expected expression '{input.current().value}'", input.current().pos)
colon = ColonNode(expr1, expr2, parent, token.pos)
expr1.parent = colon
expr2.parent = colon
return colon
return None

View File

@@ -1,28 +0,0 @@
def parseExpression(input, parent):
from smnp.ast.tools import combineParsers
from smnp.ast.parsers.access import parseAccess
from smnp.ast.parsers.colon import parseColon
from smnp.ast.parsers.identifier import parseIdentifierOrFunctionCallOrAssignment
from smnp.ast.parsers.integer import parseIntegerAndPercent
from smnp.ast.parsers.list import parseList
from smnp.ast.parsers.minus import parseMinus
from smnp.ast.parsers.note import parseNote
from smnp.ast.parsers.string import parseString
parsers = [
parseIntegerAndPercent,
parseMinus,
parseString,
parseNote,
parseList,
parseIdentifierOrFunctionCallOrAssignment,
parseAccess,
]
expr = combineParsers(parsers)(input, parent)
colon = parseColon(expr, input, parent)
if colon is not None:
return colon
return expr

View File

@@ -1,29 +0,0 @@
from smnp.ast.node.function import FunctionDefinitionNode
from smnp.ast.parsers.block import parseBlock
from smnp.ast.parsers.identifier import parseIdentifier
from smnp.ast.parsers.list import parseList
from smnp.ast.tools import assertToken
from smnp.token.type import TokenType
def parseFunctionDefinition(input, parent):
if input.isCurrent(TokenType.FUNCTION):
token = input.current()
input.ahead()
assertToken(TokenType.IDENTIFIER, input)
identifier = parseIdentifier(input, parent)
assertToken(TokenType.OPEN_PAREN, input)
args = parseList(input, parent)
assertToken(TokenType.OPEN_BRACKET, input)
body = parseBlock(input, parent)
function = FunctionDefinitionNode(identifier, args, body, parent, token.pos)
identifier.parent = function
args.parent = function
body.parent = function
return function
return None

View File

@@ -1,48 +0,0 @@
from smnp.ast.node.assignment import AssignmentNode
from smnp.ast.node.function import FunctionCallNode
from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.parsers.expression import parseExpression
from smnp.ast.parsers.list import parseList
from smnp.ast.tools import greedy
from smnp.token.type import TokenType
# id -> IDENTIFIER
def parseIdentifier(input, parent):
if input.isCurrent(TokenType.IDENTIFIER):
identifier = IdentifierNode(input.current().value, parent, input.current().pos)
input.ahead()
return identifier
return None
# identifier -> IDENTIFIER
# functionCall -> identifier list
# assignment -> identifier '=' expr
def parseIdentifierOrFunctionCallOrAssignment(input, parent):
identifier = parseIdentifier(input, parent)
# assignment -> identifier '=' expr
if identifier is not None and input.hasCurrent():
if input.current().type == TokenType.ASSIGN:
token = input.current()
input.ahead()
expr = greedy(parseExpression)(input, parent)
assignment = AssignmentNode(identifier, expr, parent, token.pos)
identifier.parent = assignment
expr.parent = assignment
return assignment
# functionCall -> identifier list
args = parseList(input, parent)
if args is not None:
functionCall = FunctionCallNode(identifier, args, parent, identifier.pos)
args.parent = functionCall
identifier.parent = functionCall
return functionCall
return identifier

View File

@@ -1,26 +0,0 @@
from smnp.ast.node.integer import IntegerLiteralNode
from smnp.ast.node.percent import PercentNode
from smnp.token.type import TokenType
# int -> INTEGER
def parseInteger(input, parent):
if input.isCurrent(TokenType.INTEGER):
integer = IntegerLiteralNode(int(input.current().value), parent, input.current().pos)
input.ahead()
return integer
return None
# percent -> int '%'
# int -> int
def parseIntegerAndPercent(input, parent):
integer = parseInteger(input, parent)
if integer is not None and input.isCurrent(TokenType.PERCENT):
percent = PercentNode(integer, parent, input.current().pos)
integer.parent = percent
input.ahead()
return percent
return integer

View File

@@ -1,54 +0,0 @@
from smnp.ast.node.list import ListNode, ListItemNode, CloseListNode
from smnp.ast.parsers.expression import parseExpression
from smnp.ast.tools import greedy, assertToken
from smnp.token.type import TokenType
# list -> CLOSE_PAREN | expr listTail
def parseList(input, parent):
if input.isCurrent(TokenType.OPEN_PAREN):
node = ListNode(parent, input.current().pos)
input.ahead()
# list -> CLOSE_PAREN (end of list)
if input.isCurrent(TokenType.CLOSE_PAREN):
close = CloseListNode(node, input.current().pos)
node.append(close)
input.ahead()
return node
# list -> expr listTail
if input.hasCurrent():
token = input.current()
expr = greedy(parseExpression)(input, parent)
item = ListItemNode(expr, node, token.pos)
expr.parent = item
node.append(item)
listTail = parseListTail(input, item)
item.append(listTail)
return node
return None
# listTail -> COMMA expr listTail | CLOSE_PAREN
def parseListTail(input, parent):
# listTail -> CLOSE_PAREN
if input.isCurrent(TokenType.CLOSE_PAREN):
close = CloseListNode(parent, input.current().pos)
input.ahead()
return close
# listTail -> COMMA expr listTail
if input.hasCurrent() and input.hasMore():
assertToken(TokenType.COMMA, input)
input.ahead()
expr = greedy(parseExpression)(input, parent)
if expr is not None:
item = ListItemNode(expr, parent, expr.pos)
expr.parent = item
listTail = parseListTail(input, item)
item.append(listTail)
listTail.parent = item
return item
return None

View File

@@ -1,17 +0,0 @@
from smnp.ast.node.integer import IntegerLiteralNode
from smnp.ast.parsers.integer import parseInteger
from smnp.token.type import TokenType
# minus -> '-' int
def parseMinus(input, parent):
if input.isCurrent(TokenType.MINUS):
token = input.current()
input.ahead()
if input.hasCurrent():
expr = parseInteger(input, parent)
return IntegerLiteralNode(-expr.value, parent, token.pos)
return None

View File

@@ -1,39 +0,0 @@
import re
from smnp.ast.node.note import NoteLiteralNode
from smnp.note.model import Note
from smnp.token.type import TokenType
# note -> NOTE
def parseNote(input, parent):
if input.isCurrent(TokenType.NOTE):
token = input.current()
value = token.value
consumedChars = 1
notePitch = value[consumedChars]
consumedChars += 1
octave = 4
duration = 4
dot = False
if consumedChars < len(value) and value[consumedChars] in ('b', '#'):
notePitch += value[consumedChars]
consumedChars += 1
if consumedChars < len(value) and re.match(r'\d', value[consumedChars]):
octave = int(value[consumedChars])
consumedChars += 1
if consumedChars < len(value) and value[consumedChars] == '.':
consumedChars += 1
durationString = ''
while consumedChars < len(value) and re.match(r'\d', value[consumedChars]):
durationString += value[consumedChars]
consumedChars += 1
duration = int(durationString)
if consumedChars < len(value) and value[consumedChars] == 'd':
dot = True
consumedChars += 1
input.ahead()
return NoteLiteralNode(Note(notePitch, octave, duration, dot), parent, token.pos)
return None

View File

@@ -1,17 +0,0 @@
from smnp.ast.node.ret import ReturnNode
from smnp.ast.parsers.expression import parseExpression
from smnp.token.type import TokenType
def parseReturn(input, parent):
if input.isCurrent(TokenType.RETURN):
token = input.current()
input.ahead()
expr = parseExpression(input, parent)
node = ReturnNode(expr, parent, token.pos)
expr.parent = node
return node
return None

View File

@@ -1,22 +0,0 @@
def parseStatement(input, parent):
from smnp.ast.tools import combineParsers
from smnp.ast.parsers.asterisk import parseAsterisk
from smnp.ast.parsers.block import parseBlock
from smnp.ast.parsers.expression import parseExpression
from smnp.ast.parsers.function import parseFunctionDefinition
from smnp.ast.parsers.ret import parseReturn
parsers = [
parseBlock,
parseFunctionDefinition,
parseReturn,
parseExpression,
]
stmt = combineParsers(parsers)(input, parent)
asterisk = parseAsterisk(stmt, input, parent)
if asterisk is not None:
return asterisk
return stmt

View File

@@ -1,12 +0,0 @@
from smnp.ast.node.string import StringLiteralNode
from smnp.token.type import TokenType
# string -> STRING
def parseString(input, parent):
if input.isCurrent(TokenType.STRING):
string = StringLiteralNode(input.current().value[1:len(input.current().value) - 1], parent, input.current().pos)
input.ahead()
return string
return None

View File

@@ -1,12 +0,0 @@
from smnp.ast.parsers.statement import parseStatement
from smnp.ast.tools import combineParsers
from smnp.error.syntax import SyntaxException
def parseToken(input, parent):
value = combineParsers([ parseStatement ])(input, parent)
if value is None:
raise SyntaxException("Unknown statement") # TODO
return value

View File

@@ -1,30 +1,8 @@
from smnp.ast.node.model import Node
from smnp.error.syntax import SyntaxException
def greedy(parser):
def _rollup(input, parent):
node = Node(None, (-1, -1))
elem = parser(input, node)
while elem is not None:
node.append(elem)
elem = parser(input, node)
return node.children[0] if len(node.children) > 0 else None
return _rollup
def assertToken(expected, input):
if not input.hasCurrent():
raise SyntaxException(f"Expected '{expected}'")
if expected != input.current().type:
raise SyntaxException(f"Expected '{expected}', found '{input.current().value}'", input.current().pos)
def combineParsers(parsers):
def combinedParser(input, parent):
for parser in parsers:
value = parser(input, parent)
if value is not None:
return value
return None
return combinedParser

View File

@@ -1,8 +1,8 @@
import sys
from smnp.ast.node.program import Program
from smnp.environment.factory import createEnvironment
from smnp.error.base import SmnpException
from smnp.newast.node.program import Program
from smnp.runtime.evaluator import evaluate
from smnp.token.tokenizer import tokenize

View File

@@ -1,54 +0,0 @@
from smnp.newast.node.expression import ExpressionNode
from smnp.newast.node.ignore import IgnoredNode
from smnp.newast.parser import Parser
from smnp.token.type import TokenType
class AccessNode(ExpressionNode):
def __init__(self, pos):
super().__init__(pos)
self.children.append(IgnoredNode(pos))
@property
def left(self):
return self[0]
@left.setter
def left(self, value):
self[0] = value
@property
def right(self):
return self[1]
@right.setter
def right(self, value):
self[1] = value
@classmethod
def _parse(cls, input):
def createNode(left, right):
node = AccessNode(right.pos)
node.left = left
node.right = right
return node
return Parser.leftAssociativeOperatorParser(
cls._literalParser(),
TokenType.DOT,
cls._parseAccessingProperty(),
createNode=createNode
)(input)
@classmethod
def _literalParser(cls):
pass
@staticmethod
def _parseAccessingProperty():
from smnp.newast.node.identifier import IdentifierNode
return Parser.oneOf(
IdentifierNode._literalParser(),
IdentifierNode._functionCallParser()
)

View File

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

View File

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

View File

@@ -1,20 +0,0 @@
from smnp.newast.node.statement import StatementNode
from smnp.newast.parser import Parser
from smnp.token.type import TokenType
class BlockNode(StatementNode):
@classmethod
def _parse(cls, input):
def createNode(start, items, end):
node = BlockNode(start.pos)
node.children = items
return node
return Parser.loop(
Parser.terminalParser(TokenType.OPEN_BRACKET),
StatementNode.parse,
Parser.terminalParser(TokenType.CLOSE_BRACKET),
createNode=createNode
)(input)

View File

@@ -1,67 +0,0 @@
from smnp.newast.node.block import BlockNode
from smnp.newast.node.identifier import IdentifierNode
from smnp.newast.node.iterable import abstractIterableParser
from smnp.newast.node.model import Node
from smnp.newast.node.none import NoneNode
from smnp.newast.node.statement import StatementNode
from smnp.newast.node.variable import TypedVariableNode
from smnp.newast.parser import Parser
from smnp.token.type import TokenType
class ArgumentsDeclarationNode(Node):
@classmethod
def _parse(cls, input):
raise RuntimeError("This class is not supposed to be automatically called")
class FunctionDefinitionNode(StatementNode):
def __init__(self, pos):
super().__init__(pos)
self.children = [NoneNode(), NoneNode(), NoneNode()]
@property
def name(self):
return self[0]
@name.setter
def name(self, value):
self[0] = value
@property
def arguments(self):
return self[1]
@arguments.setter
def arguments(self, value):
self[1] = value
@property
def body(self):
return self[2]
@body.setter
def body(self, value):
self[2] = value
@classmethod
def _parse(cls, input):
def createNode(function, name, arguments, body):
node = FunctionDefinitionNode(function.pos)
node.name = name
node.arguments = arguments
node.body = body
return node
return Parser.allOf(
Parser.terminalParser(TokenType.FUNCTION),
IdentifierNode.identifierParser(),
cls._argumentsDeclarationParser(),
BlockNode.parse,
createNode=createNode
)(input)
@staticmethod
def _argumentsDeclarationParser():
return abstractIterableParser(ArgumentsDeclarationNode, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, TypedVariableNode.parser())

View File

@@ -1,54 +0,0 @@
from smnp.newast.node.access import AccessNode
from smnp.newast.node.args import ArgumentsListNode
from smnp.newast.node.assignment import AssignmentNode
from smnp.newast.node.expression import ExpressionNode
from smnp.newast.node.invocation import FunctionCall
from smnp.newast.parser import Parser
from smnp.token.type import TokenType
class IdentifierNode(AccessNode):
def __init__(self, pos):
super().__init__(pos)
del self.children[1]
@classmethod
def _literalParser(cls):
return Parser.oneOf(
IdentifierNode._functionCallParser(),
IdentifierNode._assignmentParser(),
IdentifierNode.identifierParser()
)
@staticmethod
def _assignmentParser():
def createNode(target, assignment, value):
node = AssignmentNode(assignment.pos)
node.target = target
node.value = value
return node
return Parser.allOf(
IdentifierNode.identifierParser(),
Parser.terminalParser(TokenType.ASSIGN),
ExpressionNode.parse,
createNode=createNode
)
@staticmethod
def _functionCallParser():
def createNode(name, arguments):
node = FunctionCall(name.pos)
node.name = name
node.arguments = arguments
return node
return Parser.allOf(
IdentifierNode.identifierParser(),
ArgumentsListNode.parse,
createNode=createNode
)
@staticmethod
def identifierParser():
return Parser.terminalParser(TokenType.IDENTIFIER, lambda val, pos: IdentifierNode.withValue(val, pos))

View File

@@ -1,31 +0,0 @@
from smnp.newast.node.access import AccessNode
from smnp.newast.parser import Parser
from smnp.token.type import TokenType
class IntegerLiteralNode(AccessNode):
def __init__(self, pos):
super().__init__(pos)
del self.children[1]
@classmethod
def _literalParser(cls):
return Parser.oneOf(
cls._negativeIntegerParser(),
cls._positiveIntegerParser()
)
@classmethod
def _negativeIntegerParser(cls):
def createNode(minus, integer):
return IntegerLiteralNode.withValue(-integer.value, minus.pos)
return Parser.allOf(
Parser.terminalParser(TokenType.MINUS),
cls._positiveIntegerParser(),
createNode=createNode
)
@classmethod
def _positiveIntegerParser(cls):
return Parser.terminalParser(TokenType.INTEGER, lambda val, pos: IntegerLiteralNode.withValue(int(val), pos))

View File

@@ -1,11 +0,0 @@
from smnp.newast.node.access import AccessNode
from smnp.newast.node.expression import ExpressionNode
from smnp.newast.node.iterable import abstractIterableParser
from smnp.token.type import TokenType
class ListNode(AccessNode):
@classmethod
def _literalParser(cls):
return abstractIterableParser(ListNode, TokenType.OPEN_SQUARE, TokenType.CLOSE_SQUARE, ExpressionNode.parse)

View File

@@ -1,81 +0,0 @@
class Node:
def __init__(self, pos, children=None):
if children is None:
children = []
self.children = children
self.pos = pos
self.parent = None
for child in self.children:
if isinstance(child, Node):
child.parent = self
def __len__(self):
return len(self.children)
def __getitem__(self, key):
return self.children[key]
def __setitem__(self, key, value):
if isinstance(value, Node):
value.parent = self
self.children[key] = value
def append(self, node):
if isinstance(node, Node):
node.parent = self
self.children.append(node)
def pop(self, index):
return self.children.pop(index)
@classmethod
def _parse(cls, input):
pass
@classmethod
def parse(cls, input):
result = cls._parse(input)
if result is None:
return ParseResult.FAIL()
if not isinstance(result, ParseResult):
raise RuntimeError(f"_parse() method of '{cls.__name__}' class haven't returned ParseResult object")
return result
def print(self):
self._print(first=True)
def _print(self, prefix="", last=True, first=False):
print(prefix, '' if first else '└─' if last else '├─', self.__class__.__name__, sep="")
prefix += ' ' if last else ''
for i, child in enumerate(self.children):
last = i == len(self.children) - 1
if isinstance(child, Node):
child._print(prefix, last)
else:
print(prefix, '' if last else '', f"'{str(child)}'", sep="")
def __str__(self):
return self.__class__.__name__
class ParseResult():
def __init__(self, result, node):
if result and node is None:
raise RuntimeError("Node musn't be None if result is set to True for ParseResult")
self.result = result
self.node = node
@staticmethod
def FAIL():
return ParseResult(False, None)
@staticmethod
def OK(node):
return ParseResult(True, node)
def __str__(self):
return f"{'OK' if self.result else 'FAILED'}[{self.node}]"

View File

@@ -1,13 +0,0 @@
from smnp.newast.node.access import AccessNode
from smnp.newast.node.literal import LiteralNode
from smnp.token.type import TokenType
class NoteLiteralNode(LiteralNode, AccessNode):
def __init__(self, pos):
super().__init__(pos)
del self.children[1]
@classmethod
def _getTokenType(cls):
return TokenType.NOTE

View File

@@ -1,33 +0,0 @@
from smnp.error.syntax import SyntaxException
from smnp.newast.node.expression import ExpressionNode
from smnp.newast.node.extend import ExtendNode
from smnp.newast.node.function import FunctionDefinitionNode
from smnp.newast.node.imports import ImportNode
from smnp.newast.node.model import Node, ParseResult
from smnp.newast.node.statement import StatementNode
from smnp.newast.parser import Parser
class Program(Node):
def __init__(self):
super().__init__((-1, -1))
@classmethod
def _parse(cls, input):
def parseToken(input):
return Parser.oneOf(
FunctionDefinitionNode.parse,
ExtendNode.parse,
ExpressionNode.parse,
ImportNode.parse,
StatementNode.parse,
exception = SyntaxException(f"Unknown statement: {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,32 +0,0 @@
from smnp.newast.node.expression import ExpressionNode
from smnp.newast.node.none import NoneNode
from smnp.newast.node.statement import StatementNode
from smnp.newast.parser import Parser
from smnp.token.type import TokenType
class ReturnNode(StatementNode):
def __init__(self, pos):
super().__init__(pos)
self.children.append(NoneNode())
@property
def value(self):
return self[0]
@value.setter
def value(self, value):
self[0] = value
@classmethod
def _parse(cls, input):
def createNode(ret, value):
node = ReturnNode(ret.pos)
node.value = value
return node
return Parser.allOf(
Parser.terminalParser(TokenType.RETURN),
ExpressionNode.parse,
createNode=createNode
)(input)

View File

@@ -1,17 +0,0 @@
from smnp.newast.node.model import Node
from smnp.newast.parser import Parser
class StatementNode(Node):
@classmethod
def _parse(cls, input):
from smnp.newast.node.block import BlockNode
from smnp.newast.node.expression import ExpressionNode
from smnp.newast.node.ret import ReturnNode
return Parser.oneOf(
BlockNode.parse,
ReturnNode.parse,
ExpressionNode.parse
)(input)

View File

@@ -1,13 +0,0 @@
from smnp.newast.node.access import AccessNode
from smnp.newast.node.literal import LiteralNode
from smnp.token.type import TokenType
class StringLiteralNode(LiteralNode, AccessNode):
def __init__(self, pos):
super().__init__(pos)
del self.children[1]
@classmethod
def _getTokenType(cls):
return TokenType.STRING

View File

@@ -1,117 +0,0 @@
from smnp.newast.node.ignore import IgnoredNode
from smnp.newast.node.model import ParseResult, Node
class Parser:
@staticmethod
def nonTerminalParser(parser, createNode):
def parse(input):
token = input.current()
result = parse(input)
if result.result:
return ParseResult.OK(createNode(result.node, token.pos))
return ParseResult.FAIL()
return parse
@staticmethod
def terminalParser(expectedType, createNode=None):
def provideNode(value, pos):
if createNode is None:
return IgnoredNode(pos)
return createNode(value, pos)
def parse(input):
if input.hasCurrent() and input.current().type == expectedType:
token = input.current()
input.ahead()
return ParseResult.OK(provideNode(token.value, token.pos))
return ParseResult.FAIL()
return parse
@staticmethod
def oneOf(*parsers, exception=None):
def combinedParser(input):
snap = input.snapshot()
for parser in parsers:
value = parser(input)
if value.result:
return value
if exception is not None:
raise exception
input.reset(snap)
return ParseResult.FAIL()
return combinedParser
@staticmethod
def allOf(*parsers, createNode, exception=None):
if len(parsers) == 0:
raise RuntimeError("Pass one parser at least")
def extendedParser(input):
snap = input.snapshot()
results = []
for parser in parsers:
result = parser(input)
if not result.result:
if exception is not None:
raise exception
input.reset(snap)
return ParseResult.FAIL()
results.append(result.node)
node = createNode(*results)
if not isinstance(node, Node):
raise RuntimeError("Function 'createNode' haven't returned a Node object. Probably forget to pass 'return'")
return ParseResult.OK(node)
return extendedParser
# leftAssociative -> left | left OP right
@staticmethod
def leftAssociativeOperatorParser(leftParser, operatorTokenType, rightParser, createNode):
def parse(input):
left = leftParser(input)
if left.result:
while Parser.terminalParser(operatorTokenType)(input).result:
right = rightParser(input)
left = ParseResult.OK(createNode(left.node, right.node))
return left
return ParseResult.FAIL()
return parse
@staticmethod
def loop(startParser, itemParser, endParser, createNode):
def parse(input):
items = []
start = startParser(input)
if start.result:
while True:
end = endParser(input)
if end.result:
return ParseResult.OK(createNode(start.node, items, end.node))
item = itemParser(input)
if not item.result:
return ParseResult.FAIL()
items.append(item.node)
return ParseResult.FAIL()
return parse

View File

@@ -1,8 +0,0 @@
from smnp.error.syntax import SyntaxException
def assertToken(expected, input):
if not input.hasCurrent():
raise SyntaxException(f"Expected '{expected}'")
if expected != input.current().type:
raise SyntaxException(f"Expected '{expected}', found '{input.current().value}'", input.current().pos)