summaryrefslogtreecommitdiff
path: root/nandgame/assembler
diff options
context:
space:
mode:
authoruvok2026-01-13 16:15:26 +0100
committeruvok2026-01-13 16:15:26 +0100
commite43179345f6342f5789b622b8765625c9d96ba99 (patch)
treed4ae956f561e4f1cc4fae321ef164995be874187 /nandgame/assembler
parentf135231bfda2c788699e6b6ead29195f640858af (diff)
type annotations, top-level return...
... pull put/distribute rules to avoid handling vararg, add line numbers
Diffstat (limited to 'nandgame/assembler')
-rwxr-xr-xnandgame/assembler/parser.py129
-rw-r--r--nandgame/assembler/parsetypes.py21
2 files changed, 101 insertions, 49 deletions
diff --git a/nandgame/assembler/parser.py b/nandgame/assembler/parser.py
index a140eea..6875fe8 100755
--- a/nandgame/assembler/parser.py
+++ b/nandgame/assembler/parser.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import sys
+from typing import Any, MutableSequence, Tuple
import ply.yacc as yacc
from ply.lex import LexToken
@@ -10,22 +11,23 @@ from lexer import tokens
import parsetypes as pt
-# Error rule for syntax errors
+P = MutableSequence[Any]
-def p_program(p):
+
+def p_program(p: P) -> None:
"""program : instruction_list
| empty
"""
- pass
+ p[0] = p[1]
-def p_empty(p):
+def p_empty(p: P) -> None:
"""empty :"""
pass
-def p_instructions(p):
+def p_instructions(p: P) -> None:
"""instruction_list : instruction_list line
| line
"""
@@ -36,77 +38,89 @@ def p_instructions(p):
pass
-# try right-recursive?
-def p_instructions2(p):
- """instruction_list2 : line instruction_list2
- | line
- """
- if len(p) == 2:
- p[0] = [p[1]]
- else:
- p[0] = [p[1]] + p[2]
+# # 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
+# print(f"INSes2: {len(p)}")
+# print(f" {p[1]}")
+# if len(p) > 2:
+# print(f" {p[2]}")
+# pass
-def p_line(p):
+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):
- """instruction : no_args
- | two_arg
- | three_arg
- | jumpdest
+def p_instruction(p: P) -> None:
+ """instruction : valid_instruction
| one_arg_invalid
"""
- print(f"Item: {p[1]}")
p[0] = p[1]
- pass
-def p_jumpdest(p):
+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] = (p[1],)
+ p[0] = pt.JumpTarget(lineno=p.lineno(1), label=p[1])
-def p_no_arg(p):
+def p_no_arg(p: P) -> None:
"""no_args : opcode"""
p[0] = (*p[1], None, None, None)
pass
-def p_onearg_invalid(p):
+def p_onearg_invalid(p: P) -> None:
"""one_arg_invalid : opcode argument"""
- print(f"Invalid number of arguments: {p[1:]}")
- sys.exit(1)
+ op: Tuple[str,str] = p[1]
+ p[0] = pt.ErrorInstruction(*op, p[2], None, None, lineno=p.lineno(1), error_message="Invalid number of arguments")
pass
-def p_two_arg(p):
+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):
+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):
+def p_argument(p: P) -> None:
"""argument : number
| register
| symbol
@@ -114,38 +128,55 @@ def p_argument(p):
p[0] = p[1]
-def p_symbol(p):
+def p_symbol(p: P) -> None:
"""symbol : SYMBOL"""
p[0] = pt.Symbol(p[1])
-def p_register(p):
+def p_register(p: P) -> None:
"""register : REG"""
p[0] = pt.Register(p[1])
-def p_opcode(p):
- """opcode : OP DOT JUMP
- | OP
+def p_opcode(p: P) -> None:
+ """opcode : opcode_jmp
+ | opcode_njmp
"""
- if len(p) == 2:
- p[0] = (p[1], None)
- else:
- p[0] = (p[1], p[3])
+ 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):
+def p_number(p: P) -> None:
"""number : NUMBER
| HEXNUMBER
"""
p[0] = p[1]
-def p_error(p: LexToken):
+def p_error(p: LexToken) -> LexToken:
if p:
- print(f"Unexpected {repr(p.value)} on line {p.lineno}")
+ print(f"WARNING: Unexpected {repr(p.value)} on line {p.lineno}", file=sys.stderr)
else:
- print("Unexpected end of file.")
+ print("WARNING: Unexpected end of file.", file=sys.stderr)
+ return
+
+ while True:
+ tok = parser.token()
+ if not tok or tok.type == 'NL':
+ break
+ parser.errok()
+ return tok
parser = yacc.yacc()
diff --git a/nandgame/assembler/parsetypes.py b/nandgame/assembler/parsetypes.py
index 4769e02..8ebb206 100644
--- a/nandgame/assembler/parsetypes.py
+++ b/nandgame/assembler/parsetypes.py
@@ -1,4 +1,5 @@
from dataclasses import dataclass
+from typing import Any
@dataclass
class Symbol:
@@ -9,3 +10,23 @@ class Symbol:
class Register:
"""Define a source or dest register"""
name: str
+
+@dataclass
+class LinedElement:
+ lineno: int
+
+@dataclass
+class Instruction(LinedElement):
+ opcode: str
+ jumptarget: str
+ dest: Register
+ arg1: Any
+ arg2: Any
+
+@dataclass
+class ErrorInstruction(Instruction):
+ error_message: str
+
+@dataclass
+class JumpTarget(LinedElement):
+ label: str