diff --git a/hera/preprocessor.py b/hera/preprocessor.py index 51dfd6a..609a6f0 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, is_symbol, to_u16 +from .utils import copy_token, HERAError, is_symbol, to_u16 # Arbitrary value copied over from HERA-C. @@ -36,7 +36,14 @@ def substitute_label(op, labels): if op.name == "SETLO" and is_symbol(op.args[1]): d, v = op.args name = copy_token("SETLO", op.name) - return Op(name, [d, labels[v] & 0xFF]) + try: + label = labels[v] + except KeyError: + raise HERAError( + "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]): d, v = op.args name = copy_token("SETHI", op.name) diff --git a/test/test_preprocessor.py b/test/test_preprocessor.py index bc4edfd..8ab66b3 100644 --- a/test/test_preprocessor.py +++ b/test/test_preprocessor.py @@ -1,3 +1,5 @@ +import pytest + from lark import Token from hera.parser import Op @@ -5,16 +7,48 @@ convert, convert_set, get_labels, - preprocess, HERA_DATA_START, + preprocess, + substitute_label, ) -from hera.utils import IntToken +from hera.utils import HERAError, IntToken def R(s): return Token("REGISTER", s) +def SYM(s): + return Token("SYMBOL", s) + + +def test_substitute_label_with_SETLO(): + labels = {"N": 10} + assert substitute_label(Op(SYM("SETLO"), [R("R1"), SYM("N")]), labels) == Op( + "SETLO", ["R1", 10] + ) + + +def test_substitute_label_with_SETHI(): + labels = {"N": 10} + # 0 is substituted and not 10 because only the high bits are taken. + assert substitute_label(Op(SYM("SETHI"), [R("R1"), SYM("N")]), labels) == Op( + "SETHI", ["R1", 0] + ) + + +def test_substitute_label_with_other_op(): + labels = {"N": 10} + assert substitute_label(Op(SYM("INC"), [R("R1"), SYM("N")]), labels) == Op( + "INC", ["R1", "N"] + ) + + +def test_substitute_label_with_undefined_label(): + with pytest.raises(HERAError): + substitute_label(Op(SYM("SETLO"), [R("R1"), SYM("N")]), {}) + + def test_convert_set_with_small_positive(): assert convert_set("R5", 18) == [Op("SETLO", ["R5", 18])]