diff --git a/grammar b/grammar index 54e472f..a50250a 100644 --- a/grammar +++ b/grammar @@ -9,11 +9,9 @@ integer := '-' DIGIT+ | DIGIT+ string := '"' CHAR* '"' note := '@' PITCH PITCH_MODIFIER? DIGIT? ['.' DIGIT+ 'd'?]? identifier := ID [ID|DIGIT]* -percent := DIGIT+ '%' # Parser expr := integer accessTail | integer -expr := percent accessTail | percent expr := string accessTail | string expr := note accessTail | note expr := identifier accessTail | identifier '=' expr | functionCall | identifier diff --git a/smnp/newast/node/access.py b/smnp/newast/node/access.py new file mode 100644 index 0000000..c644343 --- /dev/null +++ b/smnp/newast/node/access.py @@ -0,0 +1,16 @@ +from smnp.newast.node.expression import ExpressionNode +from smnp.newast.node.ignore import IgnoredNode + + +class AccessNode(ExpressionNode): + def __init__(self, pos): + super().__init__(pos) + self.children.append(IgnoredNode(pos)) + + @property + def next(self): + return self[1] + + @next.setter + def next(self, value): + self[1] = value diff --git a/smnp/newast/node/integer.py b/smnp/newast/node/integer.py index 6e81224..f61d598 100644 --- a/smnp/newast/node/integer.py +++ b/smnp/newast/node/integer.py @@ -1,3 +1,4 @@ +from smnp.newast.node.access import AccessNode from smnp.newast.node.expression import ExpressionNode from smnp.newast.parser import Parser from smnp.token.type import TokenType @@ -7,5 +8,26 @@ class IntegerLiteralNode(ExpressionNode): @classmethod def _parse(cls, input): + def createNode(left, right): + node = AccessNode(right.pos) + node.value = left + node.next = right + return node + + return Parser.leftAssociativeOperatorParser( + IntegerLiteralNode._parseInteger(), + TokenType.DOT, + IntegerLiteralNode._parseAccessingProperty(), + createNode=createNode + )(input) + + + @staticmethod + def _parseInteger(): createNode = lambda v, pos: IntegerLiteralNode.withValue(v, pos) - return Parser.terminalParser(TokenType.INTEGER, createNode)(input) \ No newline at end of file + return Parser.terminalParser(TokenType.INTEGER, createNode) + + @staticmethod + def _parseAccessingProperty(): + # TODO: Just for example. It is supposed to be functionCall (and identifier there) + return IntegerLiteralNode._parseInteger() diff --git a/smnp/newast/node/model.py b/smnp/newast/node/model.py index b9cffa8..6a44748 100644 --- a/smnp/newast/node/model.py +++ b/smnp/newast/node/model.py @@ -56,6 +56,9 @@ class Node: else: print(prefix, '└' if last else '├', f"'{str(child)}'", sep="") + def __str__(self): + return self.__class__.__name__ + class ParseResult(): def __init__(self, result, node): @@ -72,4 +75,7 @@ class ParseResult(): def OK(node): return ParseResult(True, node) + def __str__(self): + return f"{'OK' if self.result else 'FAILED'}[{self.node}]" + diff --git a/smnp/newast/parser.py b/smnp/newast/parser.py index 55efe8f..99e7c01 100644 --- a/smnp/newast/parser.py +++ b/smnp/newast/parser.py @@ -1,5 +1,5 @@ from smnp.newast.node.ignore import IgnoredNode -from smnp.newast.node.model import ParseResult +from smnp.newast.node.model import ParseResult, Node class Parser: @@ -46,6 +46,7 @@ class Parser: return ParseResult.FAIL() return combinedParser + @staticmethod def allOf(*parsers, createNode, exception=None): if len(parsers) == 0: @@ -68,8 +69,36 @@ class Parser: results.append(result.node) - return ParseResult.OK(createNode(*results)) + 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 epsilon(): + def parser(input): + return ParseResult.OK(IgnoredNode((-1, -1))) + + return parser