Files
highground/ast.py

137 lines
4.7 KiB
Python
Raw Permalink Normal View History

2025-09-02 20:41:17 +10:00
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