diff --git a/smnp/ast/parser.py b/smnp/ast/parser.py index 5af9786..cbec991 100644 --- a/smnp/ast/parser.py +++ b/smnp/ast/parser.py @@ -10,6 +10,34 @@ def parse(input): class Parser: + def __init__(self, parse, name=None, parsers=None): + if parsers is None: + parsers = [] + self.parsers = parsers + + self._parse = parse + if name is None: + name = parse.__name__ + self.name = name + + def parse(self, input): + result = self._parse(input) + if result is None: + return ParseResult.FAIL() + + if not isinstance(result, ParseResult): + raise RuntimeError(f"_parse() method of '{self.__class__.__name__}' class haven't returned ParseResult object") + + return result + + def __call__(self, input): + return self.parse(input) + + def __str__(self): + return self.name + + def __repr__(self): + return self.__str__() # a -> A @staticmethod @@ -30,11 +58,11 @@ class Parser: return ParseResult.FAIL() - return parse + return Parser(parse, expectedType.name.lower()) # oneOf -> a | b | c | ... @staticmethod - def oneOf(*parsers, exception=None): + def oneOf(*parsers, exception=None, name="or"): def combinedParser(input): snap = input.snapshot() for parser in parsers: @@ -52,11 +80,11 @@ class Parser: input.reset(snap) return ParseResult.FAIL() - return combinedParser + return Parser(combinedParser, name=name, parsers=parsers) # allOf -> a b c ... @staticmethod - def allOf(*parsers, createNode, exception=None): + def allOf(*parsers, createNode, exception=None, name="all"): if len(parsers) == 0: raise RuntimeError("Pass one parser at least") @@ -88,12 +116,12 @@ class Parser: - return extendedParser + return Parser(extendedParser, name=name, parsers=parsers) # leftAssociative -> left | left OP right @staticmethod - def leftAssociativeOperatorParser(leftParser, operatorTokenTypes, rightParser, createNode): + def leftAssociativeOperatorParser(leftParser, operatorTokenTypes, rightParser, createNode, name="leftAssoc"): from smnp.ast.node.operator import OperatorNode def parse(input): @@ -109,37 +137,15 @@ class Parser: return ParseResult.FAIL() - return parse + return Parser(parse, name=name, parsers=[leftParser, '|'.join([t.value for t in operatorTokenTypes]), rightParser]) @staticmethod def oneOfTerminals(*tokenTypes, createNode=None): - return Parser.oneOf(*[ Parser.terminalParser(expectedType, createNode=createNode) for expectedType in tokenTypes ]) - - # leftAssociative -> left OP right | right - @staticmethod - def leftAssociativeOperatorOrRightParser(leftParser, operatorTokenType, rightParser, createNode): - from smnp.ast.node.operator import OperatorNode - def parse(input): - left = leftParser(input) - oneAtLeast = False - if left.result: - operator = Parser.terminalParser(operatorTokenType, lambda val, pos: OperatorNode.withChildren([val], pos))(input) - while operator.result: - oneAtLeast = True - right = rightParser(input) - left = ParseResult.OK(createNode(left.node, operator.node, right.node)) - operator = Parser.terminalParser(operatorTokenType, lambda val, pos: OperatorNode.withChildren([val], pos))(input) - - if oneAtLeast: - return left - - return ParseResult.FAIL() - - return Parser.oneOf(parse, rightParser) + return Parser.oneOf(*[ Parser.terminalParser(expectedType, createNode=createNode) for expectedType in tokenTypes ], name='|'.join([t.value for t in tokenTypes])) # loop -> start item* end @staticmethod - def loop(startParser, itemParser, endParser, createNode): + def loop(startParser, itemParser, endParser, createNode, name="loop"): def parse(input): items = [] start = startParser(input) @@ -155,10 +161,10 @@ class Parser: return ParseResult.FAIL() - return parse + return Parser(parse, name, parsers=[startParser, itemParser, endParser]) @staticmethod - def doAssert(parser, expected): + def doAssert(parser, expected, name="!!"): def parse(input): result = parser(input) @@ -169,10 +175,10 @@ class Parser: return result - return parse + return Parser(parse, name, parsers=parser) @staticmethod - def optional(parser): + def optional(parser, name="??"): def parse(input): result = parser(input) if result.result: @@ -180,14 +186,14 @@ class Parser: return ParseResult.OK(NoneNode()) - return parse + return Parser(parse, name, parsers=[parser]) @staticmethod def epsilon(): return lambda *args: ParseResult.OK(NoneNode()) @staticmethod - def many(parser, createNode): + def many(parser, createNode, name="*"): def parse(input): results = [] snap = input.snapshot() @@ -201,4 +207,4 @@ class Parser: input.reset(snap) return ParseResult.OK(createNode(results, pos) if len(results) > 0 else NoneNode()) - return parse + return Parser(parse, name, parsers=[parser]) diff --git a/smnp/calc.py b/smnp/calc.py index 2bd9a85..b387b2a 100644 --- a/smnp/calc.py +++ b/smnp/calc.py @@ -30,21 +30,21 @@ class Operation(Node): def right(self): return self[2] -def atom(input): - return Parser.terminalParser(TokenType.INTEGER, lambda val, pos: Atom(val, pos))(input) +def atom(): + return Parser.terminalParser(TokenType.INTEGER, lambda val, pos: Atom(val, pos)) -def chain(input): - return Parser.leftAssociativeOperatorParser(atom, [TokenType.DOT], atom, lambda left, op, right: Operation(left, op, right, op.pos))(input) +def chain(): + return Parser.leftAssociativeOperatorParser(atom(), [TokenType.DOT], atom(), lambda left, op, right: Operation(left, op, right, op.pos), name="chain") -def factor(input): - return Parser.leftAssociativeOperatorParser(chain, [TokenType.DOUBLE_ASTERISK], chain, lambda left, op, right: Operation(left, op, right, op.pos))(input) +def factor(): + return Parser.leftAssociativeOperatorParser(chain(), [TokenType.DOUBLE_ASTERISK], chain(), lambda left, op, right: Operation(left, op, right, op.pos), name="factor") -def term(input): - return Parser.leftAssociativeOperatorParser(factor, [TokenType.ASTERISK, TokenType.SLASH], factor, lambda left, op, right: Operation(left, op, right, op.pos))(input) - -def expr(input): - return Parser.leftAssociativeOperatorParser(term, [TokenType.PLUS, TokenType.MINUS], term, lambda left, op, right: Operation(left, op, right, op.pos))(input) +def term(): + return Parser.leftAssociativeOperatorParser(factor(), [TokenType.ASTERISK, TokenType.SLASH], factor(), lambda left, op, right: Operation(left, op, right, op.pos), name="term") +def expr(): + return Parser.leftAssociativeOperatorParser(term(), [TokenType.PLUS, TokenType.MINUS], term(), lambda left, op, right: Operation(left, op, right, op.pos), name="expr") +# def evaluate(node): if type(node) == Atom: return node.value @@ -60,7 +60,8 @@ def evaluate(node): def draft(): - tokens = tokenize(['2 + 2 * 2 / 2']) - node = expr(tokens).node + tokens = tokenize(['2**3/2 + 10 - 2']) + e = expr() + node = e(tokens).node node.print() print(evaluate(node))