File size: 4,933 Bytes
ad474c9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | """Tests for the .hdcasm compiler."""
import os
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from hdc.assembler import AsmError, assemble, roundtrip_report, split_operands # noqa: E402
D = 2000
class TestLabels(unittest.TestCase):
def test_forward_and_backward(self):
src = """
jmp done
loop:
nop
jmp loop
done:
halt
"""
prog = assemble(src, d=D)
self.assertEqual(prog.labels["loop"], 1)
self.assertEqual(prog.labels["done"], 3)
self.assertEqual(prog.listing[0].tgt, 3)
self.assertEqual(prog.listing[2].tgt, 1)
def test_numeric_target(self):
prog = assemble("jmp @2\nnop\nhalt", d=D)
self.assertEqual(prog.listing[0].tgt, 2)
def test_duplicate_label_rejected(self):
with self.assertRaises(AsmError):
assemble("loop:\n nop\nloop:\n nop", d=D)
def test_undefined_label_rejected(self):
with self.assertRaises(AsmError):
assemble("jmp nowhere", d=D)
def test_label_on_directive_line(self):
prog = assemble('here: .str greeting "hello"\n ini: nop\n halt', d=D)
self.assertEqual(prog.labels["ini"], 0)
self.assertEqual(len(prog.instrs), 2)
class TestDirectives(unittest.TestCase):
SRC = """
.name out
.data tbl 42
.str hello "hi there"
ldi R0, 1
store out, R0
prints "hi there"
halt
"""
def test_data_directory_populates_image(self):
prog = assemble(self.SRC, d=D)
self.assertEqual(prog.data["tbl"], "int:42")
self.assertEqual(prog.data["hello"], "str:hi there")
self.assertEqual(prog.cell_names, ["hello", "out", "tbl"])
self.assertIn("hi there", prog.strings)
self.assertIn(42, prog.literals)
self.assertEqual(len(prog.instrs), 4)
def test_install_writes_cells(self):
from hdc.memory import HDCStorage
prog = assemble(self.SRC, d=D)
store = HDCStorage(d=D)
names = prog.install(store)
self.assertEqual(names, ["code/0", "code/1", "code/2", "code/3"])
for name in names:
self.assertIn(name, store)
self.assertNotEqual(store.read(name), 0)
self.assertIn("out", store)
self.assertIn("tbl", store)
def test_unknown_directive_rejected(self):
with self.assertRaises(AsmError):
assemble(".frobnicate x", d=D)
class TestRoundTrip(unittest.TestCase):
SRC = """
.str hello "hdc storage"
.data acc 0
start:
ldi R0, 5
ldi R1, 37
add R2, R0, R1
out R2
bind R3, R0, R1
sim R4, R3, R0
out R4
prints hello
store acc, R2
load R5, acc
out R5
halt
"""
def test_every_cell_decodes_to_its_source_opcode(self):
prog = assemble(self.SRC, d=D)
report = roundtrip_report(prog)
n = len(prog.instrs)
self.assertIn("decode-back check: %d/%d cells round-trip" % (n, n), report)
for i, line in enumerate(prog.source):
opcode = line.text.split()[0].upper()
self.assertEqual(prog.listing[i].op, opcode, msg="cell %d" % i)
self.assertGreater(prog.listing[i].confidence, 0.15)
def test_source_lines_are_instruction_lines_only(self):
prog = assemble(self.SRC, d=D)
self.assertEqual(len(prog.source), len(prog.instrs))
def test_listing_text_contains_confidence(self):
prog = assemble(self.SRC, d=D)
text = prog.listing_text()
self.assertIn("conf", text)
self.assertIn("start:", text)
class TestErrors(unittest.TestCase):
def test_wrong_operand_count(self):
with self.assertRaises(AsmError):
assemble("add R0, R1", d=D)
def test_unknown_opcode(self):
with self.assertRaises(AsmError):
assemble("frob R0", d=D)
def test_bad_register(self):
with self.assertRaises(AsmError):
assemble("ldi R99, 1", d=D)
def test_bad_literal(self):
with self.assertRaises(AsmError):
assemble("ldi R0, banana", d=D)
def test_unquoted_string(self):
with self.assertRaises(AsmError):
assemble("prints hello", d=D)
class TestTokenizer(unittest.TestCase):
def test_commas_inside_quotes_survive(self):
self.assertEqual(
split_operands('"a, b", R0'), ['"a, b"', "R0"]
)
def test_empty(self):
self.assertEqual(split_operands(" "), [])
def test_comments_stripped(self):
prog = assemble("ldi R0, 5 ; set five\n halt # done", d=D)
self.assertEqual(len(prog.instrs), 2)
self.assertEqual(prog.listing[0].imm, 5)
if __name__ == "__main__":
unittest.main()
|