;; ========================================================================== ;; bytecode.sx — SX bytecode format definition ;; ;; Universal bytecode for SX evaluation. Produced by compiler.sx, ;; executed by platform-native VMs (OCaml, JS, WASM). ;; ;; Design principles: ;; - One byte per opcode (~65 ops, fits in u8) ;; - Variable-length encoding (1-5 bytes per instruction) ;; - Lexical scope resolved at compile time (no hash lookups) ;; - Tail calls detected statically (no thunks/trampoline) ;; - Control flow via jumps (no continuation frames for if/when/etc.) ;; - Content-addressable (deterministic binary for CID) ;; ========================================================================== ;; -------------------------------------------------------------------------- ;; Opcode constants ;; -------------------------------------------------------------------------- ;; Stack / Constants (define OP_CONST 1) ;; u16 pool_idx — push constant (define OP_NIL 2) ;; push nil (define OP_TRUE 3) ;; push true (define OP_FALSE 4) ;; push false (define OP_POP 5) ;; discard TOS (define OP_DUP 6) ;; duplicate TOS ;; Variable access (resolved at compile time) (define OP_LOCAL_GET 16) ;; u8 slot (define OP_LOCAL_SET 17) ;; u8 slot (define OP_UPVALUE_GET 18) ;; u8 idx (define OP_UPVALUE_SET 19) ;; u8 idx (define OP_GLOBAL_GET 20) ;; u16 name_idx (define OP_GLOBAL_SET 21) ;; u16 name_idx ;; Control flow (replaces if/when/cond/and/or frames) (define OP_JUMP 32) ;; i16 offset (define OP_JUMP_IF_FALSE 33) ;; i16 offset (define OP_JUMP_IF_TRUE 34) ;; i16 offset ;; Function operations (define OP_CALL 48) ;; u8 argc (define OP_TAIL_CALL 49) ;; u8 argc — reuse frame (TCO) (define OP_RETURN 50) ;; return TOS (define OP_CLOSURE 51) ;; u16 code_idx — create closure (define OP_CALL_PRIM 52) ;; u16 name_idx, u8 argc — direct primitive (define OP_APPLY 53) ;; (apply f args-list) ;; Collection construction (define OP_LIST 64) ;; u16 count — build list from stack (define OP_DICT 65) ;; u16 count — build dict from stack pairs (define OP_APPEND_BANG 66) ;; (append! TOS-1 TOS) ;; Higher-order forms (inlined loop) (define OP_ITER_INIT 80) ;; init iterator on TOS list (define OP_ITER_NEXT 81) ;; i16 end_offset — push next or jump (define OP_MAP_OPEN 82) ;; push empty accumulator (define OP_MAP_APPEND 83) ;; append TOS to accumulator (define OP_MAP_CLOSE 84) ;; pop accumulator as list (define OP_FILTER_TEST 85) ;; i16 skip — if falsy jump (skip append) ;; HO fallback (dynamic callback) (define OP_HO_MAP 88) ;; (map fn coll) (define OP_HO_FILTER 89) ;; (filter fn coll) (define OP_HO_REDUCE 90) ;; (reduce fn init coll) (define OP_HO_FOR_EACH 91) ;; (for-each fn coll) (define OP_HO_SOME 92) ;; (some fn coll) (define OP_HO_EVERY 93) ;; (every? fn coll) ;; Scope / dynamic binding (define OP_SCOPE_PUSH 96) ;; TOS = name (define OP_SCOPE_POP 97) (define OP_PROVIDE_PUSH 98) ;; TOS-1 = name, TOS = value (define OP_PROVIDE_POP 99) (define OP_CONTEXT 100) ;; TOS = name → push value (define OP_EMIT 101) ;; TOS-1 = name, TOS = value (define OP_EMITTED 102) ;; TOS = name → push collected ;; Continuations (define OP_RESET 112) ;; i16 body_len — push delimiter (define OP_SHIFT 113) ;; u8 k_slot, i16 body_len — capture k ;; Define / component (define OP_DEFINE 128) ;; u16 name_idx — bind TOS to name (define OP_DEFCOMP 129) ;; u16 template_idx (define OP_DEFISLAND 130) ;; u16 template_idx (define OP_DEFMACRO 131) ;; u16 template_idx (define OP_EXPAND_MACRO 132) ;; u8 argc — runtime macro expansion ;; String / serialize (hot path) (define OP_STR_CONCAT 144) ;; u8 count — concat N values as strings (define OP_STR_JOIN 145) ;; (join sep list) (define OP_SERIALIZE 146) ;; serialize TOS to SX string ;; Inline primitives (hot path — no hashtable lookup) (define OP_ADD 160) ;; TOS-1 + TOS → push (define OP_SUB 161) ;; TOS-1 - TOS → push (define OP_MUL 162) ;; TOS-1 * TOS → push (define OP_DIV 163) ;; TOS-1 / TOS → push (define OP_EQ 164) ;; TOS-1 = TOS → push bool (define OP_LT 165) ;; TOS-1 < TOS → push bool (define OP_GT 166) ;; TOS-1 > TOS → push bool (define OP_NOT 167) ;; !TOS → push bool (define OP_LEN 168) ;; len(TOS) → push number (define OP_FIRST 169) ;; first(TOS) → push (define OP_REST 170) ;; rest(TOS) → push list (define OP_NTH 171) ;; nth(TOS-1, TOS) → push (define OP_CONS 172) ;; cons(TOS-1, TOS) → push list (define OP_NEG 173) ;; negate TOS → push number (define OP_INC 174) ;; TOS + 1 → push (define OP_DEC 175) ;; TOS - 1 → push ;; Aser specialization (optional, 224-239 reserved) (define OP_ASER_TAG 224) ;; u16 tag_name_idx — serialize HTML tag (define OP_ASER_FRAG 225) ;; u8 child_count — serialize fragment ;; -------------------------------------------------------------------------- ;; Bytecode module structure ;; -------------------------------------------------------------------------- ;; A module contains: ;; magic: "SXBC" (4 bytes) ;; version: u16 ;; pool_count: u32 ;; pool: constant pool entries (self-describing tagged values) ;; code_count: u32 ;; codes: code objects ;; entry: u32 (index of entry-point code object) (define BYTECODE_MAGIC "SXBC") (define BYTECODE_VERSION 1) ;; Constant pool tags (define CONST_NUMBER 1) (define CONST_STRING 2) (define CONST_BOOL 3) (define CONST_NIL 4) (define CONST_SYMBOL 5) (define CONST_KEYWORD 6) (define CONST_LIST 7) (define CONST_DICT 8) (define CONST_CODE 9) ;; -------------------------------------------------------------------------- ;; Disassembler ;; -------------------------------------------------------------------------- (define opcode-name (fn (op) (cond (= op 1) "CONST" (= op 2) "NIL" (= op 3) "TRUE" (= op 4) "FALSE" (= op 5) "POP" (= op 6) "DUP" (= op 16) "LOCAL_GET" (= op 17) "LOCAL_SET" (= op 20) "GLOBAL_GET" (= op 21) "GLOBAL_SET" (= op 32) "JUMP" (= op 33) "JUMP_IF_FALSE" (= op 48) "CALL" (= op 49) "TAIL_CALL" (= op 50) "RETURN" (= op 52) "CALL_PRIM" (= op 128) "DEFINE" (= op 144) "STR_CONCAT" :else (str "OP_" op))))