From 85c090ab06128205affc63ea05c281739ebf1fa4 Mon Sep 17 00:00:00 2001 From: Ian Fisher Date: Wed, 2 Jan 2019 14:26:54 -0500 Subject: [PATCH] Emit error for undefined label during type-checking Resolves #56 --- CHANGELOG.md | 3 ++- hera/typechecker.py | 20 ++++++++++++---- test/test_typechecker.py | 51 ++++++++++++++++++++++++---------------- 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 305488c..d49b926 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), **NOTE**: As permitted by semantic versioning, backward compatibility is NOT maintained for initial development, i.e. releases before 1.0.0. ## [Unreleased] -Nothing yet. +### Fixed +- Use of undefined labels gives proper error message instead of Python exception. ## [0.4.0] - 2019-01-02 diff --git a/hera/typechecker.py b/hera/typechecker.py index 4c8c49f..4ab77fb 100644 --- a/hera/typechecker.py +++ b/hera/typechecker.py @@ -119,6 +119,9 @@ def check_one_type(pattern, arg, symbol_table): register_to_index(arg) except ValueError: return "not a valid register" + elif pattern == SYMBOL: + if not is_symbol(arg): + return "not a symbol" elif pattern == REGISTER_OR_LABEL: if not isinstance(arg, Token): return "not a register or label" @@ -133,9 +136,15 @@ def check_one_type(pattern, arg, symbol_table): return "not a valid register" elif arg.type != "SYMBOL": return "not a register or label" + else: + if arg not in symbol_table: + return "is undefined label" elif pattern == LABEL: - if not isinstance(arg, Token) or arg.type != "SYMBOL": + if not is_symbol(arg): return "not a symbol" + + if arg not in symbol_table: + return "is undefined label" elif pattern == STRING: if not isinstance(arg, Token) or arg.type != "STRING": return "not a string" @@ -144,7 +153,7 @@ def check_one_type(pattern, arg, symbol_table): try: arg = symbol_table[arg] except KeyError: - return "undefined constant" + return "is undefined constant" if not isinstance(arg, int): return "not an integer" @@ -164,6 +173,7 @@ def check_one_type(pattern, arg, symbol_table): REGISTER_OR_LABEL = "rl" LABEL = "l" STRING = "s" +SYMBOL = "sym" U4 = range(0, 2 ** 4) U5 = range(0, 2 ** 5) U16 = range(0, 2 ** 16) @@ -250,10 +260,10 @@ def check_one_type(pattern, arg, symbol_table): "FLAGS": (REGISTER,), "NOP": (), "HALT": (), - "LABEL": (LABEL,), + "LABEL": (SYMBOL,), # Data statements - "CONSTANT": (LABEL, I16), - "DLABEL": (LABEL,), + "CONSTANT": (SYMBOL, I16), + "DLABEL": (SYMBOL,), "INTEGER": (I16,), "LP_STRING": (STRING,), "DSKIP": (U16,), diff --git a/test/test_typechecker.py b/test/test_typechecker.py index 110af20..cd99f02 100644 --- a/test/test_typechecker.py +++ b/test/test_typechecker.py @@ -9,6 +9,7 @@ REGISTER, REGISTER_OR_LABEL, STRING, + SYMBOL, typecheck, typecheck_one, U4, @@ -98,12 +99,12 @@ def test_check_one_type_with_range_object(): def test_check_one_type_with_constant_symbol(): - assert check_one_type(range(0, 100), Token("SYMBOL", "n"), {"n": 42}) is None + assert check_one_type(range(0, 100), SYM("n"), {"n": 42}) is None def test_check_one_type_with_register_or_label(): assert check_one_type(REGISTER_OR_LABEL, 5, {}) == "not a register or label" - assert check_one_type(REGISTER_OR_LABEL, Token("SYMBOL", "n"), {"n": 42}) is None + assert check_one_type(REGISTER_OR_LABEL, SYM("n"), {"n": 42}) is None assert check_one_type(REGISTER_OR_LABEL, R("R1"), {}) is None @@ -113,7 +114,12 @@ def test_check_one_type_with_invalid_register(): def test_check_one_type_with_label(): assert check_one_type(LABEL, 10, {}) == "not a symbol" - assert check_one_type(LABEL, Token("SYMBOL", "n"), {"n": 42}) is None + assert check_one_type(LABEL, SYM("n"), {"n": 42}) is None + + +def test_check_one_type_with_undefined_label(): + assert check_one_type(LABEL, SYM("n"), {}) == "is undefined label" + assert check_one_type(REGISTER_OR_LABEL, SYM("n"), {}) == "is undefined label" def test_check_one_type_with_string(): @@ -121,6 +127,11 @@ def test_check_one_type_with_string(): assert check_one_type(STRING, Token("STRING", "hello"), {}) is None +def test_check_one_type_with_symbol(): + assert check_one_type(SYMBOL, 10, {}) == "not a symbol" + assert check_one_type(SYMBOL, SYM("x"), {}) is None + + def test_typecheck_SET(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("SET", [R("R1"), 42]), {}) @@ -298,21 +309,21 @@ def test_typecheck_STORE(): def test_typecheck_CALL(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("CALL", [R("R12"), R("R11")]), {}) - assert typecheck_one(Op("CALL", [R("R12"), SYM("f")]), {}) + assert typecheck_one(Op("CALL", [R("R12"), SYM("f")]), {"f": 0}) assert mock_emit_error.call_count == 0 def test_typecheck_RETURN(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("RETURN", [R("R12"), R("R11")]), {}) - assert typecheck_one(Op("RETURN", [R("R12"), SYM("f")]), {}) + assert typecheck_one(Op("RETURN", [R("R12"), SYM("f")]), {"f": 0}) assert mock_emit_error.call_count == 0 def test_typecheck_BR(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BR", [R("R11")]), {}) - assert typecheck_one(Op("BR", [SYM("l")]), {}) + assert typecheck_one(Op("BR", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0 @@ -326,7 +337,7 @@ def test_typecheck_BRR(): def test_typecheck_BL(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BL", [R("R11")]), {}) - assert typecheck_one(Op("BL", [SYM("l")]), {}) + assert typecheck_one(Op("BL", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0 @@ -340,7 +351,7 @@ def test_typecheck_BLR(): def test_typecheck_BGE(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BGE", [R("R11")]), {}) - assert typecheck_one(Op("BGE", [SYM("l")]), {}) + assert typecheck_one(Op("BGE", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0 @@ -354,7 +365,7 @@ def test_typecheck_BGER(): def test_typecheck_BLE(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BLE", [R("R11")]), {}) - assert typecheck_one(Op("BLE", [SYM("l")]), {}) + assert typecheck_one(Op("BLE", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0 @@ -368,7 +379,7 @@ def test_typecheck_BLER(): def test_typecheck_BG(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BG", [R("R11")]), {}) - assert typecheck_one(Op("BG", [SYM("l")]), {}) + assert typecheck_one(Op("BG", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0 @@ -382,7 +393,7 @@ def test_typecheck_BGR(): def test_typecheck_BULE(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BULE", [R("R11")]), {}) - assert typecheck_one(Op("BULE", [SYM("l")]), {}) + assert typecheck_one(Op("BULE", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0 @@ -396,7 +407,7 @@ def test_typecheck_BULER(): def test_typecheck_BUG(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BUG", [R("R11")]), {}) - assert typecheck_one(Op("BUG", [SYM("l")]), {}) + assert typecheck_one(Op("BUG", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0 @@ -410,7 +421,7 @@ def test_typecheck_BUGR(): def test_typecheck_BZ(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BZ", [R("R11")]), {}) - assert typecheck_one(Op("BZ", [SYM("l")]), {}) + assert typecheck_one(Op("BZ", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0 @@ -424,7 +435,7 @@ def test_typecheck_BZR(): def test_typecheck_BNZ(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BNZ", [R("R11")]), {}) - assert typecheck_one(Op("BNZ", [SYM("l")]), {}) + assert typecheck_one(Op("BNZ", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0 @@ -438,7 +449,7 @@ def test_typecheck_BNZR(): def test_typecheck_BC(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BC", [R("R11")]), {}) - assert typecheck_one(Op("BC", [SYM("l")]), {}) + assert typecheck_one(Op("BC", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0 @@ -452,7 +463,7 @@ def test_typecheck_BCR(): def test_typecheck_BNC(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BNC", [R("R11")]), {}) - assert typecheck_one(Op("BNC", [SYM("l")]), {}) + assert typecheck_one(Op("BNC", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0 @@ -466,7 +477,7 @@ def test_typecheck_BNCR(): def test_typecheck_BS(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BS", [R("R11")]), {}) - assert typecheck_one(Op("BS", [SYM("l")]), {}) + assert typecheck_one(Op("BS", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0 @@ -480,7 +491,7 @@ def test_typecheck_BSR(): def test_typecheck_BNS(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BNS", [R("R11")]), {}) - assert typecheck_one(Op("BNS", [SYM("l")]), {}) + assert typecheck_one(Op("BNS", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0 @@ -494,7 +505,7 @@ def test_typecheck_BNSR(): def test_typecheck_BV(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BV", [R("R11")]), {}) - assert typecheck_one(Op("BV", [SYM("l")]), {}) + assert typecheck_one(Op("BV", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0 @@ -508,7 +519,7 @@ def test_typecheck_BVR(): def test_typecheck_BNV(): with patch("hera.utils._emit_msg") as mock_emit_error: assert typecheck_one(Op("BNV", [R("R11")]), {}) - assert typecheck_one(Op("BNV", [SYM("l")]), {}) + assert typecheck_one(Op("BNV", [SYM("l")]), {"l": 0}) assert mock_emit_error.call_count == 0