Download Game! Currently 86 players and visitors. Last logged in:OdradekGlauriCilwandIberiamssp

Blitzer's Blog >> 72230

Back to blogs index
Posted: 10 Sep 2026 06:52 [ permalink ]
Good morning. Heres my read of the discussion, followed by a concrete design
commentary and implementation plan.

## Executive summary

The JSON-Lisp / Homoiconic Array Tree is a strong 80/20 design for a portable,
AI-friendly, human-readable program plan that can be interpreted or compiled.
It is not the absolute optimum for every constraintespecially raw token
efficiency or production-grade determinismbut it is an excellent foundation if
you formalize a few semantics early.

The biggest wins:
- Zero lexer: native `JSON.parse`.
- Homoiconic: code is data, so macros, imports, and program composition are
trivial.
- Simple compiler/VM path: tree-walk  stack bytecode  call frames.
- Natural closure/scoping story using prototype chains or explicit environment
frames.
- Async can be added cleanly with worker threads, shared memory, gas limits,
and polling.

The main risks:
- String/symbol ambiguity.
- Prototype-based environments can leak memory and are unsafe if variable
names like `__proto__` are allowed.
- Wall-clock determinism is not truly achievable with parallel workers.
- Without formal special forms, arity, and error semantics, the
self-documenting claim weakens.
- Async/polling is powerful but needs careful memory layout and cancellation
design.

## Design commentary

### 1. Representation: keep JSON-Lisp, but formalize symbols vs literals

The current examples use strings both as variable names and as potential
string literals. That is ambiguous. A clean rule:

- JSON `number`, `boolean`, `null` are literals.
- JSON string in operator or parameter position is a symbol.
- String literals use an explicit form: `["str", "hello"]`.
- Data lists use `["quote", [...]]` or `["list", ...]`.

Example:

```json
["def", "calculate_discount", ["price", "is_member"],
  ["if", "is_member",
    ["*", "price", 0.85],
    "price"]]
```

Here `"price"` is a symbol. If you wanted the literal string `"price"`, youd
write `["str", "price"]`.

This keeps the format minimal while removing a large class of bugs.

### 2. Core special forms and opcodes

You need a small, closed set of special forms. Recommended MVP:

- `def`  define function or value.
- `let`  block-scoped bindings.
- `set`  mutate existing binding.
- `if`  conditional.
- `do` / `begin`  sequence.
- `lambda`  anonymous function.
- `quote`  treat AST as data.
- `import`  preprocessor or runtime module.
- `eval`  compile and run AST at runtime.

Opcode set for the stack VM:

```text
CONST, LOAD, STORE, SET,
ADD, SUB, MUL, DIV, LT, EQ,
JMP, JMP_FALSE,
MAKE_FUNC, CALL, RET,
ENTER_SCOPE, EXIT_SCOPE,
SPAWN, POLL, CANCEL,
GAS, HALT
```

Keep the compiler and VM opcode registry in one place. That registry becomes
your self-documenting schema.

### 3. Environments: prototype chain is elegant, but use `Map` or
null-prototype objects for production

The prototype-chain trick for closures and `let` scopes is clever and minimal.
But JavaScript object property lookup has sharp edges:

- `__proto__`, `constructor`, `prototype` can cause prototype pollution.
- `hasOwnProperty` traversal is slower than a `Map` chain.
- Retaining entire parent environments can leak memory.

For an MVP, prototype chains are fine. For production, use an explicit
environment:

```js
class Env {
  constructor(parent = null) {
    this.vars = new Map();
    this.parent = parent;
  }
  get(name) { ... }
  set(name, value) { ... }
  define(name, value) { ... }
}
```

This gives you exact `STORE` vs `SET` semantics, safer names, and easier
flat-closure optimization later.

### 4. Compiler and VM: keep interpreter for tests, compiler for execution

The tree-walking interpreter is valuable for debugging and conformance tests.
The compiler to bytecode is the production path.

Key recommendations:
- Compile in post-order for standard calls.
- Use jump patching for `if`.
- Emit `RET` implicitly at end of function body.
- Add tail-call optimization later by detecting `CALL` in tail position and
reusing the frame.
- Add source metadata as a side table keyed by instruction index, not inside
the AST.

### 5. Macros and program insertion: preprocessor first, runtime `eval` later

Because the AST is data, you can:
- Copy: `fullProgram = ["let", [["price", 100]], coreLogic]`
- Reference: `["import", "math_utils"]` and expand in a preprocessor.
- Runtime eval: add `EVAL` opcode that compiles an array from the data stack
and pushes a new frame.