137 lines
4.7 KiB
Python
137 lines
4.7 KiB
Python
|
import token
|
||
|
from enum import Enum
|
||
|
|
||
|
class AstType(Enum):
|
||
|
CONSTANT = 0
|
||
|
VARIABLE = 1
|
||
|
COMPARITOR = 2
|
||
|
OPERATOR = 3
|
||
|
IF = 4
|
||
|
WHILE = 5
|
||
|
BODY = 6
|
||
|
SET = 7
|
||
|
ROOT = 8
|
||
|
FUNC_CALL = 9
|
||
|
|
||
|
operators = [token.TokenType.ADD, token.TokenType.SUBTRACT, token.TokenType.MULTIPLY, token.TokenType.DIVIDE]
|
||
|
comparitors = [token.TokenType.EQUAL, token.TokenType.GREATER, token.TokenType.LESSER]
|
||
|
constants = [token.TokenType.INTEGER, token.TokenType.DOUBLE, token.TokenType.STRING, token.TokenType.CHAR, token.TokenType.BOOLEAN]
|
||
|
|
||
|
class AstNode:
|
||
|
def __init__(self, intok: token.Token):
|
||
|
self.children = []
|
||
|
self.tok = intok
|
||
|
if intok.type == token.TokenType.ROOT:
|
||
|
self.type = AstType.ROOT
|
||
|
elif intok.type in operators:
|
||
|
self.type = AstType.OPERATOR
|
||
|
elif intok.type in comparitors:
|
||
|
self.type = AstType.COMPARITOR
|
||
|
elif intok.type in constants:
|
||
|
self.type = AstType.CONSTANT
|
||
|
elif intok.type == token.TokenType.VARIABLE:
|
||
|
self.type = AstType.VARIABLE
|
||
|
elif intok.type == token.TokenType.IF:
|
||
|
self.type = AstType.IF
|
||
|
elif intok.type == token.TokenType.WHILE:
|
||
|
self.type = AstType.WHILE
|
||
|
elif intok.type == token.TokenType.SET:
|
||
|
self.type = AstType.SET
|
||
|
else:
|
||
|
self.type = None
|
||
|
|
||
|
def __repr__(self, level=0):
|
||
|
ret = "\t" * level + f"AstNode(type={self.type}, token={self.tok})\n"
|
||
|
for child in self.children:
|
||
|
ret += child.__repr__(level + 1)
|
||
|
return ret
|
||
|
|
||
|
def build_expression_ast(line_toks: list[token.Token]) -> AstNode:
|
||
|
if not line_toks:
|
||
|
return None
|
||
|
|
||
|
if len(line_toks) == 1:
|
||
|
return AstNode(line_toks[0])
|
||
|
|
||
|
if len(line_toks) > 2 and line_toks[0].type == token.TokenType.VARIABLE and line_toks[1].type == token.TokenType.PARAMOPEN:
|
||
|
func_call_node = AstNode(line_toks[0])
|
||
|
func_call_node.type = AstType.FUNC_CALL
|
||
|
if len(line_toks) > 3: # there are arguments
|
||
|
arg_toks = line_toks[2:-1]
|
||
|
if arg_toks:
|
||
|
func_call_node.children.append(build_expression_ast(arg_toks))
|
||
|
return func_call_node
|
||
|
|
||
|
for i, tok in enumerate(line_toks):
|
||
|
if tok.type in comparitors or tok.type in operators:
|
||
|
node = AstNode(tok)
|
||
|
left = build_expression_ast(line_toks[:i])
|
||
|
right = build_expression_ast(line_toks[i+1:])
|
||
|
if left: node.children.append(left)
|
||
|
if right: node.children.append(right)
|
||
|
return node
|
||
|
|
||
|
return AstNode(line_toks[0]) if line_toks else None
|
||
|
|
||
|
|
||
|
def build_ast(toks: list[list[token.Token]]) -> AstNode:
|
||
|
root = AstNode(token.Token("", True))
|
||
|
|
||
|
line_index = 0
|
||
|
while line_index < len(toks):
|
||
|
line_toks = toks[line_index]
|
||
|
|
||
|
if not line_toks:
|
||
|
line_index += 1
|
||
|
continue
|
||
|
|
||
|
first_tok = line_toks[0]
|
||
|
|
||
|
if first_tok.type == token.TokenType.LET:
|
||
|
if len(line_toks) >= 4 and line_toks[1].type == token.TokenType.VARIABLE and line_toks[2].type == token.TokenType.SET:
|
||
|
assignment_node = AstNode(line_toks[2])
|
||
|
var_node = AstNode(line_toks[1])
|
||
|
expr_node = build_expression_ast(line_toks[3:])
|
||
|
|
||
|
assignment_node.children.append(var_node)
|
||
|
if expr_node:
|
||
|
assignment_node.children.append(expr_node)
|
||
|
root.children.append(assignment_node)
|
||
|
else:
|
||
|
print(f"Syntax error on line {line_index + 1}: Invalid assignment.")
|
||
|
|
||
|
elif first_tok.type == token.TokenType.IF:
|
||
|
if_node = AstNode(first_tok)
|
||
|
condition_node = build_expression_ast(line_toks[1:])
|
||
|
if condition_node:
|
||
|
if_node.children.append(condition_node)
|
||
|
|
||
|
body_start_index = line_index + 1
|
||
|
body_end_index = body_start_index
|
||
|
while body_end_index < len(toks):
|
||
|
if len(toks[body_end_index]) == 1 and toks[body_end_index][0].type == token.TokenType.END:
|
||
|
break
|
||
|
body_end_index += 1
|
||
|
else:
|
||
|
print(f"Syntax error on line {line_index + 1}: 'if' statement without matching 'end'.")
|
||
|
|
||
|
body_toks = toks[body_start_index:body_end_index]
|
||
|
if body_toks:
|
||
|
body_node = build_ast(body_toks)
|
||
|
body_node.type = AstType.BODY
|
||
|
if_node.children.append(body_node)
|
||
|
|
||
|
root.children.append(if_node)
|
||
|
line_index = body_end_index
|
||
|
|
||
|
elif first_tok.type == token.TokenType.END:
|
||
|
pass
|
||
|
|
||
|
else:
|
||
|
expr_node = build_expression_ast(line_toks)
|
||
|
if expr_node:
|
||
|
root.children.append(expr_node)
|
||
|
|
||
|
line_index += 1
|
||
|
|
||
|
return root
|