Improve parser to contain name and subparsers

This commit is contained in:
Bartłomiej Pluta
2019-07-10 20:53:36 +02:00
parent aefa8e8f3c
commit 66554f6c37
2 changed files with 58 additions and 51 deletions

View File

@@ -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])

View File

@@ -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))