diff --git a/smnp/ast/parser.py b/smnp/ast/parser.py index d54b1e8..5af9786 100644 --- a/smnp/ast/parser.py +++ b/smnp/ast/parser.py @@ -93,22 +93,50 @@ class Parser: # leftAssociative -> left | left OP right @staticmethod - def leftAssociativeOperatorParser(leftParser, operatorTokenType, rightParser, createNode): + def leftAssociativeOperatorParser(leftParser, operatorTokenTypes, rightParser, createNode): from smnp.ast.node.operator import OperatorNode + def parse(input): + operatorParser = Parser.oneOfTerminals(*operatorTokenTypes, createNode=lambda val, pos: OperatorNode.withChildren([val], pos)) left = leftParser(input) if left.result: - operator = Parser.terminalParser(operatorTokenType, lambda val, pos: OperatorNode.withChildren([val], pos))(input) + operator = operatorParser(input) while operator.result: 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) + operator = operatorParser(input) return left return ParseResult.FAIL() return parse + @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) + # loop -> start item* end @staticmethod def loop(startParser, itemParser, endParser, createNode): @@ -154,6 +182,10 @@ class Parser: return parse + @staticmethod + def epsilon(): + return lambda *args: ParseResult.OK(NoneNode()) + @staticmethod def many(parser, createNode): def parse(input): diff --git a/smnp/calc.py b/smnp/calc.py new file mode 100644 index 0000000..2bd9a85 --- /dev/null +++ b/smnp/calc.py @@ -0,0 +1,66 @@ +from smnp.ast.node.model import Node +from smnp.ast.parser import Parser +from smnp.token.tokenizer import tokenize +from smnp.token.type import TokenType + + +class Atom(Node): + def __init__(self, value, pos): + super().__init__(pos) + self.children = [value] + + @property + def value(self): + return self[0] + +class Operation(Node): + def __init__(self, left, op, right, pos): + super().__init__(pos) + self.children = [left, op, right] + + @property + def left(self): + return self[0] + + @property + def operator(self): + return self[1] + + @property + def right(self): + return self[2] + +def atom(input): + return Parser.terminalParser(TokenType.INTEGER, lambda val, pos: Atom(val, pos))(input) + +def chain(input): + return Parser.leftAssociativeOperatorParser(atom, [TokenType.DOT], atom, lambda left, op, right: Operation(left, op, right, op.pos))(input) + +def factor(input): + return Parser.leftAssociativeOperatorParser(chain, [TokenType.DOUBLE_ASTERISK], chain, lambda left, op, right: Operation(left, op, right, op.pos))(input) + +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 evaluate(node): + if type(node) == Atom: + return node.value + lhs = evaluate(node.left) + rhs = evaluate(node.right) + return { + "+": int(lhs) + int(rhs), + "*": int(lhs) * int(rhs), + "-": int(lhs) - int(rhs), + "/": int(lhs) / int(rhs), + "**": int(lhs) ** int(rhs) + }[node.operator.value] + +def draft(): + + tokens = tokenize(['2 + 2 * 2 / 2']) + node = expr(tokens).node + node.print() + print(evaluate(node)) diff --git a/smnp/main.py b/smnp/main.py index 214e397..da14e33 100644 --- a/smnp/main.py +++ b/smnp/main.py @@ -1,14 +1,12 @@ -import sys - +from smnp.calc import draft from smnp.error.base import SmnpException -from smnp.library.loader import loadStandardLibrary -from smnp.program.interpreter import Interpreter def main(): try: - stdLibraryEnv = loadStandardLibrary() - Interpreter.interpretFile(sys.argv[1], printTokens=False, printAst=True, execute=False, baseEnvironment=stdLibraryEnv) + #stdLibraryEnv = loadStandardLibrary() + #Interpreter.interpretFile(sys.argv[1], printTokens=False, printAst=True, execute=False, baseEnvironment=stdLibraryEnv) + draft() except SmnpException as e: print(e.message()) diff --git a/smnp/token/tokenizer.py b/smnp/token/tokenizer.py index aa8a1af..07de7f5 100644 --- a/smnp/token/tokenizer.py +++ b/smnp/token/tokenizer.py @@ -20,11 +20,14 @@ tokenizers = ( defaultTokenizer(TokenType.CLOSE_SQUARE), defaultTokenizer(TokenType.OPEN_ANGLE), defaultTokenizer(TokenType.CLOSE_ANGLE), + defaultTokenizer(TokenType.DOUBLE_ASTERISK), defaultTokenizer(TokenType.ASTERISK), defaultTokenizer(TokenType.ASSIGN), defaultTokenizer(TokenType.ARROW), defaultTokenizer(TokenType.COMMA), + defaultTokenizer(TokenType.SLASH), defaultTokenizer(TokenType.MINUS), + defaultTokenizer(TokenType.PLUS), defaultTokenizer(TokenType.DOTS), defaultTokenizer(TokenType.AMP), defaultTokenizer(TokenType.DOT), diff --git a/smnp/token/type.py b/smnp/token/type.py index 2489903..e3ab14d 100644 --- a/smnp/token/type.py +++ b/smnp/token/type.py @@ -2,6 +2,7 @@ from enum import Enum class TokenType(Enum): + DOUBLE_ASTERISK = '**' OPEN_CURLY = '{' CLOSE_CURLY = '}' OPEN_PAREN = '(' @@ -14,7 +15,9 @@ class TokenType(Enum): ASSIGN = '=' ARROW = '->' COMMA = ',' + SLASH = '/' MINUS = '-' + PLUS = '+' DOTS = '...' AMP = '&' DOT = '.'