Improve code maintainability

This commit is contained in:
Jan-Niclas Loosen
2025-12-07 23:12:35 +01:00
parent 6e9f0331ed
commit 88b8de363d
44 changed files with 45 additions and 58 deletions

View File

@@ -0,0 +1,26 @@
def prompt_choice(prompt: str, options: list) -> int:
print(prompt)
for i, opt in enumerate(options, 1):
print(f" {i}. {opt}")
while True:
try:
s = input(f"Enter choice (1-{len(options)}): ").strip()
idx = int(s) - 1
if 0 <= idx < len(options):
return idx
except Exception:
pass
print(f"Invalid. Enter a number between 1-{len(options)}.")
def prompt_confirmation(question: str, default="y"):
s = input(f"{question} (y/n) [{default}]: ").strip().lower()
if not s:
s = default
return s.startswith("y")
def search_programs():
base = Path(__file__).parent / "triplaprograms"
if not base.exists():
return []
return sorted([f for f in base.glob("*.tripla")])

100
Project-02-03/main.py Normal file
View File

@@ -0,0 +1,100 @@
import triplayacc as yacc
import triplalex as lex
import syntax
from pathlib import Path
from graphviz import Source
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import lib.console as cnsl
import os
import matplotlib
matplotlib.use("TkAgg")
# Renders a diagram of the AST
def render_diagram(dot_string: str):
# Set DPI for PNG
os.environ["GV_FILE_DPI"] = "300"
src = Source(dot_string, format="png", engine="dot")
png_path = src.render(cleanup=True)
img = mpimg.imread(png_path)
fig = plt.figure(figsize=(12, 12))
fig.canvas.manager.window.wm_title("TRIPLA AST Viewer")
plt.imshow(img)
plt.axis("off")
plt.show()
# Pretty prints the AST
def pretty_print(node, indent=0):
prefix = " " * indent
print(f"{prefix}{type(node).__name__}:")
for key, value in node.__dict__.items():
if isinstance(value, syntax.EXPRESSION):
pretty_print(value, indent + 4)
elif isinstance(value, list):
print(f"{prefix} {key}: [")
for element in value:
if isinstance(element, syntax.EXPRESSION):
pretty_print(element, indent + 4)
else:
print(" " * (indent + 4) + str(element))
print(f"{prefix} ]")
else:
print(f"{prefix} {key}: {value}")
# Exports the AST as a DOT file
def export_dot_file(dot_string: str, filename: str):
try:
Path(filename).write_text(dot_string, encoding="utf-8")
print(f"Saved DOT file as: {filename}")
except Exception as e:
print(f"Could not save DOT file: {e}")
if __name__ == "__main__":
print("\nTRIPLA Parser Tool")
while True:
choice = cnsl.prompt_choice("\nSelect action:", ["Parse .tripla", "Exit"])
if choice == 1:
print("\nBye Bye.")
break
programs = cnsl.search_programs()
if not programs:
print("\nNo .tripla files found.")
continue
idx = cnsl.prompt_choice("\nSelect program to parse:", [p.name for p in programs])
path = programs[idx]
source = path.read_text()
ast = yacc.parser.parse(source)
# Pretty print
if cnsl.prompt_confirmation("\nPretty-print AST?"):
print("")
pretty_print(ast)
# Export DOT
dot_str = ast.to_dot()
if cnsl.prompt_confirmation("Export AST as .dot file?"):
default = f"{path.stem}.dot"
cnsl = input(f"Filename [{default}]: ").strip()
if not cnsl:
cnsl = default
export_dot_file(dot_str, cnsl)
# Display AST diagram
if cnsl.prompt_confirmation("Display AST diagram?"):
render_diagram(dot_str)
print("Rendered AST diagram.")

1139
Project-02-03/parser.out Normal file

File diff suppressed because it is too large Load Diff

53
Project-02-03/parsetab.py Normal file
View File

@@ -0,0 +1,53 @@
# parsetab.py
# This file is automatically generated. Do not edit.
# pylint: disable=W,C,R
_tabversion = '3.10'
_lr_method = 'LALR'
_lr_signature = 'EleftSEMICOLONrightINleftCOMPleftEQOPleftAOPAOP ASSIGN COMMA COMP CONST DO ELSE EQOP FALSE ID IF IN LBRACE LET LOP LPAREN RBRACE RPAREN SEMICOLON THEN TRUE WHILEE : LET D IN EE : IDE : ID LPAREN A RPARENE : E AOP EE : LPAREN E RPARENE : CONSTE : ID ASSIGN EE : E SEMICOLON EE : IF B THEN E ELSE EE : WHILE B DO LBRACE E RBRACEA : EA : A COMMA ED : ID LPAREN V RPAREN LBRACE E RBRACED : D DV : IDV : V COMMA VB : E EQOP EB : E COMP EB : B EQOP BB : B LOP BB : TRUEB : FALSEB : LPAREN B RPAREN'
_lr_action_items = {'LET':([0,4,6,7,8,9,12,13,19,24,30,31,32,33,34,42,49,53,55,],[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,]),'ID':([0,2,4,6,7,8,9,10,12,13,19,23,24,25,30,31,32,33,34,42,49,51,53,55,60,],[3,11,3,3,3,3,3,11,3,3,3,11,3,39,3,3,3,3,3,3,3,39,3,3,-13,]),'LPAREN':([0,3,4,6,7,8,9,11,12,13,19,24,30,31,32,33,34,42,49,53,55,],[4,12,4,19,19,4,4,25,4,4,19,4,4,19,19,4,4,4,4,4,4,]),'CONST':([0,4,6,7,8,9,12,13,19,24,30,31,32,33,34,42,49,53,55,],[5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,]),'IF':([0,4,6,7,8,9,12,13,19,24,30,31,32,33,34,42,49,53,55,],[6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,]),'WHILE':([0,4,6,7,8,9,12,13,19,24,30,31,32,33,34,42,49,53,55,],[7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,]),'$end':([1,3,5,21,22,28,29,38,41,57,58,],[0,-2,-6,-4,-8,-7,-5,-1,-3,-9,-10,]),'AOP':([1,3,5,14,16,21,22,27,28,29,36,38,41,43,46,47,52,54,57,58,59,],[8,-2,-6,8,8,-4,8,8,8,-5,8,8,-3,8,8,8,8,8,8,-10,8,]),'SEMICOLON':([1,3,5,14,16,21,22,27,28,29,36,38,41,43,46,47,52,54,57,58,59,],[9,-2,-6,9,9,-4,-8,9,9,-5,9,-1,-3,9,9,9,9,9,9,-10,9,]),'RPAREN':([3,5,14,17,18,21,22,26,27,28,29,35,36,38,39,40,41,44,45,46,47,48,52,56,57,58,],[-2,-6,29,-21,-22,-4,-8,41,-11,-7,-5,48,29,-1,-15,50,-3,-19,-20,-17,-18,-23,-12,-16,-9,-10,]),'EQOP':([3,5,15,16,17,18,20,21,22,28,29,35,36,38,41,44,45,46,47,48,57,58,],[-2,-6,31,33,-21,-22,31,-4,-8,-7,-5,31,33,-1,-3,-19,31,-17,-18,-23,-9,-10,]),'COMP':([3,5,16,21,22,28,29,36,38,41,57,58,],[-2,-6,34,-4,-8,-7,-5,34,-1,-3,-9,-10,]),'COMMA':([3,5,21,22,26,27,28,29,38,39,40,41,52,56,57,58,],[-2,-6,-4,-8,42,-11,-7,-5,-1,-15,51,-3,-12,51,-9,-10,]),'ELSE':([3,5,21,22,28,29,38,41,43,57,58,],[-2,-6,-4,-8,-7,-5,-1,-3,53,-9,-10,]),'THEN':([3,5,15,17,18,21,22,28,29,38,41,44,45,46,47,48,57,58,],[-2,-6,30,-21,-22,-4,-8,-7,-5,-1,-3,-19,-20,-17,-18,-23,-9,-10,]),'LOP':([3,5,15,17,18,20,21,22,28,29,35,38,41,44,45,46,47,48,57,58,],[-2,-6,32,-21,-22,32,-4,-8,-7,-5,32,-1,-3,-19,32,-17,-18,-23,-9,-10,]),'DO':([3,5,17,18,20,21,22,28,29,38,41,44,45,46,47,48,57,58,],[-2,-6,-21,-22,37,-4,-8,-7,-5,-1,-3,-19,-20,-17,-18,-23,-9,-10,]),'RBRACE':([3,5,21,22,28,29,38,41,54,57,58,59,],[-2,-6,-4,-8,-7,-5,-1,-3,58,-9,-10,60,]),'ASSIGN':([3,],[13,]),'TRUE':([6,7,19,31,32,],[17,17,17,17,17,]),'FALSE':([6,7,19,31,32,],[18,18,18,18,18,]),'IN':([10,23,60,],[24,-14,-13,]),'LBRACE':([37,50,],[49,55,]),}
_lr_action = {}
for _k, _v in _lr_action_items.items():
for _x,_y in zip(_v[0],_v[1]):
if not _x in _lr_action: _lr_action[_x] = {}
_lr_action[_x][_k] = _y
del _lr_action_items
_lr_goto_items = {'E':([0,4,6,7,8,9,12,13,19,24,30,31,32,33,34,42,49,53,55,],[1,14,16,16,21,22,27,28,36,38,43,16,16,46,47,52,54,57,59,]),'D':([2,10,23,],[10,23,23,]),'B':([6,7,19,31,32,],[15,20,35,44,45,]),'A':([12,],[26,]),'V':([25,51,],[40,56,]),}
_lr_goto = {}
for _k, _v in _lr_goto_items.items():
for _x, _y in zip(_v[0], _v[1]):
if not _x in _lr_goto: _lr_goto[_x] = {}
_lr_goto[_x][_k] = _y
del _lr_goto_items
_lr_productions = [
("S' -> E","S'",1,None,None,None),
('E -> LET D IN E','E',4,'p_E_let','triplayacc.py',25),
('E -> ID','E',1,'p_E_id','triplayacc.py',29),
('E -> ID LPAREN A RPAREN','E',4,'p_E_call','triplayacc.py',33),
('E -> E AOP E','E',3,'p_E_aop','triplayacc.py',38),
('E -> LPAREN E RPAREN','E',3,'p_E_paren','triplayacc.py',42),
('E -> CONST','E',1,'p_E_const','triplayacc.py',47),
('E -> ID ASSIGN E','E',3,'p_E_assign','triplayacc.py',51),
('E -> E SEMICOLON E','E',3,'p_E_seq','triplayacc.py',55),
('E -> IF B THEN E ELSE E','E',6,'p_E_if','triplayacc.py',59),
('E -> WHILE B DO LBRACE E RBRACE','E',6,'p_E_while','triplayacc.py',63),
('A -> E','A',1,'p_A_single','triplayacc.py',72),
('A -> A COMMA E','A',3,'p_A_multiple','triplayacc.py',76),
('D -> ID LPAREN V RPAREN LBRACE E RBRACE','D',7,'p_D_single','triplayacc.py',84),
('D -> D D','D',2,'p_D_concat','triplayacc.py',88),
('V -> ID','V',1,'p_V_single','triplayacc.py',96),
('V -> V COMMA V','V',3,'p_V_multiple','triplayacc.py',100),
('B -> E EQOP E','B',3,'p_B_eqop_E','triplayacc.py',108),
('B -> E COMP E','B',3,'p_B_comp','triplayacc.py',112),
('B -> B EQOP B','B',3,'p_B_eqop_B','triplayacc.py',116),
('B -> B LOP B','B',3,'p_B_lop','triplayacc.py',120),
('B -> TRUE','B',1,'p_B_true','triplayacc.py',124),
('B -> FALSE','B',1,'p_B_false','triplayacc.py',128),
('B -> LPAREN B RPAREN','B',3,'p_B_paren','triplayacc.py',132),
]

184
Project-02-03/syntax.py Normal file
View File

@@ -0,0 +1,184 @@
class EXPRESSION:
pp_count = 0
def __init__(self):
self.pp = EXPRESSION.pp_count
EXPRESSION.pp_count += 1
@staticmethod
def copy():
return EXPRESSION()
# Returns a list of tuples (edge_name, child_expression)
def children(self):
out = []
for key, value in self.__dict__.items():
if key == "pp":
continue
if isinstance(value, EXPRESSION):
out.append((key, value))
elif isinstance(value, list):
for i, elem in enumerate(value):
if isinstance(elem, EXPRESSION):
out.append((f"{key}{i}", elem))
return out
# Export AST to dot format
def to_dot(self, visited=None, root=True):
if visited is None:
visited = set()
# Prevent infinite recursion
if id(self) in visited:
return ""
visited.add(id(self))
parts = []
# Add a header at the root node
if root:
parts.append("digraph AST {\n")
# Append to label
label = type(self).__name__
if hasattr(self, "operator"):
label += f"({self.operator})"
if hasattr(self, "name"):
label += f"({self.name})"
if hasattr(self, "value"):
label += f"({self.value})"
parts.append(f' node{self.pp} [label="{label}"];\n')
# Draw edges
for edge_name, child in self.children():
parts.append(f' node{self.pp} -> node{child.pp} [label="{edge_name}"];\n')
parts.append(child.to_dot(visited, root=False))
# Add footer at the root node
if root:
parts.append("}\n")
return "".join(parts)
class LET(EXPRESSION):
def __init__(self, decls, body):
super().__init__()
self.decl = decls
self.body = body
def __str__(self):
return "let " + ", ".join(str(d) for d in self.decl) + " in " + str(self.body)
class DECL(EXPRESSION):
def __init__(self, f_name, params, body):
super().__init__()
self.f_name = f_name
self.params = params
self.body = body
def __str__(self):
return f"{self.f_name}({self.params}) {{ {self.body} }}"
class CALL(EXPRESSION):
def __init__(self, f_name, args):
super().__init__()
self.f_name = f_name
self.arg = args
def __str__(self):
return self.f_name + "(" + ",".join(str(a) for a in self.arg) + ")"
class ID(EXPRESSION):
def __init__(self, name):
super().__init__()
self.name = name
def __str__(self):
return self.name
class CONST(EXPRESSION):
def __init__(self, value):
super().__init__()
self.value = value
def __str__(self):
return str(self.value)
class AOP(EXPRESSION):
def __init__(self, operator, arg1, arg2):
super().__init__()
self.operator = operator
self.arg1 = arg1
self.arg2 = arg2
def __str__(self):
return "(" + str(self.arg1) + " " + str(self.operator) + " " + str(self.arg2) + ")"
class EQOP(EXPRESSION):
def __init__(self, operator, arg1, arg2):
super().__init__()
self.operator = operator
self.arg1 = arg1
self.arg2 = arg2
def __str__(self):
return "(" + str(self.arg1) + " " + str(self.operator) + " " + str(self.arg2) + ")"
class COMP(EXPRESSION):
def __init__(self, operator, arg1, arg2):
super().__init__()
self.operator = operator
self.arg1 = arg1
self.arg2 = arg2
def __str__(self):
return "(" + str(self.arg1) + " " + str(self.operator) + " " + str(self.arg2) + ")"
class LOP(EXPRESSION):
def __init__(self, operator, arg1, arg2):
super().__init__()
self.operator = operator
self.arg1 = arg1
self.arg2 = arg2
def __str__(self):
return "(" + str(self.arg1) + " " + str(self.operator) + " " + str(self.arg2) + ")"
class ASSIGN(EXPRESSION):
def __init__(self, var, expr):
super().__init__()
self.var = var
self.expr = expr
def __str__(self):
return self.var.name + " = " + str(self.expr)
class SEQ(EXPRESSION):
def __init__(self, exp1, exp2):
super().__init__()
self.exp1 = exp1
self.exp2 = exp2
def __str__(self):
return str(self.exp1) + "; " + str(self.exp2)
class IF(EXPRESSION):
def __init__(self, cond, exp1, exp2):
super().__init__()
self.cond = cond
self.exp1 = exp1
self.exp2 = exp2
def __str__(self):
return "if (" + str(self.cond) + ") then { " + str(self.exp1) + " } else { " + str(self.exp2) + " }"
class WHILE(EXPRESSION):
def __init__(self, cond, body):
super().__init__()
self.cond = cond
self.body = body
def __str__(self):
return "while (" + str(self.cond) + ") do { " + str(self.body) + " }"

View File

@@ -0,0 +1,92 @@
# ------------------------------------------------------------
# Tokenizer for the TRIPLA parser
# ------------------------------------------------------------
import ply.lex as lex
reserved = {
'let': 'LET',
'in': 'IN',
'if': 'IF',
'then': 'THEN',
'else': 'ELSE',
'while': 'WHILE',
'do': 'DO',
'true': 'TRUE',
'false': 'FALSE'
}
# List of token names.
tokens = [
'ID',
'CONST',
'AOP',
'COMP',
'EQOP',
'LOP',
'ASSIGN',
'LPAREN', 'RPAREN',
'LBRACE', 'RBRACE',
'COMMA',
'SEMICOLON',
] + list(reserved.values())
# Simple tokens
t_LPAREN = r'\('
t_RPAREN = r'\)'
t_LBRACE = r'\{'
t_RBRACE = r'\}'
t_COMMA = r','
t_SEMICOLON = r';'
t_ASSIGN = r'='
# Arithmetic operators
t_AOP = r'\+|\-|\*|/'
# Comparison operators
t_COMP = r'<=|>=|<|>'
# Equality operators
t_EQOP = r'\|\||&&|==|!='
# Logical operators
t_LOP = r'\|\||&&'
# IDs
def t_ID(t):
r'[A-Za-z_][A-Za-z0-9_]*'
t.type = reserved.get(t.value, 'ID')
return t
# Constants
def t_CONST(t):
r'0|[1-9][0-9]*'
t.value = int(t.value)
return t
# Linebreaks
def t_newline(t):
r'\n+'
t.lexer.lineno += len(t.value)
# Ignore whitespace
t_ignore = ' \t'
# Single-line comment
def t_comment_single(t):
r'//.*'
pass
# Multi-line comment
def t_comment_multi(t):
r'/\*([^*]|\*+[^*/])*\*/'
t.lexer.lineno += t.value.count('\n')
pass
# Error handling
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)
# Build the lexer
lexer = lex.lex()

View File

@@ -0,0 +1,2 @@
let a(x,y,z) {x}
in a(1,2,3)

View File

@@ -0,0 +1,15 @@
let
f1(b) {
if(b==0) then 0 else f1(b-1)
}
f2(a, b) {
if(a > b) then f1(a) else f1(b);
let g(c) {
a*b*c
} in g(a*b)
}
in
f1(10); f2(10, let max(a, b) {
if(a > b) then a else b
}
in max(20, 30))

View File

@@ -0,0 +1 @@
if 2 < x && x > 9 then 1 else 0

View File

@@ -0,0 +1,4 @@
let a(x) {x}
b(y) {y}
c(z) {z}
in a(1); b(2); c(3)

View File

@@ -0,0 +1,38 @@
let
fac(x) {
if (x == 1) then 1
else fac(x-1)*x
}
fac(x) {
if (x == 1) then 1
else fac(x-1)*x
}
fac(x) {
if (x == 1) then 1
else fac(x-1)*x
}
fac(x) {
if (x == 1) then 1
else fac(x-1)*x
}
fac(x) {
if (x == 1) then 1
else fac(x-1)*x
}
fac(x) {
if (x == 1) then 1
else fac(x-1)*x
}
fac(x) {
if (x == 1) then 1
else fac(x-1)*x
}
fac(x) {
if (x == 1) then 1
else fac(x-1)*x
}
fac(x) {
if (x == 1) then 1
else fac(x-1)*x
}
in fac(3)

View File

@@ -0,0 +1 @@
let n(a) {a} in n(5) + m(5) ; let m(a) {a+1} in m(5)

View File

@@ -0,0 +1 @@
if (2 && 7 > 2) then 1 else 0

View File

@@ -0,0 +1,7 @@
let func(a,b) {
while ( a > 0 && b != a ) do {
b = b + 1;
a = a - 1
}
}
in func(10, 8)

View File

@@ -0,0 +1,12 @@
let ggT(a, b) {
if (a == b) then
a
else
do {
if (a > b) then
a = a - b
else
b = b - a
} while (a != b);
a
} in ggT(3528, 3780) // result should be 252

View File

@@ -0,0 +1,8 @@
let ggT(a, b) {
if (a == b) then
a
else if (a > b) then
ggT(a-b, b)
else
ggT(b-a, a)
} in ggT(3528, 3780) // result should be 252

View File

@@ -0,0 +1,3 @@
let f1(b) { if (b == 0) then 0 else f1(b-1) }
f2(a,b) { if (a > b) then f1(a) else f1(b) }
in f1(10); f2(10,-20)

View File

@@ -0,0 +1 @@
let m(a){a} in m(5);let m(b){b+1} in m(5)

View File

@@ -0,0 +1 @@
let n(a) {a} in n(5) ; let m(a) {a+1} in n(5) + m(5)

View File

@@ -0,0 +1 @@
if(true||true==true) then 1 else 0

View File

@@ -0,0 +1,3 @@
let g(a) {
a*a
} in g(5)

View File

@@ -0,0 +1,9 @@
let f1(b) {
if (b==0) then 0 else f1(b-1)
} in f1(10)
/*
f2(a,b) {
if (a>b) then f1(a) else f1(b)
}
in f1(10); f2(10,20)
*/

View File

@@ -0,0 +1,14 @@
let f1(b) {
if (b==0) then 0 else f1(b-1)
}
f2(a,b) {
if (a>b) then f1(a) else f1(b);
let g(c) {
a*b*c
}
in g(a*b)
}
in f1(10); f2(10, let max(a,b) {
if (a>b) then a else b
}
in max(20,30) )

View File

@@ -0,0 +1,4 @@
let func(a,b) {
a = b + 1
}
in func(10, 8)

View File

@@ -0,0 +1,9 @@
let func(a, b) {
let func(a, b) {
a * b
} in
do {
b = b + 1 * func(3, 1);
a = a - 1
} while ( a > 0 && b != a )
} in func(10, 8)

View File

@@ -0,0 +1,13 @@
let f(a, b) {
if (a == 0) then
2 + b
else
let g(a, c) {
a + c + b
} in 2 + g(a, b)
} g(c, d) {
if (c == 0) then
1 + d
else
c * f(c - 1, d)
} in f (2, 3); g (3, 2)

View File

@@ -0,0 +1,5 @@
let f(x, y) {
let g(x) {
y = x + 7
} in x = g(5); y
} in f(1, 2)

View File

@@ -0,0 +1,4 @@
let g(x, y) {
y = 3;
x
} in g(2)

View File

@@ -0,0 +1 @@
if (true) then 1 else 0

View File

@@ -0,0 +1 @@
if (true && false) then 1 else 0

View File

@@ -0,0 +1 @@
if (true || false) then 1 else 0

View File

@@ -0,0 +1 @@
if (1 > 2) then 1 else 0

View File

@@ -0,0 +1 @@
if (2 > 3 + 5) then 1 else 0

View File

@@ -0,0 +1 @@
if (1 > 2 || 3 < 5) then 1 else 0

View File

@@ -0,0 +1 @@
if (2 == 0 == false) then 1 else 0

View File

@@ -0,0 +1,2 @@
let square(x) { x*x }
in square(10)

View File

@@ -0,0 +1,4 @@
let mult(a,b) { a*b }
add(a,b) { let inc(a) { if (b!=0) then b=b-1;inc(a+1) else mult(a,1) }
in inc(a) }
in add(mult(2,3),add(4,5))

View File

@@ -0,0 +1,7 @@
let func(a,b) {
while ( a > 0 && b != a ) do {
b = b + 1;
a = a - 1
}
}
in func(10, 8)

View File

@@ -0,0 +1,3 @@
while (true) do {
3
}

View File

@@ -0,0 +1,8 @@
let wrapper(a, b) {
let ggt(noneSense) {
if a == b then a
else
if a > b then wrapper(a-b, b)
else wrapper(b-a, a)
} in ggt(0)
} in wrapper(21, 49)

View File

@@ -0,0 +1,9 @@
let wrapper(number, threshold) {
let square(x) {
if x*x > threshold
then x
else x*x
}
in square(number)
}
in wrapper(4, 10)

143
Project-02-03/triplayacc.py Normal file
View File

@@ -0,0 +1,143 @@
# ------------------------------------------------------------
# Grammar of the TRIPLA language
# ------------------------------------------------------------
import ply.yacc as yacc
import syntax as ast
from triplalex import tokens
# Operator precedence
precedence = (
('left', 'SEMICOLON'),
('right', 'IN'),
('left', 'COMP'),
('left', 'EQOP'),
('left', 'AOP'),
)
start = 'E'
# ------------------------------------------------------------
# Rules for E
# ------------------------------------------------------------
def p_E_let(p):
'E : LET D IN E'
p[0] = ast.LET(p[2], p[4])
def p_E_id(p):
'E : ID'
p[0] = ast.ID(p[1])
def p_E_call(p):
'E : ID LPAREN A RPAREN'
# E : ID(A)
p[0] = ast.CALL(p[1], p[3])
def p_E_aop(p):
'E : E AOP E'
p[0] = ast.AOP(p[2], p[1], p[3])
def p_E_paren(p):
'E : LPAREN E RPAREN'
# E : (E)
p[0] = p[2]
def p_E_const(p):
'E : CONST'
p[0] = ast.CONST(p[1])
def p_E_assign(p):
'E : ID ASSIGN E'
p[0] = ast.ASSIGN(ast.ID(p[1]), p[3])
def p_E_seq(p):
'E : E SEMICOLON E'
p[0] = ast.SEQ(p[1], p[3])
def p_E_if(p):
'E : IF B THEN E ELSE E'
p[0] = ast.IF(p[2], p[4], p[6])
def p_E_while(p):
'E : WHILE B DO LBRACE E RBRACE'
p[0] = ast.WHILE(p[2], p[5])
# ------------------------------------------------------------
# Rules for A
# ------------------------------------------------------------
def p_A_single(p):
'A : E'
p[0] = [p[1]]
def p_A_multiple(p):
'A : A COMMA E'
p[0] = p[1] + [p[3]]
# ------------------------------------------------------------
# Rules for D
# ------------------------------------------------------------
def p_D_single(p):
'D : ID LPAREN V RPAREN LBRACE E RBRACE'
p[0] = [ast.DECL(p[1], p[3], p[6])]
def p_D_concat(p):
'D : D D'
p[0] = p[1] + p[2]
# ------------------------------------------------------------
# Rules for V
# ------------------------------------------------------------
def p_V_single(p):
'V : ID'
p[0] = [p[1]]
def p_V_multiple(p):
'V : V COMMA V'
p[0] = p[1] + p[3]
# ------------------------------------------------------------
# Rules for B
# ------------------------------------------------------------
def p_B_eqop_E(p):
'B : E EQOP E'
p[0] = ast.EQOP(p[2], p[1], p[3])
def p_B_comp(p):
'B : E COMP E'
p[0] = ast.COMP(p[2], p[1], p[3])
def p_B_eqop_B(p):
'B : B EQOP B'
p[0] = ast.EQOP(p[2], p[1], p[3])
def p_B_lop(p):
'B : B LOP B'
p[0] = ast.LOP(p[2], p[1], p[3])
def p_B_true(p):
'B : TRUE'
p[0] = ast.CONST(True)
def p_B_false(p):
'B : FALSE'
p[0] = ast.CONST(False)
def p_B_paren(p):
'B : LPAREN B RPAREN'
# B : (B)
p[0] = p[2]
# Error handling
def p_error(p):
if p:
print("Syntax error at token:", p.type, "value:", p.value)
else:
print("Syntax error at EOF")
parser = yacc.yacc()