From 14e3316eecedd147cc2aeb07c9418ef3c4574730 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Mon, 18 Aug 2025 13:41:49 +0200 Subject: [PATCH] Implemented string slices --- docs/readme.md | 2 + examples/echo/echo.q | 2 +- src/core/evaluateArray.go | 90 ++++++++++++++++++++++++++++++++++-- src/expression/Parse.go | 5 ++ src/expression/Parse_test.go | 5 ++ tests/slice.q | 7 +++ tests/tests_test.go | 1 + 7 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 tests/slice.q diff --git a/docs/readme.md b/docs/readme.md index dabf069..b7396ce 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -57,6 +57,8 @@ q build examples/hello --os [linux|mac|windows] --arch [x86|arm] ## News +- **2025-08-18**: Slices for strings. +- **2025-08-17**: Struct allocation by value/reference. - **2025-08-16**: Multiple return values. - **2025-08-15**: Data structures. - **2025-08-14**: Memory load and store instructions. diff --git a/examples/echo/echo.q b/examples/echo/echo.q index a042f95..66e259a 100644 --- a/examples/echo/echo.q +++ b/examples/echo/echo.q @@ -12,6 +12,6 @@ main() { return } - io.write(string{ptr: buffer.ptr, len: n}) + io.write(buffer[..n]) } } \ No newline at end of file diff --git a/src/core/evaluateArray.go b/src/core/evaluateArray.go index f75f829..edca678 100644 --- a/src/core/evaluateArray.go +++ b/src/core/evaluateArray.go @@ -4,6 +4,7 @@ import ( "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/expression" "git.urbach.dev/cli/q/src/ssa" + "git.urbach.dev/cli/q/src/token" "git.urbach.dev/cli/q/src/types" ) @@ -17,8 +18,10 @@ func (f *Function) evaluateArray(expr *expression.Expression) (ssa.Value, error) } addressType := addressValue.Type() + var length ssa.Value if addressType == types.String { + length = addressValue.(*ssa.Struct).Arguments[1] addressValue = addressValue.(*ssa.Struct).Arguments[0] addressType = addressValue.Type() } @@ -33,10 +36,91 @@ func (f *Function) evaluateArray(expr *expression.Expression) (ssa.Value, error) if len(expr.Children) > 1 { index := expr.Children[1] - indexValue, err = f.evaluate(index) - if err != nil { - return nil, err + if index.Token.Kind == token.Range { + switch len(index.Children) { + case 1: + from, err := f.evaluate(index.Children[0]) + + if err != nil { + return nil, err + } + + newPointer := f.Append(&ssa.BinaryOp{ + Op: token.Add, + Left: addressValue, + Right: from, + }) + + newLength := f.Append(&ssa.BinaryOp{ + Op: token.Sub, + Left: length, + Right: from, + }) + + slice := &ssa.Struct{ + Typ: types.String, + Arguments: []ssa.Value{newPointer, newLength}, + Source: ssa.Source(expr.Source()), + } + + return slice, nil + + case 2: + if index.Children[0].Token.Kind == token.Invalid { + newLength, err := f.evaluate(index.Children[1]) + + if err != nil { + return nil, err + } + + slice := &ssa.Struct{ + Typ: types.String, + Arguments: []ssa.Value{addressValue, newLength}, + Source: ssa.Source(expr.Source()), + } + + return slice, nil + } + + from, err := f.evaluate(index.Children[0]) + + if err != nil { + return nil, err + } + + to, err := f.evaluate(index.Children[1]) + + if err != nil { + return nil, err + } + + newPointer := f.Append(&ssa.BinaryOp{ + Op: token.Add, + Left: addressValue, + Right: from, + }) + + newLength := f.Append(&ssa.BinaryOp{ + Op: token.Sub, + Left: to, + Right: from, + }) + + slice := &ssa.Struct{ + Typ: types.String, + Arguments: []ssa.Value{newPointer, newLength}, + Source: ssa.Source(expr.Source()), + } + + return slice, nil + } + } else { + indexValue, err = f.evaluate(index) + + if err != nil { + return nil, err + } } } else { indexValue = f.Append(&ssa.Int{Int: 0}) diff --git a/src/expression/Parse.go b/src/expression/Parse.go index 06bcecc..4faca67 100644 --- a/src/expression/Parse.go +++ b/src/expression/Parse.go @@ -101,6 +101,11 @@ func Parse(tokens token.List) *Expression { if t.Kind == token.Identifier || t.Kind == token.Number || t.Kind == token.String || t.Kind == token.Rune { if cursor != nil { node := NewLeaf(t) + + if cursor.Token.Kind == token.Range && len(cursor.Children) == 0 { + cursor.AddChild(New()) + } + cursor.AddChild(node) } else { cursor = NewLeaf(t) diff --git a/src/expression/Parse_test.go b/src/expression/Parse_test.go index a366a4b..af2f6a8 100644 --- a/src/expression/Parse_test.go +++ b/src/expression/Parse_test.go @@ -130,6 +130,11 @@ func TestParse(t *testing.T) { {"Structs 7", "a{b:c}[0]", "(@ ($ a (: b c)) 0)"}, {"Structs 8", "a{}(f)", "(λ ($ a) f)"}, {"Structs 9", "a{b:c}(f)", "(λ ($ a (: b c)) f)"}, + + {"Slices", "a[..]", "(@ a ..)"}, + {"Slices 2", "a[b..]", "(@ a (.. b))"}, + {"Slices 3", "a[..c]", "(@ a (.. c))"}, + {"Slices 4", "a[b..c]", "(@ a (.. b c))"}, } for _, test := range tests { diff --git a/tests/slice.q b/tests/slice.q new file mode 100644 index 0000000..e99ba80 --- /dev/null +++ b/tests/slice.q @@ -0,0 +1,7 @@ +import io + +main() { + io.write("Hello World"[..5]) + io.write("Hello World"[5..6]) + io.write("Hello World"[6..]) +} \ No newline at end of file diff --git a/tests/tests_test.go b/tests/tests_test.go index 774baa5..8036882 100644 --- a/tests/tests_test.go +++ b/tests/tests_test.go @@ -64,6 +64,7 @@ var tests = []run{ {"return-4", "", "", 0}, {"return-string", "", "Hello\n", 0}, {"ignore-unused-field", "", "", 0}, + {"slice", "", "Hello World", 0}, {"hello", "", "Hello\nHello\nHello\n", 0}, {"escape", "", "a\tb\nc\td\n", 0}, {"script", "", "Hello\n", 0}, -- 2.51.2