diff options
Diffstat (limited to 'nandgame')
| -rwxr-xr-x | nandgame/assembler/parser.py | 129 | ||||
| -rw-r--r-- | nandgame/assembler/parsetypes.py | 21 |
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 |
