From 3bccc227ba41f6028467c09b0c0361ac955853c1 Mon Sep 17 00:00:00 2001 From: Neemek Date: Thu, 20 Aug 2026 13:44:10 +0200 Subject: [PATCH] Add set index in list, and update lib+examples+tests --- core/compiler.go | 35 ++++++++++++++++++++++++++++++++ core/values.go | 46 ++++++++++++++++++------------------------ core/vm.go | 34 ++++++++++++++++++++++--------- examples/fib.ang | 7 +------ examples/list.ang | 14 ++++++------- examples/recursive.ang | 9 +++++---- foo.ang | 0 lib/list.ang | 2 +- lib/math.ang | 2 ++ lib/testing.ang | 14 ++++++------- tests/list.ang | 7 +++++++ tests/tuple.ang | 7 +++++++ 12 files changed, 117 insertions(+), 60 deletions(-) create mode 100644 foo.ang diff --git a/core/compiler.go b/core/compiler.go index 36f0f7a..83098d5 100644 --- a/core/compiler.go +++ b/core/compiler.go @@ -977,6 +977,41 @@ func (c *Compiler) compileAssignFromStack(to Node, sig TypeSignature, declare bo } return sig, nil + case IndexNodeType: + if declare { + return nil, c.error(fmt.Sprintf("cannot declare an indexed item"), to) + } + + n := to.(*IndexNode) + + ssig, err := c.compile(n.source) + if err != nil { + return nil, err + } + + isig, err := c.compile(n.index) + if err != nil { + return nil, err + } + + if isig.Type() != TypeInteger { + return nil, c.error(fmt.Sprintf("cannot index into list with non-integer (%s)", isig), n.index) + } + + switch ssig.Type() { + case TypeList: + lsig := ssig.(*ListSignature) + if !c.typeMatches(sig, lsig.Contents) { + return nil, c.error(fmt.Sprintf("cannot assign value of type %s to list with items of type %s", sig, lsig.Contents), to) + } + + c.add(InstructionSetIndexList) + default: + return nil, c.error(fmt.Sprintf("cannot set index of %s", ssig.Type()), n.source) + } + + return sig, nil + default: return nil, c.error(fmt.Sprintf("cannot assign to %s", to.Type()), to) } diff --git a/core/values.go b/core/values.go index 8f95a1c..efb5f41 100644 --- a/core/values.go +++ b/core/values.go @@ -117,8 +117,12 @@ type Value interface { // Get a member from the value. An error is returned if the member does not exist Get(string) (Value, error) - // Clone create a clone of the value. The returned value is a pointer to a new value of the same type as the value. - Clone() Value + // Copy create a copy of the value. A copy is a direct copy for small data (numbers) and a copy of pointer + // for bigger data (lists, tuples, dicts) + Copy() Value + + // Clone create a clone of the value. A clone is new data for all data. + //Clone() Value } type NilValue struct{} @@ -143,7 +147,7 @@ func (v *NilValue) Get(_ string) (Value, error) { return nil, errors.New("nil has no properties") } -func (v *NilValue) Clone() Value { +func (v *NilValue) Copy() Value { return &NilValue{} } @@ -175,7 +179,7 @@ func (v *BoolValue) Get(_ string) (Value, error) { return nil, errors.New("booleans have no properties") } -func (v *BoolValue) Clone() Value { +func (v *BoolValue) Copy() Value { return &BoolValue{ v.Boolean, } @@ -258,11 +262,11 @@ func (v *ObjectValue) Get(key string) (Value, error) { } } -func (v *ObjectValue) Clone() Value { +func (v *ObjectValue) Copy() Value { m := make(map[string]Value, len(v.Members)) for name, mem := range v.Members { - m[name] = mem.Clone() + m[name] = mem.Copy() } return &ObjectValue{ @@ -303,7 +307,7 @@ func (v *FloatValue) Get(_ string) (Value, error) { return nil, errors.New("numbers have no properties") } -func (v *FloatValue) Clone() Value { +func (v *FloatValue) Copy() Value { return &FloatValue{ v.Number, } @@ -334,7 +338,7 @@ func (v *IntegerValue) Get(_ string) (Value, error) { return nil, errors.New("numbers have no properties") } -func (v *IntegerValue) Clone() Value { +func (v *IntegerValue) Copy() Value { return &IntegerValue{ new(big.Int).Set(v.Number), } @@ -427,7 +431,7 @@ func (v *StringValue) Get(key string) (Value, error) { return nil, errors.New(fmt.Sprintf("string has no property \"%s\"", key)) } -func (v *StringValue) Clone() Value { +func (v *StringValue) Copy() Value { return &StringValue{ v.Text, } @@ -594,15 +598,9 @@ func (v *ListValue) Get(key string) (Value, error) { return nil, errors.New(fmt.Sprintf("list has no property \"%s\"", key)) } -func (v *ListValue) Clone() Value { - n := make([]Value, len(v.Items)) - - for i, item := range v.Items { - n[i] = item.Clone() - } - +func (v *ListValue) Copy() Value { return &ListValue{ - n, + v.Items, } } @@ -635,13 +633,9 @@ func (v *TupleValue) DebugString() string { return v.String() } -func (v *TupleValue) Clone() Value { - n := make([]Value, len(v.Items)) - for i, item := range v.Items { - n[i] = item.Clone() - } +func (v *TupleValue) Copy() Value { return &TupleValue{ - n, + v.Items, } } @@ -718,7 +712,7 @@ func (v *FunctionValue) Get(_ string) (Value, error) { return nil, errors.New("functions have no properties") } -func (v *FunctionValue) Clone() Value { +func (v *FunctionValue) Copy() Value { return &FunctionValue{ v.Name, v.Params, @@ -758,7 +752,7 @@ func (v *BuiltinFunctionValue) Get(_ string) (Value, error) { return nil, errors.New("functions have no properties") } -func (v *BuiltinFunctionValue) Clone() Value { +func (v *BuiltinFunctionValue) Copy() Value { return &BuiltinFunctionValue{ v.Name, v.Signature, @@ -807,7 +801,7 @@ func (v *RecordValue) DebugString() string { return v.String() } -func (v *RecordValue) Clone() Value { +func (v *RecordValue) Copy() Value { return &RecordValue{ v.Entries, } diff --git a/core/vm.go b/core/vm.go index 22a568e..f07890f 100644 --- a/core/vm.go +++ b/core/vm.go @@ -152,10 +152,14 @@ const ( // is the index. [..., container, index] -> [..., item] InstructionIndexTuple // InstructionIndexString index into a string. The lower item is the container, and the top item - // is the index. [..., container, index] -> [..., item]. Produces a new string with the character - // at the position + // is the index. [..., container, index] -> [..., item]. Produces a new string with only the character + // at the indexed position InstructionIndexString + // InstructionSetIndexList set the item at a given index in a list. + // [..., item, container, index] -> [..., item] + InstructionSetIndexList + // InstructionBreakpoint for debugging purposes InstructionBreakpoint ) @@ -638,7 +642,7 @@ var DefaultGlobals = map[string]Value{ n, _ := v.Number.Float64() return &FloatValue{n}, nil case *FloatValue: - return v.Clone(), nil + return v.Copy(), nil case *StringValue: num, err := strconv.ParseFloat(v.Text, FloatSize) if err != nil { @@ -978,7 +982,7 @@ func (vm *VM) Next() bool { vm.Stack.Push(v) case InstructionSetLocal: - value := vm.Stack.Peek().Clone() + value := vm.Stack.Peek().Copy() name := vm.GetConstant(vm.NextByte()).(*StringValue).Text vm.setVar(name, value) @@ -986,7 +990,7 @@ func (vm *VM) Next() bool { case InstructionDeclareLocal: vm.addVar( vm.GetConstant(vm.NextByte()).(*StringValue).Text, - vm.Stack.Peek().Clone(), + vm.Stack.Peek().Copy(), ) case InstructionGetGlobal: @@ -1070,7 +1074,7 @@ func (vm *VM) Next() bool { vm.Stack.Push(r, l) case InstructionDuplicate: - vm.Stack.Push(vm.Stack.Peek().Clone()) + vm.Stack.Push(vm.Stack.Peek().Copy()) case InstructionAccessProperty: source := vm.Stack.Pop() @@ -1112,7 +1116,7 @@ func (vm *VM) Next() bool { vm.error(fmt.Sprintf("index %d out of bounds", n)) } - vm.Stack.Push(l.Items[n].Clone()) + vm.Stack.Push(l.Items[n].Copy()) case InstructionIndexTuple: i := vm.Stack.Pop().(*IntegerValue) @@ -1124,7 +1128,7 @@ func (vm *VM) Next() bool { vm.error(fmt.Sprintf("index %d out of bounds", n)) } - vm.Stack.Push(t.Items[n].Clone()) + vm.Stack.Push(t.Items[n].Copy()) case InstructionIndexString: i := vm.Stack.Pop().(*IntegerValue) @@ -1138,6 +1142,18 @@ func (vm *VM) Next() bool { vm.Stack.Push(&StringValue{string(s.Text[n])}) + case InstructionSetIndexList: + n := vm.Stack.Pop().(*IntegerValue) + l := vm.Stack.Pop().(*ListValue) + + i := n.Number.Int64() + + if i < 0 || int64(len(l.Items)) <= i { + vm.error(fmt.Sprintf("index %d out of bounds", i)) + } + + l.Items[i] = vm.Stack.Peek().Copy() + case InstructionBreakpoint: /* // I'm keeping this @@ -1284,7 +1300,7 @@ func (vm *VM) HasNext() bool { } func (vm *VM) GetConstant(id Bytecode) Value { - return vm.chunk.Constants[id].Clone() + return vm.chunk.Constants[id].Copy() } func (vm *VM) ReadConstant() Value { diff --git a/examples/fib.ang b/examples/fib.ang index 63a432a..4860a1e 100644 --- a/examples/fib.ang +++ b/examples/fib.ang @@ -3,14 +3,9 @@ fn range(from: int, to: int) -> (fn() -> (int, bool)) { i := from - 1 - end := to - 1 fn() -> (int, bool) { - if i < end { - (i = i+1, true) - } else { - (-1, false) - } + (i = i+1, i+1 < to) } } diff --git a/examples/list.ang b/examples/list.ang index 9866d3e..d34887e 100644 --- a/examples/list.ang +++ b/examples/list.ang @@ -1,6 +1,6 @@ # Empty list -println([]) +println([]any) # List with items println([3, 1, 4, 1, 5, 9, 2, 6, 5]) @@ -8,25 +8,25 @@ println([3, 1, 4, 1, 5, 9, 2, 6, 5]) # List with items of different types println(["", "私はかっこいいです。", true, nil, nil, 1, 2]) -a := [] +a := []int -a = a + [1] -a = a + [2] +a.push(1) +a.push(2) println(a) -list := [] +list := []int x := 0 for n in 0..100 { x = x + 2*n + 1 - list = list + [x] + list.push(x) } println(list) -println(list.map(func(a) { +println(list.map(fn(a: int) -> int { return a - 1 })) println(list.length()) diff --git a/examples/recursive.ang b/examples/recursive.ang index caa0e37..f30109f 100644 --- a/examples/recursive.ang +++ b/examples/recursive.ang @@ -1,14 +1,15 @@ # This program computes the fibonacci numbers using recursion (O(2^n)) # It is very slow -func fib(x: number) number { +fn fib(x: number) number { if x <= 1 { - return x + x + } else { + fib(x - 1) + fib(x - 2) } - return fib(x - 1) + fib(x - 2) } n := 0 while n < 100 { - write(str(fib(n))) + println(fib(n)) n = n + 1 } diff --git a/foo.ang b/foo.ang new file mode 100644 index 0000000..e69de29 diff --git a/lib/list.ang b/lib/list.ang index 254912d..792751f 100644 --- a/lib/list.ang +++ b/lib/list.ang @@ -1,5 +1,5 @@ -fn ([T]) map(f: fn(T) -> R) -> [R] { +fn map(list: [T], f: fn(T) -> R) -> [R] { out := [] for v in list.iter() { diff --git a/lib/math.ang b/lib/math.ang index 120e60e..9dac01a 100644 --- a/lib/math.ang +++ b/lib/math.ang @@ -249,3 +249,5 @@ fn tan(x: float) -> float { # todo 0.0 } + +(E:, PI:, sqrt:, log:, ln:, exp:, pow:) diff --git a/lib/testing.ang b/lib/testing.ang index 59aed2c..82e7b77 100644 --- a/lib/testing.ang +++ b/lib/testing.ang @@ -1,25 +1,25 @@ NAMESPACE := "" -func namespace(name: string, test: func()) { +fn namespace(name: str, test: fn()) { NAMESPACE = name test() } -func assertEqual(a: any, b: any) { +fn eq(a: T, b: T) { if a != b { - write(format("assertion error: % should (but doesn't) equal %", [a, b])) + println(format("assertion error: % should (but doesn't) equal %", [a, b])) exit(1) } else if env("DEBUG") != "" { - write(format("assertion success: % equals %", [a, b])) + println(format("assertion success: % equals %", [a, b])) } } -func assertNotEqual(a: any, b: any) { +fn neq(a: T, b: T) { if a == b { - write(format("assertion error: % shouldn't (but does) equal %", [a, b])) + println(format("assertion error: % shouldn't (but does) equal %", [a, b])) exit(1) } else if env("DEBUG") != "" { - write(format("assertion success: % doesn't equal %", [a, b])) + println(format("assertion success: % doesn't equal %", [a, b])) } } diff --git a/tests/list.ang b/tests/list.ang index 8554f14..e98fcf8 100644 --- a/tests/list.ang +++ b/tests/list.ang @@ -34,3 +34,10 @@ assertEq("the, first, time".split(", "), ["the", "first", "time"]) # list indexing assertEq([1, 2, 3].at(1), 2) assertEq(["a", "b", "c"].at(2), "c") + +# mutating list +a := [1, 2, 3] +assertEq(a, [1, 2, 3]) + +a[1] = 4 +assertEq(a, [1, 4, 3]) diff --git a/tests/tuple.ang b/tests/tuple.ang index 81aa508..65e99e9 100644 --- a/tests/tuple.ang +++ b/tests/tuple.ang @@ -9,3 +9,10 @@ fn neighbours(n: int) -> (int, int) { } assertEq(neighbours(2), (1, 3)) + +a := (1, 2) +assertEq(a, (1, 2)) + +(x, y) := a +assertEq(x, 1) +assertEq(y, 2)