xqvm-py 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,89 @@
1
+ ### Rust ###
2
+ debug/
3
+ target/
4
+ **/*.rs.bk
5
+ *.pdb
6
+
7
+ ### System-related ###
8
+ .DS_Store
9
+ Thumbs.db
10
+
11
+ ### Secret keys ###
12
+ *.pem
13
+ *.key
14
+ *.p12
15
+ *.pfx
16
+ *.der
17
+
18
+ id_rsa
19
+ id_rsa.pub
20
+ id_ed25519
21
+ id_ed25519.pub
22
+ *.ssh
23
+
24
+ *.gpg
25
+ *.asc
26
+
27
+ .env
28
+ .env.*
29
+ *.secret
30
+ *.secrets
31
+ *.token
32
+ *.credentials
33
+ credentials.json
34
+
35
+ certs/
36
+ *.crt
37
+ *.csr
38
+
39
+ ### mdBook ###
40
+ docs/book/build/
41
+ mermaid.min.js
42
+ mermaid-init.js
43
+
44
+ ### Python ###
45
+ __pycache__/
46
+ *.py[cod]
47
+ *$py.class
48
+ *.so
49
+ *.egg-info/
50
+ *.egg
51
+ .eggs/
52
+ build/
53
+ dist/
54
+ wheels/
55
+
56
+ .venv/
57
+ .venv
58
+ venv/
59
+ env/
60
+
61
+ .pytest_cache/
62
+ .ruff_cache/
63
+ .mypy_cache/
64
+ .pytype/
65
+ .hypothesis/
66
+ .coverage
67
+ .coverage.*
68
+ htmlcov/
69
+ coverage.xml
70
+ *.cover
71
+
72
+ .ipynb_checkpoints
73
+
74
+ ### IDE ###
75
+ .idea/
76
+ .vscode/
77
+ *.swp
78
+ *.swo
79
+ *~
80
+
81
+ ### Developer-local agent/tool artifacts ###
82
+ CLAUDE.md
83
+ .claude/
84
+ graphify-out/
85
+ scratch/
86
+
87
+ ### Generated artefacts ###
88
+ # Generated by git-cliff -- see cliff.toml.
89
+ CHANGELOG.md
xqvm_py-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,87 @@
1
+ Metadata-Version: 2.4
2
+ Name: xqvm_py
3
+ Version: 0.2.0
4
+ Summary: Python reference implementation of the X-Quadratic Virtual Machine (conformance oracle).
5
+ License: AGPL-3.0-or-later
6
+ Requires-Python: >=3.13
7
+ Requires-Dist: xqffi
8
+ Description-Content-Type: text/markdown
9
+
10
+ # xqvm_py
11
+
12
+ > **Status — transitional.** `xqvm_py` is the executable conformance
13
+ > oracle for the XQuad toolchain: every vector under
14
+ > [`../conformance/`](../conformance/) must produce identical
15
+ > observable state on both this Python reference and the Rust `xqvm`
16
+ > production runtime. The arrangement is explicitly transitional. Once
17
+ > the Rust runtime is fully battle-tested, `xqvm_py` may be **dropped
18
+ > entirely** (with conformance vectors graduating to "Rust must
19
+ > produce exactly these outputs" reference data) or **demoted to a
20
+ > prototyping sandbox** for trying out new opcodes / VM features in
21
+ > Python before they earn a spec slot. Do not build infrastructure
22
+ > that hard-depends on `xqvm_py`'s existence.
23
+
24
+ Python reference implementation of the X-Quadratic Virtual Machine.
25
+ See [`../spec/xqvm/SPEC.md`](../spec/xqvm/SPEC.md) for the authoritative
26
+ technical specification.
27
+
28
+ ## Scope
29
+
30
+ - Pure-Python **executor**, **state** model, **opcodes**, **xqmx**
31
+ (sparse quadratic matrix), **vector**, and **tracer**.
32
+ - **No** assembler or disassembler: the Rust `xqasm` crate is the only
33
+ implementation, exposed to Python via `xqffi.asm` (pyo3). Tests
34
+ and the CLI shim call
35
+ [`xqvm_py.program_from_xqasm`](program.py) to turn `.xqasm` text
36
+ into an executable `Program`.
37
+
38
+ ## Layout
39
+
40
+ ```text
41
+ xqvm_py/ <-- this directory IS the package (flat layout)
42
+ __init__.py re-exports the public surface (Executor, Program, …)
43
+ __main__.py entry point for `python -m xqvm_py`
44
+ executor.py fetch-decode-execute loop
45
+ state.py stack, registers, loop control, jump table
46
+ opcodes.py Opcode enum + operand metadata
47
+ program.py Program dataclass + program_from_xqasm()
48
+ xqmx.py sparse quadratic matrix (model + sample modes)
49
+ vector.py typed vec<int> / vec<xqmx>
50
+ errors.py typed runtime errors
51
+ tracer.py step-by-step execution tracer
52
+ cli/ python -m xqvm_py run ...
53
+ tests/ pytest suite (wheel-excluded)
54
+ ```
55
+
56
+ ## Quick start
57
+
58
+ From the xquad workspace root:
59
+
60
+ ```sh
61
+ uv sync # installs xqvm_py editable + xqffi via maturin
62
+ uv run pytest xqvm_py/tests # run the full test suite
63
+ echo "PUSH 5
64
+ PUSH 3
65
+ ADD
66
+ HALT" > /tmp/prog.xqasm
67
+ uv run python -m xqvm_py run /tmp/prog.xqasm # CLI shim
68
+ ```
69
+
70
+ Programmatic use:
71
+
72
+ ```python
73
+ from xqvm_py import Executor, program_from_xqasm
74
+
75
+ prog = program_from_xqasm("PUSH 10\nPUSH 5\nADD\nSTOW r0\nHALT\n")
76
+ executor = Executor()
77
+ executor.execute(prog)
78
+ print(executor.state.get_register(0)) # 15
79
+ ```
80
+
81
+ ## Conformance
82
+
83
+ Behavioural parity with the Rust `xqvm` crate is enforced by the
84
+ `xquad-conformance` Rust test harness at
85
+ [`../conformance/`](../conformance/). New VM semantics require a new
86
+ vector. Divergence between implementations fails CI with no "drift
87
+ tracking" middle ground.
@@ -0,0 +1,78 @@
1
+ # xqvm_py
2
+
3
+ > **Status — transitional.** `xqvm_py` is the executable conformance
4
+ > oracle for the XQuad toolchain: every vector under
5
+ > [`../conformance/`](../conformance/) must produce identical
6
+ > observable state on both this Python reference and the Rust `xqvm`
7
+ > production runtime. The arrangement is explicitly transitional. Once
8
+ > the Rust runtime is fully battle-tested, `xqvm_py` may be **dropped
9
+ > entirely** (with conformance vectors graduating to "Rust must
10
+ > produce exactly these outputs" reference data) or **demoted to a
11
+ > prototyping sandbox** for trying out new opcodes / VM features in
12
+ > Python before they earn a spec slot. Do not build infrastructure
13
+ > that hard-depends on `xqvm_py`'s existence.
14
+
15
+ Python reference implementation of the X-Quadratic Virtual Machine.
16
+ See [`../spec/xqvm/SPEC.md`](../spec/xqvm/SPEC.md) for the authoritative
17
+ technical specification.
18
+
19
+ ## Scope
20
+
21
+ - Pure-Python **executor**, **state** model, **opcodes**, **xqmx**
22
+ (sparse quadratic matrix), **vector**, and **tracer**.
23
+ - **No** assembler or disassembler: the Rust `xqasm` crate is the only
24
+ implementation, exposed to Python via `xqffi.asm` (pyo3). Tests
25
+ and the CLI shim call
26
+ [`xqvm_py.program_from_xqasm`](program.py) to turn `.xqasm` text
27
+ into an executable `Program`.
28
+
29
+ ## Layout
30
+
31
+ ```text
32
+ xqvm_py/ <-- this directory IS the package (flat layout)
33
+ __init__.py re-exports the public surface (Executor, Program, …)
34
+ __main__.py entry point for `python -m xqvm_py`
35
+ executor.py fetch-decode-execute loop
36
+ state.py stack, registers, loop control, jump table
37
+ opcodes.py Opcode enum + operand metadata
38
+ program.py Program dataclass + program_from_xqasm()
39
+ xqmx.py sparse quadratic matrix (model + sample modes)
40
+ vector.py typed vec<int> / vec<xqmx>
41
+ errors.py typed runtime errors
42
+ tracer.py step-by-step execution tracer
43
+ cli/ python -m xqvm_py run ...
44
+ tests/ pytest suite (wheel-excluded)
45
+ ```
46
+
47
+ ## Quick start
48
+
49
+ From the xquad workspace root:
50
+
51
+ ```sh
52
+ uv sync # installs xqvm_py editable + xqffi via maturin
53
+ uv run pytest xqvm_py/tests # run the full test suite
54
+ echo "PUSH 5
55
+ PUSH 3
56
+ ADD
57
+ HALT" > /tmp/prog.xqasm
58
+ uv run python -m xqvm_py run /tmp/prog.xqasm # CLI shim
59
+ ```
60
+
61
+ Programmatic use:
62
+
63
+ ```python
64
+ from xqvm_py import Executor, program_from_xqasm
65
+
66
+ prog = program_from_xqasm("PUSH 10\nPUSH 5\nADD\nSTOW r0\nHALT\n")
67
+ executor = Executor()
68
+ executor.execute(prog)
69
+ print(executor.state.get_register(0)) # 15
70
+ ```
71
+
72
+ ## Conformance
73
+
74
+ Behavioural parity with the Rust `xqvm` crate is enforced by the
75
+ `xquad-conformance` Rust test harness at
76
+ [`../conformance/`](../conformance/). New VM semantics require a new
77
+ vector. Divergence between implementations fails CI with no "drift
78
+ tracking" middle ground.
@@ -0,0 +1,121 @@
1
+ # Copyright (C) 2026 Postquant Labs Incorporated
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Affero General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Affero General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Affero General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+ #
16
+ # SPDX-License-Identifier: AGPL-3.0-or-later
17
+
18
+ """xqvm_py — Python reference VM for the XQuad toolchain.
19
+
20
+ Flat-layout package: all core modules (executor, state, opcodes, program,
21
+ vector, xqmx, errors, tracer) live directly under ``xqvm_py``. Re-exports
22
+ below mirror the old ``xqvm.core`` aggregation so existing imports of the
23
+ form ``from xqvm_py import Executor, Opcode, ...`` continue to resolve.
24
+
25
+ xqvm-py is transitional (the Rust ``xqvm`` crate is the production
26
+ runtime); see the package README for the full status note.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ __version__ = "0.2.0"
32
+
33
+ from .errors import (
34
+ DivisionByZero,
35
+ InvalidOpcode,
36
+ LoopError,
37
+ RegisterNotFound,
38
+ StackOverflow,
39
+ StackUnderflow,
40
+ TargetNotFound,
41
+ TypeMismatch,
42
+ XQMXModeError,
43
+ XQVMError,
44
+ )
45
+ from .executor import Executor
46
+ from .opcodes import Opcode, OpcodeMeta, OperandType
47
+ from .program import (
48
+ Instruction,
49
+ Program,
50
+ make_program,
51
+ program_from_bytecode,
52
+ program_from_xqasm,
53
+ run_program,
54
+ )
55
+ from .state import JumpControl, MachineState, Value
56
+ from .tracer import Tracer
57
+ from .vector import Vec, VecElem
58
+ from .xqmx import (
59
+ XQMX,
60
+ XQMXDomain,
61
+ XQMXMode,
62
+ col_find,
63
+ col_indices,
64
+ col_sum,
65
+ compute_energy,
66
+ expand_exclude,
67
+ expand_implies,
68
+ expand_onehot,
69
+ require_model_mode,
70
+ require_sample_mode,
71
+ row_find,
72
+ row_indices,
73
+ row_sum,
74
+ triu,
75
+ )
76
+
77
+ __all__ = [
78
+ "XQMX",
79
+ "DivisionByZero",
80
+ "Executor",
81
+ "Instruction",
82
+ "InvalidOpcode",
83
+ "JumpControl",
84
+ "LoopError",
85
+ "MachineState",
86
+ "Opcode",
87
+ "OpcodeMeta",
88
+ "OperandType",
89
+ "Program",
90
+ "RegisterNotFound",
91
+ "StackOverflow",
92
+ "StackUnderflow",
93
+ "TargetNotFound",
94
+ "Tracer",
95
+ "TypeMismatch",
96
+ "Value",
97
+ "Vec",
98
+ "VecElem",
99
+ "XQMXDomain",
100
+ "XQMXMode",
101
+ "XQMXModeError",
102
+ "XQVMError",
103
+ "__version__",
104
+ "col_find",
105
+ "col_indices",
106
+ "col_sum",
107
+ "compute_energy",
108
+ "expand_exclude",
109
+ "expand_implies",
110
+ "expand_onehot",
111
+ "make_program",
112
+ "program_from_bytecode",
113
+ "program_from_xqasm",
114
+ "require_model_mode",
115
+ "require_sample_mode",
116
+ "row_find",
117
+ "row_indices",
118
+ "row_sum",
119
+ "run_program",
120
+ "triu",
121
+ ]
@@ -0,0 +1,27 @@
1
+ # Copyright (C) 2026 Postquant Labs Incorporated
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Affero General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Affero General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Affero General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+ #
16
+ # SPDX-License-Identifier: AGPL-3.0-or-later
17
+
18
+ """Entry point for ``python -m xqvm_py``. Delegates to ``xqvm_py.cli``."""
19
+
20
+ from __future__ import annotations
21
+
22
+ import sys
23
+
24
+ from .cli import main
25
+
26
+ if __name__ == "__main__":
27
+ sys.exit(main())
@@ -0,0 +1,33 @@
1
+ # Copyright (C) 2026 Postquant Labs Incorporated
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Affero General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Affero General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Affero General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+ #
16
+ # SPDX-License-Identifier: AGPL-3.0-or-later
17
+
18
+ """Command-line interface for the XQVM reference implementation.
19
+
20
+ Thin shim around the in-process Executor API so that xqvm-py can be
21
+ driven with the same I/O contract as the Rust `xquad run` binary. Used
22
+ by the xquad-conformance harness to validate identical outputs across
23
+ implementations.
24
+
25
+ Invocation:
26
+
27
+ python -m xqvm_py run [--text] [--inputs inputs.json | --calldata 1,2,3]
28
+ [--outputs N] PROGRAM
29
+ """
30
+
31
+ from .run import main
32
+
33
+ __all__ = ["main"]
@@ -0,0 +1,140 @@
1
+ # Copyright (C) 2026 Postquant Labs Incorporated
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Affero General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Affero General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Affero General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+ #
16
+ # SPDX-License-Identifier: AGPL-3.0-or-later
17
+
18
+ """Implementation of the ``python -m xqvm_py run`` subcommand.
19
+
20
+ Supports both ``.xqasm`` source and ``.xqb`` bytecode input. Both paths
21
+ go through the Rust assembler via ``xqffi.asm``:
22
+
23
+ * ``.xqasm`` — text is passed directly to ``program_from_xqasm``.
24
+ * ``.xqb`` — bytes are first disassembled back to ``.xqasm`` text (via
25
+ ``xqffi.asm.disassemble``) and then parsed. The disassembly output
26
+ is not fully round-trippable as source today (it carries pc offsets
27
+ and ``.N`` labels); programmatic ``.xqb`` support will be revisited
28
+ alongside the Phase 5 xqffi work.
29
+
30
+ xqvm-py ships no Python assembler of its own — the previous
31
+ ``xqvm.assembler`` tree was removed in QUI-440.
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ import argparse
37
+ import json
38
+ import sys
39
+ from pathlib import Path
40
+ from typing import Any
41
+
42
+ from xqvm_py import Executor, Program, program_from_bytecode, program_from_xqasm
43
+
44
+
45
+ def _load_program(path: Path, *, text: bool) -> Program:
46
+ """Load ``.xqasm`` text or ``.xqb`` bytecode."""
47
+ if text or path.suffix == ".xqasm":
48
+ source = path.read_text(encoding="utf-8")
49
+ return program_from_xqasm(source, name=path.stem)
50
+ if path.suffix == ".xqb":
51
+ return program_from_bytecode(path.read_bytes(), name=path.stem)
52
+ raise SystemExit(
53
+ f"xqvm-py: unknown file extension {path.suffix!r}; pass a .xqasm "
54
+ "source (or use --text to force text interpretation)."
55
+ )
56
+
57
+
58
+ def _build_input_data(args: argparse.Namespace) -> dict[int, Any]:
59
+ if args.inputs is not None:
60
+ with Path(args.inputs).open(encoding="utf-8") as f:
61
+ inputs_doc = json.load(f)
62
+ calldata = inputs_doc.get("calldata", [])
63
+ else:
64
+ calldata = list(args.calldata)
65
+ return dict(enumerate(calldata))
66
+
67
+
68
+ def _serialise_value(value: Any) -> Any:
69
+ """Convert a register value to JSON-ready primitives.
70
+
71
+ Non-scalar types (``Vec``, ``XQMX``) fall back to ``repr()`` — this
72
+ is a defensive last resort; conformance vectors stick to integer
73
+ outputs.
74
+ """
75
+ if isinstance(value, int):
76
+ return value
77
+ return repr(value)
78
+
79
+
80
+ def main(argv: list[str] | None = None) -> int:
81
+ parser = argparse.ArgumentParser(
82
+ prog="python -m xqvm_py",
83
+ description="Run XQVM programs using the Python reference VM.",
84
+ )
85
+ sub = parser.add_subparsers(dest="command", required=True)
86
+
87
+ run = sub.add_parser("run", help="Execute an XQVM program")
88
+ run.add_argument("file", type=Path, help="Program file (.xqasm or .xqb)")
89
+ run.add_argument(
90
+ "--text",
91
+ action="store_true",
92
+ help="Force interpretation of FILE as assembly text.",
93
+ )
94
+ run.add_argument(
95
+ "--calldata",
96
+ type=lambda s: [int(x) for x in s.split(",") if x],
97
+ default=[],
98
+ help="Comma-separated i64 calldata values passed to INPUT slots.",
99
+ )
100
+ run.add_argument(
101
+ "--inputs",
102
+ type=Path,
103
+ help='JSON file with {"calldata": [...]} (overrides --calldata).',
104
+ )
105
+ run.add_argument(
106
+ "--outputs",
107
+ type=int,
108
+ default=16,
109
+ help="Number of output slots reserved (outputs beyond this index are null).",
110
+ )
111
+
112
+ args = parser.parse_args(argv)
113
+
114
+ if args.command != "run":
115
+ parser.error(f"unknown command: {args.command}")
116
+
117
+ program = _load_program(args.file, text=args.text)
118
+ input_data = _build_input_data(args)
119
+
120
+ executor = Executor()
121
+ output_map = executor.execute(program, input_data=input_data)
122
+
123
+ # Unset slots are reported as null (None) to match the spec's sparse-map
124
+ # semantics: output slots never written by the program are absent.
125
+ outputs: list[Any] = [
126
+ _serialise_value(output_map[slot]) if slot in output_map else None for slot in range(args.outputs)
127
+ ]
128
+ final_stack = [_serialise_value(v) for v in executor.state.stack]
129
+
130
+ json.dump(
131
+ {"outputs": outputs, "final_stack": final_stack, "steps": executor.steps},
132
+ sys.stdout,
133
+ separators=(",", ":"),
134
+ )
135
+ sys.stdout.write("\n")
136
+ return 0
137
+
138
+
139
+ if __name__ == "__main__":
140
+ sys.exit(main())
@@ -0,0 +1,121 @@
1
+ # Copyright (C) 2026 Postquant Labs Incorporated
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Affero General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU Affero General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Affero General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+ #
16
+ # SPDX-License-Identifier: AGPL-3.0-or-later
17
+
18
+ """
19
+ XQVM Exception Hierarchy
20
+ """
21
+
22
+ from typing import Any
23
+
24
+
25
+ class XQVMError(Exception):
26
+ """Base exception for all XQVM errors."""
27
+
28
+ pass
29
+
30
+
31
+ class StackUnderflow(XQVMError):
32
+ """Raised when attempting to pop from an empty stack."""
33
+
34
+ def __init__(self, required: int = 1, available: int = 0):
35
+ self.required = required
36
+ self.available = available
37
+ super().__init__(f"Stack underflow: need {required}, have {available}")
38
+
39
+
40
+ class StackOverflow(XQVMError):
41
+ """Raised when stack exceeds maximum capacity."""
42
+
43
+ def __init__(self, max_size: int):
44
+ self.max_size = max_size
45
+ super().__init__(f"Stack overflow: maximum size {max_size} exceeded")
46
+
47
+
48
+ class TypeMismatch(XQVMError):
49
+ """Raised when an operation receives an unexpected type."""
50
+
51
+ def __init__(self, expected: str, got: str, context: str = ""):
52
+ self.expected = expected
53
+ self.got = got
54
+ self.context = context
55
+
56
+ msg = f"Type mismatch: expected {expected}, got {got}"
57
+ if context:
58
+ msg += f" in {context}"
59
+
60
+ super().__init__(msg)
61
+
62
+
63
+ class RegisterNotFound(XQVMError):
64
+ """Raised when accessing a non-existent register slot."""
65
+
66
+ def __init__(self, slot: int):
67
+ self.slot = slot
68
+ super().__init__(f"Register not found: r{slot}")
69
+
70
+
71
+ class InvalidOpcode(XQVMError):
72
+ """Raised when encountering an unknown opcode."""
73
+
74
+ def __init__(self, opcode: Any):
75
+ self.opcode = opcode
76
+ super().__init__(f"Invalid opcode: {opcode}")
77
+
78
+
79
+ class DivisionByZero(XQVMError):
80
+ """Raised when attempting to divide by zero."""
81
+
82
+ def __init__(self):
83
+ super().__init__("Division by zero")
84
+
85
+
86
+ class ArithmeticOverflow(XQVMError):
87
+ """Raised when a value outside the signed 64-bit range would enter the VM."""
88
+
89
+ def __init__(self, value: int, context: str = ""):
90
+ self.value = value
91
+ self.context = context
92
+ msg = f"Arithmetic overflow: {value} is outside signed 64-bit range"
93
+ if context:
94
+ msg += f" ({context})"
95
+ super().__init__(msg)
96
+
97
+
98
+ class TargetNotFound(XQVMError):
99
+ """Raised when a jump target does not exist."""
100
+
101
+ def __init__(self, target_id: int):
102
+ self.target_id = target_id
103
+ super().__init__(f"Target not found: {target_id}")
104
+
105
+
106
+ class LoopError(XQVMError):
107
+ """Raised for loop-related errors (e.g., NEXT outside loop)."""
108
+
109
+ def __init__(self, message: str):
110
+ super().__init__(message)
111
+
112
+
113
+ class XQMXModeError(XQVMError):
114
+ """Raised when an XQMX operation is invalid for the current mode."""
115
+
116
+ def __init__(self, operation: str, mode: str, required_mode: str):
117
+ self.operation = operation
118
+ self.mode = mode
119
+ self.required_mode = required_mode
120
+
121
+ super().__init__(f"XQMX mode error: {operation} requires {required_mode} mode, but matrix is in {mode} mode")