package vm import ( "jinx/pkg/lang/vm/code" "jinx/pkg/lang/vm/mem" "jinx/pkg/lang/vm/value" ) func (vm *VM) execPushInt(x int64) { vm.stack.Push(value.NewInt(x)) } func (vm *VM) execPushFloat(x float64) { vm.stack.Push(value.NewFloat(x)) } func (vm *VM) execPushString(str string) error { val, err := value.NewString(&vm.memory, str) if err != nil { return err } vm.stack.Push(val) return nil } func (vm *VM) execPushBool(b bool) { vm.stack.Push(value.NewBool(b)) } func (vm *VM) execPushNull() { vm.stack.Push(value.NewNull()) } func (vm *VM) execPushArray() error { val, err := value.NewArray(&vm.memory, []value.Value{}) if err != nil { return err } vm.stack.Push(val) return nil } func (vm *VM) execPushFunction(pc int) { vm.stack.Push(value.NewFunction(pc)) } func (vm *VM) execGetLocal(offset int) error { local, err := vm.stack.Local(int(offset)) if err != nil { return err } v := local.Clone(&vm.memory) vm.stack.Push(v) return nil } func (vm *VM) execGetEnv(envIndex int) error { envCell, err := vm.getMemCell(vm.stack.CurrentCallEnv(), mem.CellKindEnv, false) if err != nil { return err } env := envCell.(value.EnvCell).Get() // First check outlet. outletPtr := env.GetOutlet(envIndex) outletCell, err := vm.getMemCell(outletPtr, mem.CellKindOutlet, true) if err != nil { return err } if outletCell != nil { // Outlet is not null, so value escaped. outlet := outletCell.(value.OutletCell).Get() val := outlet.Clone(&vm.memory) vm.stack.Push(val) return nil } // Outlet is null, so value has not escaped. stackIndex := env.GetStackIndex(envIndex) val, err := vm.stack.Get(stackIndex) if err != nil { return err } val = val.Clone(&vm.memory) vm.stack.Push(val) return nil } func (vm *VM) execSetEnv(envIndex int) error { new, err := vm.stack.Pop() if err != nil { return err } envCell, err := vm.getMemCell(vm.stack.CurrentCallEnv(), mem.CellKindEnv, false) if err != nil { return err } env := envCell.(value.EnvCell).Get() outletPtr := env.GetOutlet(envIndex) outletCell, err := vm.getMemCell(outletPtr, mem.CellKindOutlet, true) if err != nil { return err } if outletCell != nil { // Outlet is not null, so value escaped. outlet := outletCell.(value.OutletCell).Get() outlet.Drop(&vm.memory) vm.memory.Set(outletPtr, value.OutletCell(new)) return nil } stackIndex := env.GetStackIndex(envIndex) old, err := vm.stack.Get(stackIndex) if err != nil { return err } old.Drop(&vm.memory) return vm.stack.Set(stackIndex, new) } func (vm *VM) execAddToEnv(localIndex int) error { f, err := vm.stack.Pop() if err != nil { return err } if f.Type().Kind != value.FunctionType { return ErrInvalidOperandType{ Op: code.OpAddToEnv, X: f.Type(), } } fn := f.Data().(value.FunctionData) var envPtr mem.Ptr if fn.Env().IsNull() { // Allocate new Env. envPtr, err = vm.memory.Allocate(mem.CellKindEnv) if err != nil { return err } vm.memory.Set(envPtr, value.EnvCell(value.NewEnv())) fn = fn.WithEnv(envPtr) } else { envPtr = fn.Env() } // Create outlet for referenced local, or use existing one. local, err := vm.stack.Local(int(localIndex)) if err != nil { return err } if local.Outlet().IsNull() { outletPtr, err := vm.memory.Allocate(mem.CellKindOutlet) if err != nil { return err } local = local.WithOutlet(outletPtr) } // Add local to env. stackIndex := vm.stack.LocalToStackIndex(localIndex) envCell, err := vm.getMemCell(envPtr, mem.CellKindEnv, false) if err != nil { return err } env := envCell.(value.EnvCell).Get() env.Add(stackIndex, local.Outlet()) vm.memory.Set(envPtr, value.EnvCell(env)) // Push function back onto stack. f = f.WithData(fn) vm.stack.Push(f) vm.stack.Set(stackIndex, local) return nil } func (vm *VM) execAdd() error { x, err := vm.popAndDrop() if err != nil { return err } y, err := vm.popAndDrop() if err != nil { return err } var res value.Value switch x.Type().Kind { case value.IntType: xv := x.Data().(value.IntData).Get() switch y.Type().Kind { case value.IntType: yv := y.Data().(value.IntData).Get() res = value.NewInt(xv + yv) case value.FloatType: yv := y.Data().(value.FloatData).Get() res = value.NewFloat(float64(xv) + yv) default: return ErrInvalidOperandTypes{ Op: code.OpAdd, X: x.Type(), Y: y.Type(), } } case value.FloatType: xv := x.Data().(value.FloatData).Get() switch y.Type().Kind { case value.IntType: yv := y.Data().(value.IntData).Get() res = value.NewFloat(xv + float64(yv)) case value.FloatType: yv := y.Data().(value.FloatData).Get() res = value.NewFloat(xv + yv) default: return ErrInvalidOperandTypes{ Op: code.OpAdd, X: x.Type(), Y: y.Type(), } } case value.StringType: switch y.Type().Kind { case value.StringType: panic("not implemented") // res = value.NewString(x.Data().(value.StringData).Get() + y.Data().(value.StringData).Get()) default: return ErrInvalidOperandTypes{ Op: code.OpAdd, X: x.Type(), Y: y.Type(), } } default: return ErrInvalidOperandTypes{ Op: code.OpAdd, X: x.Type(), Y: y.Type(), } } vm.stack.Push(res) return nil } func (vm *VM) execSub() error { x, err := vm.popAndDrop() if err != nil { return err } y, err := vm.popAndDrop() if err != nil { return err } var res value.Value switch x.Type().Kind { case value.IntType: xv := x.Data().(value.IntData).Get() switch y.Type().Kind { case value.IntType: yv := y.Data().(value.IntData).Get() res = value.NewInt(xv - yv) case value.FloatType: yv := y.Data().(value.FloatData).Get() res = value.NewFloat(float64(xv) - yv) default: return ErrInvalidOperandTypes{ Op: code.OpSub, X: x.Type(), Y: y.Type(), } } case value.FloatType: xv := x.Data().(value.FloatData).Get() switch y.Type().Kind { case value.IntType: yv := y.Data().(value.IntData).Get() res = value.NewFloat(xv - float64(yv)) case value.FloatType: yv := y.Data().(value.FloatData).Get() res = value.NewFloat(xv - yv) default: return ErrInvalidOperandTypes{ Op: code.OpSub, X: x.Type(), Y: y.Type(), } } default: return ErrInvalidOperandTypes{ Op: code.OpSub, X: x.Type(), Y: y.Type(), } } vm.stack.Push(res) return nil } func (vm *VM) execIndex() error { v, err := vm.popAndDrop() if err != nil { return err } i, err := vm.popAndDrop() if err != nil { return err } switch v.Type().Kind { case value.ArrayType: arr := v.Data().(value.ArrayData) switch i.Type().Kind { case value.IntType: idx := i.Data().(value.IntData).Get() len, err := arr.Len(&vm.memory) if err != nil { return err } if idx < 0 || idx >= int64(len) { return ErrArrayIndexOutOfBounds{ Index: int(idx), Len: int(len), } } val, err := arr.At(&vm.memory, int(idx)) if err != nil { return err } val = val.Clone(&vm.memory) vm.stack.Push(val) default: return ErrInvalidOperandTypes{ Op: code.OpIndex, X: i.Type(), Y: v.Type(), } } default: return ErrInvalidOperandTypes{ Op: code.OpIndex, X: v.Type(), Y: i.Type(), } } return nil } func (vm *VM) execLte() error { x, err := vm.popAndDrop() if err != nil { return err } y, err := vm.popAndDrop() if err != nil { return err } var res value.Value switch x.Type().Kind { case value.IntType: xv := x.Data().(value.IntData).Get() switch y.Type().Kind { case value.IntType: yv := y.Data().(value.IntData).Get() res = value.NewBool(xv <= yv) case value.FloatType: yv := y.Data().(value.FloatData).Get() res = value.NewBool(float64(xv) <= yv) default: return ErrInvalidOperandTypes{ Op: code.OpLte, X: x.Type(), Y: y.Type(), } } case value.FloatType: xv := x.Data().(value.FloatData).Get() switch y.Type().Kind { case value.IntType: yv := y.Data().(value.IntData).Get() res = value.NewBool(xv <= float64(yv)) case value.FloatType: yv := y.Data().(value.FloatData).Get() res = value.NewBool(xv <= yv) default: return ErrInvalidOperandTypes{ Op: code.OpLte, X: x.Type(), Y: y.Type(), } } default: return ErrInvalidOperandTypes{ Op: code.OpLte, X: x.Type(), Y: y.Type(), } } vm.stack.Push(res) return nil } func (vm *VM) execCall() error { f, err := vm.stack.Pop() if err != nil { return err } if f.Type().Kind != value.FunctionType { return ErrInvalidOperandType{ Op: code.OpCall, X: f.Type(), } } fn := f.Data().(value.FunctionData) if err = vm.stack.PushCall(fn.Pc(), vm.pc, fn.Env()); err != nil { return err } vm.pc = fn.Pc() return nil } func (vm *VM) execJumpIf(pc int, cond bool) error { b, err := vm.popAndDrop() if err != nil { return err } switch b.Type().Kind { case value.BoolType: bl := b.Data().(value.BoolData) if bl.Get() == cond { vm.pc = pc } default: var op code.Op if cond { op = code.OpJt } else { op = code.OpJf } return ErrInvalidOperandType{ Op: op, X: b.Type(), } } return nil } func (vm *VM) execRet() error { returned, err := vm.stack.Pop() if err != nil { return err } pc, err := vm.popCallAndDrop() if err != nil { return err } vm.stack.Push(returned) vm.pc = pc return nil } func (vm *VM) execTempArrLen() error { a, err := vm.popAndDrop() if err != nil { return err } switch a.Type().Kind { case value.ArrayType: arr := a.Data().(value.ArrayData) len, err := arr.Len(&vm.memory) if err != nil { return err } res := value.NewInt(int64(len)) vm.stack.Push(res) default: return ErrInvalidOperandTypes{ Op: code.OpTempArrLen, X: a.Type(), } } return nil } func (vm *VM) execTempArrPush() error { a, err := vm.popAndDrop() if err != nil { return err } e, err := vm.popAndDrop() if err != nil { return err } switch a.Type().Kind { case value.ArrayType: arr := a.Data().(value.ArrayData) arr.Push(&vm.memory, e) default: return ErrInvalidOperandType{ Op: code.OpTempArrPush, X: a.Type(), } } return nil }