flux-vm-js 1.0.0
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.
- package/LICENSE +21 -0
- package/README.md +229 -0
- package/flux.js +641 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SuperInstance
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# FLUX.js — JavaScript Bytecode VM
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/flux-js)
|
|
4
|
+
[](https://github.com/SuperInstance/flux-js/actions/workflows/ci-node.yml)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
> **FLUX — Fluid Language Universal eXecution**
|
|
8
|
+
> Self-contained FLUX runtime for Node.js and browsers. ~400ns/iter via V8 JIT.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install flux-js
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
const { FluxVM, assemble } = require('flux-js');
|
|
20
|
+
|
|
21
|
+
const bc = assemble('MOVI R0, 42\nHALT');
|
|
22
|
+
const vm = new FluxVM(bc);
|
|
23
|
+
vm.execute();
|
|
24
|
+
console.log(vm.reg(0)); // 42
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## What It Does
|
|
30
|
+
|
|
31
|
+
FLUX.js brings the FLUX bytecode virtual machine to JavaScript runtimes — Node.js and browsers. It implements the same register-based ISA, opcode set, and A2A agent messaging protocol as the [Python](https://github.com/SuperInstance/flux-runtime) and [Rust](https://github.com/SuperInstance/flux-core) implementations, but leverages V8 JIT compilation to achieve ~400 nanoseconds per iteration on modern hardware.
|
|
32
|
+
|
|
33
|
+
The VM provides a deterministic, sandboxed execution environment for agent logic. Programs are assembled from text into compact bytecode, then executed with cycle budgets to prevent runaway computation. The included `Interpreter` class maps natural-language patterns ("factorial of 7", "sum 1 to 100") to bytecode, making it easy to build agent systems where computation is both human-readable and machine-verifiable. A built-in `Swarm` class enables multi-agent coordination with majority-vote consensus.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Architecture
|
|
38
|
+
|
|
39
|
+
FLUX.js is the **JavaScript implementation** of the FLUX bytecode runtime in the SuperInstance ecosystem. It shares the same ISA, A2A protocol, and vocabulary system as the Python and Rust implementations, making bytecode portable across all three runtimes.
|
|
40
|
+
|
|
41
|
+
### Features
|
|
42
|
+
|
|
43
|
+
- **VM** — 16 registers, all opcodes, cycle-limited execution
|
|
44
|
+
- **Assembler** — text → bytecode with labels and comments
|
|
45
|
+
- **Disassembler** — bytecode → human-readable listing
|
|
46
|
+
- **Vocabulary** — 10 natural-language patterns
|
|
47
|
+
- **A2A Agents** — multi-agent coordination with messaging
|
|
48
|
+
- **Swarm** — vote and consensus across agents
|
|
49
|
+
- **~400 ns/iter** on V8 JIT
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## API / Usage
|
|
54
|
+
|
|
55
|
+
### Assembly Syntax
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
MOVI R0, 42 # Load immediate
|
|
59
|
+
MOV R0, R1 # Copy register
|
|
60
|
+
IADD R0, R1, R2 # R0 = R1 + R2
|
|
61
|
+
ISUB R0, R1, R2 # R0 = R1 - R2
|
|
62
|
+
IMUL R0, R1, R2 # R0 = R1 * R2
|
|
63
|
+
IDIV R0, R1, R2 # R0 = R1 / R2
|
|
64
|
+
INC R0 # R0++
|
|
65
|
+
DEC R0 # R0--
|
|
66
|
+
CMP R0, R1 # Compare → flags
|
|
67
|
+
JNZ R0, offset # Jump if not zero
|
|
68
|
+
JZ R0, offset # Jump if zero
|
|
69
|
+
JMP offset # Unconditional jump
|
|
70
|
+
PUSH R0 / POP R0 # Stack operations
|
|
71
|
+
HALT # Stop
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Natural Language
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
const { Interpreter } = require('flux-js');
|
|
78
|
+
const interp = new Interpreter();
|
|
79
|
+
|
|
80
|
+
interp.run('factorial of 7'); // { value: 5040, cycles: 24 }
|
|
81
|
+
interp.run('sum 1 to 100'); // { value: 5050, cycles: 303 }
|
|
82
|
+
interp.run('power of 2 to 10'); // { value: 1024, cycles: 34 }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### A2A Swarm
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
const { A2AAgent, Swarm, assemble } = require('flux-js');
|
|
89
|
+
|
|
90
|
+
const bc = assemble('MOVI R0, 42\nHALT');
|
|
91
|
+
const swarm = new Swarm();
|
|
92
|
+
for (let i = 0; i < 5; i++) swarm.add(new A2AAgent(`a${i}`, bc));
|
|
93
|
+
swarm.tick();
|
|
94
|
+
console.log(swarm.consensus()); // 42
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Exports
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
const { FluxVM, assemble, disassemble, Interpreter, A2AAgent, Swarm } = require('flux-js');
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
| Export | Description |
|
|
104
|
+
|--------|-------------|
|
|
105
|
+
| `FluxVM` | Bytecode virtual machine |
|
|
106
|
+
| `assemble(text)` | Text assembly → Uint8Array |
|
|
107
|
+
| `disassemble(bc)` | Bytecode → string[] |
|
|
108
|
+
| `Interpreter` | Natural language → execution |
|
|
109
|
+
| `A2AAgent` | Single agent with inbox |
|
|
110
|
+
| `Swarm` | Multi-agent coordinator |
|
|
111
|
+
|
|
112
|
+
### Built-in Vocabulary
|
|
113
|
+
|
|
114
|
+
| Pattern | Description |
|
|
115
|
+
|---------|-------------|
|
|
116
|
+
| `compute X + Y` | Addition |
|
|
117
|
+
| `compute X - Y` | Subtraction |
|
|
118
|
+
| `compute X * Y` | Multiplication |
|
|
119
|
+
| `double X` | Double |
|
|
120
|
+
| `square X` | Square |
|
|
121
|
+
| `factorial of N` | N! |
|
|
122
|
+
| `fibonacci of N` | F(N) |
|
|
123
|
+
| `sum A to B` | Sum range |
|
|
124
|
+
| `power of BASE to EXP` | Exponentiation |
|
|
125
|
+
| `hello` | Returns 42 |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Testing
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npm install
|
|
133
|
+
npm test
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Contributing
|
|
139
|
+
|
|
140
|
+
Contributions are welcome! See the [SuperInstance Contributing Guide](https://github.com/SuperInstance/SuperInstance/blob/main/CONTRIBUTING.md).
|
|
141
|
+
|
|
142
|
+
1. Fork the repo
|
|
143
|
+
2. Create a feature branch
|
|
144
|
+
3. Add tests for new functionality
|
|
145
|
+
4. Ensure `npm test` passes
|
|
146
|
+
5. Submit a PR
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 📦 Related Packages
|
|
151
|
+
|
|
152
|
+
| Package | Language | Registry | Install |
|
|
153
|
+
|---------|----------|----------|---------|
|
|
154
|
+
| **[flux-vm](https://pypi.org/project/flux-vm/)** | Python | PyPI | `pip install flux-vm` |
|
|
155
|
+
| **[fluxvm](https://crates.io/crates/fluxvm)** | Rust | crates.io | `cargo add fluxvm` |
|
|
156
|
+
| **[flux-js](https://www.npmjs.com/package/flux-js)** | JavaScript | npm | `npm install flux-js` |
|
|
157
|
+
|
|
158
|
+
Additional implementations: [C](https://github.com/SuperInstance/flux-runtime-c) · [Zig](https://github.com/SuperInstance/flux-zig) · [Go](https://github.com/SuperInstance/flux-swarm) · [Java](https://github.com/SuperInstance/flux-java) · [WASM](https://github.com/SuperInstance/flux-wasm) · [CUDA](https://github.com/SuperInstance/flux-cuda)
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Ecosystem
|
|
163
|
+
|
|
164
|
+
This repo is part of the **SuperInstance** flagship ecosystem — agent-first computation, constraint theory, and self-improving runtimes.
|
|
165
|
+
|
|
166
|
+
### FLUX Runtime Family
|
|
167
|
+
|
|
168
|
+
| Repo | Language | Description |
|
|
169
|
+
|------|----------|-------------|
|
|
170
|
+
| [flux-runtime](https://github.com/SuperInstance/flux-runtime) | Python | Full FLUX runtime: markdown→bytecode, 2037 tests, zero deps |
|
|
171
|
+
| [flux-core](https://github.com/SuperInstance/flux-core) | Rust | Register-based bytecode VM, deterministic agent computation |
|
|
172
|
+
| [flux-js](https://github.com/SuperInstance/flux-js) | JavaScript | FLUX VM for Node.js and browsers, ~400ns/iter |
|
|
173
|
+
| [flux-compiler](https://github.com/SuperInstance/flux-compiler) | Rust/Python | Formal-methods compiler for safety-critical codegen |
|
|
174
|
+
| [flux-vm](https://github.com/SuperInstance/flux-vm) | Rust | Stack-based constraint-checking VM, 50 opcodes, Turing-incomplete |
|
|
175
|
+
|
|
176
|
+
### PLATO Engine Family
|
|
177
|
+
|
|
178
|
+
| Repo | Language | Description |
|
|
179
|
+
|------|----------|-------------|
|
|
180
|
+
| [plato-server](https://github.com/SuperInstance/plato-server) | Python | Knowledge tiles, fleet sync via Matrix, HTTP API |
|
|
181
|
+
| [plato-engine-block](https://github.com/SuperInstance/plato-engine-block) | Rust | Original room runtime: no_std + alloc, builder pattern |
|
|
182
|
+
| [plato-engine-block-c](https://github.com/SuperInstance/plato-engine-block-c) | C99 | Embedded reference: zero heap alloc, bare-metal portable |
|
|
183
|
+
| [plato-engine-block-elixir](https://github.com/SuperInstance/plato-engine-block-elixir) | Elixir | BEAM supervision trees, fault tolerance, hot reload |
|
|
184
|
+
| [plato-runtime-kernel](https://github.com/SuperInstance/plato-runtime-kernel) | Rust | Spatial model: tensor grid, batons, assertion traps |
|
|
185
|
+
|
|
186
|
+
### Constraint / Theory Family
|
|
187
|
+
|
|
188
|
+
| Repo | Language | Description |
|
|
189
|
+
|------|----------|-------------|
|
|
190
|
+
| [categorical-agents](https://github.com/SuperInstance/categorical-agents) | Rust | Category theory for agent composition (functors, naturality) |
|
|
191
|
+
| [cuda-constraint-engine](https://github.com/SuperInstance/cuda-constraint-engine) | CUDA/C | GPU constraint checking at 1B+ constraints/sec |
|
|
192
|
+
| [grand-pattern-rs](https://github.com/SuperInstance/grand-pattern-rs) | Rust | Fibonacci dual-direction cellular graph architecture |
|
|
193
|
+
| [lau-hodge-theory](https://github.com/SuperInstance/lau-hodge-theory) | Rust | Hodge decomposition, Betti numbers, spectral sequences |
|
|
194
|
+
| [ternary-science](https://github.com/SuperInstance/ternary-science) | Rust | Experimental evidence for ternary intelligence, 5 conservation laws |
|
|
195
|
+
|
|
196
|
+
### Agent / Infrastructure Family
|
|
197
|
+
|
|
198
|
+
| Repo | Language | Description |
|
|
199
|
+
|------|----------|-------------|
|
|
200
|
+
| [construct-core](https://github.com/SuperInstance/construct-core) | Rust | Layered trait system: bare-metal → alloc → async agent runtime |
|
|
201
|
+
| [crab](https://github.com/SuperInstance/crab) | Bash | Agent shell for repo entry/leave (MUD-room metaphor) |
|
|
202
|
+
| [exocortex](https://github.com/SuperInstance/exocortex) | Rust | Persistent cognitive substrate, S3-compatible memory |
|
|
203
|
+
| [git-agent](https://github.com/SuperInstance/git-agent) | Python | The repo IS the agent — autonomous lifecycle via Git |
|
|
204
|
+
| [capitaine-1](https://github.com/SuperInstance/capitaine-1) | TypeScript | Git-native repo-agent, Cloudflare Workers heartbeat |
|
|
205
|
+
| [codespace-edge-rd](https://github.com/SuperInstance/codespace-edge-rd) | Research | Codespace→Edge agent lifecycle and yoke transfer protocols |
|
|
206
|
+
| [git-agent-codespace](https://github.com/SuperInstance/git-agent-codespace) | DevContainer | One-click Codespace template for Git-Agent runtimes |
|
|
207
|
+
|
|
208
|
+
### Registries
|
|
209
|
+
|
|
210
|
+
| Registry | Package | Install |
|
|
211
|
+
|----------|---------|---------|
|
|
212
|
+
| **PyPI** | `flux-vm` | `pip install flux-vm` |
|
|
213
|
+
| **crates.io** | `fluxvm` | `cargo add fluxvm` |
|
|
214
|
+
| **npm** | `flux-js` | `npm install flux-js` |
|
|
215
|
+
|
|
216
|
+
### Philosophy & Architecture
|
|
217
|
+
|
|
218
|
+
- 📖 [AI-Writings](https://github.com/SuperInstance/AI-Writings) — Philosophy, essays, and design rationale
|
|
219
|
+
- 📦 [PACKAGES.md](https://github.com/SuperInstance/SuperInstance/blob/main/PACKAGES.md) — Full package index
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
MIT
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
*Same bytecode, different shells.* 🦀
|
package/flux.js
ADDED
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FLUX.js — JavaScript Bytecode VM
|
|
3
|
+
*
|
|
4
|
+
* Self-contained FLUX runtime for Node.js and browsers.
|
|
5
|
+
* Features: VM, assembler, disassembler, vocabulary, A2A agents, swarm.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const { FluxVM, assemble, Interpreter } = require('./flux.js');
|
|
9
|
+
* const bc = assemble('MOVI R0, 7\nMOVI R1, 1\nIMUL R1, R1, R0\nDEC R0\nJNZ R0, -10\nHALT');
|
|
10
|
+
* const vm = new FluxVM(bc);
|
|
11
|
+
* vm.execute();
|
|
12
|
+
* console.log(vm.reg(1)); // 5040
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
// ─── Opcodes ──────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
const OP = {
|
|
20
|
+
NOP:0x00, MOV:0x01, LOAD:0x02, STORE:0x03,
|
|
21
|
+
JMP:0x04, JZ:0x05, JNZ:0x06, CALL:0x07,
|
|
22
|
+
IADD:0x08, ISUB:0x09, IMUL:0x0A, IDIV:0x0B,
|
|
23
|
+
IMOD:0x0C, INEG:0x0D, INC:0x0E, DEC:0x0F,
|
|
24
|
+
IAND:0x10, IOR:0x11, IXOR:0x12, INOT:0x13,
|
|
25
|
+
ISHL:0x14, ISHR:0x15,
|
|
26
|
+
PUSH:0x20, POP:0x21, DUP:0x22,
|
|
27
|
+
RET:0x28, MOVI:0x2B, CMP:0x2D,
|
|
28
|
+
JE:0x2E, JNE:0x2F,
|
|
29
|
+
FADD:0x40, FSUB:0x41, FMUL:0x42, FDIV:0x43,
|
|
30
|
+
TELL:0x60, ASK:0x61, DELEGATE:0x62, BROADCAST:0x66,
|
|
31
|
+
HALT:0x80, YIELD:0x81
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const MNEMONIC = Object.fromEntries(Object.entries(OP).map(([k,v])=>[v,k]));
|
|
35
|
+
|
|
36
|
+
// ─── VM ───────────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
class FluxVM {
|
|
39
|
+
/**
|
|
40
|
+
* Create a FLUX bytecode VM.
|
|
41
|
+
* @param {Uint8Array|ArrayBuffer} bytecode
|
|
42
|
+
* @param {number} [maxCycles=10_000_000]
|
|
43
|
+
*/
|
|
44
|
+
constructor(bytecode, maxCycles = 10_000_000) {
|
|
45
|
+
this.gp = new Int32Array(16); // 16 general-purpose registers
|
|
46
|
+
this.pc = 0;
|
|
47
|
+
this.halted = false;
|
|
48
|
+
this.cycles = 0;
|
|
49
|
+
this.stack = [];
|
|
50
|
+
this.bc = bytecode instanceof Uint8Array ? bytecode : new Uint8Array(bytecode);
|
|
51
|
+
this.maxCycles = maxCycles;
|
|
52
|
+
this.error = null;
|
|
53
|
+
this._flagZero = false;
|
|
54
|
+
this._flagSign = false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Read register */
|
|
58
|
+
reg(idx) { return this.gp[idx] || 0; }
|
|
59
|
+
|
|
60
|
+
/** Read unsigned byte */
|
|
61
|
+
_u8() { return this.bc[this.pc++]; }
|
|
62
|
+
|
|
63
|
+
/** Update condition flags based on an arithmetic result (matches Python/Rust) */
|
|
64
|
+
_setFlags(result) {
|
|
65
|
+
this._flagZero = (result === 0);
|
|
66
|
+
this._flagSign = (result < 0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Read signed 16-bit */
|
|
70
|
+
_i16() {
|
|
71
|
+
const lo = this.bc[this.pc++];
|
|
72
|
+
const hi = this.bc[this.pc++];
|
|
73
|
+
const val = lo | (hi << 8);
|
|
74
|
+
return val >= 32768 ? val - 65536 : val;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Execute until HALT or maxCycles.
|
|
79
|
+
* @returns {FluxVM} this (for chaining)
|
|
80
|
+
*/
|
|
81
|
+
execute() {
|
|
82
|
+
this.halted = false;
|
|
83
|
+
this.cycles = 0;
|
|
84
|
+
this.error = null;
|
|
85
|
+
try {
|
|
86
|
+
while (!this.halted && this.pc < this.bc.length && this.cycles < this.maxCycles) {
|
|
87
|
+
const op = this._u8();
|
|
88
|
+
this.cycles++;
|
|
89
|
+
switch (op) {
|
|
90
|
+
case 0x80: this.halted = true; break;
|
|
91
|
+
case 0x00: break; // NOP
|
|
92
|
+
case 0x01: { const d=this._u8(),s=this._u8(); this.gp[d]=this.gp[s]; break; } // MOV
|
|
93
|
+
case 0x2B: { const d=this._u8(); this.gp[d]=this._i16(); break; } // MOVI
|
|
94
|
+
// 3-operand arithmetic: [op][rd][rs1][rs2]
|
|
95
|
+
case 0x08: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=(this.gp[a]+this.gp[b])|0; this.gp[d]=r; this._setFlags(r); break; } // IADD
|
|
96
|
+
case 0x09: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=(this.gp[a]-this.gp[b])|0; this.gp[d]=r; this._setFlags(r); break; } // ISUB
|
|
97
|
+
case 0x0A: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=(this.gp[a]*this.gp[b])|0; this.gp[d]=r; this._setFlags(r); break; } // IMUL
|
|
98
|
+
case 0x0B: { const d=this._u8(),a=this._u8(),b=this._u8();
|
|
99
|
+
if(this.gp[b]===0) throw new Error('Division by zero');
|
|
100
|
+
this.gp[d]=(this.gp[a]/this.gp[b])|0; this._setFlags(this.gp[d]); break; } // IDIV
|
|
101
|
+
case 0x0C: { const d=this._u8(),a=this._u8(),b=this._u8();
|
|
102
|
+
if(this.gp[b]===0) throw new Error('Modulo by zero');
|
|
103
|
+
this.gp[d]=(this.gp[a]%this.gp[b])|0; this._setFlags(this.gp[d]); break; } // IMOD
|
|
104
|
+
case 0x0E: { const r=this._u8(); const v=(this.gp[r]+1)|0; this.gp[r]=v; this._setFlags(v); break; } // INC
|
|
105
|
+
case 0x0F: { const r=this._u8(); const v=(this.gp[r]-1)|0; this.gp[r]=v; this._setFlags(v); break; } // DEC
|
|
106
|
+
// FIX: PUSH/POP now use 0x20/0x21 (was 0x10/0x11)
|
|
107
|
+
case 0x20: this.stack.push(this.gp[this._u8()]); break; // PUSH
|
|
108
|
+
case 0x21: this.gp[this._u8()]=this.stack.pop()||0; break; // POP
|
|
109
|
+
case 0x22: { if(this.stack.length>0){ const v=this.stack[this.stack.length-1]; this.stack.push(v);} break; } // DUP
|
|
110
|
+
case 0x04: { const _r=this._u8(),off=this._i16(); this.pc=(this.pc+off)&0xFFFFFFFF; break; } // JMP
|
|
111
|
+
case 0x05: { const r=this._u8(),off=this._i16(); if(this.gp[r]===0) this.pc=(this.pc+off)&0xFFFFFFFF; break; } // JZ
|
|
112
|
+
case 0x06: { const r=this._u8(),off=this._i16(); if(this.gp[r]!==0) this.pc=(this.pc+off)&0xFFFFFFFF; break; } // JNZ
|
|
113
|
+
case 0x07: { const _r=this._u8(),off=this._i16(); this.stack.push(this.pc); this.pc=(this.pc+off)&0xFFFFFFFF; break; } // CALL
|
|
114
|
+
case 0x28: { const _r=this._u8(),_p=this._u8(); if(this.stack.length>0){ this.pc=this.stack.pop(); } break; } // RET
|
|
115
|
+
case 0x2D: { const a=this._u8(),b=this._u8();
|
|
116
|
+
this._flagZero = this.gp[a]===this.gp[b];
|
|
117
|
+
this._flagSign = this.gp[a]<this.gp[b];
|
|
118
|
+
break; } // CMP
|
|
119
|
+
case 0x2E: { const _r=this._u8(),off=this._i16(); if(this._flagZero) this.pc=(this.pc+off)&0xFFFFFFFF; break; } // JE
|
|
120
|
+
case 0x2F: { const _r=this._u8(),off=this._i16(); if(!this._flagZero) this.pc=(this.pc+off)&0xFFFFFFFF; break; } // JNE
|
|
121
|
+
case 0x10: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=this.gp[a]&this.gp[b]; this.gp[d]=r; this._setFlags(r); break; } // IAND
|
|
122
|
+
case 0x11: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=this.gp[a]|this.gp[b]; this.gp[d]=r; this._setFlags(r); break; } // IOR
|
|
123
|
+
case 0x12: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=this.gp[a]^this.gp[b]; this.gp[d]=r; this._setFlags(r); break; } // IXOR
|
|
124
|
+
case 0x13: { const d=this._u8(),s=this._u8(); const r=~this.gp[s]; this.gp[d]=r; this._setFlags(r); break; } // INOT
|
|
125
|
+
case 0x14: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=this.gp[a]<<(this.gp[b]&0x1f); this.gp[d]=r; this._setFlags(r); break; } // ISHL
|
|
126
|
+
case 0x15: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=this.gp[a]>>(this.gp[b]&0x1f); this.gp[d]=r; this._setFlags(r); break; } // ISHR
|
|
127
|
+
case 0x81: break; // YIELD
|
|
128
|
+
// A2A stubs (Format G: [op][len:u16][data:len]) — experimental, consistent with Python/Rust
|
|
129
|
+
case 0x60: case 0x61: case 0x62: case 0x66: {
|
|
130
|
+
const _len = this._u8() | (this._u8() << 8);
|
|
131
|
+
this.pc += _len; // skip variable data
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
default: throw new Error(`Unknown opcode: 0x${op.toString(16).padStart(2,'0')}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
} catch (e) { this.error = e.message; }
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Pretty-print register state */
|
|
142
|
+
dump() {
|
|
143
|
+
const regs = [];
|
|
144
|
+
for (let i = 0; i < 16; i++) if (this.gp[i] !== 0) regs.push(`R${i}=${this.gp[i]}`);
|
|
145
|
+
return `PC=${this.pc} cycles=${this.cycles} halted=${this.halted} [${regs.join(' ')}]`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ─── Assembler ────────────────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Assemble FLUX text to bytecode.
|
|
153
|
+
* Supports labels (label:), comments (// or ;), all instruction formats.
|
|
154
|
+
* @param {string} text
|
|
155
|
+
* @returns {Uint8Array}
|
|
156
|
+
*/
|
|
157
|
+
function assemble(text) {
|
|
158
|
+
const labels = {};
|
|
159
|
+
const instructions = [];
|
|
160
|
+
|
|
161
|
+
// Pass 1: collect labels and calculate sizes
|
|
162
|
+
let pc = 0;
|
|
163
|
+
for (const rawLine of text.split('\n')) {
|
|
164
|
+
let line = rawLine.trim();
|
|
165
|
+
if (!line || line.startsWith('//') || line.startsWith(';')) continue;
|
|
166
|
+
if (line.includes(':')) {
|
|
167
|
+
const [labelPart, ...rest] = line.split(':');
|
|
168
|
+
labels[labelPart.trim()] = pc;
|
|
169
|
+
line = rest.join(':').trim();
|
|
170
|
+
if (!line) continue;
|
|
171
|
+
}
|
|
172
|
+
const parts = line.replace(/,/g, ' ').split(/\s+/);
|
|
173
|
+
const mn = parts[0].toUpperCase();
|
|
174
|
+
instructions.push({ parts, pc });
|
|
175
|
+
|
|
176
|
+
// Size calculation
|
|
177
|
+
if (mn in OP) {
|
|
178
|
+
if (['HALT','NOP','DUP','YIELD'].includes(mn)) pc += 1;
|
|
179
|
+
else if (['INC','DEC','PUSH','POP','INEG','INOT'].includes(mn)) pc += 2;
|
|
180
|
+
else if (['MOV','LOAD','STORE','CMP'].includes(mn)) pc += 3;
|
|
181
|
+
else if (['IADD','ISUB','IMUL','IDIV','IMOD','IAND','IOR','IXOR','ISHL','ISHR'].includes(mn)) pc += 4;
|
|
182
|
+
else if (['MOVI','JMP','JZ','JNZ','CALL','JE','JNE'].includes(mn)) pc += 4;
|
|
183
|
+
else if (mn === 'RET') pc += 3;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Pass 2: emit bytecode
|
|
188
|
+
const bc = [];
|
|
189
|
+
const resolveValue = (token, currentPc) => {
|
|
190
|
+
token = token.trim();
|
|
191
|
+
const num = parseInt(token);
|
|
192
|
+
if (!isNaN(num)) return num;
|
|
193
|
+
if (token in labels) return labels[token] - currentPc;
|
|
194
|
+
throw new Error(`Cannot resolve: ${token}`);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
for (const { parts, pc: instPc } of instructions) {
|
|
198
|
+
const mn = parts[0].toUpperCase();
|
|
199
|
+
if (!(mn in OP)) continue;
|
|
200
|
+
const op = OP[mn];
|
|
201
|
+
|
|
202
|
+
if (['HALT','NOP','DUP','YIELD'].includes(mn)) {
|
|
203
|
+
bc.push(op);
|
|
204
|
+
} else if (['INC','DEC','PUSH','POP'].includes(mn)) {
|
|
205
|
+
bc.push(op, parseInt(parts[1].slice(1)));
|
|
206
|
+
} else if (['MOV','LOAD','STORE','CMP'].includes(mn)) {
|
|
207
|
+
bc.push(op, parseInt(parts[1].slice(1)), parseInt(parts[2].slice(1)));
|
|
208
|
+
} else if (['IADD','ISUB','IMUL','IDIV','IMOD','IAND','IOR','IXOR','ISHL','ISHR'].includes(mn)) {
|
|
209
|
+
bc.push(op, parseInt(parts[1].slice(1)), parseInt(parts[2].slice(1)),
|
|
210
|
+
parts.length > 3 ? parseInt(parts[3].slice(1)) : parseInt(parts[1].slice(1)));
|
|
211
|
+
} else if (mn === 'MOVI') {
|
|
212
|
+
bc.push(op, parseInt(parts[1].slice(1)));
|
|
213
|
+
const v = resolveValue(parts[2], bc.length + 1);
|
|
214
|
+
bc.push(v & 0xFF, (v >> 8) & 0xFF);
|
|
215
|
+
} else if (['JMP','JZ','JNZ','CALL','JE','JNE'].includes(mn)) {
|
|
216
|
+
bc.push(op, parseInt(parts[1] ? parts[1].slice(1) : '0') || 0);
|
|
217
|
+
const labelOrOff = parts[2] || parts[1];
|
|
218
|
+
const v = isNaN(parseInt(labelOrOff)) ? resolveValue(labelOrOff, bc.length + 2) : parseInt(labelOrOff);
|
|
219
|
+
bc.push(v & 0xFF, (v >> 8) & 0xFF);
|
|
220
|
+
} else if (mn === 'RET') {
|
|
221
|
+
bc.push(op, 0, 0);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return new Uint8Array(bc);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ─── Disassembler ─────────────────────────────────────────────────────────
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Disassemble bytecode to human-readable text.
|
|
232
|
+
* @param {Uint8Array} bytecode
|
|
233
|
+
* @returns {string[]}
|
|
234
|
+
*/
|
|
235
|
+
function disassemble(bytecode) {
|
|
236
|
+
const lines = [];
|
|
237
|
+
let pc = 0;
|
|
238
|
+
const bc = bytecode instanceof Uint8Array ? bytecode : new Uint8Array(bytecode);
|
|
239
|
+
|
|
240
|
+
while (pc < bc.length) {
|
|
241
|
+
const addr = pc;
|
|
242
|
+
const op = bc[pc++];
|
|
243
|
+
const mn = MNEMONIC[op] || `??? (0x${op.toString(16).padStart(2,'0')})`;
|
|
244
|
+
|
|
245
|
+
if ([0x80, 0x00, 0x22, 0x81].includes(op)) {
|
|
246
|
+
lines.push(`${addr.toString(16).padStart(4,'0')}: ${mn}`);
|
|
247
|
+
} else if ([0x0E, 0x0F, 0x20, 0x21].includes(op)) {
|
|
248
|
+
lines.push(`${addr.toString(16).padStart(4,'0')}: ${mn} R${bc[pc++]}`);
|
|
249
|
+
} else if ([0x01, 0x02, 0x03, 0x2D].includes(op)) {
|
|
250
|
+
lines.push(`${addr.toString(16).padStart(4,'0')}: ${mn} R${bc[pc++]}, R${bc[pc++]}`);
|
|
251
|
+
} else if ([0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x10, 0x11, 0x12, 0x14, 0x15].includes(op)) {
|
|
252
|
+
lines.push(`${addr.toString(16).padStart(4,'0')}: ${mn} R${bc[pc++]}, R${bc[pc++]}, R${bc[pc++]}`);
|
|
253
|
+
} else if ([0x2B, 0x04, 0x05, 0x06, 0x07, 0x2E, 0x2F].includes(op)) {
|
|
254
|
+
const rd = bc[pc++];
|
|
255
|
+
const val = bc[pc] | (bc[pc+1] << 8) | (bc[pc+1] >= 128 ? -65536 : 0);
|
|
256
|
+
pc += 2;
|
|
257
|
+
lines.push(`${addr.toString(16).padStart(4,'0')}: ${mn} R${rd}, ${val}`);
|
|
258
|
+
} else if (op === 0x28) {
|
|
259
|
+
pc += 2; // RET has 2 padding bytes
|
|
260
|
+
lines.push(`${addr.toString(16).padStart(4,'0')}: ${mn}`);
|
|
261
|
+
} else {
|
|
262
|
+
lines.push(`${addr.toString(16).padStart(4,'0')}: ${mn}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return lines;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ─── Vocabulary ───────────────────────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
class VocabEntry {
|
|
271
|
+
/**
|
|
272
|
+
* @param {RegExp|string} pattern
|
|
273
|
+
* @param {string|function} asmTemplate
|
|
274
|
+
* @param {string} name
|
|
275
|
+
* @param {number} resultReg
|
|
276
|
+
*/
|
|
277
|
+
constructor(pattern, asmTemplate, name, resultReg = 0) {
|
|
278
|
+
this.pattern = typeof pattern === 'string' ? new RegExp(pattern, 'i') : pattern;
|
|
279
|
+
this.asmTemplate = asmTemplate;
|
|
280
|
+
this.name = name;
|
|
281
|
+
this.resultReg = resultReg;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Match text against pattern
|
|
286
|
+
* @param {string} text
|
|
287
|
+
* @returns {Array|null} captured groups or null
|
|
288
|
+
*/
|
|
289
|
+
match(text) {
|
|
290
|
+
const m = text.match(this.pattern);
|
|
291
|
+
return m ? m.slice(1) : null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
class Vocabulary {
|
|
296
|
+
constructor() {
|
|
297
|
+
this.entries = [];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Add a vocabulary entry
|
|
302
|
+
* @param {VocabEntry} entry
|
|
303
|
+
*/
|
|
304
|
+
register(entry) {
|
|
305
|
+
this.entries.push(entry);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Find first matching entry for text
|
|
310
|
+
* @param {string} text
|
|
311
|
+
* @returns {VocabEntry|null}
|
|
312
|
+
*/
|
|
313
|
+
findMatch(text) {
|
|
314
|
+
for (const entry of this.entries) {
|
|
315
|
+
if (entry.match(text) !== null) {
|
|
316
|
+
return entry;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
class VocabInterpreter {
|
|
324
|
+
constructor() {
|
|
325
|
+
this.vocab = new Vocabulary();
|
|
326
|
+
this.assembler = assemble; // Use existing assemble function
|
|
327
|
+
this.vm = null;
|
|
328
|
+
|
|
329
|
+
// Register built-in entries
|
|
330
|
+
this.vocab.register(new VocabEntry(
|
|
331
|
+
/^compute\s+(\d+)\s*\+\s*(\d+)$/i,
|
|
332
|
+
(a, b) => `MOVI R0, ${a}\nMOVI R1, ${b}\nIADD R0, R0, R1\nHALT`,
|
|
333
|
+
'add',
|
|
334
|
+
0
|
|
335
|
+
));
|
|
336
|
+
this.vocab.register(new VocabEntry(
|
|
337
|
+
/^compute\s+(\d+)\s*\*\s*(\d+)$/i,
|
|
338
|
+
(a, b) => `MOVI R0, ${a}\nMOVI R1, ${b}\nIMUL R0, R0, R1\nHALT`,
|
|
339
|
+
'mul',
|
|
340
|
+
0
|
|
341
|
+
));
|
|
342
|
+
this.vocab.register(new VocabEntry(
|
|
343
|
+
/^factorial\s+of\s+(\d+)$/i,
|
|
344
|
+
(n) => `MOVI R0, ${n}\nMOVI R1, 1\nIMUL R1, R1, R0\nDEC R0\nJNZ R0, -10\nHALT`,
|
|
345
|
+
'factorial',
|
|
346
|
+
1
|
|
347
|
+
));
|
|
348
|
+
this.vocab.register(new VocabEntry(
|
|
349
|
+
/^double\s+(\d+)$/i,
|
|
350
|
+
(n) => `MOVI R0, ${n}\nIADD R0, R0, R0\nHALT`,
|
|
351
|
+
'double',
|
|
352
|
+
0
|
|
353
|
+
));
|
|
354
|
+
this.vocab.register(new VocabEntry(
|
|
355
|
+
/^square\s+(\d+)$/i,
|
|
356
|
+
(n) => `MOVI R0, ${n}\nIMUL R0, R0, R0\nHALT`,
|
|
357
|
+
'square',
|
|
358
|
+
0
|
|
359
|
+
));
|
|
360
|
+
this.vocab.register(new VocabEntry(
|
|
361
|
+
/^hello$/i,
|
|
362
|
+
() => `MOVI R0, 42\nHALT`,
|
|
363
|
+
'hello',
|
|
364
|
+
0
|
|
365
|
+
));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Run text through vocabulary interpreter
|
|
370
|
+
* @param {string} text
|
|
371
|
+
* @returns {{ value: number|null, message: string, cycles: number }}
|
|
372
|
+
*/
|
|
373
|
+
run(text) {
|
|
374
|
+
const trimmed = text.trim();
|
|
375
|
+
const entry = this.vocab.findMatch(trimmed);
|
|
376
|
+
|
|
377
|
+
if (!entry) {
|
|
378
|
+
return { value: null, message: `No vocabulary match for: ${trimmed.slice(0, 80)}`, cycles: 0 };
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const args = entry.match(trimmed).map(Number);
|
|
382
|
+
let asmText;
|
|
383
|
+
if (typeof entry.asmTemplate === 'function') {
|
|
384
|
+
asmText = entry.asmTemplate(...args);
|
|
385
|
+
} else {
|
|
386
|
+
asmText = entry.asmTemplate;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
const bc = this.assembler(asmText);
|
|
391
|
+
this.vm = new FluxVM(bc);
|
|
392
|
+
this.vm.execute();
|
|
393
|
+
return {
|
|
394
|
+
value: this.vm.reg(entry.resultReg),
|
|
395
|
+
message: `OK (${this.vm.cycles} cycles)`,
|
|
396
|
+
cycles: this.vm.cycles
|
|
397
|
+
};
|
|
398
|
+
} catch (e) {
|
|
399
|
+
return { value: null, message: `Error: ${e.message}`, cycles: 0 };
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const VOCAB = [
|
|
405
|
+
{ pattern: /^compute\s+(\d+)\s*\+\s*(\d+)$/i, name: 'add',
|
|
406
|
+
asm: (a,b) => `MOVI R0, ${a}\nMOVI R1, ${b}\nIADD R0, R0, R1\nHALT`, reg: 0 },
|
|
407
|
+
{ pattern: /^compute\s+(\d+)\s*-\s*(\d+)$/i, name: 'sub',
|
|
408
|
+
asm: (a,b) => `MOVI R0, ${a}\nMOVI R1, ${b}\nISUB R0, R0, R1\nHALT`, reg: 0 },
|
|
409
|
+
{ pattern: /^compute\s+(\d+)\s*\*\s*(\d+)$/i, name: 'mul',
|
|
410
|
+
asm: (a,b) => `MOVI R0, ${a}\nMOVI R1, ${b}\nIMUL R0, R0, R1\nHALT`, reg: 0 },
|
|
411
|
+
{ pattern: /^double\s+(\d+)$/i, name: 'double',
|
|
412
|
+
asm: (a) => `MOVI R0, ${a}\nIADD R0, R0, R0\nHALT`, reg: 0 },
|
|
413
|
+
{ pattern: /^square\s+(\d+)$/i, name: 'square',
|
|
414
|
+
asm: (a) => `MOVI R0, ${a}\nIMUL R0, R0, R0\nHALT`, reg: 0 },
|
|
415
|
+
{ pattern: /^factorial\s+of\s+(\d+)$/i, name: 'factorial',
|
|
416
|
+
asm: (n) => `MOVI R0, ${n}\nMOVI R1, 1\nIMUL R1, R1, R0\nDEC R0\nJNZ R0, -10\nHALT`, reg: 1 },
|
|
417
|
+
{ pattern: /^fibonacci\s+of\s+(\d+)$/i, name: 'fibonacci',
|
|
418
|
+
asm: (n) => `MOVI R0, 0\nMOVI R1, 1\nMOVI R2, ${n}\nMOV R3, R1\nIADD R1, R1, R0\nMOV R0, R3\nDEC R2\nJNZ R2, -16\nHALT`, reg: 0 },
|
|
419
|
+
{ pattern: /^sum\s+(\d+)\s+to\s+(\d+)$/i, name: 'sum',
|
|
420
|
+
asm: (a,b) => `MOVI R0, 0\nMOVI R1, ${b}\nIADD R0, R0, R1\nDEC R1\nJNZ R1, -10\nHALT`, reg: 0 },
|
|
421
|
+
{ pattern: /^power\s+of\s+(\d+)\s+to\s+(\d+)$/i, name: 'power',
|
|
422
|
+
asm: (base,exp) => `MOVI R0, 1\nMOVI R1, ${base}\nMOVI R2, ${exp}\nIMUL R0, R0, R1\nDEC R2\nJNZ R2, -10\nHALT`, reg: 0 },
|
|
423
|
+
{ pattern: /^hello$/i, name: 'hello',
|
|
424
|
+
asm: () => `MOVI R0, 42\nHALT`, reg: 0 },
|
|
425
|
+
];
|
|
426
|
+
|
|
427
|
+
// ─── Interpreter ──────────────────────────────────────────────────────────
|
|
428
|
+
|
|
429
|
+
class Interpreter {
|
|
430
|
+
constructor() {
|
|
431
|
+
this.vocab = [...VOCAB];
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Interpret natural language text and execute.
|
|
436
|
+
* @param {string} text
|
|
437
|
+
* @returns {{ value: number|null, message: string, cycles: number }}
|
|
438
|
+
*/
|
|
439
|
+
run(text) {
|
|
440
|
+
const trimmed = text.trim();
|
|
441
|
+
|
|
442
|
+
// Try vocabulary
|
|
443
|
+
for (const entry of this.vocab) {
|
|
444
|
+
const m = trimmed.match(entry.pattern);
|
|
445
|
+
if (m) {
|
|
446
|
+
const args = m.slice(1).map(Number);
|
|
447
|
+
const asmText = entry.asm(...args);
|
|
448
|
+
try {
|
|
449
|
+
const bc = assemble(asmText);
|
|
450
|
+
const vm = new FluxVM(bc);
|
|
451
|
+
vm.execute();
|
|
452
|
+
return { value: vm.reg(entry.reg), message: `OK (${vm.cycles} cycles)`, cycles: vm.cycles };
|
|
453
|
+
} catch (e) {
|
|
454
|
+
return { value: null, message: `Error: ${e.message}`, cycles: 0 };
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Try direct assembly
|
|
460
|
+
const firstWord = trimmed.split(/\s+/)[0]?.toUpperCase();
|
|
461
|
+
if (firstWord && firstWord in OP) {
|
|
462
|
+
try {
|
|
463
|
+
const bc = assemble(trimmed);
|
|
464
|
+
const vm = new FluxVM(bc);
|
|
465
|
+
vm.execute();
|
|
466
|
+
return { value: vm.reg(0), message: `OK (${vm.cycles} cycles, direct asm)`, cycles: vm.cycles };
|
|
467
|
+
} catch (e) {
|
|
468
|
+
return { value: null, message: `Error: ${e.message}`, cycles: 0 };
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return { value: null, message: `No match for: ${trimmed.slice(0, 80)}`, cycles: 0 };
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// ─── A2A Agents ───────────────────────────────────────────────────────────
|
|
477
|
+
|
|
478
|
+
class A2AAgent {
|
|
479
|
+
constructor(id, bytecode, role = 'worker') {
|
|
480
|
+
this.id = id;
|
|
481
|
+
this.vm = new FluxVM(bytecode);
|
|
482
|
+
this.role = role;
|
|
483
|
+
this.trust = 1.0;
|
|
484
|
+
this.inbox = [];
|
|
485
|
+
this.generation = 0;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
step() {
|
|
489
|
+
this.vm.execute();
|
|
490
|
+
this.generation++;
|
|
491
|
+
return this.vm.cycles;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
tell(other, payload) {
|
|
495
|
+
other.inbox.push({ from: this.id, type: 'TELL', payload, gen: this.generation, trust: this.trust });
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
ask(other) {
|
|
499
|
+
other.inbox.push({ from: this.id, type: 'ASK', gen: this.generation });
|
|
500
|
+
return other.vm.halted ? other.vm.reg(0) : null;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
class Swarm {
|
|
505
|
+
constructor() { this.agents = new Map(); }
|
|
506
|
+
|
|
507
|
+
add(agent) { this.agents.set(agent.id, agent); }
|
|
508
|
+
|
|
509
|
+
tick() {
|
|
510
|
+
let total = 0;
|
|
511
|
+
for (const a of this.agents.values()) total += a.step();
|
|
512
|
+
return total;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
vote(reg = 0) {
|
|
516
|
+
const counts = {};
|
|
517
|
+
for (const a of this.agents.values()) {
|
|
518
|
+
if (a.vm.halted) {
|
|
519
|
+
const v = a.vm.reg(reg);
|
|
520
|
+
counts[v] = (counts[v] || 0) + 1;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return counts;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
consensus(reg = 0) {
|
|
527
|
+
const counts = this.vote(reg);
|
|
528
|
+
if (!Object.keys(counts).length) return null;
|
|
529
|
+
return Number(Object.entries(counts).sort((a,b) => b[1]-a[1])[0][0]);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// ─── Exports ──────────────────────────────────────────────────────────────
|
|
534
|
+
|
|
535
|
+
module.exports = {
|
|
536
|
+
FluxVM,
|
|
537
|
+
assemble,
|
|
538
|
+
disassemble,
|
|
539
|
+
Interpreter,
|
|
540
|
+
A2AAgent,
|
|
541
|
+
Swarm,
|
|
542
|
+
VocabEntry,
|
|
543
|
+
Vocabulary,
|
|
544
|
+
VocabInterpreter,
|
|
545
|
+
OP,
|
|
546
|
+
VOCAB
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
// ─── CLI Demo ─────────────────────────────────────────────────────────────
|
|
550
|
+
|
|
551
|
+
if (typeof require !== 'undefined' && require.main === module) {
|
|
552
|
+
console.log('╔══════════════════════════════════════════════════╗');
|
|
553
|
+
console.log('║ FLUX.js — JavaScript Bytecode VM ║');
|
|
554
|
+
console.log('║ SuperInstance / Oracle1 ║');
|
|
555
|
+
console.log('╚══════════════════════════════════════════════════╝\n');
|
|
556
|
+
|
|
557
|
+
const interp = new Interpreter();
|
|
558
|
+
console.log(` ${interp.vocab.length} vocabulary patterns loaded\n`);
|
|
559
|
+
|
|
560
|
+
// Arithmetic
|
|
561
|
+
console.log('=== Arithmetic ===');
|
|
562
|
+
for (const expr of ['compute 3 + 4', 'compute 10 * 5', 'double 21', 'square 8']) {
|
|
563
|
+
const r = interp.run(expr);
|
|
564
|
+
console.log(` ${expr} → ${r.value}`);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Loops
|
|
568
|
+
console.log('\n=== Loops ===');
|
|
569
|
+
for (const expr of ['factorial of 7', 'fibonacci of 12', 'sum 1 to 100', 'power of 2 to 10']) {
|
|
570
|
+
const r = interp.run(expr);
|
|
571
|
+
console.log(` ${expr} → ${r.value} (${r.cycles} cycles)`);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Assembler
|
|
575
|
+
console.log('\n=== Assembler ===');
|
|
576
|
+
const bc = assemble('MOVI R0, 7\nMOVI R1, 1\nIMUL R1, R1, R0\nDEC R0\nJNZ R0, -10\nHALT');
|
|
577
|
+
const vm = new FluxVM(bc);
|
|
578
|
+
vm.execute();
|
|
579
|
+
console.log(` factorial(7) bytecode: ${Array.from(bc).map(b=>b.toString(16).padStart(2,'0')).join(' ')}`);
|
|
580
|
+
console.log(` Result: R1 = ${vm.reg(1)}`);
|
|
581
|
+
|
|
582
|
+
// Disassembler
|
|
583
|
+
console.log('\n=== Disassembler ===');
|
|
584
|
+
for (const line of disassemble(bc)) console.log(` ${line}`);
|
|
585
|
+
|
|
586
|
+
// Swarm
|
|
587
|
+
console.log('\n=== A2A Swarm ===');
|
|
588
|
+
const swarm = new Swarm();
|
|
589
|
+
const agentBc = assemble('MOVI R0, 42\nHALT');
|
|
590
|
+
for (let i = 0; i < 5; i++) {
|
|
591
|
+
swarm.add(new A2AAgent(`agent-${i}`, agentBc, ['worker','scout','navigator'][i%3]));
|
|
592
|
+
}
|
|
593
|
+
const totalCycles = swarm.tick();
|
|
594
|
+
console.log(` 5 agents, ${totalCycles} cycles, consensus: ${swarm.consensus()}`);
|
|
595
|
+
|
|
596
|
+
// Benchmark
|
|
597
|
+
console.log('\n=== Benchmark ===');
|
|
598
|
+
const N = 100000;
|
|
599
|
+
const t0 = Date.now();
|
|
600
|
+
for (let i = 0; i < N; i++) new FluxVM(bc).execute();
|
|
601
|
+
const elapsed = Date.now() - t0;
|
|
602
|
+
console.log(` factorial(7) x ${N.toLocaleString()}: ${elapsed} ms | ${(elapsed*1e6/N).toFixed(0)} ns/iter`);
|
|
603
|
+
|
|
604
|
+
console.log('\n✓ FLUX.js all systems operational!');
|
|
605
|
+
|
|
606
|
+
// Test the new vocabulary interpreter
|
|
607
|
+
console.log('\n=== Testing VocabInterpreter ===');
|
|
608
|
+
const vocabInterp = new VocabInterpreter();
|
|
609
|
+
|
|
610
|
+
// Test compute A + B
|
|
611
|
+
const addResult = vocabInterp.run('compute 3 + 4');
|
|
612
|
+
console.assert(addResult.value === 7, `Expected 7, got ${addResult.value}`);
|
|
613
|
+
console.log(` compute 3 + 4 → ${addResult.value} (${addResult.message})`);
|
|
614
|
+
|
|
615
|
+
// Test compute A * B
|
|
616
|
+
const mulResult = vocabInterp.run('compute 5 * 6');
|
|
617
|
+
console.assert(mulResult.value === 30, `Expected 30, got ${mulResult.value}`);
|
|
618
|
+
console.log(` compute 5 * 6 → ${mulResult.value} (${mulResult.message})`);
|
|
619
|
+
|
|
620
|
+
// Test factorial
|
|
621
|
+
const factResult = vocabInterp.run('factorial of 5');
|
|
622
|
+
console.assert(factResult.value === 120, `Expected 120, got ${factResult.value}`);
|
|
623
|
+
console.log(` factorial of 5 → ${factResult.value} (${factResult.message})`);
|
|
624
|
+
|
|
625
|
+
// Test double
|
|
626
|
+
const doubleResult = vocabInterp.run('double 21');
|
|
627
|
+
console.assert(doubleResult.value === 42, `Expected 42, got ${doubleResult.value}`);
|
|
628
|
+
console.log(` double 21 → ${doubleResult.value} (${addResult.message})`);
|
|
629
|
+
|
|
630
|
+
// Test square
|
|
631
|
+
const squareResult = vocabInterp.run('square 8');
|
|
632
|
+
console.assert(squareResult.value === 64, `Expected 64, got ${squareResult.value}`);
|
|
633
|
+
console.log(` square 8 → ${squareResult.value} (${squareResult.message})`);
|
|
634
|
+
|
|
635
|
+
// Test hello
|
|
636
|
+
const helloResult = vocabInterp.run('hello');
|
|
637
|
+
console.assert(helloResult.value === 42, `Expected 42, got ${helloResult.value}`);
|
|
638
|
+
console.log(` hello → ${helloResult.value} (${helloResult.message})`);
|
|
639
|
+
|
|
640
|
+
console.log('\n✓ All VocabInterpreter tests passed!');
|
|
641
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "flux-vm-js",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "FLUX.js — JavaScript Bytecode VM. Self-contained FLUX runtime for Node.js and browsers.",
|
|
5
|
+
"main": "flux.js",
|
|
6
|
+
"author": "SuperInstance",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/SuperInstance/flux-js.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"flux",
|
|
14
|
+
"vm",
|
|
15
|
+
"bytecode",
|
|
16
|
+
"runtime",
|
|
17
|
+
"virtual-machine",
|
|
18
|
+
"interpreter",
|
|
19
|
+
"assembly",
|
|
20
|
+
"superinstance"
|
|
21
|
+
],
|
|
22
|
+
"homepage": "https://github.com/SuperInstance/flux-js#readme",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/SuperInstance/flux-js/issues"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"flux.js",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"vitest": "^3.2.1"
|
|
37
|
+
}
|
|
38
|
+
}
|