From 09727f4a8c41a9fc98f9ce9d6bb980c34e4c0399 Mon Sep 17 00:00:00 2001 From: Ian Fisher Date: Thu, 13 Dec 2018 22:27:41 -0500 Subject: [PATCH] Emit error for use of undefined constants Resolves #27 --- hera/typechecker.py | 10 ++++++---- test/test_typechecker.py | 40 +++++++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/hera/typechecker.py b/hera/typechecker.py index 7867794..c0badf4 100644 --- a/hera/typechecker.py +++ b/hera/typechecker.py @@ -153,12 +153,12 @@ def check_types(name, expected, got, symtab): ordinals = ["first", "second", "third"] for ordinal, pattern, arg in zip(ordinals, expected, got): prefix = "{} arg to {} ".format(ordinal, name) - error = check_one_type(pattern, arg) + error = check_one_type(pattern, arg, symtab) if error: emit_error(prefix + error, line=arg.line, column=arg.column) -def check_one_type(pattern, arg): +def check_one_type(pattern, arg, symtab): """Verify that the argument matches the pattern. Return a string stating the error if it doesn't, return None otherwise. """ @@ -189,8 +189,10 @@ def check_one_type(pattern, arg): return "not a string" elif isinstance(pattern, range): if is_symbol(arg): - # Symbols will be resolved later. - return None + try: + arg = symtab[arg] + except KeyError: + return "undefined constant" if not isinstance(arg, int): return "not an integer" diff --git a/test/test_typechecker.py b/test/test_typechecker.py index 0895293..1dca623 100644 --- a/test/test_typechecker.py +++ b/test/test_typechecker.py @@ -21,7 +21,6 @@ # 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): @@ -34,94 +33,94 @@ def SYM(s=""): def test_check_types_with_too_few(): with patch("hera.utils._emit_msg") as mock_emit_error: - check_types(SYM(), [REGISTER, REGISTER], [R("R1")]) + 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(): with patch("hera.utils._emit_msg") as mock_emit_error: - check_types(SYM(), [REGISTER], [R("R1"), IntToken(10)]) + 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(): with patch("hera.utils._emit_msg") as mock_emit_error: - check_types(SYM(), [REGISTER], [IntToken(10)]) + 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] with patch("hera.utils._emit_msg") as mock_emit_error: - check_types(SYM(), [U16], [R("R1")]) + 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(): with patch("hera.utils._emit_msg") as mock_emit_error: - check_types(SYM(), [U16], [IntToken(65536)]) + 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(): with patch("hera.utils._emit_msg") as mock_emit_error: - check_types(SYM(), [U16], [IntToken(-1)]) + 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(): with patch("hera.utils._emit_msg") as mock_emit_error: - check_types(SYM(), [U4], [IntToken(16)]) + 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] with patch("hera.utils._emit_msg") as mock_emit_error: - check_types(SYM(), [U4], [IntToken(-1)]) + 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(): with patch("hera.utils._emit_msg") as mock_emit_error: - check_types(SYM(), [range(-10, 10)], [IntToken(-11)]) + 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] with patch("hera.utils._emit_msg") as mock_emit_error: - check_types(SYM(), [range(-10, 10)], [IntToken(10)]) + 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] with patch("hera.utils._emit_msg") as mock_emit_error: - check_types(SYM(), [range(-10, 10)], [R("R1")]) + 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] with patch("hera.utils._emit_msg") as mock_emit_error: r = range(-10, 10) - check_types("", [r, r, r], [5, -10, 9]) + check_types("", [r, r, r], [5, -10, 9], {}) assert mock_emit_error.call_count == 0 def test_check_types_with_constant_symbol(): with patch("hera.utils._emit_msg") as mock_emit_error: - check_types("", [range(0, 100)], [Token("SYMBOL", "n")]) + check_types("", [range(0, 100)], [Token("SYMBOL", "n")], {"n": 42}) assert mock_emit_error.call_count == 0 def test_check_types_with_register_or_label(): 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")]) + check_types("", [REGISTER_OR_LABEL], [Token("SYMBOL", "n")], {"n": 42}) + check_types("", [REGISTER_OR_LABEL], [R("R1")], {}) assert mock_emit_error.call_count == 0 def test_check_types_with_label(): with patch("hera.utils._emit_msg") as mock_emit_error: - check_types("", [LABEL], [Token("SYMBOL", "n")]) + check_types("", [LABEL], [Token("SYMBOL", "n")], {"n": 42}) assert mock_emit_error.call_count == 0 @@ -636,6 +635,13 @@ def test_typecheck_DSKIP(): assert mock_emit_error.call_count == 0 +def test_typecheck_undefined_symbol(): + with patch("hera.utils._emit_msg") as mock_emit_error: + typecheck_one(Op(SYM("SET"), [R("R1"), SYM("N")])) + assert mock_emit_error.call_count == 1 + assert "undefined constant" in mock_emit_error.call_args[0][0] + + def test_typecheck_unknown_instruction(): with patch("hera.utils._emit_msg") as mock_emit_error: typecheck_one(Op(SYM("IF"), [R("R1")]))