package types import "fmt" // ValueType represents the type of a value type ValueType byte const ( TypeNull ValueType = iota TypeNumber TypeString TypeBoolean TypeTable ) type Opcode byte const ( OpConstant Opcode = iota OpSetLocal OpGetLocal OpSetGlobal OpGetGlobal OpEcho OpNewTable OpSetIndex OpGetIndex OpDup OpPop OpEnterScope OpExitScope OpAdd OpSubtract OpMultiply OpDivide OpNegate OpJumpIfFalse OpJump OpEqual OpNotEqual OpLessThan OpGreaterThan OpLessEqual OpGreaterEqual OpNot ) type Instruction struct { Opcode Opcode Operand int } type Bytecode struct { Constants []any Instructions []Instruction } type Value struct { Type ValueType Data any } // Equal checks if two values are equal func (v Value) Equal(other Value) bool { if v.Type != other.Type { return false } switch v.Type { case TypeNull: return true // null == null case TypeNumber: return v.Data.(float64) == other.Data.(float64) case TypeString: return v.Data.(string) == other.Data.(string) case TypeBoolean: return v.Data.(bool) == other.Data.(bool) case TypeTable: return v.Data.(*Table).Equal(other.Data.(*Table)) default: return false } } func NewNull() Value { return Value{Type: TypeNull, Data: nil} } func NewString(s string) Value { return Value{Type: TypeString, Data: s} } func NewNumber(n float64) Value { return Value{Type: TypeNumber, Data: n} } func NewBoolean(b bool) Value { return Value{Type: TypeBoolean, Data: b} } // TableEntry maintains insertion order type TableEntry struct { Key Value Value Value } // Table with ordered entries type Table struct { Entries []TableEntry // Preserves insertion order HashMap map[string]int // Fast lookups for string keys NumMap map[float64]int // Fast lookups for number keys BoolMap map[bool]int // Fast lookups for boolean keys // We can't have a map for table keys since they're not comparable in Go // We'll handle table keys by linear search in Entries } // Equal compares two tables for equality func (t *Table) Equal(other *Table) bool { if len(t.Entries) != len(other.Entries) { return false } // Check if every key-value pair in t exists in other for _, entry := range t.Entries { found := false for _, otherEntry := range other.Entries { if entry.Key.Equal(otherEntry.Key) && entry.Value.Equal(otherEntry.Value) { found = true break } } if !found { return false } } return true } func NewTable() *Table { return &Table{ Entries: []TableEntry{}, HashMap: make(map[string]int), NumMap: make(map[float64]int), BoolMap: make(map[bool]int), } } func NewTableValue() Value { return Value{Type: TypeTable, Data: NewTable()} } // SetKey is a helper to generate a string representation of a key for maps func (t *Table) SetKey(key Value) string { switch key.Type { case TypeString: return "s:" + key.Data.(string) case TypeNumber: return "n:" + fmt.Sprintf("%v", key.Data.(float64)) case TypeBoolean: if key.Data.(bool) { return "b:true" } return "b:false" case TypeNull: return "null" case TypeTable: // For tables, we can use a simple hash or just indicate it's a table return "t:table" // This won't distinguish between different tables default: return "unknown" } } // TableSet preserves insertion order func (t *Table) Set(key, value Value) { idx := -1 // Check if the key is a table if key.Type == TypeTable { // For table keys, we need to do a linear search for i, entry := range t.Entries { if entry.Key.Type == TypeTable && entry.Key.Data.(*Table).Equal(key.Data.(*Table)) { idx = i break } } } else { // Use the existing maps for other types switch key.Type { case TypeString: if i, ok := t.HashMap[key.Data.(string)]; ok { idx = i } case TypeNumber: if i, ok := t.NumMap[key.Data.(float64)]; ok { idx = i } case TypeBoolean: if i, ok := t.BoolMap[key.Data.(bool)]; ok { idx = i } } } if idx >= 0 { // Update existing entry t.Entries[idx].Value = value } else { // Add new entry t.Entries = append(t.Entries, TableEntry{Key: key, Value: value}) idx = len(t.Entries) - 1 // Update lookup maps switch key.Type { case TypeString: t.HashMap[key.Data.(string)] = idx case TypeNumber: t.NumMap[key.Data.(float64)] = idx case TypeBoolean: t.BoolMap[key.Data.(bool)] = idx } } } func (t *Table) Get(key Value) Value { // Check if the key is a table if key.Type == TypeTable { // For table keys, we need to do a linear search for _, entry := range t.Entries { if entry.Key.Type == TypeTable && entry.Key.Data.(*Table).Equal(key.Data.(*Table)) { return entry.Value } } return NewNull() } // Use the existing maps for other types switch key.Type { case TypeString: if i, ok := t.HashMap[key.Data.(string)]; ok { return t.Entries[i].Value } case TypeNumber: if i, ok := t.NumMap[key.Data.(float64)]; ok { return t.Entries[i].Value } case TypeBoolean: if i, ok := t.BoolMap[key.Data.(bool)]; ok { return t.Entries[i].Value } } return NewNull() }