Bytecode is defined in include/compiler.h as OpCode and implemented in src/vm.c as a computed goto table. A Chunk holds code bytes, a constants pool, and a positions map for errors. Operands that refer to constants use a 24 bit index (b1<<16)|(b2<<8)|b3, jumps use a 16 bit signed offset.
- Stack: what the opcode pops and pushes.
...is the rest of the stack. - Operands: bytes that follow the opcode.
- Notes: what it does and what errors it can raise.
OP_LOAD_CONST
- Stack:
... -> ..., value - Operands: 24b const index
- Does:
PUSH(constants[idx]). If the constant isNULLit pushesVAL_NULL, if it is anintorfloatit pushesVAL_INT/VAL_FLOATdirectly, otherwise it pushes the object. Constants areisStatic.
OP_LOAD_VAR
- Stack:
... -> ..., value - Operands: 24b interned name index
- Does:
getTable(vars, name->value)and pushes a copy. IfIS_UNDEFit raisesNameError: Undefined variable "x".
OP_STORE_VAR
- Stack:
..., value -> ..., value(peeks, does not pop) - Operands: 24b name index
- Does:
peek = PEEK(0); setTable(vars,name,peek)orsetTableLocalwhen inside a class instance. IfsetTablereturns false (constant) it raisesNameError: Cannot assign to constant.
OP_DECLARE_VAR
- Stack:
..., value -> ..., value(peeks) - Operands: 24b name index + 1B flags (
0x1 mutable,0x2 ref) - Does:
declareTable(vars,name,peek,isMutable,isReference)orsetTableLocalfor instances.
OP_LOAD_LOCAL
- Stack:
... -> ..., value - Operands: 8b slot
- Does:
val = locals[base+slot]; if IS_UNDEF raise NameError: Variable used before assignment; PUSH(copyValue(val))unlessisStatic.
OP_STORE_LOCAL
- Stack:
..., value -> ..., value(peeks) - Operands: 8b slot
- Does: unboxes
int/floatfromObject*if needed,freeValue(old),locals[base+slot]=copyValue(peek).
OP_POP
- Stack:
..., value -> ... - Does:
freeValue(POP()).
All of these pop a and b (or a for unary) and push the result. The fast path checks IS_INT on both sides first, otherwise doArith handles mixed types.
OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_POW
- Stack:
..., a, b -> ..., result - Does:
a + betc.OP_DIVchecksb==0and raisesValueError: Division by zero.OP_POWusespow(double,double). String+concatenates viaaddString, string* intrepeats viamulStringwithSIZE_MAX/lenguard.
OP_EQ, OP_NE, OP_LT, OP_GT, OP_LTE, OP_GTE
- Stack:
..., a, b -> ..., 0 or 1 - Does: numeric compare, null compare, or string compare with
lencheck thenmemcmp.EQ/NEon non-string, non-number returnsfalse/truewithout error.
OP_AND, OP_OR
- Stack:
..., a, b -> ..., 0 or 1 - Does:
int(a) && int(b)etc.
OP_NEG
- Stack:
..., a -> ..., -a - Does: if
VAL_INTthen-i, ifVAL_FLOATthen-f, elseTypeError: Operand must be a number.
OP_NOT
- Stack:
..., a -> ..., !a - Does: logical not for
VAL_INT/VAL_FLOAT, elseTypeError.
OP_JUMP
- Operands: 16b signed offset
- Does:
ip += offset.
OP_JUMP_IF_FALSE
- Stack:
..., cond -> ...(pops) - Operands: 16b offset
- Does:
if (!isTruthy(POP())) ip += offset; freeValue(cond).
OP_FOR_PREP
- Stack:
..., iterable -> ..., iterable, length, index - Does: expects
iterableisOBJ_LISTorOBJ_STRING, pushes the object back, thenVAL_INT(len)andVAL_INT(0).
OP_FOR_ITER
- Stack:
..., iterable, length, index -> ..., iterable, length, index, itemor..., (cleaned) - Operands: 16b exit offset
- Does: if
index < lengthpushesitem(list->objects[index]orinitStringof one char) and bumpsindex, else popsindex,length,iterable(freeing iterable) and jumps.
OP_TRY_PUSH
- Operands: 16b offset to catch
- Does: pushes
TryFrame{ip+offset, frameTop, stackTop}ontotryStackif not full.
OP_TRY_POP
- Does: pops
tryStackif any.
OP_BREAK / OP_CONTINUE
- Returned by
vmRunasBREAK/CONTINUEobjects and handled at compile time as jumps. At runtime they just return.
OP_CALL
- Stack:
..., callee, arg1..argN -> ..., result - Operands: 8b
N - Does: pops
callee. IfOBJ_FUNCTIONchecksN==paramCount, lazy compilesfunc->bodyifchunk==NULL, checksframeTopandlocalsTop, saves state, builds a newCallFramewithvariablesorinstance->fields, copies args, zeroes remaining locals toVAL_UNDEF, bumpsframeTop/localsTop, and dispatches. IfOBJ_NATIVE_FUNCTIONchecksisVariadicvsrequiredArgCount, boxesValueargs toObject**, callsnf->function, unboxes, handlesOBJ_ERROR. IfOBJ_CLASScreates anInstanceviainitInstanceand runs its chunk.
OP_RETURN
- Stack:
..., result -> ...(to caller) - Does: pops
result, unwinds oneCallFrame, frees its locals, restoreslocalsTopandtryStackTop, handlesinstancevsvariables, freesownsChunk, pushesresultto caller or returnsvalueToObject(result).
OP_PROPERTY_ACCESS
- Stack:
..., instance -> ..., value - Operands: 24b name index
- Does: expects
OBJ_INSTANCE,getTableLocal(fields,name), pushes copy, frees instance.
OP_PROPERTY_SET
- Stack:
..., instance, value -> ..., value - Operands: 24b name index
- Does: expects
OBJ_INSTANCE,setTableLocal(fields,name,value), pushesvalue, frees instance.
OP_BUILD_LIST
- Stack:
..., v1..vN -> ..., list - Operands: 24b
N - Does: checks
N <= VM_STACK_MAXandsp-N >= stack, allocatesitems(smallBuf[64]ormalloc),valueToObjects eachPEEK, popsN,initLists, pusheslist.
OP_INDEX_GET
- Stack:
..., target, index -> ..., value - Does: expects
indexisVAL_INT, target isStringorList. For string pushesinitStringof one char, for list pushescopyValueofobjects[i]. Out of range raisesIndexError.
OP_INDEX_SET
- Stack:
..., target, index, value -> ..., 1 - Does: for
ListitfreeObjects oldobjects[i]and storesvalueToObject(value). ForStringit expectsvalueis a one-char string andiin range, thenstr->value[i]=char. Pushes1.
OP_IMPORT
- Operands: 24b path index
- Does: first scans
stdlibModulesfor a native name andinits it. OtherwiseresolveImportPathfromframe->filename,readFile,initLexer,parseProgram,compileAST, checksframeTop, builds a newCallFramewithownsChunk=trueand the samevars, and dispatches.
OP_DECLARE_VAR vs OP_STORE_VAR: DECLARE is the first VAR or CONSTVAL/CONSTREF for that name, STORE is a later plain assignment.
OP_HALT
- Does: pops result if any else
0, unwinds one frame ifframeTop > exitFrameTop(handling instance vs variables andownsChunk), pushes result and dispatches, otherwisereturn valueToObject(result)and exitsvmRun.
All jumps are patched by emitJump/patchJump/emitLoop at compile time, so offsets are already correct when the VM sees them.