indexes
This commit is contained in:
@@ -31,6 +31,7 @@ const (
|
|||||||
OpSetGlobal
|
OpSetGlobal
|
||||||
OpArray
|
OpArray
|
||||||
OpHash
|
OpHash
|
||||||
|
OpIndex
|
||||||
)
|
)
|
||||||
|
|
||||||
type Definition struct {
|
type Definition struct {
|
||||||
@@ -59,6 +60,7 @@ var definitions = map[Opcode]*Definition{
|
|||||||
OpSetGlobal: {"OpSetGlobal", []int{2}},
|
OpSetGlobal: {"OpSetGlobal", []int{2}},
|
||||||
OpArray: {"OpArray", []int{2}},
|
OpArray: {"OpArray", []int{2}},
|
||||||
OpHash: {"OpHash", []int{2}},
|
OpHash: {"OpHash", []int{2}},
|
||||||
|
OpIndex: {"OpIndex", []int{}},
|
||||||
}
|
}
|
||||||
|
|
||||||
func Lookup(op byte) (*Definition, error) {
|
func Lookup(op byte) (*Definition, error) {
|
||||||
|
|||||||
@@ -227,6 +227,19 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
|
|
||||||
c.emit(code.OpHash, len(node.Pairs)*2)
|
c.emit(code.OpHash, len(node.Pairs)*2)
|
||||||
|
|
||||||
|
case *ast.IndexExpression:
|
||||||
|
err := c.Compile(node.Left)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.Compile(node.Index)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.emit(code.OpIndex)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -390,6 +390,42 @@ func TestHashLiterals(t *testing.T) {
|
|||||||
runCompilerTests(t, tests)
|
runCompilerTests(t, tests)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIndexExpressions(t *testing.T) {
|
||||||
|
tests := []compilerTestCase{
|
||||||
|
{
|
||||||
|
input: "[1, 2, 3][1 + 1]",
|
||||||
|
expectedConstants: []interface{}{1, 2, 3, 1, 1},
|
||||||
|
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.OpConstant, 3),
|
||||||
|
code.Make(code.OpConstant, 4),
|
||||||
|
code.Make(code.OpAdd),
|
||||||
|
code.Make(code.OpIndex),
|
||||||
|
code.Make(code.OpPop),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "{1: 2}[2 - 1]",
|
||||||
|
expectedConstants: []interface{}{1, 2, 2, 1},
|
||||||
|
expectedInstructions: []code.Instructions{
|
||||||
|
code.Make(code.OpConstant, 0),
|
||||||
|
code.Make(code.OpConstant, 1),
|
||||||
|
code.Make(code.OpHash, 2),
|
||||||
|
code.Make(code.OpConstant, 2),
|
||||||
|
code.Make(code.OpConstant, 3),
|
||||||
|
code.Make(code.OpSub),
|
||||||
|
code.Make(code.OpIndex),
|
||||||
|
code.Make(code.OpPop),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
runCompilerTests(t, tests)
|
||||||
|
}
|
||||||
|
|
||||||
func runCompilerTests(t *testing.T, tests []compilerTestCase) {
|
func runCompilerTests(t *testing.T, tests []compilerTestCase) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
|||||||
48
vm/vm.go
48
vm/vm.go
@@ -160,12 +160,60 @@ func (vm *VM) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case code.OpIndex:
|
||||||
|
index := vm.pop()
|
||||||
|
left := vm.pop()
|
||||||
|
|
||||||
|
err := vm.executeIndexExpressions(left, index)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (vm *VM) executeIndexExpressions(left, index object.Object) error {
|
||||||
|
switch {
|
||||||
|
case left.Type() == object.ARRAY_OBJ && index.Type() == object.INTEGER_OBJ:
|
||||||
|
return vm.executeArrayIndex(left, index)
|
||||||
|
case left.Type() == object.HASH_OBJ:
|
||||||
|
return vm.executeHashIndex(left, index)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("index operator not supported: %s", left.Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Hashable)
|
||||||
|
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 (vm *VM) buildHash(startIndex, endIndex int) (object.Object, error) {
|
func (vm *VM) buildHash(startIndex, endIndex int) (object.Object, error) {
|
||||||
hashedPairs := make(map[object.HashKey]object.HashPair)
|
hashedPairs := make(map[object.HashKey]object.HashPair)
|
||||||
|
|
||||||
|
|||||||
@@ -279,3 +279,20 @@ func TestHashLiterals(t *testing.T) {
|
|||||||
|
|
||||||
runVmTests(t, tests)
|
runVmTests(t, tests)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIndexExpressions(t *testing.T) {
|
||||||
|
tests := []vmTestCase{
|
||||||
|
{"[1, 2, 3][1]", 2},
|
||||||
|
{"[1, 2, 3][0 + 2]", 3},
|
||||||
|
{"[[1, 1, 1]][0][0]", 1},
|
||||||
|
{"[][0]", Null},
|
||||||
|
{"[1, 2, 3][99]", Null},
|
||||||
|
{"[1][-1]", Null},
|
||||||
|
{"{1: 1, 2: 2}[1]", 1},
|
||||||
|
{"{1: 1, 2: 2}[2]", 2},
|
||||||
|
{"{1: 1}[0]", Null},
|
||||||
|
{"{}[0]", Null},
|
||||||
|
}
|
||||||
|
|
||||||
|
runVmTests(t, tests)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user