From f81279094f251c46f94f016128f77a47c7d1da25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Pluta?= Date: Sat, 6 Jul 2019 15:56:28 +0200 Subject: [PATCH] Remove old parser and move new parser to 'ast' package --- smnp/ast/node/access.py | 58 ++++++++++-- smnp/{newast => ast}/node/args.py | 6 +- smnp/ast/node/assignment.py | 32 +++++-- smnp/ast/node/asterisk.py | 32 +++++-- smnp/ast/node/block.py | 29 +++--- smnp/ast/node/colon.py | 10 -- smnp/{newast => ast}/node/expression.py | 20 ++-- smnp/{newast => ast}/node/extend.py | 14 +-- smnp/ast/node/function.py | 73 ++++++++++++--- smnp/ast/node/identifier.py | 57 ++++++++++-- smnp/{newast => ast}/node/ignore.py | 2 +- smnp/{newast => ast}/node/imports.py | 12 +-- smnp/ast/node/integer.py | 34 +++++-- smnp/{newast => ast}/node/invocation.py | 2 +- smnp/{newast => ast}/node/iterable.py | 10 +- smnp/ast/node/list.py | 22 ++--- smnp/{newast => ast}/node/literal.py | 4 +- smnp/ast/node/model.py | 85 ++++++++++++----- smnp/{newast => ast}/node/none.py | 2 +- smnp/ast/node/note.py | 16 ++-- smnp/ast/node/percent.py | 9 -- smnp/ast/node/program.py | 30 +++++- smnp/ast/node/ret.py | 35 +++++-- smnp/ast/node/statement.py | 17 ++++ smnp/ast/node/string.py | 16 ++-- smnp/{newast => ast}/node/type.py | 4 +- smnp/{newast => ast}/node/variable.py | 10 +- smnp/ast/parser.py | 115 +++++++++++++++++++++-- smnp/ast/parsers/__init__.py | 0 smnp/ast/parsers/access.py | 24 ----- smnp/ast/parsers/asterisk.py | 18 ---- smnp/ast/parsers/block.py | 47 ---------- smnp/ast/parsers/colon.py | 20 ---- smnp/ast/parsers/expression.py | 28 ------ smnp/ast/parsers/function.py | 29 ------ smnp/ast/parsers/identifier.py | 48 ---------- smnp/ast/parsers/integer.py | 26 ------ smnp/ast/parsers/list.py | 54 ----------- smnp/ast/parsers/minus.py | 17 ---- smnp/ast/parsers/note.py | 39 -------- smnp/ast/parsers/ret.py | 17 ---- smnp/ast/parsers/statement.py | 22 ----- smnp/ast/parsers/string.py | 12 --- smnp/ast/parsers/token.py | 12 --- smnp/ast/tools.py | 24 +---- smnp/main.py | 2 +- smnp/newast/__init__.py | 0 smnp/newast/node/__init__.py | 0 smnp/newast/node/access.py | 54 ----------- smnp/newast/node/assignment.py | 28 ------ smnp/newast/node/asterisk.py | 28 ------ smnp/newast/node/block.py | 20 ---- smnp/newast/node/function.py | 67 -------------- smnp/newast/node/identifier.py | 54 ----------- smnp/newast/node/integer.py | 31 ------- smnp/newast/node/list.py | 11 --- smnp/newast/node/model.py | 81 ---------------- smnp/newast/node/note.py | 13 --- smnp/newast/node/program.py | 33 ------- smnp/newast/node/ret.py | 32 ------- smnp/newast/node/statement.py | 17 ---- smnp/newast/node/string.py | 13 --- smnp/newast/parser.py | 117 ------------------------ smnp/newast/tools.py | 8 -- 64 files changed, 573 insertions(+), 1229 deletions(-) rename smnp/{newast => ast}/node/args.py (61%) delete mode 100644 smnp/ast/node/colon.py rename smnp/{newast => ast}/node/expression.py (70%) rename smnp/{newast => ast}/node/extend.py (82%) rename smnp/{newast => ast}/node/ignore.py (68%) rename smnp/{newast => ast}/node/imports.py (86%) rename smnp/{newast => ast}/node/invocation.py (91%) rename smnp/{newast => ast}/node/iterable.py (93%) rename smnp/{newast => ast}/node/literal.py (79%) rename smnp/{newast => ast}/node/none.py (75%) delete mode 100644 smnp/ast/node/percent.py create mode 100644 smnp/ast/node/statement.py rename smnp/{newast => ast}/node/type.py (71%) rename smnp/{newast => ast}/node/variable.py (82%) delete mode 100644 smnp/ast/parsers/__init__.py delete mode 100644 smnp/ast/parsers/access.py delete mode 100644 smnp/ast/parsers/asterisk.py delete mode 100644 smnp/ast/parsers/block.py delete mode 100644 smnp/ast/parsers/colon.py delete mode 100644 smnp/ast/parsers/expression.py delete mode 100644 smnp/ast/parsers/function.py delete mode 100644 smnp/ast/parsers/identifier.py delete mode 100644 smnp/ast/parsers/integer.py delete mode 100644 smnp/ast/parsers/list.py delete mode 100644 smnp/ast/parsers/minus.py delete mode 100644 smnp/ast/parsers/note.py delete mode 100644 smnp/ast/parsers/ret.py delete mode 100644 smnp/ast/parsers/statement.py delete mode 100644 smnp/ast/parsers/string.py delete mode 100644 smnp/ast/parsers/token.py delete mode 100644 smnp/newast/__init__.py delete mode 100644 smnp/newast/node/__init__.py delete mode 100644 smnp/newast/node/access.py delete mode 100644 smnp/newast/node/assignment.py delete mode 100644 smnp/newast/node/asterisk.py delete mode 100644 smnp/newast/node/block.py delete mode 100644 smnp/newast/node/function.py delete mode 100644 smnp/newast/node/identifier.py delete mode 100644 smnp/newast/node/integer.py delete mode 100644 smnp/newast/node/list.py delete mode 100644 smnp/newast/node/model.py delete mode 100644 smnp/newast/node/note.py delete mode 100644 smnp/newast/node/program.py delete mode 100644 smnp/newast/node/ret.py delete mode 100644 smnp/newast/node/statement.py delete mode 100644 smnp/newast/node/string.py delete mode 100644 smnp/newast/parser.py delete mode 100644 smnp/newast/tools.py diff --git a/smnp/ast/node/access.py b/smnp/ast/node/access.py index a8198af..8f7d5a8 100644 --- a/smnp/ast/node/access.py +++ b/smnp/ast/node/access.py @@ -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] \ No newline at end of file + @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() + ) diff --git a/smnp/newast/node/args.py b/smnp/ast/node/args.py similarity index 61% rename from smnp/newast/node/args.py rename to smnp/ast/node/args.py index edfd870..24f0e08 100644 --- a/smnp/newast/node/args.py +++ b/smnp/ast/node/args.py @@ -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 diff --git a/smnp/ast/node/assignment.py b/smnp/ast/node/assignment.py index c1c4f14..6b64921 100644 --- a/smnp/ast/node/assignment.py +++ b/smnp/ast/node/assignment.py @@ -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] \ No newline at end of file + @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") \ No newline at end of file diff --git a/smnp/ast/node/asterisk.py b/smnp/ast/node/asterisk.py index 43403a0..cfd6cb3 100644 --- a/smnp/ast/node/asterisk.py +++ b/smnp/ast/node/asterisk.py @@ -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] \ No newline at end of file + @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") \ No newline at end of file diff --git a/smnp/ast/node/block.py b/smnp/ast/node/block.py index a407257..b9ba7bc 100644 --- a/smnp/ast/node/block.py +++ b/smnp/ast/node/block.py @@ -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 \ No newline at end of file + return Parser.loop( + Parser.terminalParser(TokenType.OPEN_BRACKET), + StatementNode.parse, + Parser.terminalParser(TokenType.CLOSE_BRACKET), + createNode=createNode + )(input) \ No newline at end of file diff --git a/smnp/ast/node/colon.py b/smnp/ast/node/colon.py deleted file mode 100644 index b65e8e7..0000000 --- a/smnp/ast/node/colon.py +++ /dev/null @@ -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] \ No newline at end of file diff --git a/smnp/newast/node/expression.py b/smnp/ast/node/expression.py similarity index 70% rename from smnp/newast/node/expression.py rename to smnp/ast/node/expression.py index b6be2e1..1dec6e8 100644 --- a/smnp/newast/node/expression.py +++ b/smnp/ast/node/expression.py @@ -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, diff --git a/smnp/newast/node/extend.py b/smnp/ast/node/extend.py similarity index 82% rename from smnp/newast/node/extend.py rename to smnp/ast/node/extend.py index 33535a5..0611bb7 100644 --- a/smnp/newast/node/extend.py +++ b/smnp/ast/node/extend.py @@ -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 diff --git a/smnp/ast/node/function.py b/smnp/ast/node/function.py index b892f76..0fe2baf 100644 --- a/smnp/ast/node/function.py +++ b/smnp/ast/node/function.py @@ -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] \ No newline at end of file + @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()) \ No newline at end of file diff --git a/smnp/ast/node/identifier.py b/smnp/ast/node/identifier.py index 526aefc..a6f1869 100644 --- a/smnp/ast/node/identifier.py +++ b/smnp/ast/node/identifier.py @@ -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] \ No newline at end of file + @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)) diff --git a/smnp/newast/node/ignore.py b/smnp/ast/node/ignore.py similarity index 68% rename from smnp/newast/node/ignore.py rename to smnp/ast/node/ignore.py index bf127bb..17f2b32 100644 --- a/smnp/newast/node/ignore.py +++ b/smnp/ast/node/ignore.py @@ -1,4 +1,4 @@ -from smnp.newast.node.model import Node +from smnp.ast.node.model import Node class IgnoredNode(Node): diff --git a/smnp/newast/node/imports.py b/smnp/ast/node/imports.py similarity index 86% rename from smnp/newast/node/imports.py rename to smnp/ast/node/imports.py index 5a075b1..ded151b 100644 --- a/smnp/newast/node/imports.py +++ b/smnp/ast/node/imports.py @@ -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 diff --git a/smnp/ast/node/integer.py b/smnp/ast/node/integer.py index 57a8058..ff2ca42 100644 --- a/smnp/ast/node/integer.py +++ b/smnp/ast/node/integer.py @@ -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] \ No newline at end of file + @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)) \ No newline at end of file diff --git a/smnp/newast/node/invocation.py b/smnp/ast/node/invocation.py similarity index 91% rename from smnp/newast/node/invocation.py rename to smnp/ast/node/invocation.py index 0db3099..0574985 100644 --- a/smnp/newast/node/invocation.py +++ b/smnp/ast/node/invocation.py @@ -1,4 +1,4 @@ -from smnp.newast.node.access import AccessNode +from smnp.ast.node.access import AccessNode class FunctionCall(AccessNode): diff --git a/smnp/newast/node/iterable.py b/smnp/ast/node/iterable.py similarity index 93% rename from smnp/newast/node/iterable.py rename to smnp/ast/node/iterable.py index 11619e2..f816023 100644 --- a/smnp/newast/node/iterable.py +++ b/smnp/ast/node/iterable.py @@ -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 diff --git a/smnp/ast/node/list.py b/smnp/ast/node/list.py index 4aaf583..f1194eb 100644 --- a/smnp/ast/node/list.py +++ b/smnp/ast/node/list.py @@ -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 \ No newline at end of file + @classmethod + def _literalParser(cls): + return abstractIterableParser(ListNode, TokenType.OPEN_SQUARE, TokenType.CLOSE_SQUARE, ExpressionNode.parse) diff --git a/smnp/newast/node/literal.py b/smnp/ast/node/literal.py similarity index 79% rename from smnp/newast/node/literal.py rename to smnp/ast/node/literal.py index 4c77a72..bdfc76b 100644 --- a/smnp/newast/node/literal.py +++ b/smnp/ast/node/literal.py @@ -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): diff --git a/smnp/ast/node/model.py b/smnp/ast/node/model.py index 19ae4ee..6a44748 100644 --- a/smnp/ast/node/model.py +++ b/smnp/ast/node/model.py @@ -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: - child.parent = self - - def __repr__(self): - return self.__str__() + if isinstance(child, Node): + child.parent = self 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): - node.parent = self + 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 \ No newline at end of file diff --git a/smnp/newast/node/none.py b/smnp/ast/node/none.py similarity index 75% rename from smnp/newast/node/none.py rename to smnp/ast/node/none.py index d912a63..5ddb409 100644 --- a/smnp/newast/node/none.py +++ b/smnp/ast/node/none.py @@ -1,4 +1,4 @@ -from smnp.newast.node.model import Node +from smnp.ast.node.model import Node class NoneNode(Node): diff --git a/smnp/ast/node/note.py b/smnp/ast/node/note.py index 4f9aa69..e4c02b0 100644 --- a/smnp/ast/node/note.py +++ b/smnp/ast/node/note.py @@ -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] \ No newline at end of file + @classmethod + def _getTokenType(cls): + return TokenType.NOTE \ No newline at end of file diff --git a/smnp/ast/node/percent.py b/smnp/ast/node/percent.py deleted file mode 100644 index 59b2185..0000000 --- a/smnp/ast/node/percent.py +++ /dev/null @@ -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] \ No newline at end of file diff --git a/smnp/ast/node/program.py b/smnp/ast/node/program.py index a98f3c1..59879e8 100644 --- a/smnp/ast/node/program.py +++ b/smnp/ast/node/program.py @@ -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)) \ No newline at end of file + 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) \ No newline at end of file diff --git a/smnp/ast/node/ret.py b/smnp/ast/node/ret.py index ccd4bdb..17f1f79 100644 --- a/smnp/ast/node/ret.py +++ b/smnp/ast/node/ret.py @@ -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] \ No newline at end of file + @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) \ No newline at end of file diff --git a/smnp/ast/node/statement.py b/smnp/ast/node/statement.py new file mode 100644 index 0000000..eec2cff --- /dev/null +++ b/smnp/ast/node/statement.py @@ -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) \ No newline at end of file diff --git a/smnp/ast/node/string.py b/smnp/ast/node/string.py index f7775d2..4034d3d 100644 --- a/smnp/ast/node/string.py +++ b/smnp/ast/node/string.py @@ -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] \ No newline at end of file + @classmethod + def _getTokenType(cls): + return TokenType.STRING \ No newline at end of file diff --git a/smnp/newast/node/type.py b/smnp/ast/node/type.py similarity index 71% rename from smnp/newast/node/type.py rename to smnp/ast/node/type.py index 8553ba2..98ffc4a 100644 --- a/smnp/newast/node/type.py +++ b/smnp/ast/node/type.py @@ -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 diff --git a/smnp/newast/node/variable.py b/smnp/ast/node/variable.py similarity index 82% rename from smnp/newast/node/variable.py rename to smnp/ast/node/variable.py index 4721132..060e42b 100644 --- a/smnp/newast/node/variable.py +++ b/smnp/ast/node/variable.py @@ -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): diff --git a/smnp/ast/parser.py b/smnp/ast/parser.py index e870963..dc09a70 100644 --- a/smnp/ast/parser.py +++ b/smnp/ast/parser.py @@ -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"] \ No newline at end of file + + 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 + diff --git a/smnp/ast/parsers/__init__.py b/smnp/ast/parsers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/smnp/ast/parsers/access.py b/smnp/ast/parsers/access.py deleted file mode 100644 index 743a811..0000000 --- a/smnp/ast/parsers/access.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/ast/parsers/asterisk.py b/smnp/ast/parsers/asterisk.py deleted file mode 100644 index 2306876..0000000 --- a/smnp/ast/parsers/asterisk.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/ast/parsers/block.py b/smnp/ast/parsers/block.py deleted file mode 100644 index 2de4212..0000000 --- a/smnp/ast/parsers/block.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/ast/parsers/colon.py b/smnp/ast/parsers/colon.py deleted file mode 100644 index 318362b..0000000 --- a/smnp/ast/parsers/colon.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/ast/parsers/expression.py b/smnp/ast/parsers/expression.py deleted file mode 100644 index 9fee60e..0000000 --- a/smnp/ast/parsers/expression.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/ast/parsers/function.py b/smnp/ast/parsers/function.py deleted file mode 100644 index d659584..0000000 --- a/smnp/ast/parsers/function.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/ast/parsers/identifier.py b/smnp/ast/parsers/identifier.py deleted file mode 100644 index ef3da31..0000000 --- a/smnp/ast/parsers/identifier.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/ast/parsers/integer.py b/smnp/ast/parsers/integer.py deleted file mode 100644 index 7316863..0000000 --- a/smnp/ast/parsers/integer.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/ast/parsers/list.py b/smnp/ast/parsers/list.py deleted file mode 100644 index 3af2181..0000000 --- a/smnp/ast/parsers/list.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/ast/parsers/minus.py b/smnp/ast/parsers/minus.py deleted file mode 100644 index a3e9c3a..0000000 --- a/smnp/ast/parsers/minus.py +++ /dev/null @@ -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 diff --git a/smnp/ast/parsers/note.py b/smnp/ast/parsers/note.py deleted file mode 100644 index 6ec74b4..0000000 --- a/smnp/ast/parsers/note.py +++ /dev/null @@ -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 diff --git a/smnp/ast/parsers/ret.py b/smnp/ast/parsers/ret.py deleted file mode 100644 index 5fedee4..0000000 --- a/smnp/ast/parsers/ret.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/ast/parsers/statement.py b/smnp/ast/parsers/statement.py deleted file mode 100644 index e9a16e5..0000000 --- a/smnp/ast/parsers/statement.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/ast/parsers/string.py b/smnp/ast/parsers/string.py deleted file mode 100644 index b21caca..0000000 --- a/smnp/ast/parsers/string.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/ast/parsers/token.py b/smnp/ast/parsers/token.py deleted file mode 100644 index ac76411..0000000 --- a/smnp/ast/parsers/token.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/ast/tools.py b/smnp/ast/tools.py index cb3749e..215df23 100644 --- a/smnp/ast/tools.py +++ b/smnp/ast/tools.py @@ -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): +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 diff --git a/smnp/main.py b/smnp/main.py index d77ea91..feb6b56 100644 --- a/smnp/main.py +++ b/smnp/main.py @@ -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 diff --git a/smnp/newast/__init__.py b/smnp/newast/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/smnp/newast/node/__init__.py b/smnp/newast/node/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/smnp/newast/node/access.py b/smnp/newast/node/access.py deleted file mode 100644 index a1bed48..0000000 --- a/smnp/newast/node/access.py +++ /dev/null @@ -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() - ) diff --git a/smnp/newast/node/assignment.py b/smnp/newast/node/assignment.py deleted file mode 100644 index 9c1804f..0000000 --- a/smnp/newast/node/assignment.py +++ /dev/null @@ -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") \ No newline at end of file diff --git a/smnp/newast/node/asterisk.py b/smnp/newast/node/asterisk.py deleted file mode 100644 index fc3c612..0000000 --- a/smnp/newast/node/asterisk.py +++ /dev/null @@ -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") \ No newline at end of file diff --git a/smnp/newast/node/block.py b/smnp/newast/node/block.py deleted file mode 100644 index 0c28178..0000000 --- a/smnp/newast/node/block.py +++ /dev/null @@ -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) \ No newline at end of file diff --git a/smnp/newast/node/function.py b/smnp/newast/node/function.py deleted file mode 100644 index 8e9ca51..0000000 --- a/smnp/newast/node/function.py +++ /dev/null @@ -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()) \ No newline at end of file diff --git a/smnp/newast/node/identifier.py b/smnp/newast/node/identifier.py deleted file mode 100644 index ac8cdf6..0000000 --- a/smnp/newast/node/identifier.py +++ /dev/null @@ -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)) diff --git a/smnp/newast/node/integer.py b/smnp/newast/node/integer.py deleted file mode 100644 index 8341455..0000000 --- a/smnp/newast/node/integer.py +++ /dev/null @@ -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)) \ No newline at end of file diff --git a/smnp/newast/node/list.py b/smnp/newast/node/list.py deleted file mode 100644 index a469ca2..0000000 --- a/smnp/newast/node/list.py +++ /dev/null @@ -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) diff --git a/smnp/newast/node/model.py b/smnp/newast/node/model.py deleted file mode 100644 index 6a44748..0000000 --- a/smnp/newast/node/model.py +++ /dev/null @@ -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}]" - - diff --git a/smnp/newast/node/note.py b/smnp/newast/node/note.py deleted file mode 100644 index fb6bcf0..0000000 --- a/smnp/newast/node/note.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/newast/node/program.py b/smnp/newast/node/program.py deleted file mode 100644 index 7acfaad..0000000 --- a/smnp/newast/node/program.py +++ /dev/null @@ -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) \ No newline at end of file diff --git a/smnp/newast/node/ret.py b/smnp/newast/node/ret.py deleted file mode 100644 index aa38fb6..0000000 --- a/smnp/newast/node/ret.py +++ /dev/null @@ -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) \ No newline at end of file diff --git a/smnp/newast/node/statement.py b/smnp/newast/node/statement.py deleted file mode 100644 index 7103880..0000000 --- a/smnp/newast/node/statement.py +++ /dev/null @@ -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) \ No newline at end of file diff --git a/smnp/newast/node/string.py b/smnp/newast/node/string.py deleted file mode 100644 index 851ee8e..0000000 --- a/smnp/newast/node/string.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/newast/parser.py b/smnp/newast/parser.py deleted file mode 100644 index 96c1c26..0000000 --- a/smnp/newast/parser.py +++ /dev/null @@ -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 - diff --git a/smnp/newast/tools.py b/smnp/newast/tools.py deleted file mode 100644 index 215df23..0000000 --- a/smnp/newast/tools.py +++ /dev/null @@ -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)