857 lines
15 KiB
Go
857 lines
15 KiB
Go
package core
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
"testing"
|
|
)
|
|
|
|
func CompareChunks(t *testing.T, got *Chunk, want *Chunk) {
|
|
if len(got.Constants) != len(want.Constants) {
|
|
t.Errorf("constant count does not match; got %v, expected %v", len(got.Constants), len(want.Constants))
|
|
}
|
|
|
|
for i, v := range got.Constants {
|
|
if i < len(want.Constants) && !v.Equals(want.Constants[i]) {
|
|
t.Errorf("constant %d does not match (%s and %s)", i, v.String(), want.Constants[i].String())
|
|
}
|
|
}
|
|
|
|
if len(got.Bytecode) != len(want.Bytecode) {
|
|
t.Errorf("bytecode size does not match; got %v, expected %v", len(got.Bytecode), len(want.Bytecode))
|
|
}
|
|
|
|
t.Log("instruction \t\tchunk got \t\tchunk want")
|
|
|
|
i := 0
|
|
|
|
for ; i < len(got.Bytecode); i++ {
|
|
v := got.Bytecode[i]
|
|
if i < len(want.Bytecode) {
|
|
if v != want.Bytecode[i] {
|
|
t.Errorf("i=%d mismatch \t%d (%s) \t\t%d (%s)", i, v, v.String(), want.Bytecode[i], want.Bytecode[i].String())
|
|
} else {
|
|
t.Logf("i=%d match \t%d (%s) \t\t%d (%s)", i, v, v.String(), want.Bytecode[i], want.Bytecode[i].String())
|
|
}
|
|
} else {
|
|
t.Errorf("i=%d mismatch \t%d (%s) \t\t- (None)", i, v, v.String())
|
|
}
|
|
}
|
|
|
|
// if want bytecode is greater than got bytecode
|
|
for ; i < len(want.Bytecode); i++ {
|
|
v := want.Bytecode[i]
|
|
t.Errorf("i=%d mismatch \t- (None) \t\t%d (%s)", i, v, v.String())
|
|
}
|
|
}
|
|
|
|
func TestNewVM(t *testing.T) {
|
|
// constants
|
|
chunk := NewChunk([]Bytecode{
|
|
InstructionConstant, 0,
|
|
}, []Value{
|
|
&FloatValue{0},
|
|
})
|
|
stackSize := Pos(256)
|
|
callstackSize := Pos(256)
|
|
|
|
vm := NewVM(chunk, stackSize, callstackSize)
|
|
|
|
// should start at first instruction
|
|
if vm.ip != 0 {
|
|
t.Errorf("vm.ip = %d, want 0", vm.ip)
|
|
}
|
|
|
|
// should have given instructions
|
|
for i, v := range chunk.Bytecode {
|
|
if v != vm.chunk.Bytecode[i] {
|
|
t.Errorf("vm.Bytecode[%d] = %d, want %d", i, vm.chunk.Bytecode[i], v)
|
|
}
|
|
}
|
|
|
|
// should have given constants
|
|
for i, v := range chunk.Constants {
|
|
if v != vm.chunk.Constants[i] {
|
|
t.Errorf("vm.Constants[%d] = %d, want %d", i, vm.chunk.Constants[i], v)
|
|
}
|
|
}
|
|
|
|
// should have given stack size
|
|
if vm.Stack.Capacity != stackSize {
|
|
t.Errorf("vm.stack.Capacity = %d, want %d", vm.Stack.Capacity, stackSize)
|
|
}
|
|
|
|
// should have given call stack size
|
|
if vm.call.Capacity != callstackSize {
|
|
t.Errorf("vm.call.Capacity = %d, want %d", vm.call.Capacity, callstackSize)
|
|
}
|
|
}
|
|
|
|
func BenchmarkNewVM(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = NewVM(nil, 256, 256)
|
|
}
|
|
}
|
|
|
|
func GetExecutionTestData() map[string]struct {
|
|
chunk *Chunk
|
|
resultingStack []Value
|
|
resultingScope []map[string]Value
|
|
} {
|
|
return map[string]struct {
|
|
chunk *Chunk
|
|
resultingStack []Value
|
|
resultingScope []map[string]Value
|
|
}{
|
|
"two_plus_one": {
|
|
NewChunk([]Bytecode{
|
|
InstructionConstant, 0,
|
|
InstructionConstant, 1,
|
|
InstructionAddFloat,
|
|
},
|
|
[]Value{
|
|
&FloatValue{1}, &FloatValue{2},
|
|
}),
|
|
[]Value{
|
|
&FloatValue{3},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"push_constant": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 0,
|
|
},
|
|
[]Value{
|
|
&FloatValue{1},
|
|
},
|
|
),
|
|
[]Value{
|
|
&FloatValue{1},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"push_true": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionTrue,
|
|
},
|
|
[]Value{},
|
|
),
|
|
[]Value{
|
|
&BoolValue{true},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"push_false": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionFalse,
|
|
},
|
|
[]Value{},
|
|
),
|
|
[]Value{
|
|
&BoolValue{false},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"push_nil": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionNil,
|
|
},
|
|
[]Value{},
|
|
),
|
|
[]Value{
|
|
&NilValue{},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"empty": {
|
|
NewChunk(
|
|
[]Bytecode{},
|
|
[]Value{},
|
|
),
|
|
[]Value{},
|
|
[]map[string]Value{},
|
|
},
|
|
// (2 + 1) * 5 / (6 - 2)
|
|
"full_arithmetic": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 0,
|
|
InstructionConstant, 1,
|
|
InstructionAddFloat,
|
|
InstructionConstant, 2,
|
|
InstructionMulFloat,
|
|
InstructionConstant, 3,
|
|
InstructionConstant, 0,
|
|
InstructionSubFloat,
|
|
InstructionDivFloat,
|
|
},
|
|
[]Value{
|
|
&FloatValue{2}, &FloatValue{1}, &FloatValue{5}, &FloatValue{6},
|
|
},
|
|
),
|
|
[]Value{
|
|
&FloatValue{3.75},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"equality_true": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 0,
|
|
InstructionConstant, 0,
|
|
InstructionEquals,
|
|
},
|
|
[]Value{
|
|
&FloatValue{1},
|
|
},
|
|
),
|
|
[]Value{
|
|
&BoolValue{true},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"equality_false": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 0,
|
|
InstructionConstant, 1,
|
|
InstructionEquals,
|
|
},
|
|
[]Value{
|
|
&FloatValue{1}, &FloatValue{2},
|
|
},
|
|
),
|
|
[]Value{
|
|
&BoolValue{false},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"inequality_false": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 0,
|
|
InstructionConstant, 0,
|
|
InstructionNotEqual,
|
|
},
|
|
[]Value{
|
|
&FloatValue{1},
|
|
},
|
|
),
|
|
[]Value{
|
|
&BoolValue{false},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"inequality_true": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 0,
|
|
InstructionConstant, 1,
|
|
InstructionNotEqual,
|
|
},
|
|
[]Value{
|
|
&FloatValue{1}, &FloatValue{2},
|
|
},
|
|
),
|
|
[]Value{
|
|
&BoolValue{true},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"not_true": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionTrue,
|
|
InstructionNot,
|
|
},
|
|
[]Value{},
|
|
),
|
|
[]Value{
|
|
&BoolValue{false},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"not_false": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionFalse,
|
|
InstructionNot,
|
|
},
|
|
[]Value{},
|
|
),
|
|
[]Value{
|
|
&BoolValue{true},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"jump": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionJump, 0, 2,
|
|
InstructionConstant, 0, // should not execute
|
|
InstructionConstant, 1, // should execute
|
|
},
|
|
[]Value{
|
|
&FloatValue{0}, &FloatValue{1},
|
|
},
|
|
),
|
|
[]Value{
|
|
&FloatValue{1},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"jump_false/false": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionFalse,
|
|
InstructionJumpFalse, 0, 2,
|
|
InstructionConstant, 0, // should not execute
|
|
InstructionConstant, 1, // should execute
|
|
},
|
|
[]Value{
|
|
&FloatValue{0}, &FloatValue{1},
|
|
},
|
|
),
|
|
[]Value{
|
|
&FloatValue{1},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"jump_false/true": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionTrue,
|
|
InstructionJumpFalse, 0, 2,
|
|
InstructionConstant, 0, // should execute
|
|
InstructionConstant, 1, // should execute
|
|
},
|
|
[]Value{
|
|
&FloatValue{0}, &FloatValue{1},
|
|
},
|
|
),
|
|
[]Value{
|
|
&FloatValue{0}, &FloatValue{1},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"declare_local": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 0,
|
|
InstructionDeclareLocal, 1,
|
|
},
|
|
[]Value{
|
|
&FloatValue{0}, &StringValue{"a"},
|
|
},
|
|
),
|
|
[]Value{&FloatValue{0}},
|
|
[]map[string]Value{
|
|
{
|
|
"a": &FloatValue{0},
|
|
},
|
|
},
|
|
},
|
|
"assign_local": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 0,
|
|
InstructionDeclareLocal, 1,
|
|
InstructionPop,
|
|
InstructionConstant, 2,
|
|
InstructionSetLocal, 1, // reassign
|
|
InstructionPop,
|
|
},
|
|
[]Value{
|
|
&FloatValue{0}, &StringValue{"a"}, &FloatValue{1},
|
|
},
|
|
),
|
|
[]Value{},
|
|
[]map[string]Value{
|
|
{
|
|
"a": &FloatValue{1},
|
|
},
|
|
},
|
|
},
|
|
"get_local": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 0,
|
|
InstructionDeclareLocal, 1,
|
|
},
|
|
[]Value{
|
|
&FloatValue{0}, &StringValue{"a"},
|
|
},
|
|
),
|
|
[]Value{
|
|
&FloatValue{0},
|
|
},
|
|
[]map[string]Value{
|
|
{
|
|
"a": &FloatValue{0},
|
|
},
|
|
},
|
|
},
|
|
"get_reassigned_local": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 0,
|
|
InstructionDeclareLocal, 1,
|
|
InstructionPop,
|
|
InstructionGetLocal, 1,
|
|
InstructionConstant, 2,
|
|
InstructionSetLocal, 1, // reassign
|
|
InstructionPop,
|
|
InstructionGetLocal, 1,
|
|
},
|
|
[]Value{
|
|
&FloatValue{0}, &StringValue{"a"}, &FloatValue{1},
|
|
},
|
|
),
|
|
[]Value{
|
|
&FloatValue{0},
|
|
&FloatValue{1},
|
|
},
|
|
[]map[string]Value{
|
|
{
|
|
"a": &FloatValue{1},
|
|
},
|
|
},
|
|
},
|
|
"variable_scope": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 0,
|
|
InstructionDeclareLocal, 1,
|
|
InstructionPop,
|
|
InstructionDescend,
|
|
InstructionConstant, 2,
|
|
InstructionDeclareLocal, 3,
|
|
InstructionPop,
|
|
InstructionDescend,
|
|
InstructionConstant, 4,
|
|
InstructionDeclareLocal, 5,
|
|
InstructionPop,
|
|
InstructionAscend,
|
|
InstructionAscend,
|
|
},
|
|
[]Value{
|
|
&FloatValue{0}, &StringValue{"a"},
|
|
&FloatValue{1}, &StringValue{"b"},
|
|
&FloatValue{2}, &StringValue{"c"},
|
|
},
|
|
),
|
|
[]Value{},
|
|
[]map[string]Value{
|
|
{
|
|
"a": &FloatValue{0},
|
|
},
|
|
},
|
|
},
|
|
"function_call": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 0,
|
|
InstructionConstant, 1,
|
|
InstructionConstant, 2,
|
|
InstructionCall,
|
|
},
|
|
[]Value{
|
|
&FloatValue{1},
|
|
&FloatValue{2},
|
|
&FunctionValue{
|
|
Name: "sum",
|
|
Params: []FunctionParameter{
|
|
{
|
|
"a",
|
|
&FloatSignature{},
|
|
},
|
|
{
|
|
"b",
|
|
&FloatSignature{},
|
|
},
|
|
},
|
|
Chunk: NewChunk(
|
|
[]Bytecode{
|
|
InstructionGetLocal, 0,
|
|
InstructionGetLocal, 1,
|
|
InstructionAddFloat,
|
|
InstructionReturn,
|
|
},
|
|
[]Value{
|
|
&StringValue{"a"}, &StringValue{"b"},
|
|
},
|
|
),
|
|
},
|
|
},
|
|
),
|
|
[]Value{
|
|
&FloatValue{3},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"function_calling_function": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 3,
|
|
InstructionDeclareLocal, 4,
|
|
InstructionPop,
|
|
InstructionConstant, 0,
|
|
InstructionConstant, 1,
|
|
InstructionConstant, 2,
|
|
InstructionCall,
|
|
},
|
|
[]Value{
|
|
&FloatValue{1},
|
|
&FloatValue{2},
|
|
&FunctionValue{
|
|
Name: "sum",
|
|
Params: []FunctionParameter{
|
|
{
|
|
"a",
|
|
&FloatSignature{},
|
|
},
|
|
{
|
|
"b",
|
|
&FloatSignature{},
|
|
},
|
|
},
|
|
Chunk: NewChunk(
|
|
[]Bytecode{
|
|
InstructionGetLocal, 0,
|
|
InstructionGetLocal, 2, InstructionCall, // square the number
|
|
InstructionGetLocal, 1,
|
|
InstructionGetLocal, 2, InstructionCall, // square the number
|
|
InstructionAddFloat,
|
|
InstructionReturn,
|
|
},
|
|
[]Value{
|
|
&StringValue{"a"}, &StringValue{"b"}, &StringValue{"square"},
|
|
},
|
|
),
|
|
},
|
|
&FunctionValue{
|
|
Name: "square",
|
|
Params: []FunctionParameter{
|
|
{
|
|
"n",
|
|
&FloatSignature{},
|
|
},
|
|
},
|
|
Chunk: NewChunk(
|
|
[]Bytecode{
|
|
InstructionGetLocal, 0,
|
|
InstructionGetLocal, 0,
|
|
InstructionMulFloat,
|
|
InstructionReturn,
|
|
},
|
|
[]Value{
|
|
&StringValue{"n"},
|
|
},
|
|
),
|
|
},
|
|
&StringValue{"square"},
|
|
},
|
|
),
|
|
[]Value{
|
|
&FloatValue{5},
|
|
},
|
|
[]map[string]Value{
|
|
{
|
|
"square": &FunctionValue{
|
|
Name: "square",
|
|
Params: []FunctionParameter{
|
|
{
|
|
"n",
|
|
&FloatSignature{},
|
|
},
|
|
},
|
|
Chunk: NewChunk(
|
|
[]Bytecode{
|
|
InstructionGetLocal, 0,
|
|
InstructionGetLocal, 0,
|
|
InstructionMulFloat,
|
|
InstructionReturn,
|
|
},
|
|
[]Value{
|
|
&StringValue{"n"},
|
|
},
|
|
),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"list_concat": {
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 0,
|
|
InstructionConstant, 1,
|
|
InstructionConcatLists,
|
|
},
|
|
[]Value{
|
|
&ListValue{
|
|
[]Value{
|
|
&FloatValue{1},
|
|
&FloatValue{2},
|
|
},
|
|
},
|
|
&ListValue{
|
|
[]Value{
|
|
&FloatValue{3},
|
|
},
|
|
},
|
|
},
|
|
),
|
|
[]Value{
|
|
&ListValue{
|
|
[]Value{
|
|
&FloatValue{1},
|
|
&FloatValue{2},
|
|
&FloatValue{3},
|
|
},
|
|
},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
"form_tuple": {
|
|
&Chunk{
|
|
Bytecode: []Bytecode{
|
|
InstructionConstant, 0,
|
|
InstructionConstant, 1,
|
|
InstructionFormTuple, 0, 2,
|
|
},
|
|
Constants: []Value{
|
|
&IntegerValue{big.NewInt(1)},
|
|
&IntegerValue{big.NewInt(2)},
|
|
},
|
|
},
|
|
[]Value{
|
|
&TupleValue{
|
|
Items: []Value{
|
|
&IntegerValue{big.NewInt(1)},
|
|
&IntegerValue{big.NewInt(2)},
|
|
},
|
|
},
|
|
},
|
|
[]map[string]Value{},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestVM_Execution(t *testing.T) {
|
|
data := GetExecutionTestData()
|
|
|
|
for name, test := range data {
|
|
t.Run(name, func(t *testing.T) {
|
|
vm := NewVM(test.chunk, 256, 256)
|
|
|
|
for vm.Next() {
|
|
}
|
|
|
|
CompareStacks(t, test.resultingStack, vm.Stack)
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkVM_Execution(b *testing.B) {
|
|
data := GetExecutionTestData()
|
|
|
|
for name, test := range data {
|
|
b.Run(name, func(b *testing.B) {
|
|
for n := 0; n < b.N; n++ {
|
|
vm := NewVM(test.chunk, 256, 256)
|
|
for vm.Next() {
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVM_NextByte(t *testing.T) {
|
|
vm := NewVM(
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionConstant, 0,
|
|
},
|
|
[]Value{
|
|
&FloatValue{0},
|
|
},
|
|
),
|
|
16,
|
|
16,
|
|
)
|
|
|
|
b, err := vm.TryNextByte()
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if b != InstructionConstant {
|
|
t.Errorf("got %v; want %v", b, InstructionConstant)
|
|
}
|
|
|
|
b, err = vm.TryNextByte()
|
|
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if b != 0 {
|
|
t.Errorf("got %v; want %v", b, 0)
|
|
}
|
|
|
|
b, err = vm.TryNextByte()
|
|
|
|
if err == nil {
|
|
t.Errorf("didn't get expected error")
|
|
}
|
|
|
|
if b != 0 {
|
|
t.Errorf("got %v; want %v", b, nil)
|
|
}
|
|
}
|
|
|
|
func TestVM_NextU16_Empty(t *testing.T) {
|
|
defer func() {
|
|
if r := recover(); r == nil {
|
|
t.Fatalf("didn't panic when not enough bytes")
|
|
}
|
|
}()
|
|
|
|
vm := NewVM(
|
|
NewChunk(
|
|
[]Bytecode{},
|
|
[]Value{},
|
|
),
|
|
16,
|
|
16,
|
|
)
|
|
vm.NextU16()
|
|
}
|
|
|
|
func TestVM_NextU16_One(t *testing.T) {
|
|
defer func() {
|
|
if r := recover(); r == nil {
|
|
t.Fatalf("didn't panic when not enough bytes")
|
|
}
|
|
}()
|
|
|
|
vm := NewVM(
|
|
NewChunk(
|
|
[]Bytecode{
|
|
0,
|
|
},
|
|
[]Value{},
|
|
),
|
|
16,
|
|
16,
|
|
)
|
|
|
|
vm.NextU16()
|
|
}
|
|
|
|
func TestVM_NextU16(t *testing.T) {
|
|
for i := 0; i <= 0xFFFF; i++ {
|
|
t.Run(fmt.Sprintf("value-%d", i), func(t *testing.T) {
|
|
vm := NewVM(
|
|
NewChunk(
|
|
[]Bytecode{
|
|
Bytecode((i >> 8) & 0xFF),
|
|
Bytecode(i & 0xFF),
|
|
},
|
|
[]Value{},
|
|
),
|
|
16,
|
|
16,
|
|
)
|
|
|
|
b := vm.NextU16()
|
|
|
|
if uint16(i) != b {
|
|
t.Errorf("got %v; want %v", b, i)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVM_Jump(t *testing.T) {
|
|
vm := NewVM(
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionJump, 0, 2,
|
|
InstructionConstant, 0,
|
|
InstructionConstant, 1,
|
|
InstructionConstant, 2,
|
|
},
|
|
[]Value{
|
|
&FloatValue{0}, &FloatValue{1}, &FloatValue{2},
|
|
},
|
|
),
|
|
16,
|
|
16,
|
|
)
|
|
|
|
vm.Next()
|
|
|
|
if vm.ip != 5 {
|
|
t.Errorf("jumped got %v; want %v", vm.ip-3, 2)
|
|
}
|
|
}
|
|
|
|
func TestVM_JumpFalse(t *testing.T) {
|
|
vm := NewVM(
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionFalse,
|
|
InstructionJumpFalse, 0, 2,
|
|
InstructionConstant, 0,
|
|
InstructionConstant, 1,
|
|
InstructionConstant, 2,
|
|
},
|
|
[]Value{
|
|
&FloatValue{0}, &FloatValue{1}, &FloatValue{2},
|
|
},
|
|
),
|
|
16,
|
|
16,
|
|
)
|
|
|
|
vm.Next()
|
|
vm.Next()
|
|
|
|
if vm.ip != 6 {
|
|
t.Errorf("jumped got %v; want %v", vm.ip-4, 2)
|
|
}
|
|
}
|
|
|
|
func TestVM_DontJumpFalse(t *testing.T) {
|
|
vm := NewVM(
|
|
NewChunk(
|
|
[]Bytecode{
|
|
InstructionTrue,
|
|
InstructionJumpFalse, 0, 2,
|
|
InstructionConstant, 0,
|
|
InstructionConstant, 1,
|
|
InstructionConstant, 2,
|
|
},
|
|
[]Value{
|
|
&FloatValue{0}, &FloatValue{1}, &FloatValue{2},
|
|
},
|
|
),
|
|
16,
|
|
16,
|
|
)
|
|
|
|
vm.Next()
|
|
vm.Next()
|
|
|
|
if vm.ip != 4 {
|
|
t.Errorf("ip is %v; want %v", vm.ip, 4)
|
|
}
|
|
}
|
|
|
|
func TestVM_GetGlobal(t *testing.T) {}
|