Mako/types/types.go
2025-05-06 17:01:47 -05:00

256 lines
5.0 KiB
Go

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()
}