summaryrefslogtreecommitdiff
path: root/nandgame/assembler/py_nand_ass/parser.py
diff options
context:
space:
mode:
authoruvok2026-01-14 20:44:16 +0100
committeruvok2026-01-14 20:44:16 +0100
commit1561eff8780dc15dc5ea46d7225cc49a46f709ca (patch)
tree130d44ef295ff2113fc56c592a78780035449dff /nandgame/assembler/py_nand_ass/parser.py
parent281414ea9b42e213b85b95b7072b73d1f1e3f240 (diff)
Restructure asembler as package
Diffstat (limited to 'nandgame/assembler/py_nand_ass/parser.py')
-rwxr-xr-xnandgame/assembler/py_nand_ass/parser.py184
1 files changed, 184 insertions, 0 deletions
diff --git a/nandgame/assembler/py_nand_ass/parser.py b/nandgame/assembler/py_nand_ass/parser.py
new file mode 100755
index 0000000..7cc4930
--- /dev/null
+++ b/nandgame/assembler/py_nand_ass/parser.py
@@ -0,0 +1,184 @@
+#!/usr/bin/env python3
+
+import sys
+from typing import Any, MutableSequence, Tuple
+
+import ply.yacc as yacc
+from ply.lex import LexToken
+
+# Get the token map from the lexer. This is required.
+from .lexer import tokens
+
+from . import parser_types as pt
+
+
+P = MutableSequence[Any]
+
+
+def p_program(p: P) -> None:
+ """program : instruction_list
+ | empty
+ """
+ p[0] = p[1]
+
+
+def p_empty(p: P) -> None:
+ """empty :"""
+ pass
+
+
+def p_instructions(p: P) -> None:
+ """instruction_list : instruction_list line
+ | line
+ """
+ if len(p) == 2:
+ p[0] = [p[1]]
+ else:
+ p[0] = p[1] + [p[2]]
+ pass
+
+
+# # try right-recursive?
+# def p_instructions2(p: P) -> None:
+# """instruction_list2 : line instruction_list2
+# | line
+# """
+# if len(p) == 2:
+# p[0] = [p[1]]
+# else:
+# p[0] = [p[1]] + p[2]
+
+# print(f"INSes2: {len(p)}")
+# print(f" {p[1]}")
+# if len(p) > 2:
+# print(f" {p[2]}")
+# pass
+
+
+def p_line(p: P) -> None:
+ """line : instruction NL
+ | jumpdest NL
+ | NL
+ """
+ # | instruction error NL
+ # | jumpdest error NL
+ if len(p) == 2:
+ pass
+ elif len(p) == 3:
+ p[0] = p[1]
+ print(f"Item: {p[0]}")
+ # if error handling
+ else:
+ p[0] = p[1]
+ assert False
+
+
+def p_instruction(p: P) -> None:
+ """instruction : valid_instruction
+ | one_arg_invalid
+ """
+ p[0] = p[1]
+
+
+def p_valid_instruction(p: P) -> None:
+ """valid_instruction : no_args
+ | two_arg
+ | three_arg
+ """
+ tp: Tuple[Any, Any, Any, Any, Any] = p[1]
+ p[0] = pt.Instruction(p.lineno(1), *tp)
+
+
+def p_jumpdest(p: P) -> None:
+ """jumpdest : symbol COLON"""
+ p[0] = pt.JumpTarget(lineno=p.lineno(1), label=p[1])
+
+
+def p_no_arg(p: P) -> None:
+ """no_args : opcode"""
+ p[0] = (*p[1], None, None, None)
+ pass
+
+
+def p_onearg_invalid(p: P) -> None:
+ """one_arg_invalid : opcode argument"""
+ op: tuple[str, str] = p[1]
+ p[0] = pt.ErrorInstruction(
+ p.lineno(1), op[1], "No opcode only supports one argument."
+ )
+ pass
+
+
+def p_two_arg(p: P) -> None:
+ """two_arg : opcode register COMMA argument"""
+ p[0] = (*p[1], p[2], p[4], None)
+ pass
+
+
+def p_three_arg(p: P) -> None:
+ """three_arg : opcode register COMMA argument COMMA argument"""
+ p[0] = (*p[1], p[2], p[4], p[6])
+ pass
+
+
+# checks which combinations are allowed is done one level up
+def p_argument(p: P) -> None:
+ """argument : number
+ | register
+ | symbol
+ """
+ p[0] = p[1]
+
+
+def p_symbol(p: P) -> None:
+ """symbol : SYMBOL"""
+ p[0] = pt.Symbol(p[1])
+
+
+def p_register(p: P) -> None:
+ """register : REG"""
+ p[0] = pt.Register(p[1])
+
+
+def p_opcode(p: P) -> None:
+ """opcode : opcode_jmp
+ | opcode_njmp
+ """
+ p[0] = p[1]
+
+
+def p_opcode_jmp(p: P) -> None:
+ """opcode_jmp : OP DOT JUMP"""
+ p[0] = (p[1], p[3])
+
+
+def p_opcode_njmp(p: P) -> None:
+ """opcode_njmp : OP"""
+ p[0] = (p[1], None)
+
+
+def p_number(p: P) -> None:
+ """number : NUMBER
+ | HEXNUMBER
+ """
+ p[0] = pt.Immediate(p[1])
+
+
+def p_error(p: LexToken | None) -> LexToken|None:
+ if p:
+ print(
+ f"WARNING: Unexpected {repr(p.value)} on line {p.lineno}", file=sys.stderr
+ )
+ else:
+ print("WARNING: Unexpected end of file.", file=sys.stderr)
+ return
+
+ while True:
+ tok: LexToken = parser.token()
+ if not tok or tok.type == 'NL':
+ break
+ parser.errok()
+ return tok
+
+
+parser: yacc.LRParser = yacc.yacc()