diff --git a/smnp/ast/__init__.py b/smnp/ast/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/smnp/ast/node/access.py b/smnp/ast/node/access.py index e69de29..a8198af 100644 --- a/smnp/ast/node/access.py +++ b/smnp/ast/node/access.py @@ -0,0 +1,10 @@ +from smnp.ast.node.model import Node + + +class AccessNode(Node): + def __init__(self, element, property, parent, pos): + Node.__init__(self, parent, pos) + self.children.extend([element, property]) + + self.element = self.children[0] + self.property = self.children[1] \ No newline at end of file diff --git a/smnp/ast/node/assignment.py b/smnp/ast/node/assignment.py index e69de29..c1c4f14 100644 --- a/smnp/ast/node/assignment.py +++ b/smnp/ast/node/assignment.py @@ -0,0 +1,10 @@ +from smnp.ast.node.model import Node + + +class AssignmentNode(Node): + def __init__(self, target, value, parent, pos): + Node.__init__(self, parent, pos) + self.children.extend([target, value]) + + self.target = self.children[0] + self.value = self.children[1] \ No newline at end of file diff --git a/smnp/ast/node/asterisk.py b/smnp/ast/node/asterisk.py index e69de29..43403a0 100644 --- a/smnp/ast/node/asterisk.py +++ b/smnp/ast/node/asterisk.py @@ -0,0 +1,10 @@ +from smnp.ast.node.model import Node + + +class AsteriskNode(Node): + def __init__(self, iterator, statement, parent, pos): + Node.__init__(self, parent, pos) + self.children.extend([iterator, statement]) + + self.iterator = self.children[0] + self.statement = self.children[1] \ No newline at end of file diff --git a/smnp/ast/node/block.py b/smnp/ast/node/block.py index e69de29..a407257 100644 --- a/smnp/ast/node/block.py +++ b/smnp/ast/node/block.py @@ -0,0 +1,17 @@ +from smnp.ast.node.model import Node + + +class BlockNode(Node): + pass + + +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 diff --git a/smnp/ast/node/colon.py b/smnp/ast/node/colon.py index e69de29..b65e8e7 100644 --- a/smnp/ast/node/colon.py +++ b/smnp/ast/node/colon.py @@ -0,0 +1,10 @@ +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/ast/node/function.py b/smnp/ast/node/function.py index e69de29..b892f76 100644 --- a/smnp/ast/node/function.py +++ b/smnp/ast/node/function.py @@ -0,0 +1,20 @@ +from smnp.ast.node.model import Node + + +class FunctionCallNode(Node): + def __init__(self, identifier, arguments, parent, pos): + Node.__init__(self, parent, pos) + self.children.extend([identifier, arguments]) + + self.identifier = self.children[0] + self.arguments = self.children[1] + + +class FunctionDefinitionNode(Node): + def __init__(self, name, parameters, body, parent, pos): + Node.__init__(self, parent, pos) + self.children.extend([name, parameters, body]) + + self.name = self.children[0] + self.parameters = self.children[1] + self.body = self.children[2] \ No newline at end of file diff --git a/smnp/ast/node/identifier.py b/smnp/ast/node/identifier.py index e69de29..526aefc 100644 --- a/smnp/ast/node/identifier.py +++ b/smnp/ast/node/identifier.py @@ -0,0 +1,9 @@ +from smnp.ast.node.model import Node + + +class IdentifierNode(Node): + def __init__(self, identifier, parent, pos): + Node.__init__(self, parent, pos) + self.children.append(identifier) + + self.identifier = self.children[0] \ No newline at end of file diff --git a/smnp/ast/node/integer.py b/smnp/ast/node/integer.py index e69de29..57a8058 100644 --- a/smnp/ast/node/integer.py +++ b/smnp/ast/node/integer.py @@ -0,0 +1,9 @@ +from smnp.ast.node.model import Node + + +class IntegerLiteralNode(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/list.py b/smnp/ast/node/list.py index e69de29..4aaf583 100644 --- a/smnp/ast/node/list.py +++ b/smnp/ast/node/list.py @@ -0,0 +1,17 @@ +from smnp.ast.node.model import Node + + +class ListNode(Node): + pass + + +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 diff --git a/smnp/ast/node/model.py b/smnp/ast/node/model.py index b863393..19ae4ee 100644 --- a/smnp/ast/node/model.py +++ b/smnp/ast/node/model.py @@ -24,6 +24,9 @@ class Node: def pop(self, index): return self.children.pop(index) + def print(self): + print(self._print(0)) + def _print(self, level): string = f"{pad(level)}{self.__class__.__name__}({self.parent.__class__.__name__}):\n" for child in self.children: @@ -38,4 +41,4 @@ class Node: def pad(level): - return (" " * level) \ No newline at end of file + return " " * level \ No newline at end of file diff --git a/smnp/ast/node/note.py b/smnp/ast/node/note.py index e69de29..4f9aa69 100644 --- a/smnp/ast/node/note.py +++ b/smnp/ast/node/note.py @@ -0,0 +1,9 @@ +from smnp.ast.node.model import Node + + +class NoteLiteralNode(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/percent.py b/smnp/ast/node/percent.py index e69de29..59b2185 100644 --- a/smnp/ast/node/percent.py +++ b/smnp/ast/node/percent.py @@ -0,0 +1,9 @@ +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 e69de29..a98f3c1 100644 --- a/smnp/ast/node/program.py +++ b/smnp/ast/node/program.py @@ -0,0 +1,6 @@ +from smnp.ast.node.model import Node + + +class Program(Node): + def __init__(self): + Node.__init__(self, None, (-1, -1)) \ No newline at end of file diff --git a/smnp/ast/node/ret.py b/smnp/ast/node/ret.py index e69de29..ccd4bdb 100644 --- a/smnp/ast/node/ret.py +++ b/smnp/ast/node/ret.py @@ -0,0 +1,9 @@ +from smnp.ast.node.model import Node + + +class ReturnNode(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/string.py b/smnp/ast/node/string.py index e69de29..f7775d2 100644 --- a/smnp/ast/node/string.py +++ b/smnp/ast/node/string.py @@ -0,0 +1,9 @@ +from smnp.ast.node.model import Node + + +class StringLiteralNode(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/parser.py b/smnp/ast/parser.py new file mode 100644 index 0000000..e870963 --- /dev/null +++ b/smnp/ast/parser.py @@ -0,0 +1,12 @@ +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 + + +__all__ = ["parse"] \ No newline at end of file diff --git a/smnp/ast/parsers/__init__.py b/smnp/ast/parsers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/smnp/ast/parsers/access.py b/smnp/ast/parsers/access.py index e69de29..124e5e1 100644 --- a/smnp/ast/parsers/access.py +++ b/smnp/ast/parsers/access.py @@ -0,0 +1,24 @@ +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.current().type == 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/assignment.py b/smnp/ast/parsers/assignment.py index e69de29..139597f 100644 --- a/smnp/ast/parsers/assignment.py +++ b/smnp/ast/parsers/assignment.py @@ -0,0 +1,2 @@ + + diff --git a/smnp/ast/parsers/asterisk.py b/smnp/ast/parsers/asterisk.py index e69de29..2fde4ef 100644 --- a/smnp/ast/parsers/asterisk.py +++ b/smnp/ast/parsers/asterisk.py @@ -0,0 +1,18 @@ +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.current().type == 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 index e69de29..a6320b8 100644 --- a/smnp/ast/parsers/block.py +++ b/smnp/ast/parsers/block.py @@ -0,0 +1,46 @@ +from smnp.ast.node.block import BlockNode, CloseBlockNode, BlockItemNode +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.hasCurrent() and input.current().type == 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.hasCurrent() and input.current().type == 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 index e69de29..9ee7333 100644 --- a/smnp/ast/parsers/colon.py +++ b/smnp/ast/parsers/colon.py @@ -0,0 +1,19 @@ +from smnp.ast.node.colon import ColonNode +from smnp.error.syntax import SyntaxException +from smnp.token.type import TokenType + + +# colon -> expr ':' expr +def parseColon(expr1, input, parent): + if input.hasCurrent() and input.current().type == TokenType.COLON: + token = input.current() + input.ahead() + expr2 = parseExpression(input, parent) + + if expr2 is None: + raise SyntaxException(input.current().pos, f"Expected expression '{input.current().value}'") + 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 index e69de29..9fee60e 100644 --- a/smnp/ast/parsers/expression.py +++ b/smnp/ast/parsers/expression.py @@ -0,0 +1,28 @@ +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 index e69de29..dfd1785 100644 --- a/smnp/ast/parsers/function.py +++ b/smnp/ast/parsers/function.py @@ -0,0 +1,29 @@ +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.current().type == 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 index e69de29..8cb896f 100644 --- a/smnp/ast/parsers/identifier.py +++ b/smnp/ast/parsers/identifier.py @@ -0,0 +1,47 @@ +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.token.type import TokenType + + +# id -> IDENTIFIER +def parseIdentifier(input, parent): + if input.current().type == 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 = 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 index e69de29..d68b178 100644 --- a/smnp/ast/parsers/integer.py +++ b/smnp/ast/parsers/integer.py @@ -0,0 +1,26 @@ +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.current().type == 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.hasCurrent() and input.current().type == 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 index e69de29..e059faa 100644 --- a/smnp/ast/parsers/list.py +++ b/smnp/ast/parsers/list.py @@ -0,0 +1,54 @@ +from smnp.ast.node.list import ListNode, ListItemNode, CloseListNode +from smnp.ast.parsers.expression import parseExpression +from smnp.ast.tools import rollup, assertToken +from smnp.token.type import TokenType + + +# list -> CLOSE_PAREN | expr listTail +def parseList(input, parent): + if input.current().type == TokenType.OPEN_PAREN: + node = ListNode(parent, input.current().pos) + input.ahead() + + # list -> CLOSE_PAREN (end of list) + if input.hasCurrent() and input.current().type == 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 = parseExpression(input, node) + 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.hasCurrent() and input.current().type == 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 = rollup(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 index e69de29..544bfdd 100644 --- a/smnp/ast/parsers/minus.py +++ b/smnp/ast/parsers/minus.py @@ -0,0 +1,14 @@ +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.current().type == TokenType.MINUS: + token = input.current() + input.ahead() + + expr = parseInteger(input, parent) + + return IntegerLiteralNode(-expr.value, parent, token.pos) diff --git a/smnp/ast/parsers/note.py b/smnp/ast/parsers/note.py index e69de29..8933867 100644 --- a/smnp/ast/parsers/note.py +++ b/smnp/ast/parsers/note.py @@ -0,0 +1,38 @@ +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.current().type == 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 \ No newline at end of file diff --git a/smnp/ast/parsers/ret.py b/smnp/ast/parsers/ret.py index e69de29..b09e5f2 100644 --- a/smnp/ast/parsers/ret.py +++ b/smnp/ast/parsers/ret.py @@ -0,0 +1,17 @@ +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.current().type == 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 index e69de29..e9a16e5 100644 --- a/smnp/ast/parsers/statement.py +++ b/smnp/ast/parsers/statement.py @@ -0,0 +1,22 @@ +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 index e69de29..f2634ac 100644 --- a/smnp/ast/parsers/string.py +++ b/smnp/ast/parsers/string.py @@ -0,0 +1,12 @@ +from smnp.ast.node.string import StringLiteralNode +from smnp.token.type import TokenType + + +# string -> STRING +def parseString(input, parent): + if input.current().type == 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 index e69de29..afaaad6 100644 --- a/smnp/ast/parsers/token.py +++ b/smnp/ast/parsers/token.py @@ -0,0 +1,12 @@ +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(None, "Unknown statement") # TODO + + return value \ No newline at end of file diff --git a/smnp/ast/tools.py b/smnp/ast/tools.py new file mode 100644 index 0000000..058afb5 --- /dev/null +++ b/smnp/ast/tools.py @@ -0,0 +1,30 @@ +from smnp.ast.node.model import Node +from smnp.error.syntax import SyntaxException + + +def rollup(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): + if not input.hasCurrent(): + raise SyntaxException(None, f"Expected '{expected}'") + if expected != input.current().type: + raise SyntaxException(input.current().pos, f"Expected '{expected}', found '{input.current().value}'") + + +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 b111f17..2febc1b 100644 --- a/smnp/main.py +++ b/smnp/main.py @@ -2,23 +2,23 @@ import sys from smnp.error.syntax import SyntaxException from smnp.error.runtime import RuntimeException from smnp.token.tokenizer import tokenize +from smnp.ast.parser import parse #from Tokenizer import tokenize -#from Parser import parse #from Evaluator import evaluate #from Environment import createEnvironment #from Error import SyntaxException, RuntimeException def main(): - try: + try: with open(sys.argv[1], 'r') as source: - lines = [line.rstrip('\n') for line in source.readlines()] - + lines = [line.rstrip('\n') for line in source.readlines()] + #env = createEnvironment() - - tokens = tokenize(lines) - print(tokens) - #ast = parse(tokens) - + + tokens = tokenize(lines) + + ast = parse(tokens) + #evaluate(ast, env) except SyntaxException as e: print(e.msg) diff --git a/smnp/token/model.py b/smnp/token/model.py index 4f77028..727f7a1 100644 --- a/smnp/token/model.py +++ b/smnp/token/model.py @@ -8,6 +8,7 @@ class Token: def __repr__(self): return self.__str__() + class TokenList: def __init__(self, tokens = []): self.tokens = tokens diff --git a/smnp/token/tokenizer.py b/smnp/token/tokenizer.py index 830dc16..7e3322d 100644 --- a/smnp/token/tokenizer.py +++ b/smnp/token/tokenizer.py @@ -65,6 +65,7 @@ def tokenize(lines): return TokenList(filterTokens(filters, tokens)) + def combinedTokenizer(line, current, lineNumber): for tokenizer in tokenizers: consumedChars, token = tokenizer(line, current, lineNumber) @@ -72,10 +73,12 @@ def combinedTokenizer(line, current, lineNumber): return (consumedChars, token) return (0, None) + def filterTokens(filters, tokens): if not filters: return tokens - return filterTokens(filters[1:], (token for token in tokens if filters[0](token))) + return list(filterTokens(filters[1:], (token for token in tokens if filters[0](token)))) + __all__ = ["tokenize"]