Create ast package
This commit is contained in:
0
smnp/ast/__init__.py
Normal file
0
smnp/ast/__init__.py
Normal file
@@ -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]
|
||||||
@@ -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]
|
||||||
@@ -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]
|
||||||
@@ -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
|
||||||
@@ -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]
|
||||||
@@ -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]
|
||||||
@@ -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]
|
||||||
@@ -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]
|
||||||
@@ -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
|
||||||
@@ -24,6 +24,9 @@ class Node:
|
|||||||
def pop(self, index):
|
def pop(self, index):
|
||||||
return self.children.pop(index)
|
return self.children.pop(index)
|
||||||
|
|
||||||
|
def print(self):
|
||||||
|
print(self._print(0))
|
||||||
|
|
||||||
def _print(self, level):
|
def _print(self, level):
|
||||||
string = f"{pad(level)}{self.__class__.__name__}({self.parent.__class__.__name__}):\n"
|
string = f"{pad(level)}{self.__class__.__name__}({self.parent.__class__.__name__}):\n"
|
||||||
for child in self.children:
|
for child in self.children:
|
||||||
@@ -38,4 +41,4 @@ class Node:
|
|||||||
|
|
||||||
|
|
||||||
def pad(level):
|
def pad(level):
|
||||||
return (" " * level)
|
return " " * level
|
||||||
@@ -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]
|
||||||
@@ -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]
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from smnp.ast.node.model import Node
|
||||||
|
|
||||||
|
|
||||||
|
class Program(Node):
|
||||||
|
def __init__(self):
|
||||||
|
Node.__init__(self, None, (-1, -1))
|
||||||
@@ -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]
|
||||||
@@ -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]
|
||||||
12
smnp/ast/parser.py
Normal file
12
smnp/ast/parser.py
Normal file
@@ -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"]
|
||||||
0
smnp/ast/parsers/__init__.py
Normal file
0
smnp/ast/parsers/__init__.py
Normal file
@@ -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
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
30
smnp/ast/tools.py
Normal file
30
smnp/ast/tools.py
Normal file
@@ -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
|
||||||
18
smnp/main.py
18
smnp/main.py
@@ -2,23 +2,23 @@ import sys
|
|||||||
from smnp.error.syntax import SyntaxException
|
from smnp.error.syntax import SyntaxException
|
||||||
from smnp.error.runtime import RuntimeException
|
from smnp.error.runtime import RuntimeException
|
||||||
from smnp.token.tokenizer import tokenize
|
from smnp.token.tokenizer import tokenize
|
||||||
|
from smnp.ast.parser import parse
|
||||||
#from Tokenizer import tokenize
|
#from Tokenizer import tokenize
|
||||||
#from Parser import parse
|
|
||||||
#from Evaluator import evaluate
|
#from Evaluator import evaluate
|
||||||
#from Environment import createEnvironment
|
#from Environment import createEnvironment
|
||||||
#from Error import SyntaxException, RuntimeException
|
#from Error import SyntaxException, RuntimeException
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
try:
|
try:
|
||||||
with open(sys.argv[1], 'r') as source:
|
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()
|
#env = createEnvironment()
|
||||||
|
|
||||||
tokens = tokenize(lines)
|
tokens = tokenize(lines)
|
||||||
print(tokens)
|
|
||||||
#ast = parse(tokens)
|
ast = parse(tokens)
|
||||||
|
|
||||||
#evaluate(ast, env)
|
#evaluate(ast, env)
|
||||||
except SyntaxException as e:
|
except SyntaxException as e:
|
||||||
print(e.msg)
|
print(e.msg)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class Token:
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.__str__()
|
return self.__str__()
|
||||||
|
|
||||||
|
|
||||||
class TokenList:
|
class TokenList:
|
||||||
def __init__(self, tokens = []):
|
def __init__(self, tokens = []):
|
||||||
self.tokens = tokens
|
self.tokens = tokens
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ def tokenize(lines):
|
|||||||
|
|
||||||
return TokenList(filterTokens(filters, tokens))
|
return TokenList(filterTokens(filters, tokens))
|
||||||
|
|
||||||
|
|
||||||
def combinedTokenizer(line, current, lineNumber):
|
def combinedTokenizer(line, current, lineNumber):
|
||||||
for tokenizer in tokenizers:
|
for tokenizer in tokenizers:
|
||||||
consumedChars, token = tokenizer(line, current, lineNumber)
|
consumedChars, token = tokenizer(line, current, lineNumber)
|
||||||
@@ -72,10 +73,12 @@ def combinedTokenizer(line, current, lineNumber):
|
|||||||
return (consumedChars, token)
|
return (consumedChars, token)
|
||||||
return (0, None)
|
return (0, None)
|
||||||
|
|
||||||
|
|
||||||
def filterTokens(filters, tokens):
|
def filterTokens(filters, tokens):
|
||||||
if not filters:
|
if not filters:
|
||||||
return tokens
|
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"]
|
__all__ = ["tokenize"]
|
||||||
|
|||||||
Reference in New Issue
Block a user