diff --git a/code/code.go b/code/code.go index 940124c..7b8c2ea 100644 --- a/code/code.go +++ b/code/code.go @@ -29,6 +29,7 @@ const ( OpNull OpGetGlobal OpSetGlobal + OpArray ) type Definition struct { @@ -55,6 +56,7 @@ var definitions = map[Opcode]*Definition{ OpNull: {"OpNull", []int{}}, OpGetGlobal: {"OpGetGlobal", []int{2}}, OpSetGlobal: {"OpSetGlobal", []int{2}}, + OpArray: {"OpArray", []int{2}}, } func Lookup(op byte) (*Definition, error) { diff --git a/compiler/compiler.go b/compiler/compiler.go index be6ffe2..24dc0f6 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -194,6 +194,16 @@ func (c *Compiler) Compile(node ast.Node) error { str := &object.String{Value: node.Value} c.emit(code.OpConstant, c.addConstant(str)) + case *ast.ArrayLiteral: + for _, el := range node.Elements { + err := c.Compile(el) + if err != nil { + return err + } + } + + c.emit(code.OpArray, len(node.Elements)) + } return nil diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index 5cdc524..150b394 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -302,6 +302,49 @@ func TestStringExpressions(t *testing.T) { runCompilerTests(t, tests) } +func TestArrayLiterals(t *testing.T) { + tests := []compilerTestCase{ + { + input: "[]", + expectedConstants: []interface{}{}, + expectedInstructions: []code.Instructions{ + code.Make(code.OpArray, 0), + code.Make(code.OpPop), + }, + }, + { + input: "[1, 2, 3]", + expectedConstants: []interface{}{1, 2, 3}, + expectedInstructions: []code.Instructions{ + code.Make(code.OpConstant, 0), + code.Make(code.OpConstant, 1), + code.Make(code.OpConstant, 2), + code.Make(code.OpArray, 3), + code.Make(code.OpPop), + }, + }, + { + input: "[1 + 2, 3 - 4, 5 * 6]", + expectedConstants: []interface{}{1, 2, 3, 4, 5, 6}, + expectedInstructions: []code.Instructions{ + code.Make(code.OpConstant, 0), + code.Make(code.OpConstant, 1), + code.Make(code.OpAdd), + code.Make(code.OpConstant, 2), + code.Make(code.OpConstant, 3), + code.Make(code.OpSub), + code.Make(code.OpConstant, 4), + code.Make(code.OpConstant, 5), + code.Make(code.OpMul), + code.Make(code.OpArray, 3), + code.Make(code.OpPop), + }, + }, + } + + runCompilerTests(t, tests) +} + func runCompilerTests(t *testing.T, tests []compilerTestCase) { t.Helper() diff --git a/vm/vm.go b/vm/vm.go index b59d920..0e32f19 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -133,12 +133,34 @@ func (vm *VM) Run() error { return err } + case code.OpArray: + numElements := int(code.ReadUint16(vm.instructions[ip+1:])) + ip += 2 + + array := vm.buildArray(vm.sp-numElements, vm.sp) + vm.sp = vm.sp - numElements + + err := vm.push(array) + if err != nil { + return err + } + } } return nil } +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 isTruthy(obj object.Object) bool { switch obj := obj.(type) { diff --git a/vm/vm_test.go b/vm/vm_test.go index 187e3ba..bb7783b 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -61,6 +61,24 @@ func testExpectedObject(t *testing.T, expected interface{}, actual object.Object t.Errorf("testStringObject failed: %s", err) } + case []int: + array, ok := actual.(*object.Array) + if !ok { + t.Errorf("object not Array: %T (%+v)", actual, actual) + } + + if len(array.Elements) != len(expected) { + t.Errorf("wrong num of elements. want=%d, got=%d", len(expected), len(array.Elements)) + return + } + + for i, expectedElem := range expected { + err := testIntegerObject(int64(expectedElem), array.Elements[i]) + if err != nil { + t.Errorf("testIntgerObject failed: %s", err) + } + } + case *object.Null: if actual != Null { t.Errorf("object is not Null: %T (%+v)", actual, actual) @@ -204,3 +222,13 @@ func TestStringExpressions(t *testing.T) { runVmTests(t, tests) } + +func TestArrayLiterals(t *testing.T) { + tests := []vmTestCase{ + {"[]", []int{}}, + {"[1, 2, 3]", []int{1, 2, 3}}, + {"[1 + 2, 3 * 4, 5 + 6]", []int{3, 12, 11}}, + } + + runVmTests(t, tests) +}