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.
- xqvm_py-0.2.0/.gitignore +89 -0
- xqvm_py-0.2.0/PKG-INFO +87 -0
- xqvm_py-0.2.0/README.md +78 -0
- xqvm_py-0.2.0/__init__.py +121 -0
- xqvm_py-0.2.0/__main__.py +27 -0
- xqvm_py-0.2.0/cli/__init__.py +33 -0
- xqvm_py-0.2.0/cli/run.py +140 -0
- xqvm_py-0.2.0/errors.py +121 -0
- xqvm_py-0.2.0/executor.py +980 -0
- xqvm_py-0.2.0/opcodes.py +857 -0
- xqvm_py-0.2.0/program.py +180 -0
- xqvm_py-0.2.0/pyproject.toml +37 -0
- xqvm_py-0.2.0/state.py +281 -0
- xqvm_py-0.2.0/tests/__init__.py +18 -0
- xqvm_py-0.2.0/tests/conftest.py +72 -0
- xqvm_py-0.2.0/tests/test_executor.py +2757 -0
- xqvm_py-0.2.0/tests/test_opcodes.py +349 -0
- xqvm_py-0.2.0/tests/test_opcodes_yaml.py +113 -0
- xqvm_py-0.2.0/tests/test_program.py +128 -0
- xqvm_py-0.2.0/tests/test_state.py +531 -0
- xqvm_py-0.2.0/tests/test_tracer.py +313 -0
- xqvm_py-0.2.0/tests/test_vector.py +352 -0
- xqvm_py-0.2.0/tests/test_xqmx.py +629 -0
- xqvm_py-0.2.0/tracer.py +275 -0
- xqvm_py-0.2.0/vector.py +169 -0
- xqvm_py-0.2.0/xqmx.py +512 -0
xqvm_py-0.2.0/.gitignore
ADDED
|
@@ -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.
|
xqvm_py-0.2.0/README.md
ADDED
|
@@ -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"]
|
xqvm_py-0.2.0/cli/run.py
ADDED
|
@@ -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())
|
xqvm_py-0.2.0/errors.py
ADDED
|
@@ -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")
|