| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- // Author: simon
- // Author: ynwdlxm@163.com
- // Date: 2022/10/22 13:55
- // Desc: Stack VM implement
- package vm
- import (
- "fmt"
- "github/runnignwater/monkey/code"
- "github/runnignwater/monkey/compiler"
- "github/runnignwater/monkey/object"
- )
- const StackSize = 2048
- const GlobalSize = 65535
- const MaxFrames = 1024
- var True = &object.Boolean{Value: true}
- var False = &object.Boolean{Value: false}
- var Null = &object.Null{}
- type VM struct {
- constants []object.Object
- instructions code.Instructions
- stack []object.Object
- sp int // Always points to the next value. Top of stack is stack[sp-1]
- globals []object.Object
- frames []*Frame
- framesIndex int
- }
- func New(byteCode *compiler.ByteCode) *VM {
- mainFn := &object.CompileFunction{Instructions: byteCode.Instructions}
- mainFrame := NewFrame(mainFn, 0)
- frames := make([]*Frame, MaxFrames)
- frames[0] = mainFrame
- return &VM{
- instructions: byteCode.Instructions,
- constants: byteCode.Constants,
- stack: make([]object.Object, StackSize),
- sp: 0,
- globals: make([]object.Object, GlobalSize),
- frames: frames,
- framesIndex: 1,
- }
- }
- func NewWithGlobalsStore(byteCode *compiler.ByteCode, s []object.Object) *VM {
- vm := New(byteCode)
- vm.globals = s
- return vm
- }
- // func (vm *VM) StackTop() object.Object {
- // if vm.sp == 0 {
- // return nil
- // }
- // return vm.stack[vm.sp-1]
- // }
- func (vm *VM) Run() error {
- var ip int
- var ins code.Instructions
- var op code.Opcode
- for vm.currentFrame().ip < len(vm.currentFrame().Instructions())-1 {
- vm.currentFrame().ip++
- ip = vm.currentFrame().ip
- ins = vm.currentFrame().Instructions()
- op = code.Opcode(ins[ip])
- switch op {
- case code.OpConstant:
- constIndex := code.ReadUint16(ins[ip+1:])
- vm.currentFrame().ip += 2
- // 执行
- err := vm.push(vm.constants[constIndex])
- if err != nil {
- return err
- }
- case code.OpTrue:
- err := vm.push(True)
- if err != nil {
- return err
- }
- case code.OpFalse:
- err := vm.push(False)
- if err != nil {
- return err
- }
- case code.OpAdd, code.OpSub, code.OpMul, code.OpDiv:
- err := vm.executeBinaryOperation(op)
- if err != nil {
- return err
- }
- case code.OpEqual, code.OpNotEqual, code.OpGreaterThan:
- err := vm.executeComparison(op)
- if err != nil {
- return err
- }
- case code.OpMinus:
- err := vm.executeMinusOperator()
- if err != nil {
- return err
- }
- case code.OpBang:
- err := vm.executeBangOperator()
- if err != nil {
- return err
- }
- case code.OpPop:
- vm.pop()
- case code.OpJump:
- pos := int(code.ReadUint16(ins[ip+1:]))
- vm.currentFrame().ip = pos - 1
- case code.OpJumpNotTruthy:
- pos := int(code.ReadUint16(ins[ip+1:]))
- vm.currentFrame().ip += 2
- condition := vm.pop()
- if !isTruthy(condition) {
- vm.currentFrame().ip = pos - 1
- }
- case code.OpNull:
- err := vm.push(Null)
- if err != nil {
- return err
- }
- case code.OpSetGlobal:
- globalIndex := code.ReadUint16(ins[ip+1:])
- vm.currentFrame().ip += 2
- vm.globals[globalIndex] = vm.pop()
- case code.OpGetGlobal:
- globalIndex := code.ReadUint16(ins[ip+1:])
- vm.currentFrame().ip += 2
- err := vm.push(vm.globals[globalIndex])
- if err != nil {
- return err
- }
- case code.OpArray:
- numElements := int(code.ReadUint16(ins[ip+1:]))
- vm.currentFrame().ip += 2
- array := vm.buildArray(vm.sp-numElements, vm.sp)
- vm.sp -= numElements
- err := vm.push(array)
- if err != nil {
- return err
- }
- case code.OpHash:
- numElements := int(code.ReadUint16(ins[ip+1:]))
- vm.currentFrame().ip += 2
- hash, err := vm.buildHash(vm.sp-numElements, vm.sp)
- if err != nil {
- return err
- }
- vm.sp -= numElements
- err = vm.push(hash)
- if err != nil {
- return err
- }
- case code.OpIndex:
- index := vm.pop()
- left := vm.pop()
- err := vm.executeIndexExpression(left, index)
- if err != nil {
- return err
- }
- case code.OpCall:
- vm.currentFrame().ip += 1
- fn, ok := vm.stack[vm.sp-1].(*object.CompileFunction)
- if !ok {
- return fmt.Errorf("calling non-function")
- }
- frame := NewFrame(fn, vm.sp)
- vm.pushFrame(frame)
- vm.sp = frame.basePointer + fn.NumLocals
- case code.OpReturnValue:
- returnValue := vm.pop()
- frame := vm.popFrame()
- vm.sp = frame.basePointer - 1
- err := vm.push(returnValue)
- if err != nil {
- return err
- }
- case code.OpReturn:
- frame := vm.popFrame()
- vm.sp = frame.basePointer - 1
- err := vm.push(Null)
- if err != nil {
- return err
- }
- case code.OpSetLocal:
- localIndex := code.ReadUint8(ins[ip+1:])
- vm.currentFrame().ip += 1
- frame := vm.currentFrame()
- vm.stack[frame.basePointer+int(localIndex)] = vm.pop()
- case code.OpGetLocal:
- localIndex := code.ReadUint8(ins[ip+1:])
- vm.currentFrame().ip += 1
- frame := vm.currentFrame()
- err := vm.push(vm.stack[frame.basePointer+int(localIndex)])
- if err != nil {
- return err
- }
- }
- }
- return nil
- }
- func isTruthy(obj object.Object) bool {
- switch obj := obj.(type) {
- case *object.Boolean:
- return obj.Value
- case *object.Null:
- return false
- default:
- return true
- }
- }
- func (vm *VM) currentFrame() *Frame {
- return vm.frames[vm.framesIndex-1]
- }
- func (vm *VM) pushFrame(f *Frame) {
- vm.frames[vm.framesIndex] = f
- vm.framesIndex++
- }
- func (vm *VM) popFrame() *Frame {
- vm.framesIndex--
- return vm.frames[vm.framesIndex]
- }
- func (vm *VM) push(o object.Object) error {
- if vm.sp >= StackSize {
- return fmt.Errorf("stack overflow")
- }
- vm.stack[vm.sp] = o
- vm.sp++
- return nil
- }
- func (vm *VM) pop() object.Object {
- o := vm.stack[vm.sp-1]
- vm.sp--
- return o
- }
- func (vm *VM) executeIndexExpression(left, index object.Object) error {
- switch {
- case left.Type() == object.ArrayObj && index.Type() == object.IntegerObj:
- return vm.executeArrayIndex(left, index)
- case left.Type() == object.HashObj:
- return vm.executeHashIndex(left, index)
- default:
- return fmt.Errorf("index operator not supported: %s", left.Type())
- }
- }
- func (vm *VM) buildArray(startIndex, endIndex int) object.Object {
- elements := make([]object.Object, endIndex-startIndex)
- for i := startIndex; i < endIndex; i++ {
- elements[i-startIndex] = vm.stack[i]
- }
- return &object.Array{Elements: elements}
- }
- func (vm *VM) buildHash(startIndex, endIndex int) (object.Object, error) {
- hashPairs := make(map[object.HashKey]object.HashPair)
- for i := startIndex; i < endIndex; i += 2 {
- key := vm.stack[i]
- value := vm.stack[i+1]
- pair := object.HashPair{Key: key, Value: value}
- hashKey, ok := key.(object.Hashtable)
- if !ok {
- return nil, fmt.Errorf("unusable as hash key: %s", key.Type())
- }
- hashPairs[hashKey.HashKey()] = pair
- }
- return &object.Hash{Pairs: hashPairs}, nil
- }
- func (vm *VM) executeBinaryOperation(op code.Opcode) error {
- right := vm.pop()
- left := vm.pop()
- leftType := left.Type()
- rightType := right.Type()
- switch {
- case leftType == object.IntegerObj && rightType == object.IntegerObj:
- return vm.executeBinaryIntegerOperation(op, left, right)
- case leftType == object.StringObj && rightType == object.StringObj:
- return vm.executeBinaryStringOperation(op, left, right)
- default:
- return fmt.Errorf("unsupported types for binary operation: %s %s", leftType, rightType)
- }
- }
- func (vm *VM) executeComparison(op code.Opcode) error {
- right := vm.pop()
- left := vm.pop()
- if left.Type() == object.IntegerObj || right.Type() == object.IntegerObj {
- return vm.executeIntegerComparison(op, left, right)
- }
- switch op {
- case code.OpEqual:
- return vm.push(nativeBoolToBooleanObject(right == left))
- case code.OpNotEqual:
- return vm.push(nativeBoolToBooleanObject(right != left))
- default:
- return fmt.Errorf("unknown operator: %d (%s %s)", op, left.Type(), right.Type())
- }
- }
- func (vm *VM) LastPopStackElem() object.Object {
- return vm.stack[vm.sp]
- }
- func (vm *VM) executeBinaryIntegerOperation(
- op code.Opcode,
- left, right object.Object,
- ) error {
- leftValue := left.(*object.Integer).Value
- rightValue := right.(*object.Integer).Value
- var result int64
- switch op {
- case code.OpAdd:
- result = leftValue + rightValue
- case code.OpSub:
- result = leftValue - rightValue
- case code.OpMul:
- result = leftValue * rightValue
- case code.OpDiv:
- result = leftValue / rightValue
- default:
- return fmt.Errorf("unknown integer operator: %d", op)
- }
- return vm.push(&object.Integer{Value: result})
- }
- func (vm *VM) executeBinaryStringOperation(
- op code.Opcode,
- left, right object.Object,
- ) error {
- if op != code.OpAdd {
- return fmt.Errorf("unknown string operator: %d", op)
- }
- leftValue := left.(*object.String).Value
- rightValue := right.(*object.String).Value
- return vm.push(&object.String{Value: leftValue + rightValue})
- }
- func (vm *VM) executeIntegerComparison(
- op code.Opcode,
- left, right object.Object,
- ) error {
- leftValue := left.(*object.Integer).Value
- rightValue := right.(*object.Integer).Value
- switch op {
- case code.OpEqual:
- return vm.push(nativeBoolToBooleanObject(rightValue == leftValue))
- case code.OpNotEqual:
- return vm.push(nativeBoolToBooleanObject(rightValue != leftValue))
- case code.OpGreaterThan:
- return vm.push(nativeBoolToBooleanObject(leftValue > rightValue))
- default:
- return fmt.Errorf("unknown operator: %d", op)
- }
- }
- func (vm *VM) executeMinusOperator() error {
- operand := vm.pop()
- if operand.Type() != object.IntegerObj {
- return fmt.Errorf("unsupported type for nagation: %s", operand.Type())
- }
- value := operand.(*object.Integer).Value
- return vm.push(&object.Integer{Value: -value})
- }
- func (vm *VM) executeBangOperator() error {
- operand := vm.pop()
- switch operand {
- case True:
- return vm.push(False)
- case False:
- return vm.push(True)
- case Null:
- return vm.push(True)
- default:
- return vm.push(False)
- }
- }
- func (vm *VM) executeArrayIndex(array, index object.Object) error {
- arrayObject := array.(*object.Array)
- i := index.(*object.Integer).Value
- max := int64(len(arrayObject.Elements) - 1)
- if i < 0 || i > max {
- return vm.push(Null)
- }
- return vm.push(arrayObject.Elements[i])
- }
- func (vm *VM) executeHashIndex(hash, index object.Object) error {
- hashObject := hash.(*object.Hash)
- key, ok := index.(object.Hashtable)
- if !ok {
- return fmt.Errorf("unusable as hash key: %s", index.Type())
- }
- pair, ok := hashObject.Pairs[key.HashKey()]
- if !ok {
- return vm.push(Null)
- }
- return vm.push(pair.Value)
- }
- func nativeBoolToBooleanObject(input bool) *object.Boolean {
- if input {
- return True
- }
- return False
- }
|