summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nandgame/assembler/experiments/lexer1.py52
-rw-r--r--nandgame/assembler/experiments/parser1.py57
-rw-r--r--nandgame/assembler/experiments/parser2.py67
-rw-r--r--nandgame/assembler/experiments/parser3.py82
4 files changed, 258 insertions, 0 deletions
diff --git a/nandgame/assembler/experiments/lexer1.py b/nandgame/assembler/experiments/lexer1.py
new file mode 100644
index 0000000..515a792
--- /dev/null
+++ b/nandgame/assembler/experiments/lexer1.py
@@ -0,0 +1,52 @@
+# lexer.py
+import ply.lex as lex
+
+tokens = (
+ "MOV",
+ "ADD",
+ "JMP",
+ "REGISTER",
+ "NUMBER",
+ "IDENT",
+ "COMMA",
+ "COLON",
+)
+
+t_COMMA = r","
+t_COLON = r":"
+
+t_MOV = r"mov"
+t_ADD = r"add"
+t_JMP = r"jmp"
+
+
+def t_REGISTER(t):
+ r"[A-Z]"
+ return t
+
+
+def t_NUMBER(t):
+ r"\#[0-9]+"
+ t.value = int(t.value[1:])
+ return t
+
+
+def t_IDENT(t):
+ r"[a-zA-Z_][a-zA-Z0-9_]*"
+ return t
+
+
+t_ignore = " \t"
+
+
+def t_newline(t):
+ r"\n+"
+ lexer.lineno += 1
+ pass
+
+
+def t_error(t):
+ raise SyntaxError(f"Illegal character {t.value[0]!r}")
+
+
+lexer = lex.lex()
diff --git a/nandgame/assembler/experiments/parser1.py b/nandgame/assembler/experiments/parser1.py
new file mode 100644
index 0000000..b845688
--- /dev/null
+++ b/nandgame/assembler/experiments/parser1.py
@@ -0,0 +1,57 @@
+# parser.py
+import ply.yacc as yacc
+from lexer import tokens
+
+
+# AST node types
+class Label:
+ def __init__(self, name):
+ self.name = name
+
+
+class Instr:
+ def __init__(self, op, args):
+ self.op = op
+ self.args = args
+
+
+def p_program(p):
+ """program : program line
+ | line"""
+ if len(p) == 2:
+ p[0] = [p[1]]
+ else:
+ p[0] = p[1] + [p[2]]
+
+
+def p_line(p):
+ """line : label
+ | instruction
+ | empty"""
+ p[0] = p[1]
+
+
+def p_label(p):
+ """label : IDENT COLON"""
+ p[0] = Label(p[1])
+
+
+def p_instruction(p):
+ """instruction : MOV REGISTER COMMA NUMBER
+ | ADD REGISTER COMMA REGISTER
+ | JMP IDENT"""
+ op = p[1]
+ args = p[2:]
+ p[0] = Instr(op, args)
+
+
+def p_empty(p):
+ "empty :"
+ p[0] = None
+
+
+def p_error(p):
+ raise SyntaxError("Syntax error")
+
+
+parser = yacc.yacc()
diff --git a/nandgame/assembler/experiments/parser2.py b/nandgame/assembler/experiments/parser2.py
new file mode 100644
index 0000000..af793fa
--- /dev/null
+++ b/nandgame/assembler/experiments/parser2.py
@@ -0,0 +1,67 @@
+# parser.py
+import ply.yacc as yacc
+from lexer import tokens
+
+
+class Label:
+ def __init__(self, name):
+ self.name = name
+
+
+class Instr:
+ def __init__(self, op, args):
+ self.op = op
+ self.args = args
+
+
+def p_program(p):
+ """program : lines"""
+ p[0] = p[1]
+
+
+def p_lines(p):
+ """lines : lines line
+ | line"""
+ if len(p) == 2:
+ p[0] = [p[1]]
+ else:
+ p[0] = p[1] + [p[2]]
+
+
+def p_line(p):
+ """line : label
+ | instruction
+ | empty"""
+ 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"""
+ p[0] = Instr("mov", [p[2], p[4]])
+
+
+def p_instruction_add(p):
+ """instruction : ADD REGISTER COMMA REGISTER"""
+ p[0] = Instr("add", [p[2], p[4]])
+
+
+def p_instruction_jmp(p):
+ """instruction : JMP IDENT"""
+ p[0] = Instr("jmp", [p[2]])
+
+
+def p_empty(p):
+ "empty :"
+ p[0] = None
+
+
+def p_error(p):
+ raise SyntaxError("Syntax error")
+
+
+parser = yacc.yacc()
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()