diff --git a/hera/config.py b/hera/config.py index 762891d..7489f23 100644 --- a/hera/config.py +++ b/hera/config.py @@ -4,6 +4,10 @@ LINES = None +# Whether or not an error has been registered. +SEEN_ERROR = False + + def _make_ansi(*params): return "\033[" + ";".join(map(str, params)) + "m" diff --git a/hera/main.py b/hera/main.py index eeff35d..9ac137a 100644 --- a/hera/main.py +++ b/hera/main.py @@ -96,15 +96,15 @@ 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) - # TODO: Seems like typechecking should happen after preprocessing. - errors = typecheck(program) - if errors: - for error in errors: - emit_error(error.msg, line=error.line, column=error.column) + typecheck(program) + if config.SEEN_ERROR: + sys.exit(3) + + program = preprocess(program) + if config.SEEN_ERROR: sys.exit(3) try: - program = preprocess(program) vm.exec_many(program, lines=lines_to_exec) except HERAError as e: emit_error(str(e), line=e.line, column=e.column, exit=True) diff --git a/hera/preprocessor.py b/hera/preprocessor.py index e9f5381..bb6a2b0 100644 --- a/hera/preprocessor.py +++ b/hera/preprocessor.py @@ -6,7 +6,7 @@ from lark import Token from .parser import Op -from .utils import copy_token, emit_error, HERAError, is_symbol, to_u16 +from .utils import copy_token, emit_error, is_symbol, to_u16 # Arbitrary value copied over from HERA-C. @@ -39,9 +39,9 @@ def substitute_label(op, labels): try: label = labels[v] except KeyError: - raise HERAError( + emit_error( "undefined symbol `{}`".format(v), line=op.name.line, column=v.column - ) from None + ) else: return Op(name, [d, label & 0xFF]) elif op.name == "SETHI" and is_symbol(op.args[1]): diff --git a/hera/typechecker.py b/hera/typechecker.py index e899309..68050d8 100644 --- a/hera/typechecker.py +++ b/hera/typechecker.py @@ -3,33 +3,24 @@ Author: Ian Fisher (iafisher@protonmail.com) Version: December 2018 """ -from collections import namedtuple - from lark import Token -from .utils import is_symbol, register_to_index - - -ErrorInfo = namedtuple("ErrorInfo", ["msg", "line", "column"]) +from .utils import emit_error, is_symbol, register_to_index def typecheck(program): - """Type-check the program and return a list of errors encountered.""" - errors = [] + """Type-check the program and emit errors as appropriate.""" for op in program: - errors.extend(typecheck_one(op)) - return errors + typecheck_one(op) def typecheck_one(op): - """Type-check a single HERA op and return a list of errors encountered.""" + """Type-check a single HERA op and emit errors as appropriate.""" params = _types_map.get(op.name) if params is not None: - return check_types(op.name, params, op.args) + check_types(op.name, params, op.args) else: - return [ - ErrorInfo("unknown instruction `{}`".format(op.name), op.name.line, None) - ] + emit_error("unknown instruction `{}`".format(op.name), line=op.name.line) # Constants to pass to check_types @@ -131,29 +122,22 @@ def typecheck_one(op): def check_types(name, expected, got): - """Verify that the given args match the expected ones and return a list of errors. - `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 expected argument - types to the operation. `args` is a tuple or list of the actual arguments given. + """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 + expected argument types to the operation. `args` is a tuple or list of the actual + arguments given. """ - errors = [] - if len(got) < len(expected): - errors.append( - ErrorInfo( - "too few args to {} (expected {})".format(name, len(expected)), - name.line, - None, - ) + emit_error( + "too few args to {} (expected {})".format(name, len(expected)), + line=name.line, ) if len(expected) < len(got): - errors.append( - ErrorInfo( - "too many args to {} (expected {})".format(name, len(expected)), - name.line, - None, - ) + emit_error( + "too many args to {} (expected {})".format(name, len(expected)), + line=name.line, ) ordinals = ["first", "second", "third"] @@ -161,9 +145,7 @@ def check_types(name, expected, got): prefix = "{} arg to {} ".format(ordinal, name) error = check_one_type(pattern, arg) if error: - errors.append(ErrorInfo(prefix + error, arg.line, arg.column)) - - return errors + emit_error(prefix + error, line=arg.line, column=arg.column) def check_one_type(pattern, arg): diff --git a/hera/utils.py b/hera/utils.py index 62bb2f8..81b6ce4 100644 --- a/hera/utils.py +++ b/hera/utils.py @@ -110,6 +110,7 @@ def is_symbol(s): def emit_error(msg, *, line=None, column=None, exit=False): """Print an error message to stderr.""" msg = config.ANSI_RED_BOLD + "Error" + config.ANSI_RESET + ": " + msg + config.SEEN_ERROR = True _emit_msg(msg, line=line, column=column, exit=exit) diff --git a/test/test_preprocessor.py b/test/test_preprocessor.py index 8ab66b3..0f6f759 100644 --- a/test/test_preprocessor.py +++ b/test/test_preprocessor.py @@ -1,4 +1,5 @@ import pytest +from unittest.mock import patch from lark import Token @@ -45,8 +46,9 @@ def test_substitute_label_with_other_op(): def test_substitute_label_with_undefined_label(): - with pytest.raises(HERAError): + with patch("hera.utils._emit_msg") as mock_emit_error: substitute_label(Op(SYM("SETLO"), [R("R1"), SYM("N")]), {}) + assert mock_emit_error.call_count == 1 def test_convert_set_with_small_positive(): diff --git a/test/test_typechecker.py b/test/test_typechecker.py index e380ae9..ad1b347 100644 --- a/test/test_typechecker.py +++ b/test/test_typechecker.py @@ -1,4 +1,5 @@ import pytest +from unittest.mock import patch from lark import Token @@ -25,459 +26,623 @@ def SYM(s=""): def test_check_types_with_too_few(): - errors = check_types(SYM(), [REGISTER, REGISTER], [R("R1")]) - assert len(errors) == 1 - assert "too few" in errors[0].msg + with patch("hera.utils._emit_msg") as mock_emit_error: + check_types(SYM(), [REGISTER, REGISTER], [R("R1")]) + assert mock_emit_error.call_count == 1 + assert "too few" in mock_emit_error.call_args[0][0] def test_check_types_with_too_many(): - errors = check_types(SYM(), [REGISTER], [R("R1"), IntToken(10)]) - assert len(errors) == 1 - assert "too many" in errors[0].msg + with patch("hera.utils._emit_msg") as mock_emit_error: + check_types(SYM(), [REGISTER], [R("R1"), IntToken(10)]) + assert mock_emit_error.call_count == 1 + assert "too many" in mock_emit_error.call_args[0][0] def test_check_types_with_wrong_type(): - errors = check_types(SYM(), [REGISTER], [IntToken(10)]) - assert len(errors) == 1 - assert "not a register" in errors[0].msg + with patch("hera.utils._emit_msg") as mock_emit_error: + check_types(SYM(), [REGISTER], [IntToken(10)]) + assert mock_emit_error.call_count == 1 + assert "not a register" in mock_emit_error.call_args[0][0] - errors2 = check_types(SYM(), [U16], [R("R1")]) - assert len(errors2) == 1 - assert "not an integer" in errors2[0].msg + with patch("hera.utils._emit_msg") as mock_emit_error: + check_types(SYM(), [U16], [R("R1")]) + assert mock_emit_error.call_count == 1 + assert "not an integer" in mock_emit_error.call_args[0][0] def test_check_types_with_u16_out_of_range(): - errors = check_types(SYM(), [U16], [IntToken(65536)]) - assert len(errors) == 1 - assert "out of range" in errors[0].msg + with patch("hera.utils._emit_msg") as mock_emit_error: + check_types(SYM(), [U16], [IntToken(65536)]) + assert mock_emit_error.call_count == 1 + assert "out of range" in mock_emit_error.call_args[0][0] def test_check_types_with_negative_u16(): - errors = check_types(SYM(), [U16], [IntToken(-1)]) - assert len(errors) == 1 - assert "must not be negative" in errors[0].msg + with patch("hera.utils._emit_msg") as mock_emit_error: + check_types(SYM(), [U16], [IntToken(-1)]) + assert mock_emit_error.call_count == 1 + assert "must not be negative" in mock_emit_error.call_args[0][0] def test_check_types_with_u4_out_of_range(): - errors = check_types(SYM(), [U4], [IntToken(16)]) - assert len(errors) == 1 - assert "out of range" in errors[0].msg + with patch("hera.utils._emit_msg") as mock_emit_error: + check_types(SYM(), [U4], [IntToken(16)]) + assert mock_emit_error.call_count == 1 + assert "out of range" in mock_emit_error.call_args[0][0] - errors2 = check_types(SYM(), [U4], [IntToken(-1)]) - assert len(errors2) == 1 - assert "must not be negative" in errors2[0].msg + with patch("hera.utils._emit_msg") as mock_emit_error: + check_types(SYM(), [U4], [IntToken(-1)]) + assert mock_emit_error.call_count == 1 + assert "must not be negative" in mock_emit_error.call_args[0][0] def test_check_types_with_range_object(): - errors = check_types(SYM(), [range(-10, 10)], [IntToken(-11)]) - assert len(errors) == 1 - assert "out of range" in errors[0].msg + with patch("hera.utils._emit_msg") as mock_emit_error: + check_types(SYM(), [range(-10, 10)], [IntToken(-11)]) + assert mock_emit_error.call_count == 1 + assert "out of range" in mock_emit_error.call_args[0][0] - errors2 = check_types(SYM(), [range(-10, 10)], [IntToken(10)]) - assert len(errors2) == 1 - assert "out of range" in errors2[0].msg + with patch("hera.utils._emit_msg") as mock_emit_error: + check_types(SYM(), [range(-10, 10)], [IntToken(10)]) + assert mock_emit_error.call_count == 1 + assert "out of range" in mock_emit_error.call_args[0][0] - errors3 = check_types(SYM(), [range(-10, 10)], [R("R1")]) - assert len(errors3) == 1 - assert "not an integer" in errors3[0].msg + with patch("hera.utils._emit_msg") as mock_emit_error: + check_types(SYM(), [range(-10, 10)], [R("R1")]) + assert mock_emit_error.call_count == 1 + assert "not an integer" in mock_emit_error.call_args[0][0] - r = range(-10, 10) - assert check_types("", [r, r, r], [5, -10, 9]) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + r = range(-10, 10) + check_types("", [r, r, r], [5, -10, 9]) + assert mock_emit_error.call_count == 0 def test_check_types_with_constant_symbol(): - assert check_types("", [range(0, 100)], [Token("SYMBOL", "n")]) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + check_types("", [range(0, 100)], [Token("SYMBOL", "n")]) + assert mock_emit_error.call_count == 0 def test_check_types_with_register_or_label(): - assert check_types("", [REGISTER_OR_LABEL], [Token("SYMBOL", "n")]) == [] - assert check_types("", [REGISTER_OR_LABEL], [R("R1")]) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + check_types("", [REGISTER_OR_LABEL], [Token("SYMBOL", "n")]) + check_types("", [REGISTER_OR_LABEL], [R("R1")]) + assert mock_emit_error.call_count == 0 def test_check_types_with_label(): - assert check_types("", [LABEL], [Token("SYMBOL", "n")]) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + check_types("", [LABEL], [Token("SYMBOL", "n")]) + assert mock_emit_error.call_count == 0 def test_typecheck_SET(): - assert typecheck_one(Op("SET", [R("R1"), 42])) == [] - assert typecheck_one(Op("SET", [R("R1"), 0xFFFF])) == [] - assert typecheck_one(Op("SET", [R("R1"), -0x7FFF])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("SET", [R("R1"), 42])) + typecheck_one(Op("SET", [R("R1"), 0xFFFF])) + typecheck_one(Op("SET", [R("R1"), -0x7FFF])) + assert mock_emit_error.call_count == 0 def test_typecheck_SETLO(): - assert typecheck_one(Op("SETLO", [R("R2"), 42])) == [] - assert typecheck_one(Op("SETLO", [R("R2"), 0xFF])) == [] - assert typecheck_one(Op("SETLO", [R("R2"), -0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("SETLO", [R("R2"), 42])) + typecheck_one(Op("SETLO", [R("R2"), 0xFF])) + typecheck_one(Op("SETLO", [R("R2"), -0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_SETHI(): - assert typecheck_one(Op("SETHI", [R("R2"), 42])) == [] - assert typecheck_one(Op("SETHI", [R("R2"), 0xFF])) == [] - assert typecheck_one(Op("SETHI", [R("R2"), -0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("SETHI", [R("R2"), 42])) + typecheck_one(Op("SETHI", [R("R2"), 0xFF])) + typecheck_one(Op("SETHI", [R("R2"), -0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_AND(): - assert typecheck_one(Op("AND", [R("R3"), R("R4"), R("R5")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("AND", [R("R3"), R("R4"), R("R5")])) + assert mock_emit_error.call_count == 0 def test_typecheck_OR(): - assert typecheck_one(Op("OR", [R("R3"), R("R4"), R("R5")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("OR", [R("R3"), R("R4"), R("R5")])) + assert mock_emit_error.call_count == 0 def test_typecheck_ADD(): - assert typecheck_one(Op("ADD", [R("R3"), R("R4"), R("R5")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("ADD", [R("R3"), R("R4"), R("R5")])) + assert mock_emit_error.call_count == 0 def test_typecheck_SUB(): - assert typecheck_one(Op("SUB", [R("R3"), R("R4"), R("R5")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("SUB", [R("R3"), R("R4"), R("R5")])) + assert mock_emit_error.call_count == 0 def test_typecheck_MUL(): - assert typecheck_one(Op("MUL", [R("R3"), R("R4"), R("R5")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("MUL", [R("R3"), R("R4"), R("R5")])) + assert mock_emit_error.call_count == 0 def test_typecheck_XOR(): - assert typecheck_one(Op("XOR", [R("R3"), R("R4"), R("R5")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("XOR", [R("R3"), R("R4"), R("R5")])) + assert mock_emit_error.call_count == 0 def test_typecheck_INC(): - assert typecheck_one(Op("INC", [R("R6"), 42])) == [] - assert typecheck_one(Op("INC", [R("R6"), 1])) == [] - assert typecheck_one(Op("INC", [R("R6"), 64])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("INC", [R("R6"), 42])) + typecheck_one(Op("INC", [R("R6"), 1])) + typecheck_one(Op("INC", [R("R6"), 64])) + assert mock_emit_error.call_count == 0 def test_typecheck_DEC(): - assert typecheck_one(Op("DEC", [R("R6"), 42])) == [] - assert typecheck_one(Op("DEC", [R("R6"), 1])) == [] - assert typecheck_one(Op("DEC", [R("R6"), 64])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("DEC", [R("R6"), 42])) + typecheck_one(Op("DEC", [R("R6"), 1])) + typecheck_one(Op("DEC", [R("R6"), 64])) + assert mock_emit_error.call_count == 0 def test_typecheck_LSL(): - assert typecheck_one(Op("LSL", [R("R7"), R("R8")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("LSL", [R("R7"), R("R8")])) + assert mock_emit_error.call_count == 0 def test_typecheck_LSR(): - assert typecheck_one(Op("LSR", [R("R7"), R("R8")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("LSR", [R("R7"), R("R8")])) + assert mock_emit_error.call_count == 0 def test_typecheck_LSL8(): - assert typecheck_one(Op("LSL8", [R("R7"), R("R8")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("LSL8", [R("R7"), R("R8")])) + assert mock_emit_error.call_count == 0 def test_typecheck_LSR8(): - assert typecheck_one(Op("LSR8", [R("R7"), R("R8")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("LSR8", [R("R7"), R("R8")])) + assert mock_emit_error.call_count == 0 def test_typecheck_ASL(): - assert typecheck_one(Op("ASL", [R("R7"), R("R8")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("ASL", [R("R7"), R("R8")])) + assert mock_emit_error.call_count == 0 def test_typecheck_ASR(): - assert typecheck_one(Op("ASR", [R("R7"), R("R8")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("ASR", [R("R7"), R("R8")])) + assert mock_emit_error.call_count == 0 def test_typecheck_SAVEF(): - assert typecheck_one(Op("SAVEF", [R("R9")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("SAVEF", [R("R9")])) + assert mock_emit_error.call_count == 0 def test_typecheck_RSTRF(): - assert typecheck_one(Op("RSTRF", [R("R9")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("RSTRF", [R("R9")])) + assert mock_emit_error.call_count == 0 def test_typecheck_FON(): - assert typecheck_one(Op("FON", [0b10101])) == [] - assert typecheck_one(Op("FON", [0b01000])) == [] - assert typecheck_one(Op("FON", [0b11111])) == [] - assert typecheck_one(Op("FON", [0])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("FON", [0b10101])) + typecheck_one(Op("FON", [0b01000])) + typecheck_one(Op("FON", [0b11111])) + typecheck_one(Op("FON", [0])) + assert mock_emit_error.call_count == 0 def test_typecheck_FOFF(): - assert typecheck_one(Op("FOFF", [0b10101])) == [] - assert typecheck_one(Op("FOFF", [0b01000])) == [] - assert typecheck_one(Op("FOFF", [0b11111])) == [] - assert typecheck_one(Op("FOFF", [0])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("FOFF", [0b10101])) + typecheck_one(Op("FOFF", [0b01000])) + typecheck_one(Op("FOFF", [0b11111])) + typecheck_one(Op("FOFF", [0])) + assert mock_emit_error.call_count == 0 def test_typecheck_FSET5(): - assert typecheck_one(Op("FSET5", [0b10101])) == [] - assert typecheck_one(Op("FSET5", [0b01000])) == [] - assert typecheck_one(Op("FSET5", [0b11111])) == [] - assert typecheck_one(Op("FSET5", [0])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("FSET5", [0b10101])) + typecheck_one(Op("FSET5", [0b01000])) + typecheck_one(Op("FSET5", [0b11111])) + typecheck_one(Op("FSET5", [0])) + assert mock_emit_error.call_count == 0 def test_typecheck_FSET4(): - assert typecheck_one(Op("FSET4", [0b1010])) == [] - assert typecheck_one(Op("FSET4", [0b0100])) == [] - assert typecheck_one(Op("FSET4", [0b1111])) == [] - assert typecheck_one(Op("FSET4", [0])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("FSET4", [0b1010])) + typecheck_one(Op("FSET4", [0b0100])) + typecheck_one(Op("FSET4", [0b1111])) + typecheck_one(Op("FSET4", [0])) + assert mock_emit_error.call_count == 0 def test_typecheck_LOAD(): - assert typecheck_one(Op("LOAD", [R("R1"), 0, R("R2")])) == [] - assert typecheck_one(Op("LOAD", [R("R1"), 0b11111, R("R2")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("LOAD", [R("R1"), 0, R("R2")])) + typecheck_one(Op("LOAD", [R("R1"), 0b11111, R("R2")])) + assert mock_emit_error.call_count == 0 def test_typecheck_STORE(): - assert typecheck_one(Op("STORE", [R("R1"), 0, R("R2")])) == [] - assert typecheck_one(Op("STORE", [R("R1"), 0b11111, R("R2")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("STORE", [R("R1"), 0, R("R2")])) + typecheck_one(Op("STORE", [R("R1"), 0b11111, R("R2")])) + assert mock_emit_error.call_count == 0 def test_typecheck_CALL(): - assert typecheck_one(Op("CALL", [R("R12"), R("R11")])) == [] - assert typecheck_one(Op("CALL", [R("R12"), SYM("f")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("CALL", [R("R12"), R("R11")])) + typecheck_one(Op("CALL", [R("R12"), SYM("f")])) + assert mock_emit_error.call_count == 0 def test_typecheck_RETURN(): - assert typecheck_one(Op("RETURN", [R("R12"), R("R11")])) == [] - assert typecheck_one(Op("RETURN", [R("R12"), SYM("f")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("RETURN", [R("R12"), R("R11")])) + typecheck_one(Op("RETURN", [R("R12"), SYM("f")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BR(): - assert typecheck_one(Op("BR", [R("R11")])) == [] - assert typecheck_one(Op("BR", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BR", [R("R11")])) + typecheck_one(Op("BR", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BRR(): - assert typecheck_one(Op("BRR", [0xFF])) == [] - assert typecheck_one(Op("BRR", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BRR", [0xFF])) + typecheck_one(Op("BRR", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_BL(): - assert typecheck_one(Op("BL", [R("R11")])) == [] - assert typecheck_one(Op("BL", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BL", [R("R11")])) + typecheck_one(Op("BL", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BLR(): - assert typecheck_one(Op("BLR", [0xFF])) == [] - assert typecheck_one(Op("BLR", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BLR", [0xFF])) + typecheck_one(Op("BLR", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_BGE(): - assert typecheck_one(Op("BGE", [R("R11")])) == [] - assert typecheck_one(Op("BGE", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BGE", [R("R11")])) + typecheck_one(Op("BGE", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BGER(): - assert typecheck_one(Op("BGER", [0xFF])) == [] - assert typecheck_one(Op("BGER", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BGER", [0xFF])) + typecheck_one(Op("BGER", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_BLE(): - assert typecheck_one(Op("BLE", [R("R11")])) == [] - assert typecheck_one(Op("BLE", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BLE", [R("R11")])) + typecheck_one(Op("BLE", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BLER(): - assert typecheck_one(Op("BLER", [0xFF])) == [] - assert typecheck_one(Op("BLER", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BLER", [0xFF])) + typecheck_one(Op("BLER", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_BG(): - assert typecheck_one(Op("BG", [R("R11")])) == [] - assert typecheck_one(Op("BG", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BG", [R("R11")])) + typecheck_one(Op("BG", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BGR(): - assert typecheck_one(Op("BGR", [0xFF])) == [] - assert typecheck_one(Op("BGR", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BGR", [0xFF])) + typecheck_one(Op("BGR", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_BULE(): - assert typecheck_one(Op("BULE", [R("R11")])) == [] - assert typecheck_one(Op("BULE", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BULE", [R("R11")])) + typecheck_one(Op("BULE", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BULER(): - assert typecheck_one(Op("BULER", [0xFF])) == [] - assert typecheck_one(Op("BULER", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BULER", [0xFF])) + typecheck_one(Op("BULER", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_BUG(): - assert typecheck_one(Op("BUG", [R("R11")])) == [] - assert typecheck_one(Op("BUG", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BUG", [R("R11")])) + typecheck_one(Op("BUG", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BUGR(): - assert typecheck_one(Op("BUGR", [0xFF])) == [] - assert typecheck_one(Op("BUGR", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BUGR", [0xFF])) + typecheck_one(Op("BUGR", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_BZ(): - assert typecheck_one(Op("BZ", [R("R11")])) == [] - assert typecheck_one(Op("BZ", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BZ", [R("R11")])) + typecheck_one(Op("BZ", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BZR(): - assert typecheck_one(Op("BZR", [0xFF])) == [] - assert typecheck_one(Op("BZR", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BZR", [0xFF])) + typecheck_one(Op("BZR", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_BNZ(): - assert typecheck_one(Op("BNZ", [R("R11")])) == [] - assert typecheck_one(Op("BNZ", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BNZ", [R("R11")])) + typecheck_one(Op("BNZ", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BNZR(): - assert typecheck_one(Op("BNZR", [0xFF])) == [] - assert typecheck_one(Op("BNZR", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BNZR", [0xFF])) + typecheck_one(Op("BNZR", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_BC(): - assert typecheck_one(Op("BC", [R("R11")])) == [] - assert typecheck_one(Op("BC", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BC", [R("R11")])) + typecheck_one(Op("BC", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BCR(): - assert typecheck_one(Op("BCR", [0xFF])) == [] - assert typecheck_one(Op("BCR", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BCR", [0xFF])) + typecheck_one(Op("BCR", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_BNC(): - assert typecheck_one(Op("BNC", [R("R11")])) == [] - assert typecheck_one(Op("BNC", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BNC", [R("R11")])) + typecheck_one(Op("BNC", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BNCR(): - assert typecheck_one(Op("BNCR", [0xFF])) == [] - assert typecheck_one(Op("BNCR", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BNCR", [0xFF])) + typecheck_one(Op("BNCR", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_BS(): - assert typecheck_one(Op("BS", [R("R11")])) == [] - assert typecheck_one(Op("BS", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BS", [R("R11")])) + typecheck_one(Op("BS", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BSR(): - assert typecheck_one(Op("BSR", [0xFF])) == [] - assert typecheck_one(Op("BSR", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BSR", [0xFF])) + typecheck_one(Op("BSR", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_BNS(): - assert typecheck_one(Op("BNS", [R("R11")])) == [] - assert typecheck_one(Op("BNS", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BNS", [R("R11")])) + typecheck_one(Op("BNS", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BNSR(): - assert typecheck_one(Op("BNSR", [0xFF])) == [] - assert typecheck_one(Op("BNSR", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BNSR", [0xFF])) + typecheck_one(Op("BNSR", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_BV(): - assert typecheck_one(Op("BV", [R("R11")])) == [] - assert typecheck_one(Op("BV", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BV", [R("R11")])) + typecheck_one(Op("BV", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BVR(): - assert typecheck_one(Op("BVR", [0xFF])) == [] - assert typecheck_one(Op("BVR", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BVR", [0xFF])) + typecheck_one(Op("BVR", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_BNV(): - assert typecheck_one(Op("BNV", [R("R11")])) == [] - assert typecheck_one(Op("BNV", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BNV", [R("R11")])) + typecheck_one(Op("BNV", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_BNVR(): - assert typecheck_one(Op("BNVR", [0xFF])) == [] - assert typecheck_one(Op("BNVR", [-0x7F])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("BNVR", [0xFF])) + typecheck_one(Op("BNVR", [-0x7F])) + assert mock_emit_error.call_count == 0 def test_typecheck_SETRF(): - assert typecheck_one(Op("SETRF", [R("R1"), 42])) == [] - assert typecheck_one(Op("SETRF", [R("R1"), 0xFFFF])) == [] - assert typecheck_one(Op("SETRF", [R("R1"), -0x7FFF])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("SETRF", [R("R1"), 42])) + typecheck_one(Op("SETRF", [R("R1"), 0xFFFF])) + typecheck_one(Op("SETRF", [R("R1"), -0x7FFF])) + assert mock_emit_error.call_count == 0 def test_typecheck_MOVE(): - assert typecheck_one(Op("MOVE", [R("R1"), R("R2")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("MOVE", [R("R1"), R("R2")])) + assert mock_emit_error.call_count == 0 def test_typecheck_CMP(): - assert typecheck_one(Op("CMP", [R("R1"), R("R2")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("CMP", [R("R1"), R("R2")])) + assert mock_emit_error.call_count == 0 def test_typecheck_NEG(): - assert typecheck_one(Op("NEG", [R("R1"), R("R2")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("NEG", [R("R1"), R("R2")])) + assert mock_emit_error.call_count == 0 def test_typecheck_NOT(): - assert typecheck_one(Op("NOT", [R("R1"), R("R2")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("NOT", [R("R1"), R("R2")])) + assert mock_emit_error.call_count == 0 def test_typecheck_CBON(): - assert typecheck_one(Op("CBON", [])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("CBON", [])) + assert mock_emit_error.call_count == 0 def test_typecheck_CON(): - assert typecheck_one(Op("CON", [])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("CON", [])) + assert mock_emit_error.call_count == 0 def test_typecheck_COFF(): - assert typecheck_one(Op("COFF", [])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("COFF", [])) + assert mock_emit_error.call_count == 0 def test_typecheck_CCBOFF(): - assert typecheck_one(Op("CCBOFF", [])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("CCBOFF", [])) + assert mock_emit_error.call_count == 0 def test_typecheck_FLAGS(): - assert typecheck_one(Op("FLAGS", [R("R1")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("FLAGS", [R("R1")])) + assert mock_emit_error.call_count == 0 def test_typecheck_NOP(): - assert typecheck_one(Op("NOP", [])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("NOP", [])) + assert mock_emit_error.call_count == 0 def test_typecheck_HALT(): - assert typecheck_one(Op("HALT", [])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("HALT", [])) + assert mock_emit_error.call_count == 0 def test_typecheck_LABEL(): - assert typecheck_one(Op("LABEL", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("LABEL", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_CONSTANT(): - assert typecheck_one(Op("CONSTANT", [SYM("N"), 0xFFFF])) == [] - assert typecheck_one(Op("CONSTANT", [SYM("N"), -0x7FFF])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("CONSTANT", [SYM("N"), 0xFFFF])) + typecheck_one(Op("CONSTANT", [SYM("N"), -0x7FFF])) + assert mock_emit_error.call_count == 0 def test_typecheck_DLABEL(): - assert typecheck_one(Op("DLABEL", [SYM("l")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("DLABEL", [SYM("l")])) + assert mock_emit_error.call_count == 0 def test_typecheck_INTEGER(): - assert typecheck_one(Op("INTEGER", [0xFFFF])) == [] - assert typecheck_one(Op("INTEGER", [-0x7FFF])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("INTEGER", [0xFFFF])) + typecheck_one(Op("INTEGER", [-0x7FFF])) + assert mock_emit_error.call_count == 0 def test_typecheck_LP_STRING(): - assert typecheck_one(Op("LP_STRING", [Token("STRING", "hello!")])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("LP_STRING", [Token("STRING", "hello!")])) + assert mock_emit_error.call_count == 0 def test_typecheck_DSKIP(): - assert typecheck_one(Op("DSKIP", [0xFFFF])) == [] - assert typecheck_one(Op("DSKIP", [0])) == [] + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op("DSKIP", [0xFFFF])) + typecheck_one(Op("DSKIP", [0])) + assert mock_emit_error.call_count == 0 def test_typecheck_unknown_instruction(): - errors = typecheck_one(Op(SYM("IF"), [R("R1")])) - assert len(errors) == 1 - assert "unknown instruction" in errors[0].msg - assert "IF" in errors[0].msg - - -def test_typecheck_unknown_instruction(): - errors = typecheck_one(Op(SYM("IF"), [R("R1")])) - assert len(errors) == 1 - assert "unknown instruction" in errors[0].msg - assert "IF" in errors[0].msg + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op(SYM("IF"), [R("R1")])) + assert mock_emit_error.call_count == 1 + assert "unknown instruction" in mock_emit_error.call_args[0][0] + assert "IF" in mock_emit_error.call_args[0][0] def test_typecheck_unknown_branch_instruction(): - errors = typecheck_one(Op(SYM("BNW"), [R("R1")])) - assert len(errors) == 1 - assert "unknown instruction" in errors[0].msg - assert "BNW" in errors[0].msg + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op(SYM("BNW"), [R("R1")])) + assert mock_emit_error.call_count == 1 + assert "unknown instruction" in mock_emit_error.call_args[0][0] + assert "BNW" in mock_emit_error.call_args[0][0] def test_typecheck_single_error(): @@ -487,25 +652,28 @@ def test_typecheck_single_error(): Op(SYM("SETHI"), [R("R1"), IntToken(1000)]), ] - errors = typecheck(program) - - assert len(errors) == 1 - assert "SETHI" in errors[0].msg - assert "out of range" in errors[0].msg + with patch("hera.utils._emit_msg") as mock_emit_error: + errors = typecheck(program) + assert mock_emit_error.call_count == 1 + assert "SETHI" in mock_emit_error.call_args[0][0] + assert "out of range" in mock_emit_error.call_args[0][0] def test_typecheck_multiple_errors(): program = [Op(SYM("ADD"), [R("R1"), IntToken(10)]), Op(SYM("INC"), [R("R3")])] - errors = typecheck(program) - - assert len(errors) == 3 + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck(program) + assert mock_emit_error.call_count == 3 - assert "ADD" in errors[0].msg - assert "too few" in errors[0].msg + call_args = mock_emit_error.call_args_list[0][0] + assert "ADD" in call_args[0] + assert "too few" in call_args[0] - assert "ADD" in errors[1].msg - assert "not a register" in errors[1].msg + call_args = mock_emit_error.call_args_list[1][0] + assert "ADD" in call_args[0] + assert "not a register" in call_args[0] - assert "INC" in errors[2].msg - assert "too few" in errors[2].msg + call_args = mock_emit_error.call_args_list[2][0] + assert "INC" in call_args[0] + assert "too few" in call_args[0]