From 79a7b8bb1d41ea1911420b9e58815ea37f57d26f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Pluta?= Date: Mon, 15 Jul 2019 20:17:40 +0200 Subject: [PATCH] Add optional semicolon at the end of statements and move loop parser from factor to expression (change precedence) --- smnp/ast/node/expression.py | 64 ++++++++++++++++++++++++++- smnp/ast/node/factor.py | 53 ---------------------- smnp/ast/node/statement.py | 30 +++++++++---- smnp/module/iterable/__init__.py | 4 +- smnp/runtime/evaluators/expression.py | 4 +- smnp/token/tokenizer.py | 1 + smnp/token/type.py | 1 + 7 files changed, 90 insertions(+), 67 deletions(-) diff --git a/smnp/ast/node/expression.py b/smnp/ast/node/expression.py index db939bc..c1c4382 100644 --- a/smnp/ast/node/expression.py +++ b/smnp/ast/node/expression.py @@ -1,4 +1,6 @@ -from smnp.ast.node.operator import BinaryOperator +from smnp.ast.node.model import Node +from smnp.ast.node.none import NoneNode +from smnp.ast.node.operator import BinaryOperator, Operator from smnp.ast.node.term import TermParser from smnp.ast.parser import Parser from smnp.token.type import TokenType @@ -20,7 +22,34 @@ class Or(BinaryOperator): pass -def ExpressionParser(input): +class Loop(BinaryOperator): + def __init__(self, pos): + super().__init__(pos) + self.children.append(NoneNode()) + + @property + def parameters(self): + return self[3] + + @parameters.setter + def parameters(self, value): + self[3] = value + + @classmethod + def loop(cls, left, parameters, operator, right): + node = cls(left.pos) + node.left = left + node.parameters = parameters + node.operator = operator + node.right = right + return node + + +class LoopParameters(Node): + pass + + +def ExpressionWithoutLoopParser(input): expr1 = Parser.leftAssociativeOperatorParser( TermParser, [TokenType.PLUS, TokenType.MINUS], @@ -49,3 +78,34 @@ def ExpressionParser(input): lambda left, op, right: Or.withValues(left, op, right) )(input) + +def LoopParser(input): + from smnp.ast.node.identifier import IdentifierLiteralParser + from smnp.ast.node.iterable import abstractIterableParser + from smnp.ast.node.statement import StatementParser + + loopParameters = Parser.allOf( + Parser.terminal(TokenType.AS), + Parser.oneOf( + Parser.wrap(IdentifierLiteralParser, lambda id: LoopParameters.withChildren([id], id.pos)), + abstractIterableParser(LoopParameters, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, IdentifierLiteralParser) + ), + createNode=lambda asKeyword, parameters: parameters, + name="loop parameters" + ) + + return Parser.allOf( + ExpressionWithoutLoopParser, + Parser.optional(loopParameters), + Parser.terminal(TokenType.DASH, createNode=Operator.withValue), + StatementParser, + createNode=Loop.loop, + name="dash-loop" + )(input) + + +def ExpressionParser(input): + return Parser.oneOf( + LoopParser, + ExpressionWithoutLoopParser + )(input) diff --git a/smnp/ast/node/factor.py b/smnp/ast/node/factor.py index afc78e4..404a4f3 100644 --- a/smnp/ast/node/factor.py +++ b/smnp/ast/node/factor.py @@ -1,6 +1,3 @@ -from smnp.ast.node.iterable import abstractIterableParser -from smnp.ast.node.model import Node -from smnp.ast.node.none import NoneNode from smnp.ast.node.operator import BinaryOperator, Operator, UnaryOperator from smnp.ast.node.unit import UnitParser from smnp.ast.parser import Parser @@ -13,38 +10,8 @@ class NotOperator(UnaryOperator): class Power(BinaryOperator): pass - -class Loop(BinaryOperator): - def __init__(self, pos): - super().__init__(pos) - self.children.append(NoneNode()) - - @property - def parameters(self): - return self[3] - - @parameters.setter - def parameters(self, value): - self[3] = value - - @classmethod - def loop(cls, left, parameters, operator, right): - node = cls(left.pos) - node.left = left - node.parameters = parameters - node.operator = operator - node.right = right - return node - - -class LoopParameters(Node): - pass - - def FactorParser(input): from smnp.ast.node.expression import ExpressionParser - from smnp.ast.node.statement import StatementParser - from smnp.ast.node.identifier import IdentifierLiteralParser parentheses = Parser.allOf( Parser.terminal(TokenType.OPEN_PAREN), @@ -75,27 +42,7 @@ def FactorParser(input): name="not" ) - loopParameters = Parser.allOf( - Parser.terminal(TokenType.AS), - Parser.oneOf( - Parser.wrap(IdentifierLiteralParser, lambda id: LoopParameters.withChildren([id], id.pos)), - abstractIterableParser(LoopParameters, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, IdentifierLiteralParser) - ), - createNode=lambda asKeyword, parameters: parameters, - name="loop parameters" - ) - - loopFactor = Parser.allOf( - powerFactor, - Parser.optional(loopParameters), - Parser.terminal(TokenType.DASH, createNode=Operator.withValue), - StatementParser, - createNode=Loop.loop, - name="dash-loop" - ) - return Parser.oneOf( - loopFactor, notOperator, powerFactor, name="factor" diff --git a/smnp/ast/node/statement.py b/smnp/ast/node/statement.py index dc0d922..23d3996 100644 --- a/smnp/ast/node/statement.py +++ b/smnp/ast/node/statement.py @@ -1,5 +1,6 @@ from smnp.ast.node.model import Node from smnp.ast.parser import Parser +from smnp.token.type import TokenType class Statement(Node): @@ -13,11 +14,24 @@ def StatementParser(input): from smnp.ast.node.ret import ReturnParser from smnp.ast.node.throw import ThrowParser - return Parser.oneOf( - IfElseStatementParser, - ExpressionParser, - BlockParser, - ReturnParser, - ThrowParser, - name="statement" - )(input) + return withSemicolon( + Parser.oneOf( + IfElseStatementParser, + ExpressionParser, # Must be above BlockParser because of Map's syntax with curly braces + BlockParser, + ReturnParser, + ThrowParser, + name="statement" + ), optional=True)(input) + + +def withSemicolon(parser, optional=False, doAssert=False): + semicolonParser = Parser.optional(Parser.terminal(TokenType.SEMICOLON)) if optional else Parser.terminal( + TokenType.SEMICOLON, doAssert=doAssert) + + return Parser.allOf( + parser, + semicolonParser, + createNode=lambda stmt, semicolon: stmt, + name="semicolon" + "?" if optional else "" + ) diff --git a/smnp/module/iterable/__init__.py b/smnp/module/iterable/__init__.py index a3fc3b2..d302af9 100644 --- a/smnp/module/iterable/__init__.py +++ b/smnp/module/iterable/__init__.py @@ -1,4 +1,4 @@ -from smnp.module.iterable.function import combine, map, range, get +from smnp.module.iterable.function import combine, map, get -functions = [ combine.function, map.function, range.function ] +functions = [ combine.function, map.function ] methods = [ get.function ] \ No newline at end of file diff --git a/smnp/runtime/evaluators/expression.py b/smnp/runtime/evaluators/expression.py index 2418f70..46b70b6 100644 --- a/smnp/runtime/evaluators/expression.py +++ b/smnp/runtime/evaluators/expression.py @@ -1,6 +1,6 @@ from smnp.ast.node.condition import IfElse -from smnp.ast.node.expression import Sum, Relation, And, Or -from smnp.ast.node.factor import NotOperator, Power, Loop +from smnp.ast.node.expression import Sum, Relation, And, Or, Loop +from smnp.ast.node.factor import NotOperator, Power from smnp.ast.node.identifier import FunctionCall, Assignment from smnp.ast.node.term import Product from smnp.ast.node.unit import MinusOperator, Access diff --git a/smnp/token/tokenizer.py b/smnp/token/tokenizer.py index 017b88a..7c91ab1 100644 --- a/smnp/token/tokenizer.py +++ b/smnp/token/tokenizer.py @@ -27,6 +27,7 @@ tokenizers = ( defaultTokenizer(TokenType.CLOSE_SQUARE), defaultTokenizer(TokenType.OPEN_ANGLE), defaultTokenizer(TokenType.CLOSE_ANGLE), + defaultTokenizer(TokenType.SEMICOLON), defaultTokenizer(TokenType.ASTERISK), defaultTokenizer(TokenType.ASSIGN), defaultTokenizer(TokenType.COMMA), diff --git a/smnp/token/type.py b/smnp/token/type.py index 7c47bc2..91b4811 100644 --- a/smnp/token/type.py +++ b/smnp/token/type.py @@ -12,6 +12,7 @@ class TokenType(Enum): CLOSE_SQUARE = ']' OPEN_ANGLE = '<' CLOSE_ANGLE = '>' + SEMICOLON = ';' ASTERISK = '*' ASSIGN = '=' ARROW = '->'