Skip to main content

lex_bytecode/
op.rs

1//! Bytecode instruction set per spec §8.2.
2
3use serde::{Deserialize, Serialize};
4
5/// Constant pool entry.
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
7pub enum Const {
8    Int(i64),
9    Float(f64),
10    Bool(bool),
11    Str(String),
12    Bytes(Vec<u8>),
13    Unit,
14    /// A field name, used by MAKE_RECORD/GET_FIELD.
15    FieldName(String),
16    /// A variant tag, used by MAKE_VARIANT/TEST_VARIANT/GET_VARIANT.
17    VariantName(String),
18    /// An AST NodeId, attached to Call / EffectCall for trace keying (§10.1).
19    NodeId(String),
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
23pub enum Op {
24    // stack manipulation
25    PushConst(u32),
26    Pop,
27    Dup,
28
29    // locals
30    LoadLocal(u16),
31    StoreLocal(u16),
32
33    // constructors / pattern matching
34    /// Builds a record from `count` (name_const_idx, value) pairs on the stack.
35    /// Stack: [name_idx_n, val_n, ..., name_idx_1, val_1] but encoded as
36    /// alternating `<name_idx_const_u32> <value popped from stack>` — for
37    /// simplicity we instead push `count` values and `count` field name
38    /// constants in the same op as a `Vec<u32>` of name indices.
39    MakeRecord { field_name_indices: Vec<u32> },
40    MakeTuple(u16),
41    MakeList(u32),
42    MakeVariant { name_idx: u32, arity: u16 },
43    GetField(u32),         // field name const idx
44    GetElem(u16),          // tuple element index
45    TestVariant(u32),      // pushes Bool: top-of-stack matches variant name?
46    GetVariant(u32),       // extracts payload (replaces variant on stack with its args list)
47    GetVariantArg(u16),    // pop variant, push its i'th arg
48    GetListLen,
49    GetListElem(u32),
50    /// Pop [list, value]; push list with `value` appended.
51    ListAppend,
52    /// Pop list; push it indexed by the integer on top.
53    /// Stack: [list, idx] → [list[idx]]. (Like GetListElem(u32) but
54    /// the index is dynamic.)
55    GetListElemDyn,
56
57    // control flow
58    Jump(i32),
59    JumpIf(i32),     // pops Bool
60    JumpIfNot(i32),
61    Call { fn_id: u32, arity: u16, node_id_idx: u32 },
62    TailCall { fn_id: u32, arity: u16, node_id_idx: u32 },
63    /// Build a Value::Closure: pop `capture_count` values (in order) and
64    /// pair them with `fn_id`.
65    MakeClosure { fn_id: u32, capture_count: u16 },
66    /// Call a closure: pop `arity` args + 1 closure (top of stack), invoke.
67    CallClosure { arity: u16, node_id_idx: u32 },
68    /// Stable sort-by-key (#338). Stack: `[xs, f]` (xs underneath).
69    /// Pops the key-fn `f` and the list `xs`, applies `f` to each
70    /// element to derive a sortable key, returns the list reordered
71    /// so keys ascend. Keys must be one of `Int` / `Float` / `Str`;
72    /// other key types pair-wise compare as equal (preserving
73    /// insertion order). `node_id_idx` is the originating NodeId.
74    SortByKey { node_id_idx: u32 },
75    /// Parallel map (#305 slice 1). Stack: `[xs, f]` (xs underneath).
76    /// Pops the closure `f` and the list `xs`, applies `f` to each
77    /// element in parallel via OS threads, pushes the result list
78    /// in input order. `node_id_idx` is the originating NodeId for
79    /// trace keying. The pool size is capped by
80    /// `LEX_PAR_MAX_CONCURRENCY` (default = available CPU cores).
81    ///
82    /// Slice 1 limitation: closures invoking effects fail at
83    /// runtime with `VmError::Effect`. The per-thread effect handler
84    /// split is queued as slice 2.
85    ParallelMap { node_id_idx: u32 },
86    /// EFFECT_CALL `<effect_kind_const_idx>` `<op_name_const_idx>` `<arity>`.
87    /// Pops `arity` args, dispatches to a host effect handler, pushes result.
88    /// `node_id_idx` points to a `Const::NodeId` for trace keying.
89    EffectCall { kind_idx: u32, op_idx: u32, arity: u16, node_id_idx: u32 },
90    Return,
91    Panic(u32),     // pushes constant message and aborts
92
93    // arithmetic — typed (per spec §8.2). `NumAdd`/etc. dispatch on operand
94    // type at runtime; emitted when the compiler doesn't have type info.
95    // The post-M5 plan is to lower NumAdd → IntAdd|FloatAdd in a typed pass.
96    IntAdd, IntSub, IntMul, IntDiv, IntMod, IntNeg,
97    IntEq, IntLt, IntLe,
98    FloatAdd, FloatSub, FloatMul, FloatDiv, FloatNeg,
99    FloatEq, FloatLt, FloatLe,
100    NumAdd, NumSub, NumMul, NumDiv, NumMod, NumNeg,
101    NumEq, NumLt, NumLe,
102    BoolAnd, BoolOr, BoolNot,
103
104    // strings
105    StrConcat, StrLen, StrEq,
106    BytesLen, BytesEq,
107}