Mako/vm/vm.go

278 lines
6.4 KiB
Go

package vm
import (
"fmt"
"git.sharkk.net/Sharkk/Mako/types"
)
// Scope represents a lexical scope
type Scope struct {
Variables map[string]types.Value
}
type VM struct {
constants []any
globals map[string]types.Value
scopes []Scope // Stack of local scopes
stack []types.Value
sp int // Stack pointer
}
func New() *VM {
return &VM{
globals: make(map[string]types.Value),
scopes: []Scope{}, // Initially no scopes
stack: make([]types.Value, 1024),
sp: 0,
}
}
func (vm *VM) Run(bytecode *types.Bytecode) {
vm.constants = bytecode.Constants
for ip := 0; ip < len(bytecode.Instructions); ip++ {
instruction := bytecode.Instructions[ip]
switch instruction.Opcode {
case types.OpConstant:
constIndex := instruction.Operand
constant := vm.constants[constIndex]
switch v := constant.(type) {
case string:
vm.push(types.NewString(v))
case float64:
vm.push(types.NewNumber(v))
}
case types.OpSetLocal:
constIndex := instruction.Operand
name := vm.constants[constIndex].(string)
value := vm.pop()
// Set in current scope if it exists
if len(vm.scopes) > 0 {
vm.scopes[len(vm.scopes)-1].Variables[name] = value
} else {
// No scope, set as global
vm.globals[name] = value
}
case types.OpGetLocal:
constIndex := instruction.Operand
name := vm.constants[constIndex].(string)
// Check local scopes from innermost to outermost
found := false
for i := len(vm.scopes) - 1; i >= 0; i-- {
if val, ok := vm.scopes[i].Variables[name]; ok {
vm.push(val)
found = true
break
}
}
// If not found in locals, check globals
if !found {
if val, ok := vm.globals[name]; ok {
vm.push(val)
} else {
vm.push(types.NewNull())
}
}
case types.OpSetGlobal:
constIndex := instruction.Operand
name := vm.constants[constIndex].(string)
value := vm.pop()
vm.globals[name] = value
case types.OpGetGlobal:
constIndex := instruction.Operand
name := vm.constants[constIndex].(string)
if val, ok := vm.globals[name]; ok {
vm.push(val)
} else {
vm.push(types.NewNull())
}
case types.OpEnterScope:
// Push a new scope
vm.scopes = append(vm.scopes, Scope{
Variables: make(map[string]types.Value),
})
case types.OpExitScope:
// Pop the current scope
if len(vm.scopes) > 0 {
vm.scopes = vm.scopes[:len(vm.scopes)-1]
}
case types.OpNewTable:
vm.push(types.NewTableValue())
case types.OpSetIndex:
value := vm.pop()
key := vm.pop()
tableVal := vm.pop()
if tableVal.Type != types.TypeTable {
fmt.Println("Error: attempt to index non-table value")
vm.push(types.NewNull())
continue
}
table := tableVal.Data.(*types.Table)
table.Set(key, value)
vm.push(tableVal)
case types.OpGetIndex:
key := vm.pop()
tableVal := vm.pop()
if tableVal.Type != types.TypeTable {
fmt.Println("Error: attempt to index non-table value")
vm.push(types.NewNull())
continue
}
table := tableVal.Data.(*types.Table)
value := table.Get(key)
vm.push(value)
case types.OpDup:
if vm.sp > 0 {
vm.push(vm.stack[vm.sp-1])
}
case types.OpPop:
vm.pop()
case types.OpEcho:
value := vm.pop()
switch value.Type {
case types.TypeString:
fmt.Println(value.Data.(string))
case types.TypeNumber:
fmt.Println(value.Data.(float64))
case types.TypeBoolean:
fmt.Println(value.Data.(bool))
case types.TypeNull:
fmt.Println("null")
case types.TypeTable:
fmt.Println(vm.formatTable(value.Data.(*types.Table)))
}
// Arithmetic operations
case types.OpAdd:
right := vm.pop()
left := vm.pop()
if left.Type == types.TypeNumber && right.Type == types.TypeNumber {
result := left.Data.(float64) + right.Data.(float64)
vm.push(types.NewNumber(result))
} else if left.Type == types.TypeString && right.Type == types.TypeString {
// String concatenation
result := left.Data.(string) + right.Data.(string)
vm.push(types.NewString(result))
} else {
fmt.Println("Error: cannot add values of different types")
vm.push(types.NewNull())
}
case types.OpSubtract:
right := vm.pop()
left := vm.pop()
if left.Type == types.TypeNumber && right.Type == types.TypeNumber {
result := left.Data.(float64) - right.Data.(float64)
vm.push(types.NewNumber(result))
} else {
fmt.Println("Error: cannot subtract non-number values")
vm.push(types.NewNull())
}
case types.OpMultiply:
right := vm.pop()
left := vm.pop()
if left.Type == types.TypeNumber && right.Type == types.TypeNumber {
result := left.Data.(float64) * right.Data.(float64)
vm.push(types.NewNumber(result))
} else {
fmt.Println("Error: cannot multiply non-number values")
vm.push(types.NewNull())
}
case types.OpDivide:
right := vm.pop()
left := vm.pop()
if left.Type == types.TypeNumber && right.Type == types.TypeNumber {
// Check for division by zero
if right.Data.(float64) == 0 {
fmt.Println("Error: division by zero")
vm.push(types.NewNull())
} else {
result := left.Data.(float64) / right.Data.(float64)
vm.push(types.NewNumber(result))
}
} else {
fmt.Println("Error: cannot divide non-number values")
vm.push(types.NewNull())
}
case types.OpNegate:
operand := vm.pop()
if operand.Type == types.TypeNumber {
result := -operand.Data.(float64)
vm.push(types.NewNumber(result))
} else {
fmt.Println("Error: cannot negate non-number value")
vm.push(types.NewNull())
}
}
}
}
func (vm *VM) push(value types.Value) {
vm.stack[vm.sp] = value
vm.sp++
}
func (vm *VM) pop() types.Value {
vm.sp--
return vm.stack[vm.sp]
}
func (vm *VM) formatTable(table *types.Table) string {
result := "{"
for i, entry := range table.Entries {
result += vm.formatValue(entry.Key) + " = " + vm.formatValue(entry.Value)
if i < len(table.Entries)-1 {
result += ", "
}
}
result += "}"
return result
}
func (vm *VM) formatValue(value types.Value) string {
switch value.Type {
case types.TypeString:
return "\"" + value.Data.(string) + "\""
case types.TypeNumber:
return fmt.Sprintf("%v", value.Data.(float64))
case types.TypeBoolean:
return fmt.Sprintf("%v", value.Data.(bool))
case types.TypeNull:
return "null"
case types.TypeTable:
return vm.formatTable(value.Data.(*types.Table))
default:
return "unknown"
}
}