File size: 3,643 Bytes
fc3e70a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

"""Tests: Phases 12-13 — SUBLEQ IR and VM."""

import pytest
from semantic_engine.subleq import (
    SubleqInstruction, SubleqProgram, SubleqMemory, SubleqVM,
    HALT_ADDRESS, MAX_MEMORY,
)
from semantic_engine.types import PipelineError


def halt_prog() -> SubleqProgram:
    prog = SubleqProgram()
    prog.data[0] = 0  # zero cell
    prog.add(SubleqInstruction(A=0, B=0, C=HALT_ADDRESS, comment="HALT"))
    return prog


def test_instruction_repr():
    instr = SubleqInstruction(A=1, B=2, C=3)
    r = repr(instr)
    assert "SUBLEQ" in r


def test_instruction_halt_repr():
    instr = SubleqInstruction(A=0, B=0, C=HALT_ADDRESS)
    assert "HALT" in repr(instr)


def test_program_add_and_label():
    prog = SubleqProgram()
    prog.add(SubleqInstruction(A=0, B=0, C=HALT_ADDRESS, label="start"))
    assert "start" in prog.labels
    assert prog.labels["start"] == 0


def test_program_resolve_label():
    prog = SubleqProgram()
    prog.add(SubleqInstruction(A=0, B=0, C=HALT_ADDRESS, label="end"))
    assert prog.resolve_label("end") == 0


def test_program_resolve_missing_label():
    prog = SubleqProgram()
    with pytest.raises(PipelineError):
        prog.resolve_label("missing")


def test_assemble_disassemble_roundtrip():
    prog = SubleqProgram()
    prog.add(SubleqInstruction(A=1, B=2, C=3))
    prog.add(SubleqInstruction(A=4, B=5, C=HALT_ADDRESS))
    binary = prog.assemble()
    recovered = SubleqProgram.disassemble(binary)
    assert len(recovered.instructions) == 2
    assert recovered.instructions[0].A == 1
    assert recovered.instructions[0].B == 2
    assert recovered.instructions[1].C == HALT_ADDRESS


def test_assemble_bad_alignment():
    with pytest.raises(PipelineError):
        SubleqProgram.disassemble(b"\x00\x01\x02\x03")


def test_memory_read_write():
    mem = SubleqMemory()
    mem.write(100, 42)
    assert mem.read(100) == 42


def test_memory_out_of_bounds():
    mem = SubleqMemory()
    with pytest.raises(PipelineError):
        mem.read(MAX_MEMORY + 1)
    with pytest.raises(PipelineError):
        mem.write(-1, 0)


def test_vm_halts():
    prog = halt_prog()
    vm = SubleqVM()
    vm.load(prog)
    vm.execute()
    assert vm.halted


def test_vm_step_returns_false_on_halt():
    prog = halt_prog()
    vm = SubleqVM()
    vm.load(prog)
    while not vm.halted:
        result = vm.step()
    assert not result


def test_vm_trace_recorded():
    prog = halt_prog()
    vm = SubleqVM()
    vm.load(prog)
    trace = vm.execute()
    assert len(trace) >= 1
    assert trace[0].pc >= 0  # PC starts at base offset


def test_vm_max_steps_exceeded():
    prog = SubleqProgram()
    # Infinite loop: SUBLEQ 0 0 0
    prog.data[0] = 0
    prog.add(SubleqInstruction(A=0, B=0, C=0))
    vm = SubleqVM(max_steps=10)
    vm.load(prog)
    with pytest.raises(PipelineError) as exc:
        vm.execute()
    assert "max" in str(exc.value).lower() or "steps" in str(exc.value).lower()


def test_verify_halts():
    prog = halt_prog()
    vm = SubleqVM()
    assert vm.verify_halts(prog)


def test_subleq_arithmetic():
    """SUBLEQ(A, B, C): mem[B] -= mem[A]; branch if <=0"""
    prog = SubleqProgram()
    # Use addresses 10, 11 for data (well below base=100 for instructions)
    prog.data[10] = 5
    prog.data[11] = 3
    prog.add(SubleqInstruction(A=10, B=11, C=HALT_ADDRESS))
    vm = SubleqVM()
    vm.load(prog)
    vm.execute()
    assert vm.memory.read(11) == -2