341 lines
10 KiB
Go
341 lines
10 KiB
Go
package compiler
|
|
|
|
// Opcode represents a single bytecode instruction
|
|
type Opcode uint8
|
|
|
|
const (
|
|
// Stack Operations
|
|
OpLoadConst Opcode = iota // Load constant onto stack [idx]
|
|
OpLoadLocal // Load local variable [slot]
|
|
OpStoreLocal // Store top of stack to local [slot]
|
|
OpLoadGlobal // Load global variable [idx]
|
|
OpStoreGlobal // Store top of stack to global [idx]
|
|
OpPop // Pop top value from stack
|
|
OpDup // Duplicate top value on stack
|
|
|
|
// Specialized Local Operations (no operands needed)
|
|
OpLoadLocal0 // Load local slot 0
|
|
OpLoadLocal1 // Load local slot 1
|
|
OpLoadLocal2 // Load local slot 2
|
|
OpStoreLocal0 // Store to local slot 0
|
|
OpStoreLocal1 // Store to local slot 1
|
|
OpStoreLocal2 // Store to local slot 2
|
|
|
|
// Specialized Constants (no operands needed)
|
|
OpLoadTrue // Load true constant
|
|
OpLoadFalse // Load false constant
|
|
OpLoadNil // Load nil constant
|
|
OpLoadZero // Load number 0
|
|
OpLoadOne // Load number 1
|
|
|
|
// Arithmetic Operations
|
|
OpAdd // a + b
|
|
OpSub // a - b
|
|
OpMul // a * b
|
|
OpDiv // a / b
|
|
OpNeg // -a
|
|
OpMod // a % b
|
|
|
|
// Specialized Arithmetic
|
|
OpAddConst // local + constant [constIdx]
|
|
OpSubConst // local - constant [constIdx]
|
|
OpInc // increment local [slot]
|
|
OpDec // decrement local [slot]
|
|
|
|
// Comparison Operations
|
|
OpEq // a == b
|
|
OpNeq // a != b
|
|
OpLt // a < b
|
|
OpLte // a <= b
|
|
OpGt // a > b
|
|
OpGte // a >= b
|
|
|
|
// Logical Operations
|
|
OpNot // not a
|
|
OpAnd // a and b
|
|
OpOr // a or b
|
|
|
|
// Control Flow
|
|
OpJump // Unconditional jump [offset]
|
|
OpJumpIfTrue // Jump if top of stack is true [offset]
|
|
OpJumpIfFalse // Jump if top of stack is false [offset]
|
|
OpCall // Call function [argCount]
|
|
OpReturn // Return from function
|
|
OpReturnNil // Return nil from function
|
|
|
|
// Specialized Control Flow
|
|
OpTestAndJump // Test local and jump [slot, offset]
|
|
OpLoopBack // Optimized loop back jump [offset]
|
|
|
|
// Table Operations
|
|
OpNewTable // Create new empty table
|
|
OpGetIndex // table[key] -> value
|
|
OpSetIndex // table[key] = value
|
|
OpGetField // table.field -> value [fieldIdx]
|
|
OpSetField // table.field = value [fieldIdx]
|
|
OpTableInsert // Insert value into table at next index
|
|
|
|
// Specialized Table Operations
|
|
OpGetLocalField // local.field -> value [slot, fieldIdx]
|
|
OpSetLocalField // local.field = value [slot, fieldIdx]
|
|
OpGetConstField // table.constField -> value [fieldName]
|
|
|
|
// Struct Operations
|
|
OpNewStruct // Create new struct instance [structId]
|
|
OpGetProperty // struct.field -> value [fieldIdx]
|
|
OpSetProperty // struct.field = value [fieldIdx]
|
|
OpCallMethod // Call method on struct [methodIdx, argCount]
|
|
|
|
// Function Operations
|
|
OpClosure // Create closure from function [funcIdx, upvalueCount]
|
|
OpGetUpvalue // Get upvalue [idx]
|
|
OpSetUpvalue // Set upvalue [idx]
|
|
OpCloseUpvalue // Close upvalue (move to heap)
|
|
|
|
// Specialized Function Operations
|
|
OpCallLocal0 // Call function in local slot 0 [argCount]
|
|
OpCallLocal1 // Call function in local slot 1 [argCount]
|
|
|
|
// Array Operations
|
|
OpNewArray // Create new array with size [size]
|
|
OpArrayAppend // Append value to array
|
|
OpArrayGet // Optimized array[index] access
|
|
OpArraySet // Optimized array[index] = value
|
|
OpArrayLen // Get array length
|
|
|
|
// Type Operations
|
|
OpGetType // Get type of value on stack
|
|
OpCast // Cast value to type [typeId]
|
|
OpIsType // Check if value is type [typeId]
|
|
|
|
// Type Checks (faster than generic OpGetType)
|
|
OpIsNumber // Check if top of stack is number
|
|
OpIsString // Check if top of stack is string
|
|
OpIsTable // Check if top of stack is table
|
|
OpIsBool // Check if top of stack is bool
|
|
OpIsNil // Check if top of stack is nil
|
|
|
|
// I/O Operations
|
|
OpEcho // Echo value to output
|
|
OpExit // Exit with code
|
|
|
|
// Special Operations
|
|
OpNoop // No operation
|
|
OpBreak // Break from loop
|
|
OpContinue // Continue loop iteration
|
|
|
|
// Debug Operations
|
|
OpDebugPrint // Debug print stack top
|
|
OpDebugStack // Debug print entire stack
|
|
)
|
|
|
|
// Instruction represents a single bytecode instruction with operands
|
|
type Instruction struct {
|
|
Op Opcode
|
|
Operands []uint16 // Variable length operands
|
|
}
|
|
|
|
// Chunk represents a compiled chunk of bytecode
|
|
type Chunk struct {
|
|
Code []uint8 // Raw bytecode stream
|
|
Constants []Value // Constant pool
|
|
Lines []int // Line numbers for debugging
|
|
Functions []Function // Function definitions
|
|
Structs []Struct // Struct definitions
|
|
}
|
|
|
|
// Value represents a runtime value in the VM
|
|
type Value struct {
|
|
Type ValueType
|
|
Data any // Actual value data
|
|
}
|
|
|
|
// ValueType represents the type of a runtime value
|
|
type ValueType uint8
|
|
|
|
const (
|
|
ValueNil ValueType = iota
|
|
ValueBool
|
|
ValueNumber
|
|
ValueString
|
|
ValueTable
|
|
ValueFunction
|
|
ValueStruct
|
|
ValueArray
|
|
ValueUpvalue
|
|
)
|
|
|
|
// Function represents a compiled function
|
|
type Function struct {
|
|
Name string // Function name (empty for anonymous)
|
|
Arity int // Number of parameters
|
|
Variadic bool // Whether function accepts variable args
|
|
LocalCount int // Number of local variable slots
|
|
UpvalCount int // Number of upvalues
|
|
Chunk Chunk // Function bytecode
|
|
Defaults []Value // Default parameter values
|
|
}
|
|
|
|
// Struct represents a compiled struct definition
|
|
type Struct struct {
|
|
Name string // Struct name
|
|
Fields []StructField // Field definitions
|
|
Methods map[string]uint16 // Method name -> function index
|
|
ID uint16 // Unique struct identifier
|
|
}
|
|
|
|
// StructField represents a field in a struct
|
|
type StructField struct {
|
|
Name string // Field name
|
|
Type ValueType // Field type
|
|
Offset uint16 // Offset in struct layout
|
|
}
|
|
|
|
// Table represents a key-value table/map
|
|
type Table struct {
|
|
Array map[int]Value // Array part (integer keys)
|
|
Hash map[string]Value // Hash part (string keys)
|
|
Meta *Table // Metatable for operations
|
|
}
|
|
|
|
// Array represents a dynamic array
|
|
type Array struct {
|
|
Elements []Value // Array elements
|
|
Count int // Current element count
|
|
Capacity int // Current capacity
|
|
}
|
|
|
|
// StructInstance represents an instance of a struct
|
|
type StructInstance struct {
|
|
StructID uint16 // Reference to struct definition
|
|
Fields map[string]Value // Field values
|
|
}
|
|
|
|
// Upvalue represents a captured variable
|
|
type Upvalue struct {
|
|
Location *Value // Pointer to actual value location
|
|
Closed Value // Closed-over value (when moved to heap)
|
|
IsClosed bool // Whether upvalue has been closed
|
|
}
|
|
|
|
// Instruction encoding helpers
|
|
|
|
// EncodeInstruction encodes an instruction into bytecode
|
|
func EncodeInstruction(op Opcode, operands ...uint16) []uint8 {
|
|
bytes := []uint8{uint8(op)}
|
|
for _, operand := range operands {
|
|
bytes = append(bytes, uint8(operand&0xFF), uint8(operand>>8))
|
|
}
|
|
return bytes
|
|
}
|
|
|
|
// DecodeInstruction decodes bytecode into instruction
|
|
func DecodeInstruction(code []uint8, offset int) (Opcode, []uint16, int) {
|
|
if offset >= len(code) {
|
|
return OpNoop, nil, offset
|
|
}
|
|
|
|
op := Opcode(code[offset])
|
|
operands := []uint16{}
|
|
nextOffset := offset + 1
|
|
|
|
// Decode operands based on instruction type
|
|
operandCount := GetOperandCount(op)
|
|
for range operandCount {
|
|
if nextOffset+1 >= len(code) {
|
|
break
|
|
}
|
|
operand := uint16(code[nextOffset]) | (uint16(code[nextOffset+1]) << 8)
|
|
operands = append(operands, operand)
|
|
nextOffset += 2
|
|
}
|
|
|
|
return op, operands, nextOffset
|
|
}
|
|
|
|
// GetOperandCount returns the number of operands for an instruction
|
|
func GetOperandCount(op Opcode) int {
|
|
switch op {
|
|
// No operand instructions
|
|
case OpPop, OpDup, OpAdd, OpSub, OpMul, OpDiv, OpNeg, OpMod,
|
|
OpEq, OpNeq, OpLt, OpLte, OpGt, OpGte, OpNot, OpAnd, OpOr,
|
|
OpReturn, OpReturnNil, OpNewTable, OpGetIndex, OpSetIndex,
|
|
OpTableInsert, OpArrayAppend, OpArrayGet, OpArraySet, OpArrayLen,
|
|
OpGetType, OpIsNumber, OpIsString, OpIsTable, OpIsBool, OpIsNil,
|
|
OpEcho, OpExit, OpNoop, OpBreak, OpContinue, OpDebugPrint, OpDebugStack,
|
|
OpLoadLocal0, OpLoadLocal1, OpLoadLocal2, OpStoreLocal0, OpStoreLocal1, OpStoreLocal2,
|
|
OpLoadTrue, OpLoadFalse, OpLoadNil, OpLoadZero, OpLoadOne:
|
|
return 0
|
|
|
|
// Single operand instructions
|
|
case OpLoadConst, OpLoadLocal, OpStoreLocal, OpLoadGlobal, OpStoreGlobal,
|
|
OpJump, OpJumpIfTrue, OpJumpIfFalse, OpCall, OpGetField, OpSetField,
|
|
OpNewStruct, OpGetProperty, OpSetProperty, OpNewArray, OpCast, OpIsType,
|
|
OpAddConst, OpSubConst, OpInc, OpDec, OpGetConstField, OpCallLocal0, OpCallLocal1:
|
|
return 1
|
|
|
|
// Two operand instructions
|
|
case OpCallMethod, OpClosure, OpTestAndJump, OpGetLocalField, OpSetLocalField:
|
|
return 2
|
|
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// Instruction size calculation
|
|
func InstructionSize(op Opcode) int {
|
|
return 1 + (GetOperandCount(op) * 2) // 1 byte opcode + 2 bytes per operand
|
|
}
|
|
|
|
// Check if instruction is a specialized version of another
|
|
func IsSpecializedInstruction(op Opcode) bool {
|
|
switch op {
|
|
case OpLoadLocal0, OpLoadLocal1, OpLoadLocal2,
|
|
OpStoreLocal0, OpStoreLocal1, OpStoreLocal2,
|
|
OpLoadTrue, OpLoadFalse, OpLoadNil, OpLoadZero, OpLoadOne,
|
|
OpAddConst, OpSubConst, OpInc, OpDec,
|
|
OpGetLocalField, OpSetLocalField, OpGetConstField,
|
|
OpCallLocal0, OpCallLocal1, OpTestAndJump, OpLoopBack:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
var opcodeNames = map[Opcode]string{
|
|
OpLoadConst: "OP_LOAD_CONST",
|
|
OpLoadLocal: "OP_LOAD_LOCAL",
|
|
OpStoreLocal: "OP_STORE_LOCAL",
|
|
OpLoadLocal0: "OP_LOAD_LOCAL_0",
|
|
OpLoadLocal1: "OP_LOAD_LOCAL_1",
|
|
OpLoadLocal2: "OP_LOAD_LOCAL_2",
|
|
OpStoreLocal0: "OP_STORE_LOCAL_0",
|
|
OpStoreLocal1: "OP_STORE_LOCAL_1",
|
|
OpStoreLocal2: "OP_STORE_LOCAL_2",
|
|
OpLoadTrue: "OP_LOAD_TRUE",
|
|
OpLoadFalse: "OP_LOAD_FALSE",
|
|
OpLoadNil: "OP_LOAD_NIL",
|
|
OpLoadZero: "OP_LOAD_ZERO",
|
|
OpLoadOne: "OP_LOAD_ONE",
|
|
OpAdd: "OP_ADD",
|
|
OpSub: "OP_SUB",
|
|
OpMul: "OP_MUL",
|
|
OpDiv: "OP_DIV",
|
|
OpAddConst: "OP_ADD_CONST",
|
|
OpSubConst: "OP_SUB_CONST",
|
|
OpInc: "OP_INC",
|
|
OpDec: "OP_DEC",
|
|
OpJump: "OP_JUMP",
|
|
OpJumpIfTrue: "OP_JUMP_TRUE",
|
|
OpJumpIfFalse: "OP_JUMP_FALSE",
|
|
OpTestAndJump: "OP_TEST_AND_JUMP",
|
|
OpLoopBack: "OP_LOOP_BACK",
|
|
OpReturn: "OP_RETURN",
|
|
OpGetLocalField: "OP_GET_LOCAL_FIELD",
|
|
OpSetLocalField: "OP_SET_LOCAL_FIELD",
|
|
OpCallLocal0: "OP_CALL_LOCAL_0",
|
|
OpCallLocal1: "OP_CALL_LOCAL_1",
|
|
OpEcho: "OP_ECHO",
|
|
}
|