From 80f1a2d78cf84aea5bcc6a6f690a7db104333927 Mon Sep 17 00:00:00 2001 From: Chuck Smith Date: Fri, 15 Mar 2024 15:35:45 -0400 Subject: [PATCH] clone --- evaluator/evaluator.go | 7 ++++++- evaluator/evaluator_test.go | 2 ++ examples/ fibi.monkey | 18 ++++++++++++++++++ examples/foo.monkey | 2 -- object/object.go | 29 +++++++++++++++++++++++++++-- vm/vm.go | 7 ++++++- 6 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 examples/ fibi.monkey delete mode 100644 examples/foo.monkey diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index df6210b..667c891 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -70,7 +70,12 @@ func Eval(node ast.Node, env *object.Environment) object.Object { if isError(val) { return val } - env.Set(node.Name.Value, val) + + if mutable, ok := val.(object.Mutable); ok { + env.Set(node.Name.Value, mutable.Clone()) + } else { + env.Set(node.Name.Value, val) + } case *ast.Identifier: return evalIdentifier(node, env) diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go index e9e713d..98257b8 100644 --- a/evaluator/evaluator_test.go +++ b/evaluator/evaluator_test.go @@ -251,6 +251,7 @@ func TestAssignmentStatements(t *testing.T) { {"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;", 15}, {"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; b;", 5}, } for _, tt := range tests { @@ -474,6 +475,7 @@ func TestArrayIndexExpressions(t *testing.T) { } for _, tt := range tests { + t.Log(tt.input) evaluated := testEval(tt.input) integer, ok := tt.expected.(int) diff --git a/examples/ fibi.monkey b/examples/ fibi.monkey new file mode 100644 index 0000000..e41ce7d --- /dev/null +++ b/examples/ fibi.monkey @@ -0,0 +1,18 @@ +let fib = fn(n) { + if (n < 3) { + return 1 + } + let a = 1 + let b = 1 + let c = 0 + let i = 0 + while (i < n - 2) { + c = a + b + b = a + a = c + i = i + 1 + } + return a +} + +print(fib(35)) \ No newline at end of file diff --git a/examples/foo.monkey b/examples/foo.monkey deleted file mode 100644 index efbe5d4..0000000 --- a/examples/foo.monkey +++ /dev/null @@ -1,2 +0,0 @@ -let a = 0; -let a = a + 1; \ No newline at end of file diff --git a/object/object.go b/object/object.go index 9b6f841..51b10aa 100644 --- a/object/object.go +++ b/object/object.go @@ -29,6 +29,7 @@ const ( // 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 { + Clone() Object Set(obj Object) } @@ -56,6 +57,9 @@ func (i *Integer) Inspect() string { func (i *Integer) Set(obj Object) { i.Value = obj.(*Integer).Value } +func (i *Integer) Clone() Object { + return &Integer{Value: i.Value} +} type Boolean struct { Value bool @@ -67,8 +71,11 @@ func (b *Boolean) Type() ObjectType { func (b *Boolean) Inspect() string { return fmt.Sprintf("%t", b.Value) } -func (i *Boolean) Set(obj Object) { - i.Value = obj.(*Boolean).Value +func (b *Boolean) Set(obj Object) { + b.Value = obj.(*Boolean).Value +} +func (b *Boolean) Clone() Object { + return &Boolean{Value: b.Value} } type Null struct{} @@ -105,6 +112,9 @@ func (e *Error) Inspect() string { func (e *Error) Set(obj Object) { e.Message = obj.(*Error).Message } +func (e *Error) Clone() Object { + return &Error{Message: e.Message} +} type Function struct { Parameters []*ast.Identifier @@ -147,6 +157,9 @@ func (s *String) Inspect() string { func (s *String) Set(obj Object) { s.Value = obj.(*String).Value } +func (s *String) Clone() Object { + return &String{Value: s.Value} +} type BuiltinFunction func(args ...Object) Object @@ -185,6 +198,11 @@ func (ao *Array) Inspect() 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 @@ -247,6 +265,13 @@ func (h *Hash) Inspect() 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 diff --git a/vm/vm.go b/vm/vm.go index 1baa48b..18eb73a 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -156,7 +156,12 @@ func (vm *VM) Run() error { globalIndex := code.ReadUint16(ins[ip+1:]) vm.currentFrame().ip += 2 - vm.globals[globalIndex] = vm.pop() + ref := vm.pop() + if mutable, ok := ref.(object.Mutable); ok { + vm.globals[globalIndex] = mutable.Clone() + } else { + vm.globals[globalIndex] = ref + } case code.OpAssign: ident := vm.pop()