Change assignment into expressions
This commit is contained in:
99
vm/vm.go
99
vm/vm.go
@@ -186,6 +186,11 @@ func (vm *VM) Run() error {
|
||||
vm.currentFrame().ip += 2
|
||||
vm.globals[globalIndex] = vm.pop()
|
||||
|
||||
err := vm.push(Null)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case code.OpAssignLocal:
|
||||
localIndex := code.ReadUint8(ins[ip+1:])
|
||||
vm.currentFrame().ip += 1
|
||||
@@ -193,6 +198,11 @@ func (vm *VM) Run() error {
|
||||
frame := vm.currentFrame()
|
||||
vm.stack[frame.basePointer+int(localIndex)] = vm.pop()
|
||||
|
||||
err := vm.push(Null)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case code.OpGetGlobal:
|
||||
globalIndex := code.ReadUint16(ins[ip+1:])
|
||||
vm.currentFrame().ip += 2
|
||||
@@ -229,11 +239,21 @@ func (vm *VM) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
case code.OpIndex:
|
||||
case code.OpSetItem:
|
||||
value := vm.pop()
|
||||
index := vm.pop()
|
||||
left := vm.pop()
|
||||
|
||||
err := vm.executeIndexExpressions(left, index)
|
||||
err := vm.executeSetItem(left, index, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case code.OpGetItem:
|
||||
index := vm.pop()
|
||||
left := vm.pop()
|
||||
|
||||
err := vm.executeGetItem(left, index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -328,20 +348,39 @@ func (vm *VM) Run() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vm *VM) executeIndexExpressions(left, index object.Object) error {
|
||||
func (vm *VM) executeSetItem(left, index, value object.Object) error {
|
||||
switch {
|
||||
case left.Type() == object.STRING_OBJ && index.Type() == object.INTEGER_OBJ:
|
||||
return vm.executeStringIndex(left, index)
|
||||
case left.Type() == object.ARRAY_OBJ && index.Type() == object.INTEGER_OBJ:
|
||||
return vm.executeArrayIndex(left, index)
|
||||
return vm.executeArraySetItem(left, index, value)
|
||||
case left.Type() == object.HASH_OBJ:
|
||||
return vm.executeHashIndex(left, index)
|
||||
return vm.executeHashSetItem(left, index, value)
|
||||
default:
|
||||
return fmt.Errorf("index operator not supported: %s", left.Type())
|
||||
return fmt.Errorf(
|
||||
"set item operation not supported: left=%s index=%s",
|
||||
left.Type(), index.Type(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (vm *VM) executeArrayIndex(array, index object.Object) error {
|
||||
func (vm *VM) executeGetItem(left, index object.Object) error {
|
||||
switch {
|
||||
case left.Type() == object.STRING_OBJ && index.Type() == object.INTEGER_OBJ:
|
||||
return vm.executeStringGetItem(left, index)
|
||||
case left.Type() == object.STRING_OBJ && index.Type() == object.STRING_OBJ:
|
||||
return vm.executeStringIndex(left, index)
|
||||
case left.Type() == object.ARRAY_OBJ && index.Type() == object.INTEGER_OBJ:
|
||||
return vm.executeArrayGetItem(left, index)
|
||||
case left.Type() == object.HASH_OBJ:
|
||||
return vm.executeHashGetItem(left, index)
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"index operator not supported: left=%s index=%s",
|
||||
left.Type(), index.Type(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (vm *VM) executeArrayGetItem(array, index object.Object) error {
|
||||
arrayObject := array.(*object.Array)
|
||||
i := index.(*object.Integer).Value
|
||||
max := int64(len(arrayObject.Elements) - 1)
|
||||
@@ -353,7 +392,20 @@ func (vm *VM) executeArrayIndex(array, index object.Object) error {
|
||||
return vm.push(arrayObject.Elements[i])
|
||||
}
|
||||
|
||||
func (vm *VM) executeHashIndex(hash, index object.Object) error {
|
||||
func (vm *VM) executeArraySetItem(array, index, value object.Object) error {
|
||||
arrayObject := array.(*object.Array)
|
||||
i := index.(*object.Integer).Value
|
||||
max := int64(len(arrayObject.Elements) - 1)
|
||||
|
||||
if i < 0 || i > max {
|
||||
return fmt.Errorf("index out of bounds: %d", i)
|
||||
}
|
||||
|
||||
arrayObject.Elements[i] = value
|
||||
return vm.push(Null)
|
||||
}
|
||||
|
||||
func (vm *VM) executeHashGetItem(hash, index object.Object) error {
|
||||
hashObject := hash.(*object.Hash)
|
||||
|
||||
key, ok := index.(object.Hashable)
|
||||
@@ -369,6 +421,20 @@ func (vm *VM) executeHashIndex(hash, index object.Object) error {
|
||||
return vm.push(pair.Value)
|
||||
}
|
||||
|
||||
func (vm *VM) executeHashSetItem(hash, index, value object.Object) error {
|
||||
hashObject := hash.(*object.Hash)
|
||||
|
||||
key, ok := index.(object.Hashable)
|
||||
if !ok {
|
||||
return fmt.Errorf("unusable as hash key: %s", index.Type())
|
||||
}
|
||||
|
||||
hashed := key.HashKey()
|
||||
hashObject.Pairs[hashed] = object.HashPair{Key: index, Value: value}
|
||||
|
||||
return vm.push(Null)
|
||||
}
|
||||
|
||||
func (vm *VM) buildHash(startIndex, endIndex int) (object.Object, error) {
|
||||
hashedPairs := make(map[object.HashKey]object.HashPair)
|
||||
|
||||
@@ -621,7 +687,7 @@ func (vm *VM) pushClosure(constIndex, numFree int) error {
|
||||
return vm.push(closure)
|
||||
}
|
||||
|
||||
func (vm *VM) executeStringIndex(str, index object.Object) error {
|
||||
func (vm *VM) executeStringGetItem(str, index object.Object) error {
|
||||
stringObject := str.(*object.String)
|
||||
i := index.(*object.Integer).Value
|
||||
max := int64(len(stringObject.Value) - 1)
|
||||
@@ -633,6 +699,17 @@ func (vm *VM) executeStringIndex(str, index object.Object) error {
|
||||
return vm.push(&object.String{Value: string(stringObject.Value[i])})
|
||||
}
|
||||
|
||||
func (vm *VM) executeStringIndex(str, index object.Object) error {
|
||||
stringObject := str.(*object.String)
|
||||
substr := index.(*object.String).Value
|
||||
|
||||
return vm.push(
|
||||
&object.Integer{
|
||||
Value: int64(strings.Index(stringObject.Value, substr)),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (vm *VM) executeStringComparison(op code.Opcode, left, right object.Object) error {
|
||||
leftValue := left.(*object.String).Value
|
||||
rightValue := right.(*object.String).Value
|
||||
|
||||
@@ -267,6 +267,8 @@ func TestConditionals(t *testing.T) {
|
||||
{"if (false) { 10 } else { 10; let b = 5; }", Null},
|
||||
{"if (true) { let a = 5; } else { 10 }", Null},
|
||||
{"let x = 0; if (true) { x = 1; }; if (false) { x = 2; }; x", 1},
|
||||
{"if (1 < 2) { 10 } else if (1 == 2) { 20 }", 10},
|
||||
{"if (1 > 2) { 10 } else if (1 == 2) { 20 } else { 30 }", 30},
|
||||
}
|
||||
|
||||
runVmTests(t, tests)
|
||||
@@ -437,7 +439,7 @@ func TestFirstClassFunctions(t *testing.T) {
|
||||
input: `
|
||||
let returnsOneReturner = fn() {
|
||||
let returnsOne = fn() { return 1; };
|
||||
returnsOne;
|
||||
return returnsOne;
|
||||
};
|
||||
returnsOneReturner()();
|
||||
`,
|
||||
@@ -450,13 +452,13 @@ func TestFirstClassFunctions(t *testing.T) {
|
||||
|
||||
func TestCallingFunctionsWithBindings(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let one = fn() { let one = 1; return one };
|
||||
one();
|
||||
`,
|
||||
expected: 1,
|
||||
},
|
||||
//{
|
||||
// input: `
|
||||
// let one = fn() { let one = 1; return one };
|
||||
// one();
|
||||
// `,
|
||||
// expected: 1,
|
||||
//},
|
||||
{
|
||||
input: `
|
||||
let oneAndTwo = fn() { let one = 1; let two = 2; return one + two; };
|
||||
@@ -664,7 +666,7 @@ func TestClosures(t *testing.T) {
|
||||
{
|
||||
input: `
|
||||
let newClosure = fn(a) {
|
||||
fn() { return a; };
|
||||
return fn() { return a; };
|
||||
};
|
||||
let closure = newClosure(99);
|
||||
closure();
|
||||
@@ -674,7 +676,7 @@ func TestClosures(t *testing.T) {
|
||||
{
|
||||
input: `
|
||||
let newAdder = fn(a, b) {
|
||||
fn(c) { return a + b + c };
|
||||
return fn(c) { return a + b + c };
|
||||
};
|
||||
let adder = newAdder(1, 2);
|
||||
adder(8);
|
||||
@@ -685,7 +687,7 @@ func TestClosures(t *testing.T) {
|
||||
input: `
|
||||
let newAdder = fn(a, b) {
|
||||
let c = a + b;
|
||||
fn(d) { return c + d };
|
||||
return fn(d) { return c + d };
|
||||
};
|
||||
let adder = newAdder(1, 2);
|
||||
adder(8);
|
||||
@@ -819,6 +821,8 @@ func TestIterations(t *testing.T) {
|
||||
{"while (false) { }", nil},
|
||||
{"let n = 0; while (n < 10) { let n = n + 1 }; n", 10},
|
||||
{"let n = 10; while (n > 0) { let n = n - 1 }; n", 0},
|
||||
{"let n = 0; while (n < 10) { let n = n + 1 }", nil},
|
||||
{"let n = 10; while (n > 0) { let n = n - 1 }", nil},
|
||||
{"let n = 0; while (n < 10) { n = n + 1 }; n", 10},
|
||||
{"let n = 10; while (n > 0) { n = n - 1 }; n", 0},
|
||||
{"let n = 0; while (n < 10) { n = n + 1 }", nil},
|
||||
@@ -828,9 +832,27 @@ func TestIterations(t *testing.T) {
|
||||
runVmTests(t, tests)
|
||||
}
|
||||
|
||||
func TestAssignmentStatements(t *testing.T) {
|
||||
func TestIndexAssignmentStatements(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{"let one = 0; one = 1", 1},
|
||||
{"let xs = [1, 2, 3]; xs[1] = 4; xs[1];", 4},
|
||||
}
|
||||
|
||||
runVmTests(t, tests)
|
||||
}
|
||||
|
||||
func TestAssignmentExpressions(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{"let a = 0; a = 5;", nil},
|
||||
{"let a = 0; a = 5; a;", 5},
|
||||
{"let a = 0; a = 5 * 5;", nil},
|
||||
{"let a = 0; a = 5 * 5; a;", 25},
|
||||
{"let a = 0; a = 5; let b = 0; b = a;", nil},
|
||||
{"let a = 0; a = 5; let b = 0; b = a; b;", 5},
|
||||
{"let a = 0; a = 5; let b = 0; b = a; let c = 0; c = a + b + 5;", nil},
|
||||
{"let a = 0; a = 5; let b = 0; b = a; let c = 0; c = a + b + 5; c;", 15},
|
||||
{"let a = 5; let b = a; a = 0;", nil},
|
||||
{"let a = 5; let b = a; a = 0; b;", 5},
|
||||
{"let one = 0; one = 1", nil},
|
||||
{"let one = 0; one = 1; one", 1},
|
||||
{"let one = 0; one = 1; let two = 0; two = 2; one + two", 3},
|
||||
{"let one = 0; one = 1; let two = 0; two = one + one; one + two", 3},
|
||||
|
||||
Reference in New Issue
Block a user