Add Block statements

This commit is contained in:
Bartłomiej Pluta
2019-07-01 17:38:36 +02:00
parent 95114d19e9
commit 0e6a2ea3da
3 changed files with 184 additions and 65 deletions

12
AST.py
View File

@@ -6,6 +6,8 @@ class Node:
self.children = []
self.parent = parent
self.pos = pos
for child in self.children:
child.parent = self
def __repr__(self):
return self.__str__()
@@ -17,6 +19,7 @@ class Node:
return self.children[index]
def append(self, node):
node.parent = self
self.children.append(node)
def pop(self, index):
@@ -54,6 +57,15 @@ class BlockNode(Node):
#def __str__(self):
#return "B{\n" + "\n".join([str(e) for e in self.children]) + "\n}"
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):
def __init__(self, parent, pos):
Node.__init__(self, parent, pos)
class ListNode(Node):
def __init__(self, parent, pos):

228
Parser.py
View File

@@ -4,7 +4,9 @@ from Error import SyntaxException
from Note import Note, NotePitch
import re
def assertToken(expected, input):
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}'")
@@ -21,20 +23,25 @@ def returnAndGoAhead(input, getValue):
return value
# int -> INTEGER
# percent -> int '%'
def parseIntegerAndPercent(input, parent):
def parseInteger(input, parent):
if input.current().type == TokenType.INTEGER:
integer = IntegerLiteralNode(input.current().value, parent, input.current().pos)
integer = IntegerLiteralNode(int(input.current().value), parent, input.current().pos)
input.ahead()
if input.hasMore() and input.current().type == TokenType.PERCENT:
percent = PercentNode(input.current().value, parent, input.current().pos)
integer.parent = percent
input.ahead()
return percent
return integer
return None
# percent -> 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
# string -> STRING
def parseString(input, parent):
if input.current().type == TokenType.STRING:
@@ -83,48 +90,51 @@ def parseList(input, parent):
input.ahead()
# list -> CLOSE_PAREN (end of list)
if input.current().type == TokenType.CLOSE_PAREN:
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
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
if input.hasCurrent():
token = input.current()
expr = rollup(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.current().type == TokenType.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
assertToken(TokenType.COMMA, input)
input.ahead()
expr = 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
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
# colon -> expr ':' expr
def parseColon(input, parent):
if input.hasMore(1) and input.current().type == TokenType.COLON:
if input.hasMore() and input.current().type == TokenType.COLON:
expr1 = parent.pop(-1)
token = input.current()
@@ -138,49 +148,143 @@ def parseColon(input, parent):
return colon
return None
def parseAsterisk(input, parent):
if input.hasMore(1) and input.current().type == TokenType.ASTERISK:
expr1 = parent.pop(-1)
# minus -> '-' int
def parseMinus(input, parent):
if input.current().type == TokenType.MINUS:
token = input.current()
input.ahead()
expr2 = parseExpression(input, parent)
expr = parseInteger(input, parent)
asterisk = AsteriskNode(expr1, expr2, parent, token.pos)
expr1.parent = asterisk
expr2.parent = asterisk
return IntegerLiteralNode(-expr.value, parent, token.pos)
# block -> '{' '}' | '{' blockItem
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
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 rollStatement(input, parent):
node = Node(None, (-1, -1))
stmt = parseStatement(input, node)
while stmt is not None:
node.append(stmt)
stmt = parseStatement(input, node)
return node.children[0]
def parseStatement(input, parent):
value = runParsers(input, parent, [
parseBlock,
parseExpression,
])
return value
def parseExpression(input, parent):
expr = runParsers(input, parent, [
parseIntegerAndPercent,
parseMinus,
parseString,
parseNote,
parseList,
])
colon = parseColon(expr, input, parent)
if colon is not None:
return colon
asterisk = parseAsterisk(expr, input, parent)
if asterisk is not None:
return asterisk
return expr
# 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
def parseStatement(input, parent):
return runParsers(input, parent, [
parseAsterisk,
parseExpression,
])
# 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
def parseExpression(input, parent):
value = runParsers(input, parent, [
parseIntegerAndPercent,
parseString,
parseNote,
parseList,
parseColon,
])
if value is None:
raise SyntaxException(input.current().pos, f"Expression expected")
return value
def parseToken(input, parent):
def parseToken(input, parent):
value = runParsers(input, parent, [
parseStatement
])
])
if value is None:
raise SyntaxException(input.current().pos, "Unknown statement")
raise SyntaxException(None, "Unknown statement") #TODO
return value

View File

@@ -27,9 +27,12 @@ class Tokens:
def prev(self, number=1):
return self.tokens[self.cursor - number]
def hasMore(self, count=0):
def hasMore(self, count=1):
return self.cursor + count < len(self.tokens)
def hasCurrent(self):
return self.cursor < len(self.tokens)
def ahead(self):
self.cursor += 1
@@ -66,7 +69,7 @@ class TokenType(Enum):
PERCENT = 14
MINUS = 15
FUNCTION = 16
RETURN = 17
RETURN = 17
class Token:
def __init__(self, type, value, pos):
@@ -226,7 +229,7 @@ tokenizers = (
tokenizeOpenBracket,
tokenizeCloseBracket,
tokenizeAssign,
tokenizeColon,
tokenizeColon,
tokenizePercent,
tokenizeMinus,
tokenizeComment,