diff options
| author | Mel <einebeere@gmail.com> | 2022-05-17 23:07:33 +0200 |
|---|---|---|
| committer | Mel <einebeere@gmail.com> | 2022-05-17 23:07:33 +0200 |
| commit | ec5ee8647bcbf6ab073711c6892710776925c54d (patch) | |
| tree | 1228a72291123e0f520616b9d21f1e013e49351d /pkg | |
| parent | b09a14147d397904722ee7c25e4defc56135b96f (diff) | |
| download | jinx-ec5ee8647bcbf6ab073711c6892710776925c54d.tar.zst jinx-ec5ee8647bcbf6ab073711c6892710776925c54d.zip | |
Lang VM Prototype
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/lang/vm/code/code.go | 59 | ||||
| -rw-r--r-- | pkg/lang/vm/code/op.go | 34 | ||||
| -rw-r--r-- | pkg/lang/vm/exec.go | 135 | ||||
| -rw-r--r-- | pkg/lang/vm/stack.go | 37 | ||||
| -rw-r--r-- | pkg/lang/vm/value/data.go | 17 | ||||
| -rw-r--r-- | pkg/lang/vm/value/type.go | 19 | ||||
| -rw-r--r-- | pkg/lang/vm/value/value.go | 52 | ||||
| -rw-r--r-- | pkg/lang/vm/vm.go | 108 |
8 files changed, 461 insertions, 0 deletions
diff --git a/pkg/lang/vm/code/code.go b/pkg/lang/vm/code/code.go new file mode 100644 index 0000000..ab06d27 --- /dev/null +++ b/pkg/lang/vm/code/code.go @@ -0,0 +1,59 @@ +package code + +import ( + "bytes" + "encoding/binary" + "math" + "strings" +) + +type Code struct { + code []byte +} + +func (c *Code) Len() int { + return len(c.code) +} + +func (c *Code) GetOp(at int) (Op, int) { + return Op(c.code[at]), 1 +} + +func (c *Code) GetUint(at int) (uint64, int) { + advance := 8 + x := binary.LittleEndian.Uint64(c.code[at : at+advance]) + return x, advance +} + +func (c *Code) GetInt(at int) (int64, int) { + x, advance := c.GetUint(at) + return int64(x), advance +} + +func (c *Code) GetFloat(at int) (float64, int) { + x, advance := c.GetUint(at) + return math.Float64frombits(x), advance +} + +func (c *Code) GetString(at int) (string, int) { + advance := 0 + reader := bytes.NewReader(c.code[at:]) + builder := strings.Builder{} + + for { + r, size, err := reader.ReadRune() + advance += size + + if err != nil { + break + } + + if r == 0 { + break + } + + builder.WriteRune(r) + } + + return builder.String(), advance +} diff --git a/pkg/lang/vm/code/op.go b/pkg/lang/vm/code/op.go new file mode 100644 index 0000000..ae37603 --- /dev/null +++ b/pkg/lang/vm/code/op.go @@ -0,0 +1,34 @@ +package code + +type Op uint8 + +const ( + OpNop Op = iota + OpHalt + + OpPushInt + OpPushFloat + OpPushString + OpPushTrue + OpPushFalse + OpPushNull + OpPushArray + OpPushFunction + OpPushObject + + OpGetGlobal + OpGetLocal + OpGetMember + OpGetArg + OpGetEnv + + OpAdd + OpSub + OpIndex + OpCall + + OpJmp + OpJez + + OpRet +) diff --git a/pkg/lang/vm/exec.go b/pkg/lang/vm/exec.go new file mode 100644 index 0000000..241e5f8 --- /dev/null +++ b/pkg/lang/vm/exec.go @@ -0,0 +1,135 @@ +package vm + +import "jinx/pkg/lang/vm/value" + +func (vm *VM) execPushInt(x int64) { + vm.stack.Top().Push(value.NewInt(x)) +} + +func (vm *VM) execPushFloat(x float64) { + vm.stack.Top().Push(value.NewFloat(x)) +} + +func (vm *VM) execPushString(str string) { + vm.stack.Top().Push(value.NewString(str)) +} + +func (vm *VM) execPushBool(b bool) { + vm.stack.Top().Push(value.NewBool(b)) +} + +func (vm *VM) execPushNull() { + vm.stack.Top().Push(value.NewNull()) +} + +func (vm *VM) execPushArray() { + vm.stack.Top().Push(value.NewArray([]value.Value{})) +} + +func (vm *VM) execGetLocal(offset int) { + top := vm.stack.Top() + top.Push(top.At(int(offset))) +} + +func (vm *VM) execGetArg() { + vm.stack.Top().Push(vm.stack.Prev().Pop()) +} + +func (vm *VM) execAdd() { + top := vm.stack.Top() + + x := top.Pop() + y := top.Pop() + + var res value.Value + + switch x.Type().Kind { + case value.IntType: + xv := x.Data().(int64) + switch y.Type().Kind { + case value.IntType: + res = value.NewInt(xv + y.Data().(int64)) + case value.FloatType: + res = value.NewFloat(float64(xv) + y.Data().(float64)) + default: + panic("invalid operand types") + } + case value.FloatType: + xv := x.Data().(float64) + switch y.Type().Kind { + case value.IntType: + res = value.NewFloat(xv + float64(y.Data().(int64))) + case value.FloatType: + res = value.NewFloat(xv + y.Data().(float64)) + default: + panic("invalid operand types") + } + case value.StringType: + switch y.Type().Kind { + case value.StringType: + res = value.NewString(x.Data().(string) + y.Data().(string)) + default: + panic("invalid operand types") + } + default: + panic("invalid operand types") + } + + top.Push(res) +} + +func (vm *VM) execSub() { + top := vm.stack.Top() + + x := top.Pop() + y := top.Pop() + + var res value.Value + + switch x.Type().Kind { + case value.IntType: + xv := x.Data().(int64) + switch y.Type().Kind { + case value.IntType: + res = value.NewInt(xv - y.Data().(int64)) + case value.FloatType: + res = value.NewFloat(float64(xv) - y.Data().(float64)) + default: + panic("invalid operand types") + } + case value.FloatType: + xv := x.Data().(float64) + switch y.Type().Kind { + case value.IntType: + res = value.NewFloat(xv - float64(y.Data().(int64))) + case value.FloatType: + res = value.NewFloat(xv - y.Data().(float64)) + default: + panic("invalid operand types") + } + default: + panic("invalid operand types") + } + + top.Push(res) +} + +func (vm *VM) execIndex() { + top := vm.stack.Top() + + v := top.Pop() + i := top.Pop() + + switch v.Type().Kind { + case value.ArrayType: + arr := v.Data().([]value.Value) + idx := i.Data().(int64) + if idx < 0 || idx >= int64(len(arr)) { + panic("index out of bounds") + } + + top.Push(arr[idx]) + default: + panic("invalid operand types") + } +} diff --git a/pkg/lang/vm/stack.go b/pkg/lang/vm/stack.go new file mode 100644 index 0000000..433c0b7 --- /dev/null +++ b/pkg/lang/vm/stack.go @@ -0,0 +1,37 @@ +package vm + +import "jinx/pkg/lang/vm/value" + +type CallStack []*LocalStack + +func (cs *CallStack) Push() { + *cs = append(*cs, &LocalStack{}) +} + +func (cs *CallStack) Pop() { + *cs = (*cs)[:len(*cs)-1] +} + +func (cs *CallStack) Top() *LocalStack { + return (*cs)[len(*cs)-1] +} + +func (cs *CallStack) Prev() *LocalStack { + return (*cs)[len(*cs)-2] +} + +type LocalStack []value.Value + +func (ls *LocalStack) Push(v value.Value) { + *ls = append(*ls, v) +} + +func (ls *LocalStack) Pop() value.Value { + v := (*ls)[len(*ls)-1] + *ls = (*ls)[:len(*ls)-1] + return v +} + +func (ls *LocalStack) At(at int) value.Value { + return (*ls)[at] +} diff --git a/pkg/lang/vm/value/data.go b/pkg/lang/vm/value/data.go new file mode 100644 index 0000000..1d63d32 --- /dev/null +++ b/pkg/lang/vm/value/data.go @@ -0,0 +1,17 @@ +package value + +type IntData int64 + +type FloatData float64 + +type StringData string + +type BoolData bool + +type ArrayData []Value + +type NullData struct{} + +type FunctionData struct{} // TODO + +type ObjectData struct{} // TODO diff --git a/pkg/lang/vm/value/type.go b/pkg/lang/vm/value/type.go new file mode 100644 index 0000000..1aec251 --- /dev/null +++ b/pkg/lang/vm/value/type.go @@ -0,0 +1,19 @@ +package value + +type TypeKind int + +const ( + IntType TypeKind = iota + FloatType + StringType + BoolType + ArrayType + NullType + FunctionType + + ObjectType +) + +type Type struct { + Kind TypeKind +} diff --git a/pkg/lang/vm/value/value.go b/pkg/lang/vm/value/value.go new file mode 100644 index 0000000..e932ed3 --- /dev/null +++ b/pkg/lang/vm/value/value.go @@ -0,0 +1,52 @@ +package value + +type Value struct { + t Type + d any +} + +func NewInt(x int64) Value { + t := Type{Kind: IntType} + return Value{t: t, d: IntData(x)} +} + +func NewFloat(x float64) Value { + t := Type{Kind: FloatType} + return Value{t: t, d: FloatData(x)} +} + +func NewString(str string) Value { + t := Type{Kind: StringType} + return Value{t: t, d: StringData(str)} +} + +func NewBool(b bool) Value { + t := Type{Kind: BoolType} + return Value{t: t, d: BoolData(b)} +} + +func NewArray(arr []Value) Value { + t := Type{Kind: ArrayType} + return Value{t: t, d: ArrayData(arr)} +} + +func NewNull() Value { + t := Type{Kind: NullType} + return Value{t: t} +} + +func NewFunction() Value { + panic("not implemented") +} + +func NewObject() Value { + panic("not implemented") +} + +func (v Value) Type() Type { + return v.t +} + +func (v Value) Data() any { + return v.d +} diff --git a/pkg/lang/vm/vm.go b/pkg/lang/vm/vm.go new file mode 100644 index 0000000..434eae3 --- /dev/null +++ b/pkg/lang/vm/vm.go @@ -0,0 +1,108 @@ +package vm + +import ( + "fmt" + "jinx/pkg/lang/vm/code" +) + +type VM struct { + code *code.Code + pc int + stack CallStack +} + +func New(code *code.Code) *VM { + return &VM{ + code: code, + pc: 0, + } +} + +func (vm *VM) Run() { + for vm.pc < vm.code.Len() { + op, advance := vm.code.GetOp(vm.pc) + vm.pc += advance + vm.step(op) + } +} + +type stepDecision int + +const ( + stepDecisionContinue stepDecision = iota + stepDecisionHalt +) + +func (vm *VM) step(op code.Op) stepDecision { + switch op { + case code.OpNop: + // do nothing + case code.OpHalt: + return stepDecisionHalt + + case code.OpPushInt: + x, advance := vm.code.GetInt(vm.pc) + vm.pc += advance + + vm.execPushInt(x) + case code.OpPushFloat: + x, advance := vm.code.GetFloat(vm.pc) + vm.pc += advance + + vm.execPushFloat(x) + case code.OpPushString: + str, advance := vm.code.GetString(vm.pc) + vm.pc += advance + + vm.execPushString(str) + case code.OpPushNull: + vm.execPushNull() + case code.OpPushTrue: + vm.execPushBool(true) + case code.OpPushFalse: + vm.execPushBool(false) + case code.OpPushArray: + vm.execPushArray() + case code.OpPushFunction: + panic("not implemented") + case code.OpPushObject: + panic("not implemented") + + case code.OpGetGlobal: + panic("not implemented") + case code.OpGetLocal: + offset, advance := vm.code.GetInt(vm.pc) + vm.pc += advance + + vm.execGetLocal(int(offset)) + case code.OpGetMember: + panic("not implemented") + case code.OpGetArg: + vm.execGetArg() + case code.OpGetEnv: + panic("not implemented") + + case code.OpAdd: + vm.execAdd() + case code.OpSub: + vm.execSub() + case code.OpIndex: + vm.execIndex() + case code.OpCall: + panic("not implemented") + + case code.OpJmp: + pc, _ := vm.code.GetUint(vm.pc) + vm.pc = int(pc) + case code.OpJez: + panic("not implemented") + + case code.OpRet: + panic("not implemented") + + default: + panic(fmt.Errorf("unimplemented op: %v", op)) + } + + return stepDecisionContinue +} |
