From 8f0537ba6cb6e6137b962a15e3a61905eaa22b07 Mon Sep 17 00:00:00 2001 From: Ian Fisher Date: Thu, 13 Dec 2018 22:16:49 -0500 Subject: [PATCH] Make symbol table an explicit parameter of typechecker and preprocessor --- hera/main.py | 7 +++++-- hera/preprocessor.py | 15 +++++---------- hera/symtab.py | 3 ++- hera/typechecker.py | 10 +++++----- test/test_preprocessor.py | 15 +++++---------- test/test_typechecker.py | 7 +++++++ 6 files changed, 29 insertions(+), 28 deletions(-) diff --git a/hera/main.py b/hera/main.py index 9ac137a..a7d544a 100644 --- a/hera/main.py +++ b/hera/main.py @@ -21,6 +21,7 @@ from . import config from .parser import parse from .preprocessor import preprocess +from .symtab import get_symtab from .typechecker import typecheck from .utils import emit_error, print_register_debug, HERAError from .vm import VirtualMachine @@ -96,11 +97,13 @@ def execute_program(program, *, lines_to_exec=None, no_dump_state=False, vm=None except HERAError as e: emit_error(str(e), line=e.line, column=e.column, exit=True) - typecheck(program) + symtab = get_symtab(program) + + typecheck(program, symtab) if config.SEEN_ERROR: sys.exit(3) - program = preprocess(program) + program = preprocess(program, symtab) if config.SEEN_ERROR: sys.exit(3) diff --git a/hera/preprocessor.py b/hera/preprocessor.py index bed09f0..4adfca9 100644 --- a/hera/preprocessor.py +++ b/hera/preprocessor.py @@ -6,11 +6,10 @@ from lark import Token from .parser import Op -from .symtab import get_symtab from .utils import copy_token, emit_error, is_symbol, to_u16 -def preprocess(program): +def preprocess(program, symtab): """Preprocess the program (a list of Op objects) into valid input for the exec_many method on the VirtualMachine class. @@ -19,22 +18,18 @@ def preprocess(program): - Resolves labels into their line numbers. """ program = [op for old_op in program for op in convert(old_op)] - - labels = get_symtab(program) - - program = [substitute_label(op, labels) for op in program] + program = [substitute_label(op, symtab) for op in program] program = [op for op in program if op.name not in ("LABEL", "DLABEL", "CONSTANT")] - return program -def substitute_label(op, labels): +def substitute_label(op, symtab): """Substitute any label in the instruction with its concrete value.""" if op.name == "SETLO" and is_symbol(op.args[1]): d, v = op.args name = copy_token("SETLO", op.name) try: - label = labels[v] + label = symtab[v] except KeyError: emit_error( "undefined symbol `{}`".format(v), line=op.name.line, column=v.column @@ -44,7 +39,7 @@ def substitute_label(op, labels): elif op.name == "SETHI" and is_symbol(op.args[1]): d, v = op.args name = copy_token("SETHI", op.name) - return Op(name, [d, labels[v] >> 8]) + return Op(name, [d, symtab[v] >> 8]) else: return op diff --git a/hera/symtab.py b/hera/symtab.py index 902269b..1af01f7 100644 --- a/hera/symtab.py +++ b/hera/symtab.py @@ -1,3 +1,4 @@ +from .preprocessor import convert from .utils import emit_error @@ -25,7 +26,7 @@ def get_symtab(program): elif op.name == "LP_STRING": dc += len(op.args[0]) + 1 else: - pc += 1 + pc += len(convert(op)) if dc >= 0xFFFF and odc < 0xFFFF: emit_error("past the end of available memory", line=op.name.line) diff --git a/hera/typechecker.py b/hera/typechecker.py index a7fd2fa..7867794 100644 --- a/hera/typechecker.py +++ b/hera/typechecker.py @@ -11,7 +11,7 @@ DATA_STATEMENTS = set(["CONSTANT", "DLABEL", "INTEGER", "LP_STRING", "DSKIP"]) -def typecheck(program): +def typecheck(program, symtab): """Type-check the program and emit errors as appropriate.""" end_of_data = False for op in program: @@ -21,14 +21,14 @@ def typecheck(program): else: if op.name in DATA_STATEMENTS: emit_error("data statement after instruction", line=op.name.line) - typecheck_one(op) + typecheck_one(op, symtab) -def typecheck_one(op): +def typecheck_one(op, symtab): """Type-check a single HERA op and emit errors as appropriate.""" params = _types_map.get(op.name) if params is not None: - check_types(op.name, params, op.args) + check_types(op.name, params, op.args, symtab) else: emit_error("unknown instruction `{}`".format(op.name), line=op.name.line) @@ -131,7 +131,7 @@ def typecheck_one(op): } -def check_types(name, expected, got): +def check_types(name, expected, got, symtab): """Verify that the given args match the expected ones and emit errors as appropriate. `name` is the name of the HERA op, as a Token object. `expected` is a tuple or list of constants (REGISTER, U16, etc., defined above) representing the diff --git a/test/test_preprocessor.py b/test/test_preprocessor.py index 582fb67..1f7d786 100644 --- a/test/test_preprocessor.py +++ b/test/test_preprocessor.py @@ -4,12 +4,7 @@ from lark import Token from hera.parser import Op -from hera.preprocessor import ( - convert, - convert_set, - preprocess, - substitute_label, -) +from hera.preprocessor import convert, convert_set, preprocess, substitute_label from hera.utils import HERAError, IntToken @@ -167,8 +162,8 @@ def test_convert_not(): def test_preprocess_constant(): - program = [ - Op(Token("SYMBOL", "CONSTANT"), [Token("SYMBOL", "n"), IntToken(100)]), - Op(Token("SYMBOL", "SET"), [R("R1"), Token("SYMBOL", "n")]), + program = [Op(Token("SYMBOL", "SET"), [R("R1"), Token("SYMBOL", "n")])] + assert preprocess(program, {"n": 100}) == [ + Op("SETLO", ["R1", 100]), + Op("SETHI", ["R1", 0]), ] - assert preprocess(program) == [Op("SETLO", ["R1", 100]), Op("SETHI", ["R1", 0])] diff --git a/test/test_typechecker.py b/test/test_typechecker.py index e5695af..0895293 100644 --- a/test/test_typechecker.py +++ b/test/test_typechecker.py @@ -1,3 +1,4 @@ +import functools import pytest from unittest.mock import patch @@ -17,6 +18,12 @@ from hera.utils import HERAError, IntToken +# TODO: Get rid of these and explicitly pass the empty symbol table in. +typecheck = functools.partial(typecheck, symtab={}) +typecheck_one = functools.partial(typecheck_one, symtab={}) +check_types = functools.partial(check_types, symtab={}) + + def R(s): return Token("REGISTER", s)