Files
hakorune/docs/development/roadmap/phases/phase-17/BLUEPRINT_MIN.md

3.5 KiB
Raw Blame History

Phase 17 — Minimal, Clean, Small (Blueprint)

Purpose: ship a tiny, beautiful vertical slice that runs Core13 IR endtoend, is observable, and easy to extend. Keep everything additive and simple.

  1. Scope (MVP)
  • IR: Core13 only (Loop IR placeholder only).
  • Exec: Wrap existing VM with a tiny ExecEngine adapter.
  • Remote: One transport only — NDJSON over stdio (nyash-engine-core13).
  • CLI: run, ir-emit, ir-run. Trace: Enter/Exit only.
  1. ExecEngine (tiny)
  • Value: Int(i64) | Bool(bool) | Str(String) | Box(u64) | None.
  • Trait:
    • load(&Core13Module) -> ModuleHandle
    • get(&ModuleHandle, func: &str) -> FuncHandle
    • call(&FuncHandle, args: &[Value]) -> Result<Value>
  • Adapter: VMEngine delegates to existing VM (execute_module), converts Value <-> NyashBox.
  1. Core13 IR JSON (minimal)
  • module: { schema:1, name:"m", funcs:[ { name, params:[], ret:"i64|bool|string|box|void", blocks:[...] } ] }
  • block: { id:0, inst:[...], term:{...} }
  • inst (13 only): Const, BinOp, Compare, Jump, Branch, Return, Phi, Call, BoxCall, ExternCall, TypeOp, Safepoint, Barrier.
  • example Const: { op:"Const", dst:1, ty:"i64", value:42 }
  • example Return: { term:"Return", value:1 }
  1. NDJSON protocol (stdio)
  • Common: every request has op,id,schema(=1); every response {ok:true|false,id,...}; unknown keys ignored.
  • Ops (MVP): load_module, call, ping, trace_sub (Enter/Exit only).
  • Requests:
    • load_module: {op:"load_module",id:1,ir:"core13",format:"json",bytes:"<base64>"}
    • call: {op:"call",id:2,module_id:1,func:"main",args:[]}
    • ping: {op:"ping",id:3}
    • trace_sub: {op:"trace_sub",id:4,mask:["EnterFunc","ExitFunc"],flush_ms:50?}
  • Responses:
    • {ok:true,id:1,module_id:1,features:["interp","trace"],ir:"core13"}
    • {ok:true,id:2,value:42,events_dropped:0}
    • {ok:true,id:3,now:1725600000}
    • {ok:true,id:4} (events then stream as separate lines)
  • Events (lines): {"event":"EnterFunc","func":"main"} / {"event":"ExitFunc","func":"main"}.
  1. CLI UX
  • nyash run --engine=vm apps/hello.nyash
  • nyash run --engine=remote --exe ./nyash-engine-core13 apps/hello.nyash
  • nyash ir-emit --ir=core13 --format=json apps/hello.nyash > out.json
  • nyash ir-run --engine=vm < out.json
  1. Milestones
  • M1: ExecEngine + VMAdapter + run --engine=vm (tests: add/if/string.length)
  • M2: Core13 serde/verify + ir-emit/ir-run (roundtrip + 1 exec test)
  • M3: nyash-engine-core13 NDJSON (load/call/ping/trace_sub) + run --engine=remote parity
  1. Design rules (beauty & simplicity)
  • Additive evolution only: version fields present (schema), unknown keys ignored.
  • Deterministic JSON: stable key order where practical to make diffs readable.
  • Naming: short, explicit; avoid redundancy; keep method ids optional.
  • Observability first: ship with Enter/Exit trace; branch/extern later.

Appendix: Two request/response examples

  1. Load then call REQ: { "op":"load_module", "id":1, "schema":1, "ir":"core13", "format":"json", "bytes":"<base64>" } RESP:{ "ok":true, "id":1, "module_id":1, "features":["interp","trace"], "ir":"core13" } REQ: { "op":"call", "id":2, "schema":1, "module_id":1, "func":"main", "args":[] } RESP:{ "ok":true, "id":2, "value":42, "events_dropped":0 }

  2. Trace subscribe and ping REQ: { "op":"trace_sub", "id":10, "schema":1, "mask":["EnterFunc","ExitFunc"] } RESP:{ "ok":true, "id":10 } EVT: { "event":"EnterFunc", "func":"main" } EVT: { "event":"ExitFunc", "func":"main" } REQ: { "op":"ping", "id":11, "schema":1 } RESP:{ "ok":true, "id":11, "now":1725600000 }