summaryrefslogtreecommitdiff
path: root/nandgame/assembler/experiments/parser3.py
diff options
context:
space:
mode:
Diffstat (limited to 'nandgame/assembler/experiments/parser3.py')
-rw-r--r--nandgame/assembler/experiments/parser3.py82
1 files changed, 82 insertions, 0 deletions
diff --git a/nandgame/assembler/experiments/parser3.py b/nandgame/assembler/experiments/parser3.py
new file mode 100644
index 0000000..f62f2e7
--- /dev/null
+++ b/nandgame/assembler/experiments/parser3.py
@@ -0,0 +1,82 @@
+# parser.py
+import ply.yacc as yacc
+from lexer import tokens
+
+# --- AST node types ---
+
+
+class Label:
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return f"Label({self.name!r})"
+
+
+class Instr:
+ def __init__(self, op, args):
+ self.op = op # "mov", "add", "jmp"
+ self.args = args # list of operands
+
+ def __repr__(self):
+ return f"Instr({self.op!r}, {self.args!r})"
+
+
+# --- Grammar ---
+
+
+def p_program(p):
+ """program : lines"""
+ p[0] = p[1]
+
+
+def p_lines_multi(p):
+ """lines : lines line"""
+ # p[1] is the list so far, p[2] is a single line (Label or Instr)
+ p[0] = p[1]
+ if p[2] is not None:
+ p[0].append(p[2])
+
+
+def p_lines_single(p):
+ """lines : line"""
+ # start list with the single line (unless it's None; but we never produce None here)
+ p[0] = [p[1]] if p[1] is not None else []
+
+
+def p_line(p):
+ """line : label
+ | instruction"""
+ p[0] = p[1]
+
+
+def p_label(p):
+ """label : IDENT COLON"""
+ p[0] = Label(p[1])
+
+
+def p_instruction_mov(p):
+ """instruction : MOV REGISTER COMMA NUMBER"""
+ # mov A, #123
+ p[0] = Instr("mov", [p[2], p[4]])
+
+
+def p_instruction_add(p):
+ """instruction : ADD REGISTER COMMA REGISTER"""
+ # add B, A
+ p[0] = Instr("add", [p[2], p[4]])
+
+
+def p_instruction_jmp(p):
+ """instruction : JMP IDENT"""
+ # jmp label
+ p[0] = Instr("jmp", [p[2]])
+
+
+def p_error(p):
+ if p is None:
+ raise SyntaxError("Syntax error at EOF")
+ else:
+ raise SyntaxError(f"Syntax error at {p.value!r} (type {p.type})")
+
+parser = yacc.yacc()