diff --git a/code/code.go b/code/code.go index 15a0f53..368de8d 100644 --- a/code/code.go +++ b/code/code.go @@ -27,7 +27,8 @@ const ( OpJumpNotTruthy OpJump OpNull - OpAssign + OpAssignGlobal + OpAssignLocal OpGetGlobal OpSetGlobal OpArray @@ -52,6 +53,8 @@ type Definition struct { var definitions = map[Opcode]*Definition{ OpConstant: {"OpConstant", []int{2}}, + OpAssignGlobal: {"OpAssignGlobal", []int{2}}, + OpAssignLocal: {"OpAssignLocal", []int{1}}, OpAdd: {"OpAdd", []int{}}, OpPop: {"OpPop", []int{}}, OpSub: {"OpSub", []int{}}, @@ -67,7 +70,6 @@ var definitions = map[Opcode]*Definition{ OpJumpNotTruthy: {"OpJumpNotTruthy", []int{2}}, OpJump: {"OpJump", []int{2}}, OpNull: {"OpNull", []int{}}, - OpAssign: {"OpAssign", []int{}}, OpGetGlobal: {"OpGetGlobal", []int{2}}, OpSetGlobal: {"OpSetGlobal", []int{2}}, OpArray: {"OpArray", []int{2}}, diff --git a/compiler/compiler.go b/compiler/compiler.go index 2744f9a..0a3201b 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -210,13 +210,11 @@ func (c *Compiler) Compile(node ast.Node) error { } if symbol.Scope == GlobalScope { - c.emit(code.OpGetGlobal, symbol.Index) + c.emit(code.OpAssignGlobal, symbol.Index) } else { - c.emit(code.OpGetLocal, symbol.Index) + c.emit(code.OpAssignLocal, symbol.Index) } - c.emit(code.OpAssign) - case *ast.LetStatement: symbol, ok := c.symbolTable.Resolve(node.Name.Value) if !ok { diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index 9e74ed6..3c768a6 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -779,8 +779,7 @@ func TestAssignmentStatementScopes(t *testing.T) { 55, []code.Instructions{ code.Make(code.OpConstant, 1), - code.Make(code.OpGetGlobal, 0), - code.Make(code.OpAssign), + code.Make(code.OpAssignGlobal, 0), code.Make(code.OpReturn), }, }, @@ -791,48 +790,19 @@ func TestAssignmentStatementScopes(t *testing.T) { code.Make(code.OpPop), }, }, - { input: ` - fn() { - let num = 55; - num - } - `, + fn() { let num = 0; num = 55; } + `, expectedConstants: []interface{}{ + 0, 55, - []code.Instructions{ - code.Make(code.OpConstant, 0), - code.Make(code.OpSetLocal, 0), - code.Make(code.OpGetLocal, 0), - code.Make(code.OpReturnValue), - }, - }, - expectedInstructions: []code.Instructions{ - code.Make(code.OpClosure, 1, 0), - code.Make(code.OpPop), - }, - }, - { - input: ` - fn() { - let a = 55; - let b = 77; - a + b - } - `, - expectedConstants: []interface{}{ - 55, - 77, []code.Instructions{ code.Make(code.OpConstant, 0), code.Make(code.OpSetLocal, 0), code.Make(code.OpConstant, 1), - code.Make(code.OpSetLocal, 1), - code.Make(code.OpGetLocal, 0), - code.Make(code.OpGetLocal, 1), - code.Make(code.OpAdd), - code.Make(code.OpReturnValue), + code.Make(code.OpAssignLocal, 0), + code.Make(code.OpReturn), }, }, expectedInstructions: []code.Instructions{ @@ -840,24 +810,6 @@ func TestAssignmentStatementScopes(t *testing.T) { code.Make(code.OpPop), }, }, - { - input: ` - let a = 0; - let a = a + 1; - `, - expectedConstants: []interface{}{ - 0, - 1, - }, - expectedInstructions: []code.Instructions{ - code.Make(code.OpConstant, 0), - code.Make(code.OpSetGlobal, 0), - code.Make(code.OpGetGlobal, 0), - code.Make(code.OpConstant, 1), - code.Make(code.OpAdd), - code.Make(code.OpSetGlobal, 0), - }, - }, } runCompilerTests(t, tests) diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index 667c891..e81dc74 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -46,9 +46,9 @@ func Eval(node ast.Node, env *object.Environment) object.Object { return &object.ReturnValue{Value: val} case *ast.AssignmentStatement: - ident := evalIdentifier(node.Name, env) - if isError(ident) { - return ident + obj := evalIdentifier(node.Name, env) + if isError(obj) { + return obj } val := Eval(node.Value, env) @@ -56,12 +56,7 @@ func Eval(node ast.Node, env *object.Environment) object.Object { return val } - obj, ok := ident.(object.Mutable) - if !ok { - return newError("cannot assign to %s", ident.Type()) - } - - obj.Set(val) + env.Set(node.Name.Value, val) return val @@ -71,8 +66,8 @@ func Eval(node ast.Node, env *object.Environment) object.Object { return val } - if mutable, ok := val.(object.Mutable); ok { - env.Set(node.Name.Value, mutable.Clone()) + if immutable, ok := val.(object.Immutable); ok { + env.Set(node.Name.Value, immutable.Clone()) } else { env.Set(node.Name.Value, val) } diff --git a/object/builtins.go b/object/builtins.go index 5555bc0..9c5324b 100644 --- a/object/builtins.go +++ b/object/builtins.go @@ -146,7 +146,11 @@ var Builtins = []struct { newElements := make([]Object, length+1, length+1) copy(newElements, arr.Elements) - newElements[length] = args[1] + if immutable, ok := args[1].(Immutable); ok { + newElements[length] = immutable.Clone() + } else { + newElements[length] = args[1] + } return &Array{Elements: newElements} }, diff --git a/object/object.go b/object/object.go index 51b10aa..59a8cbf 100644 --- a/object/object.go +++ b/object/object.go @@ -26,11 +26,10 @@ const ( CLOSURE_OBJ = "CLOSURE" ) -// Mutable is the interface for all mutable objects which must implement -// the Set() method which rebinds its internal value for assignment statements -type Mutable interface { +// Mutable is the interface for all immutable objects which must implement +// the Clone() method used by binding names to values. +type Immutable interface { Clone() Object - Set(obj Object) } type Object interface { @@ -54,9 +53,6 @@ func (i *Integer) Type() ObjectType { func (i *Integer) Inspect() string { return fmt.Sprintf("%d", i.Value) } -func (i *Integer) Set(obj Object) { - i.Value = obj.(*Integer).Value -} func (i *Integer) Clone() Object { return &Integer{Value: i.Value} } @@ -71,9 +67,6 @@ func (b *Boolean) Type() ObjectType { func (b *Boolean) Inspect() string { return fmt.Sprintf("%t", b.Value) } -func (b *Boolean) Set(obj Object) { - b.Value = obj.(*Boolean).Value -} func (b *Boolean) Clone() Object { return &Boolean{Value: b.Value} } @@ -109,9 +102,6 @@ func (e *Error) Type() ObjectType { func (e *Error) Inspect() string { return "Error: " + e.Message } -func (e *Error) Set(obj Object) { - e.Message = obj.(*Error).Message -} func (e *Error) Clone() Object { return &Error{Message: e.Message} } @@ -154,9 +144,6 @@ func (s *String) Type() ObjectType { func (s *String) Inspect() string { return s.Value } -func (s *String) Set(obj Object) { - s.Value = obj.(*String).Value -} func (s *String) Clone() Object { return &String{Value: s.Value} } @@ -195,14 +182,6 @@ func (ao *Array) Inspect() string { return out.String() } -func (ao *Array) Set(obj Object) { - ao.Elements = obj.(*Array).Elements -} -func (ao *Array) Clone() Object { - elements := make([]Object, len(ao.Elements)) - copy(elements, ao.Elements) - return &Array{Elements: elements} -} type HashKey struct { Type ObjectType @@ -262,21 +241,10 @@ func (h *Hash) Inspect() string { return out.String() } -func (h *Hash) Set(obj Object) { - h.Pairs = obj.(*Hash).Pairs -} -func (h *Hash) Clone() Object { - pairs := make(map[HashKey]HashPair) - for k, v := range h.Pairs { - pairs[k] = v - } - return &Hash{Pairs: pairs} -} func (cf *CompiledFunction) Type() ObjectType { return COMPILED_FUNCTION_OBJ } - func (cf *CompiledFunction) Inspect() string { return fmt.Sprintf("CompiledFunction[%p]", cf) } @@ -289,7 +257,6 @@ type Closure struct { func (c *Closure) Type() ObjectType { return CLOSURE_OBJ } - func (c *Closure) Inspect() string { return fmt.Sprintf("Closure[%p]", c) } diff --git a/vm/vm.go b/vm/vm.go index 18eb73a..869ed00 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -157,22 +157,23 @@ func (vm *VM) Run() error { vm.currentFrame().ip += 2 ref := vm.pop() - if mutable, ok := ref.(object.Mutable); ok { - vm.globals[globalIndex] = mutable.Clone() + if immutable, ok := ref.(object.Immutable); ok { + vm.globals[globalIndex] = immutable.Clone() } else { vm.globals[globalIndex] = ref } - case code.OpAssign: - ident := vm.pop() - val := vm.pop() + case code.OpAssignGlobal: + globalIndex := code.ReadUint16(ins[ip+1:]) + vm.currentFrame().ip += 2 + vm.globals[globalIndex] = vm.pop() - obj, ok := ident.(object.Mutable) - if !ok { - return fmt.Errorf("cannot assign to %s", ident.Type()) - } + case code.OpAssignLocal: + localIndex := code.ReadUint8(ins[ip+1:]) + vm.currentFrame().ip += 1 - obj.Set(val) + frame := vm.currentFrame() + vm.stack[frame.basePointer+int(localIndex)] = vm.pop() case code.OpGetGlobal: globalIndex := code.ReadUint16(ins[ip+1:])