From 61b18456d7c8a89fdddee64dd410ff9d30a6327e Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 11 May 2020 23:34:50 +0800 Subject: [PATCH 001/106] #234 Added new func to std_seq.go. --- syntax/std_seq.go | 36 ++++++++++++++++++++++++++++++++++++ syntax/std_seq_test.go | 5 +++++ 2 files changed, 41 insertions(+) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 4259ad2e..fda3b94e 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -64,5 +64,41 @@ func stdSeq() rel.Attr { return rel.NewTupleAttr("seq", rel.NewNativeFunctionAttr("concat", stdSeqConcat), rel.NewNativeFunctionAttr("repeat", stdSeqRepeat), + createNestedFuncAttr("contains", 2, func(args ...rel.Value) rel.Value { + return rel.NewBool(strings.Contains(mustAsString(args[0]), mustAsString(args[1]))) + }), + createNestedFuncAttr("split", 2, func(args ...rel.Value) rel.Value { + splitted := strings.Split(mustAsString(args[0]), mustAsString(args[1])) + vals := make([]rel.Value, 0, len(splitted)) + for _, s := range splitted { + vals = append(vals, rel.NewString([]rune(s))) + } + return rel.NewArray(vals...) + }), + createNestedFuncAttr("sub", 3, func(args ...rel.Value) rel.Value { + return rel.NewString( + []rune( + strings.ReplaceAll( + mustAsString(args[0]), + mustAsString(args[1]), + mustAsString(args[2]), + ), + ), + ) + }), + createNestedFuncAttr("has_prefix", 2, func(args ...rel.Value) rel.Value { + return rel.NewBool(strings.HasPrefix(mustAsString(args[0]), mustAsString(args[1]))) + }), + createNestedFuncAttr("has_suffix", 2, func(args ...rel.Value) rel.Value { + return rel.NewBool(strings.HasPrefix(mustAsString(args[0]), mustAsString(args[1]))) + }), + createNestedFuncAttr("join", 2, func(args ...rel.Value) rel.Value { + strs := args[0].(rel.Set) + toJoin := make([]string, 0, strs.Count()) + for i, ok := strs.(rel.Set).ArrayEnumerator(); ok && i.MoveNext(); { + toJoin = append(toJoin, mustAsString(i.Current())) + } + return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[1])))) + }), ) } diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index 9365218a..7b73ad70 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -36,3 +36,8 @@ func TestSeqRepeat(t *testing.T) { AssertCodePanics(t, `//seq.repeat(2, 3.4)`) } + +func TestSeqContains(t *testing.T) { + t.Parallel() + +} From 1ca6a2531eaa88b2388c405bdc5c4c51a3ce16f0 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 11 May 2020 23:46:44 +0800 Subject: [PATCH 002/106] #234 Fixed issue found by CI linter. --- syntax/std_seq_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index 7b73ad70..d5ab1a14 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -39,5 +39,4 @@ func TestSeqRepeat(t *testing.T) { func TestSeqContains(t *testing.T) { t.Parallel() - } From cedb62b003133df1fe249642694fb3fccd861cb0 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 12 May 2020 13:32:34 +0800 Subject: [PATCH 003/106] #234 Move test cases to correct place. --- syntax/std_seq_test.go | 43 ++++++++++++++++++++++++++++++++++++++++++ syntax/std_str_test.go | 42 ----------------------------------------- 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index d5ab1a14..64af3e70 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -40,3 +40,46 @@ func TestSeqRepeat(t *testing.T) { func TestSeqContains(t *testing.T) { t.Parallel() } + +/////////////////// +func TestStrSub(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, + `"this is a test"`, + `//str.sub("this is not a test", "is not", "is")`) + AssertCodesEvalToSameValue(t, + `"this is a test"`, + `//str.sub("this is not a test", "not ", "")`) + AssertCodesEvalToSameValue(t, + `"this is still a test"`, + `//str.sub("this is still a test", "doesn't matter", "hello there")`) + assertExprPanics(t, `//str.sub("hello there", "test", 1)`) +} + +func TestStrSplit(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, + `["t", "h", "i", "s", " ", "i", "s", " ", "a", " ", "t", "e", "s", "t"]`, + `//str.split("this is a test", "")`) + AssertCodesEvalToSameValue(t, `["this", "is", "a", "test"]`, `//str.split("this is a test", " ") `) + AssertCodesEvalToSameValue(t, `["this is a test"] `, `//str.split("this is a test", ",") `) + AssertCodesEvalToSameValue(t, `["th", " ", " a test"] `, `//str.split("this is a test", "is")`) + assertExprPanics(t, `//str.split("this is a test", 1)`) +} + +func TestStrContains(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, `true `, `//str.contains("this is a test", "") `) + AssertCodesEvalToSameValue(t, `true `, `//str.contains("this is a test", "is a test") `) + AssertCodesEvalToSameValue(t, `false`, `//str.contains("this is a test", "is not a test")`) + assertExprPanics(t, `//str.contains(123, 124)`) +} + +func TestStrJoin(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, `"" `, `//str.join([], ",") `) + AssertCodesEvalToSameValue(t, `",," `, `//str.join(["", "", ""], ",") `) + AssertCodesEvalToSameValue(t, `"this is a test" `, `//str.join(["this", "is", "a", "test"], " ")`) + AssertCodesEvalToSameValue(t, `"this" `, `//str.join(["this"], ",") `) + assertExprPanics(t, `//str.join("this", 2)`) +} diff --git a/syntax/std_str_test.go b/syntax/std_str_test.go index f42ccda3..5eadbc59 100644 --- a/syntax/std_str_test.go +++ b/syntax/std_str_test.go @@ -6,31 +6,6 @@ import ( "github.com/stretchr/testify/assert" ) -func TestStrSub(t *testing.T) { - t.Parallel() - AssertCodesEvalToSameValue(t, - `"this is a test"`, - `//str.sub("this is not a test", "is not", "is")`) - AssertCodesEvalToSameValue(t, - `"this is a test"`, - `//str.sub("this is not a test", "not ", "")`) - AssertCodesEvalToSameValue(t, - `"this is still a test"`, - `//str.sub("this is still a test", "doesn't matter", "hello there")`) - assertExprPanics(t, `//str.sub("hello there", "test", 1)`) -} - -func TestStrSplit(t *testing.T) { - t.Parallel() - AssertCodesEvalToSameValue(t, - `["t", "h", "i", "s", " ", "i", "s", " ", "a", " ", "t", "e", "s", "t"]`, - `//str.split("this is a test", "")`) - AssertCodesEvalToSameValue(t, `["this", "is", "a", "test"]`, `//str.split("this is a test", " ") `) - AssertCodesEvalToSameValue(t, `["this is a test"] `, `//str.split("this is a test", ",") `) - AssertCodesEvalToSameValue(t, `["th", " ", " a test"] `, `//str.split("this is a test", "is")`) - assertExprPanics(t, `//str.split("this is a test", 1)`) -} - func TestStrLower(t *testing.T) { t.Parallel() AssertCodesEvalToSameValue(t, `"" `, `//str.lower("") `) @@ -58,23 +33,6 @@ func TestStrTitle(t *testing.T) { assertExprPanics(t, `//str.title(123)`) } -func TestStrContains(t *testing.T) { - t.Parallel() - AssertCodesEvalToSameValue(t, `true `, `//str.contains("this is a test", "") `) - AssertCodesEvalToSameValue(t, `true `, `//str.contains("this is a test", "is a test") `) - AssertCodesEvalToSameValue(t, `false`, `//str.contains("this is a test", "is not a test")`) - assertExprPanics(t, `//str.contains(123, 124)`) -} - -func TestStrJoin(t *testing.T) { - t.Parallel() - AssertCodesEvalToSameValue(t, `"" `, `//str.join([], ",") `) - AssertCodesEvalToSameValue(t, `",," `, `//str.join(["", "", ""], ",") `) - AssertCodesEvalToSameValue(t, `"this is a test" `, `//str.join(["this", "is", "a", "test"], " ")`) - AssertCodesEvalToSameValue(t, `"this" `, `//str.join(["this"], ",") `) - assertExprPanics(t, `//str.join("this", 2)`) -} - func assertExprPanics(t *testing.T, code string) { assert.Panics(t, func() { AssertCodesEvalToSameValue(t, `"doesn't matter"`, code) }) } From 7ca11fb014f8b0410c3f52c61e16ba6d446ffc09 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 12 May 2020 14:33:59 +0800 Subject: [PATCH 004/106] #234 Check main body of changes. --- syntax/std_seq.go | 111 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 94 insertions(+), 17 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index fda3b94e..cc61e52e 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -2,6 +2,7 @@ package syntax import ( "fmt" + "reflect" "strings" "github.com/arr-ai/arrai/rel" @@ -65,17 +66,46 @@ func stdSeq() rel.Attr { rel.NewNativeFunctionAttr("concat", stdSeqConcat), rel.NewNativeFunctionAttr("repeat", stdSeqRepeat), createNestedFuncAttr("contains", 2, func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.Contains(mustAsString(args[0]), mustAsString(args[1]))) + return process("contains", args...) }), createNestedFuncAttr("split", 2, func(args ...rel.Value) rel.Value { - splitted := strings.Split(mustAsString(args[0]), mustAsString(args[1])) - vals := make([]rel.Value, 0, len(splitted)) - for _, s := range splitted { - vals = append(vals, rel.NewString([]rune(s))) - } - return rel.NewArray(vals...) + return process("split", args...) }), createNestedFuncAttr("sub", 3, func(args ...rel.Value) rel.Value { + return process("sub", args...) + }), + createNestedFuncAttr("has_prefix", 2, func(args ...rel.Value) rel.Value { + return process("has_prefix", args...) + }), + createNestedFuncAttr("has_suffix", 2, func(args ...rel.Value) rel.Value { + return process("has_suffix", args...) + }), + createNestedFuncAttr("join", 2, func(args ...rel.Value) rel.Value { + return process("join", args...) + }), + ) +} + +func process(apiName string, args ...rel.Value) rel.Value { + handler := handlerMapping[typeMethod{reflect.TypeOf(args[0]), apiName}] + return handler(args...) +} + +// This seq API handlders mapping, the key is API name + '_' + data type. +var ( + handlerMapping = map[typeMethod]func(...rel.Value) rel.Value{ + // API contains + typeMethod{reflect.TypeOf(rel.String{}), "contains"}: func(args ...rel.Value) rel.Value { + return rel.NewBool(strings.HasPrefix(mustAsString(args[0]), mustAsString(args[1]))) + }, + typeMethod{reflect.TypeOf(rel.Array{}), "contains"}: func(args ...rel.Value) rel.Value { + return nil + }, + typeMethod{reflect.TypeOf(rel.Bytes{}), "contains"}: func(args ...rel.Value) rel.Value { + return nil + }, + // API sub + typeMethod{reflect.TypeOf(rel.String{}), "sub"}: func(args ...rel.Value) rel.Value { return rel.NewString( []rune( strings.ReplaceAll( @@ -85,20 +115,67 @@ func stdSeq() rel.Attr { ), ), ) - }), - createNestedFuncAttr("has_prefix", 2, func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.HasPrefix(mustAsString(args[0]), mustAsString(args[1]))) - }), - createNestedFuncAttr("has_suffix", 2, func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.HasPrefix(mustAsString(args[0]), mustAsString(args[1]))) - }), - createNestedFuncAttr("join", 2, func(args ...rel.Value) rel.Value { + }, + typeMethod{reflect.TypeOf(rel.Array{}), "sub"}: func(args ...rel.Value) rel.Value { + return nil + }, + typeMethod{reflect.TypeOf(rel.Bytes{}), "sub"}: func(args ...rel.Value) rel.Value { + return nil + }, + // API split + typeMethod{reflect.TypeOf(rel.String{}), "split"}: func(args ...rel.Value) rel.Value { + splitted := strings.Split(mustAsString(args[0]), mustAsString(args[1])) + vals := make([]rel.Value, 0, len(splitted)) + for _, s := range splitted { + vals = append(vals, rel.NewString([]rune(s))) + } + return rel.NewArray(vals...) + }, + typeMethod{reflect.TypeOf(rel.Array{}), "split"}: func(args ...rel.Value) rel.Value { + return nil + }, + typeMethod{reflect.TypeOf(rel.Bytes{}), "split"}: func(args ...rel.Value) rel.Value { + return nil + }, + // API join + typeMethod{reflect.TypeOf(rel.String{}), "join"}: func(args ...rel.Value) rel.Value { strs := args[0].(rel.Set) toJoin := make([]string, 0, strs.Count()) for i, ok := strs.(rel.Set).ArrayEnumerator(); ok && i.MoveNext(); { toJoin = append(toJoin, mustAsString(i.Current())) } return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[1])))) - }), - ) + }, + typeMethod{reflect.TypeOf(rel.Array{}), "join"}: func(args ...rel.Value) rel.Value { + return nil + }, + typeMethod{reflect.TypeOf(rel.Bytes{}), "join"}: func(args ...rel.Value) rel.Value { + return nil + }, + // API has_prefix + typeMethod{reflect.TypeOf(rel.String{}), "has_prefix"}: func(args ...rel.Value) rel.Value { + return rel.NewBool(strings.HasPrefix(mustAsString(args[0]), mustAsString(args[1]))) + }, + typeMethod{reflect.TypeOf(rel.Array{}), "has_prefix"}: func(args ...rel.Value) rel.Value { + return nil + }, + typeMethod{reflect.TypeOf(rel.Bytes{}), "has_prefix"}: func(args ...rel.Value) rel.Value { + return nil + }, + // API has_suffix + typeMethod{reflect.TypeOf(rel.String{}), "has_suffix"}: func(args ...rel.Value) rel.Value { + return rel.NewBool(strings.HasSuffix(mustAsString(args[0]), mustAsString(args[1]))) + }, + typeMethod{reflect.TypeOf(rel.Array{}), "has_suffix"}: func(args ...rel.Value) rel.Value { + return nil + }, + typeMethod{reflect.TypeOf(rel.Bytes{}), "has_suffix"}: func(args ...rel.Value) rel.Value { + return nil + }, + } +) + +type typeMethod struct { + t reflect.Type + api string } From 2a6453a4c0f59c1eb0b2a40dd84ed5290e244b7d Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 12 May 2020 14:35:47 +0800 Subject: [PATCH 005/106] #234 Check main body of changes. --- syntax/std_seq.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index cc61e52e..7ae49ef7 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -176,6 +176,6 @@ var ( ) type typeMethod struct { - t reflect.Type - api string + t reflect.Type + apiName string } From 0074e35e851255693fc0a04836bfc143772e7c0f Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 12 May 2020 14:46:45 +0800 Subject: [PATCH 006/106] #234 Check in test case changes. --- syntax/std_seq.go | 2 +- syntax/std_seq_test.go | 36 ++++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 7ae49ef7..e11b7462 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -96,7 +96,7 @@ var ( handlerMapping = map[typeMethod]func(...rel.Value) rel.Value{ // API contains typeMethod{reflect.TypeOf(rel.String{}), "contains"}: func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.HasPrefix(mustAsString(args[0]), mustAsString(args[1]))) + return rel.NewBool(strings.Contains(mustAsString(args[0]), mustAsString(args[1]))) }, typeMethod{reflect.TypeOf(rel.Array{}), "contains"}: func(args ...rel.Value) rel.Value { return nil diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index 64af3e70..d55c4c1f 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -46,40 +46,40 @@ func TestStrSub(t *testing.T) { t.Parallel() AssertCodesEvalToSameValue(t, `"this is a test"`, - `//str.sub("this is not a test", "is not", "is")`) + `//seq.sub("this is not a test", "is not", "is")`) AssertCodesEvalToSameValue(t, `"this is a test"`, - `//str.sub("this is not a test", "not ", "")`) + `//seq.sub("this is not a test", "not ", "")`) AssertCodesEvalToSameValue(t, `"this is still a test"`, - `//str.sub("this is still a test", "doesn't matter", "hello there")`) - assertExprPanics(t, `//str.sub("hello there", "test", 1)`) + `//seq.sub("this is still a test", "doesn't matter", "hello there")`) + assertExprPanics(t, `//seq.sub("hello there", "test", 1)`) } func TestStrSplit(t *testing.T) { t.Parallel() AssertCodesEvalToSameValue(t, `["t", "h", "i", "s", " ", "i", "s", " ", "a", " ", "t", "e", "s", "t"]`, - `//str.split("this is a test", "")`) - AssertCodesEvalToSameValue(t, `["this", "is", "a", "test"]`, `//str.split("this is a test", " ") `) - AssertCodesEvalToSameValue(t, `["this is a test"] `, `//str.split("this is a test", ",") `) - AssertCodesEvalToSameValue(t, `["th", " ", " a test"] `, `//str.split("this is a test", "is")`) - assertExprPanics(t, `//str.split("this is a test", 1)`) + `//seq.split("this is a test", "")`) + AssertCodesEvalToSameValue(t, `["this", "is", "a", "test"]`, `//seq.split("this is a test", " ") `) + AssertCodesEvalToSameValue(t, `["this is a test"] `, `//seq.split("this is a test", ",") `) + AssertCodesEvalToSameValue(t, `["th", " ", " a test"] `, `//seq.split("this is a test", "is")`) + assertExprPanics(t, `//seq.split("this is a test", 1)`) } func TestStrContains(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, `true `, `//str.contains("this is a test", "") `) - AssertCodesEvalToSameValue(t, `true `, `//str.contains("this is a test", "is a test") `) - AssertCodesEvalToSameValue(t, `false`, `//str.contains("this is a test", "is not a test")`) - assertExprPanics(t, `//str.contains(123, 124)`) + AssertCodesEvalToSameValue(t, `true `, `//seq.contains("this is a test", "") `) + AssertCodesEvalToSameValue(t, `true `, `//seq.contains("this is a test", "is a test") `) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains("this is a test", "is not a test")`) + assertExprPanics(t, `//seq.contains(123, 124)`) } func TestStrJoin(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, `"" `, `//str.join([], ",") `) - AssertCodesEvalToSameValue(t, `",," `, `//str.join(["", "", ""], ",") `) - AssertCodesEvalToSameValue(t, `"this is a test" `, `//str.join(["this", "is", "a", "test"], " ")`) - AssertCodesEvalToSameValue(t, `"this" `, `//str.join(["this"], ",") `) - assertExprPanics(t, `//str.join("this", 2)`) + // AssertCodesEvalToSameValue(t, `"" `, `//seq.join([], ",") `) + // AssertCodesEvalToSameValue(t, `",," `, `//seq.join(["", "", ""], ",") `) + // AssertCodesEvalToSameValue(t, `"this is a test" `, `//seq.join(["this", "is", "a", "test"], " ")`) + // AssertCodesEvalToSameValue(t, `"this" `, `//seq.join(["this"], ",") `) + // assertExprPanics(t, `//seq.join("this", 2)`) } From 29550858477ae881b5ff8cd5533674ab2b252404 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 12 May 2020 17:12:14 +0800 Subject: [PATCH 007/106] #234 Added code to implement API . --- syntax/std_seq.go | 17 ++++++++++++++++- syntax/std_seq_test.go | 25 ++++++++++++++++--------- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index e11b7462..7741e826 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -99,7 +99,22 @@ var ( return rel.NewBool(strings.Contains(mustAsString(args[0]), mustAsString(args[1]))) }, typeMethod{reflect.TypeOf(rel.Array{}), "contains"}: func(args ...rel.Value) rel.Value { - return nil + a := args[0].(rel.Array) + switch b := args[1].(type) { + case rel.Array: + return ContainsArray(a, b) + case rel.Value: + arrayEnum, _ := a.ArrayEnumerator() + if arrayEnum != nil { + for arrayEnum.MoveNext() { + if arrayEnum.Current().Equal(b) { + return rel.NewBool(true) + } + } + } + + } + return rel.NewBool(false) }, typeMethod{reflect.TypeOf(rel.Bytes{}), "contains"}: func(args ...rel.Value) rel.Value { return nil diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index d55c4c1f..ce4ea482 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -37,8 +37,23 @@ func TestSeqRepeat(t *testing.T) { AssertCodePanics(t, `//seq.repeat(2, 3.4)`) } -func TestSeqContains(t *testing.T) { +func TestContains(t *testing.T) { t.Parallel() + // string + // AssertCodesEvalToSameValue(t, `true `, `//seq.contains("this is a test", "") `) + // AssertCodesEvalToSameValue(t, `true `, `//seq.contains("this is a test", "is a test") `) + // AssertCodesEvalToSameValue(t, `false`, `//seq.contains("this is a test", "is not a test")`) + // assertExprPanics(t, `//seq.contains(123, 124)`) + // array + // AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],1)`) + // AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],3)`) + // AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],5)`) + + // AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'A')`) + // AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'E')`) + // AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'C')`) + + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['B','C'])`) } /////////////////// @@ -67,14 +82,6 @@ func TestStrSplit(t *testing.T) { assertExprPanics(t, `//seq.split("this is a test", 1)`) } -func TestStrContains(t *testing.T) { - t.Parallel() - AssertCodesEvalToSameValue(t, `true `, `//seq.contains("this is a test", "") `) - AssertCodesEvalToSameValue(t, `true `, `//seq.contains("this is a test", "is a test") `) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains("this is a test", "is not a test")`) - assertExprPanics(t, `//seq.contains(123, 124)`) -} - func TestStrJoin(t *testing.T) { t.Parallel() // AssertCodesEvalToSameValue(t, `"" `, `//seq.join([], ",") `) From 854cb2e6d7308adf26e351148a3245a237183303 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 12 May 2020 17:38:00 +0800 Subject: [PATCH 008/106] #234 Added code to implement API . --- syntax/std_seq_helper.go | 28 ++++++++++++++++++++++++++++ syntax/std_seq_test.go | 26 ++++++++++++++++---------- 2 files changed, 44 insertions(+), 10 deletions(-) create mode 100644 syntax/std_seq_helper.go diff --git a/syntax/std_seq_helper.go b/syntax/std_seq_helper.go new file mode 100644 index 00000000..dd98b21e --- /dev/null +++ b/syntax/std_seq_helper.go @@ -0,0 +1,28 @@ +package syntax + +import ( + "github.com/arr-ai/arrai/rel" +) + +// ContainsArray check if array a contains array b. +func ContainsArray(a rel.Array, b rel.Array) rel.Value { + // Get index of b[0] in a + bOffset := 0 + bVals := b.Values() + arrayEnum, _ := a.ArrayEnumerator() + for arrayEnum.MoveNext() { + if bOffset < len(bVals) && arrayEnum.Current().Equal(bVals[bOffset]) { + bOffset++ + } else { + if bOffset > 0 && bOffset < len(bVals) { + return rel.NewBool(false) + } + } + } + + if bOffset == len(bVals) { + return rel.NewBool(true) + } + + return rel.NewBool(false) +} diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index ce4ea482..0d0a26e9 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -40,20 +40,26 @@ func TestSeqRepeat(t *testing.T) { func TestContains(t *testing.T) { t.Parallel() // string - // AssertCodesEvalToSameValue(t, `true `, `//seq.contains("this is a test", "") `) - // AssertCodesEvalToSameValue(t, `true `, `//seq.contains("this is a test", "is a test") `) - // AssertCodesEvalToSameValue(t, `false`, `//seq.contains("this is a test", "is not a test")`) - // assertExprPanics(t, `//seq.contains(123, 124)`) + AssertCodesEvalToSameValue(t, `true `, `//seq.contains("this is a test", "") `) + AssertCodesEvalToSameValue(t, `true `, `//seq.contains("this is a test", "is a test") `) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains("this is a test", "is not a test")`) + assertExprPanics(t, `//seq.contains(123, 124)`) // array - // AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],1)`) - // AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],3)`) - // AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],5)`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],1)`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],3)`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],5)`) - // AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'A')`) - // AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'E')`) - // AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'C')`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'A')`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'E')`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'C')`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['A','B','C'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['B','C'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['B','C','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['A','B','C','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['A','B','C','D','E','F'])`) } /////////////////// From 605ad83aa449b15b934c43babf30e6ae250e647a Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 12 May 2020 23:10:12 +0800 Subject: [PATCH 009/106] #234 Removed funcs which have been moved to seq. --- syntax/std_str.go | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/syntax/std_str.go b/syntax/std_str.go index c98fc2f9..d6833073 100644 --- a/syntax/std_str.go +++ b/syntax/std_str.go @@ -71,47 +71,11 @@ var ( func stdStr() rel.Attr { return rel.NewTupleAttr("str", - createNestedFuncAttr("contains", 2, func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.Contains(mustAsString(args[0]), mustAsString(args[1]))) - }), rel.NewAttr("expand", stdStrExpand), - createNestedFuncAttr("has_prefix", 2, func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.HasPrefix(mustAsString(args[0]), mustAsString(args[1]))) - }), - createNestedFuncAttr("has_suffix", 2, func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.HasPrefix(mustAsString(args[0]), mustAsString(args[1]))) - }), - createNestedFuncAttr("join", 2, func(args ...rel.Value) rel.Value { - strs := args[0].(rel.Set) - toJoin := make([]string, 0, strs.Count()) - for i, ok := strs.(rel.Set).ArrayEnumerator(); ok && i.MoveNext(); { - toJoin = append(toJoin, mustAsString(i.Current())) - } - return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[1])))) - }), createNestedFuncAttr("lower", 1, func(args ...rel.Value) rel.Value { return rel.NewString([]rune(strings.ToLower(mustAsString(args[0])))) }), rel.NewAttr("repr", stdStrRepr), - createNestedFuncAttr("split", 2, func(args ...rel.Value) rel.Value { - splitted := strings.Split(mustAsString(args[0]), mustAsString(args[1])) - vals := make([]rel.Value, 0, len(splitted)) - for _, s := range splitted { - vals = append(vals, rel.NewString([]rune(s))) - } - return rel.NewArray(vals...) - }), - createNestedFuncAttr("sub", 3, func(args ...rel.Value) rel.Value { - return rel.NewString( - []rune( - strings.ReplaceAll( - mustAsString(args[0]), - mustAsString(args[1]), - mustAsString(args[2]), - ), - ), - ) - }), createNestedFuncAttr("title", 1, func(args ...rel.Value) rel.Value { return rel.NewString([]rune(strings.Title(mustAsString(args[0])))) }), From ba370bedd3081d269bd15d64dd4cc852e269a7c4 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 12 May 2020 23:13:15 +0800 Subject: [PATCH 010/106] #234 Made operator join work. --- syntax/std_seq.go | 9 +++++++-- syntax/std_seq_test.go | 10 +++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 7741e826..eff99ba7 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -153,7 +153,7 @@ var ( return nil }, // API join - typeMethod{reflect.TypeOf(rel.String{}), "join"}: func(args ...rel.Value) rel.Value { + typeMethod{reflect.TypeOf(rel.GenericSet{}), "join"}: func(args ...rel.Value) rel.Value { strs := args[0].(rel.Set) toJoin := make([]string, 0, strs.Count()) for i, ok := strs.(rel.Set).ArrayEnumerator(); ok && i.MoveNext(); { @@ -162,7 +162,12 @@ var ( return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[1])))) }, typeMethod{reflect.TypeOf(rel.Array{}), "join"}: func(args ...rel.Value) rel.Value { - return nil + strs := args[0].(rel.Set) + toJoin := make([]string, 0, strs.Count()) + for i, ok := strs.(rel.Set).ArrayEnumerator(); ok && i.MoveNext(); { + toJoin = append(toJoin, mustAsString(i.Current())) + } + return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[1])))) }, typeMethod{reflect.TypeOf(rel.Bytes{}), "join"}: func(args ...rel.Value) rel.Value { return nil diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index 0d0a26e9..2a618ecd 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -90,9 +90,9 @@ func TestStrSplit(t *testing.T) { func TestStrJoin(t *testing.T) { t.Parallel() - // AssertCodesEvalToSameValue(t, `"" `, `//seq.join([], ",") `) - // AssertCodesEvalToSameValue(t, `",," `, `//seq.join(["", "", ""], ",") `) - // AssertCodesEvalToSameValue(t, `"this is a test" `, `//seq.join(["this", "is", "a", "test"], " ")`) - // AssertCodesEvalToSameValue(t, `"this" `, `//seq.join(["this"], ",") `) - // assertExprPanics(t, `//seq.join("this", 2)`) + AssertCodesEvalToSameValue(t, `"" `, `//seq.join([], ",") `) + AssertCodesEvalToSameValue(t, `",," `, `//seq.join(["", "", ""], ",") `) + AssertCodesEvalToSameValue(t, `"this is a test" `, `//seq.join(["this", "is", "a", "test"], " ")`) + AssertCodesEvalToSameValue(t, `"this" `, `//seq.join(["this"], ",") `) + assertExprPanics(t, `//seq.join("this", 2)`) } From 0e41758bccc6876df9e810f719c9e32eb85e6f79 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 12 May 2020 23:55:11 +0800 Subject: [PATCH 011/106] #234 Added code to implement for rel.Array. --- syntax/std_seq.go | 21 ++++----------------- syntax/std_seq_helper.go | 30 ++++++++++++++++++++++++++++-- syntax/std_seq_test.go | 11 ++++++++++- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index eff99ba7..00679f5d 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -98,24 +98,11 @@ var ( typeMethod{reflect.TypeOf(rel.String{}), "contains"}: func(args ...rel.Value) rel.Value { return rel.NewBool(strings.Contains(mustAsString(args[0]), mustAsString(args[1]))) }, - typeMethod{reflect.TypeOf(rel.Array{}), "contains"}: func(args ...rel.Value) rel.Value { - a := args[0].(rel.Array) - switch b := args[1].(type) { - case rel.Array: - return ContainsArray(a, b) - case rel.Value: - arrayEnum, _ := a.ArrayEnumerator() - if arrayEnum != nil { - for arrayEnum.MoveNext() { - if arrayEnum.Current().Equal(b) { - return rel.NewBool(true) - } - } - } - } - return rel.NewBool(false) + typeMethod{reflect.TypeOf(rel.Array{}), "contains"}: func(args ...rel.Value) rel.Value { + return ArrayContains(args[0].(rel.Array), args[1]) }, + typeMethod{reflect.TypeOf(rel.Bytes{}), "contains"}: func(args ...rel.Value) rel.Value { return nil }, @@ -132,7 +119,7 @@ var ( ) }, typeMethod{reflect.TypeOf(rel.Array{}), "sub"}: func(args ...rel.Value) rel.Value { - return nil + return ArraySub(args[0].(rel.Array), args[1], args[2]) }, typeMethod{reflect.TypeOf(rel.Bytes{}), "sub"}: func(args ...rel.Value) rel.Value { return nil diff --git a/syntax/std_seq_helper.go b/syntax/std_seq_helper.go index dd98b21e..8f1c3fba 100644 --- a/syntax/std_seq_helper.go +++ b/syntax/std_seq_helper.go @@ -4,8 +4,25 @@ import ( "github.com/arr-ai/arrai/rel" ) -// ContainsArray check if array a contains array b. -func ContainsArray(a rel.Array, b rel.Array) rel.Value { +// ArrayContains check if array a contains b, and b can be rel.Value or rel.Array. +func ArrayContains(a rel.Array, b rel.Value) rel.Value { + switch b := b.(type) { + case rel.Array: + return arrayContainsArray(a, b) + case rel.Value: + arrayEnum, _ := a.ArrayEnumerator() + if arrayEnum != nil { + for arrayEnum.MoveNext() { + if arrayEnum.Current().Equal(b) { + return rel.NewBool(true) + } + } + } + } + return rel.NewBool(false) +} + +func arrayContainsArray(a, b rel.Array) rel.Value { // Get index of b[0] in a bOffset := 0 bVals := b.Values() @@ -26,3 +43,12 @@ func ContainsArray(a rel.Array, b rel.Array) rel.Value { return rel.NewBool(false) } + +// ArraySub substitutes all b in a with c. +func ArraySub(a rel.Array, b, c rel.Value) rel.Value { + // switch b := b.(type) { + // case rel.Array: + // return arrayContainsArray(a, b) + // case rel.Value: + return nil +} diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index 2a618ecd..7cc7ed4c 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -60,11 +60,14 @@ func TestContains(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['B','C','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['A','B','C','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['A','B','C','D','E','F'])`) + // bytes + } /////////////////// -func TestStrSub(t *testing.T) { +func TestSub(t *testing.T) { t.Parallel() + // string AssertCodesEvalToSameValue(t, `"this is a test"`, `//seq.sub("this is not a test", "is not", "is")`) @@ -75,6 +78,12 @@ func TestStrSub(t *testing.T) { `"this is still a test"`, `//seq.sub("this is still a test", "doesn't matter", "hello there")`) assertExprPanics(t, `//seq.sub("hello there", "test", 1)`) + // array + // AssertCodesEvalToSameValue(t, + // `"this is a test"`, + // `//seq.sub(["this", "is", "not", "a", "test"], ["is", "not"], "is")`) + // bytes + } func TestStrSplit(t *testing.T) { From 0a90275d936abd379bcfaa54ac349d78f42b52f9 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 12 May 2020 23:59:21 +0800 Subject: [PATCH 012/106] #234 Fixed issue found by CI linter. --- syntax/std_seq_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index 7cc7ed4c..ba957e48 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -83,7 +83,6 @@ func TestSub(t *testing.T) { // `"this is a test"`, // `//seq.sub(["this", "is", "not", "a", "test"], ["is", "not"], "is")`) // bytes - } func TestStrSplit(t *testing.T) { From 6aaf621618eaadbeb434c0cb02d6d9e98a36c3fa Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 12 May 2020 23:59:54 +0800 Subject: [PATCH 013/106] #234 Fixed issue found by CI linter. --- syntax/std_seq_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index ba957e48..80579670 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -61,7 +61,6 @@ func TestContains(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['A','B','C','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['A','B','C','D','E','F'])`) // bytes - } /////////////////// From 2da41401253dca868265f3a466029e72004a6303 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 13 May 2020 00:01:39 +0800 Subject: [PATCH 014/106] #234 Fixed issue found by CI linter. --- syntax/std_seq.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 00679f5d..e93b5e99 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -91,14 +91,10 @@ func process(apiName string, args ...rel.Value) rel.Value { return handler(args...) } -// This seq API handlders mapping, the key is API name + '_' + data type. +// This seq API handlders mapping. var ( handlerMapping = map[typeMethod]func(...rel.Value) rel.Value{ // API contains - typeMethod{reflect.TypeOf(rel.String{}), "contains"}: func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.Contains(mustAsString(args[0]), mustAsString(args[1]))) - }, - typeMethod{reflect.TypeOf(rel.Array{}), "contains"}: func(args ...rel.Value) rel.Value { return ArrayContains(args[0].(rel.Array), args[1]) }, @@ -106,6 +102,10 @@ var ( typeMethod{reflect.TypeOf(rel.Bytes{}), "contains"}: func(args ...rel.Value) rel.Value { return nil }, + + typeMethod{reflect.TypeOf(rel.String{}), "contains"}: func(args ...rel.Value) rel.Value { + return rel.NewBool(strings.Contains(mustAsString(args[0]), mustAsString(args[1]))) + }, // API sub typeMethod{reflect.TypeOf(rel.String{}), "sub"}: func(args ...rel.Value) rel.Value { return rel.NewString( From b12e57ffe0e651471596bea415eceeaf76aca1b3 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 13 May 2020 00:11:28 +0800 Subject: [PATCH 015/106] #234 Added some pseudo code. --- syntax/std_seq_helper.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/syntax/std_seq_helper.go b/syntax/std_seq_helper.go index 8f1c3fba..04d6ecb3 100644 --- a/syntax/std_seq_helper.go +++ b/syntax/std_seq_helper.go @@ -46,6 +46,8 @@ func arrayContainsArray(a, b rel.Array) rel.Value { // ArraySub substitutes all b in a with c. func ArraySub(a rel.Array, b, c rel.Value) rel.Value { + // bArray := rel.NewArray(b) + // cArray := rel.NewArray(c) // switch b := b.(type) { // case rel.Array: // return arrayContainsArray(a, b) From 6f21ca696790cc35dcc7e00ace4ef4b7ac7738ce Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 13 May 2020 09:51:32 +0800 Subject: [PATCH 016/106] #234 Fixed issue found by go lint. --- syntax/std_seq.go | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index e93b5e99..6932e666 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -95,19 +95,19 @@ func process(apiName string, args ...rel.Value) rel.Value { var ( handlerMapping = map[typeMethod]func(...rel.Value) rel.Value{ // API contains - typeMethod{reflect.TypeOf(rel.Array{}), "contains"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.Array{}), "contains"}: func(args ...rel.Value) rel.Value { return ArrayContains(args[0].(rel.Array), args[1]) }, - typeMethod{reflect.TypeOf(rel.Bytes{}), "contains"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.Bytes{}), "contains"}: func(args ...rel.Value) rel.Value { return nil }, - typeMethod{reflect.TypeOf(rel.String{}), "contains"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.String{}), "contains"}: func(args ...rel.Value) rel.Value { return rel.NewBool(strings.Contains(mustAsString(args[0]), mustAsString(args[1]))) }, // API sub - typeMethod{reflect.TypeOf(rel.String{}), "sub"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.String{}), "sub"}: func(args ...rel.Value) rel.Value { return rel.NewString( []rune( strings.ReplaceAll( @@ -118,14 +118,14 @@ var ( ), ) }, - typeMethod{reflect.TypeOf(rel.Array{}), "sub"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.Array{}), "sub"}: func(args ...rel.Value) rel.Value { return ArraySub(args[0].(rel.Array), args[1], args[2]) }, - typeMethod{reflect.TypeOf(rel.Bytes{}), "sub"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.Bytes{}), "sub"}: func(args ...rel.Value) rel.Value { return nil }, // API split - typeMethod{reflect.TypeOf(rel.String{}), "split"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.String{}), "split"}: func(args ...rel.Value) rel.Value { splitted := strings.Split(mustAsString(args[0]), mustAsString(args[1])) vals := make([]rel.Value, 0, len(splitted)) for _, s := range splitted { @@ -133,14 +133,14 @@ var ( } return rel.NewArray(vals...) }, - typeMethod{reflect.TypeOf(rel.Array{}), "split"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.Array{}), "split"}: func(args ...rel.Value) rel.Value { return nil }, - typeMethod{reflect.TypeOf(rel.Bytes{}), "split"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.Bytes{}), "split"}: func(args ...rel.Value) rel.Value { return nil }, // API join - typeMethod{reflect.TypeOf(rel.GenericSet{}), "join"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.GenericSet{}), "join"}: func(args ...rel.Value) rel.Value { strs := args[0].(rel.Set) toJoin := make([]string, 0, strs.Count()) for i, ok := strs.(rel.Set).ArrayEnumerator(); ok && i.MoveNext(); { @@ -148,7 +148,7 @@ var ( } return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[1])))) }, - typeMethod{reflect.TypeOf(rel.Array{}), "join"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.Array{}), "join"}: func(args ...rel.Value) rel.Value { strs := args[0].(rel.Set) toJoin := make([]string, 0, strs.Count()) for i, ok := strs.(rel.Set).ArrayEnumerator(); ok && i.MoveNext(); { @@ -156,27 +156,27 @@ var ( } return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[1])))) }, - typeMethod{reflect.TypeOf(rel.Bytes{}), "join"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.Bytes{}), "join"}: func(args ...rel.Value) rel.Value { return nil }, // API has_prefix - typeMethod{reflect.TypeOf(rel.String{}), "has_prefix"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.String{}), "has_prefix"}: func(args ...rel.Value) rel.Value { return rel.NewBool(strings.HasPrefix(mustAsString(args[0]), mustAsString(args[1]))) }, - typeMethod{reflect.TypeOf(rel.Array{}), "has_prefix"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.Array{}), "has_prefix"}: func(args ...rel.Value) rel.Value { return nil }, - typeMethod{reflect.TypeOf(rel.Bytes{}), "has_prefix"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.Bytes{}), "has_prefix"}: func(args ...rel.Value) rel.Value { return nil }, // API has_suffix - typeMethod{reflect.TypeOf(rel.String{}), "has_suffix"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.String{}), "has_suffix"}: func(args ...rel.Value) rel.Value { return rel.NewBool(strings.HasSuffix(mustAsString(args[0]), mustAsString(args[1]))) }, - typeMethod{reflect.TypeOf(rel.Array{}), "has_suffix"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.Array{}), "has_suffix"}: func(args ...rel.Value) rel.Value { return nil }, - typeMethod{reflect.TypeOf(rel.Bytes{}), "has_suffix"}: func(args ...rel.Value) rel.Value { + {reflect.TypeOf(rel.Bytes{}), "has_suffix"}: func(args ...rel.Value) rel.Value { return nil }, } From 509fc6ce8901764e0089023599618ac58f9a401d Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 13 May 2020 09:57:48 +0800 Subject: [PATCH 017/106] #234 Updated sample and docs as has_prefix has been moved to seq. --- docs/std-str.md | 6 +++--- examples/grpc/grpc-proto.arrai | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/std-str.md b/docs/std-str.md index 2af6c359..802c721f 100644 --- a/docs/std-str.md +++ b/docs/std-str.md @@ -73,7 +73,7 @@ Usage: | `//str.title("laser noises pew pew pew")` | `"Laser Noises Pew Pew Pew"` | | `//str.title("pew")` | `"Pew"` | -## `//str.has_prefix(s <: string, prefix <: string) <: bool` +## `//seq.has_prefix(s <: string, prefix <: string) <: bool` `has_prefix` checks whether the string `s` is prefixed by `prefix`. It returns a boolean. @@ -81,8 +81,8 @@ Usage: | example | equals | |:-|:-| -| `//str.has_prefix("I'm running out of stuff to write", "I'm")` | `true` | -| `//str.has_prefix("I'm running out of stuff to write", "to write")` | `{}` which is equal to `false` | +| `//seq.has_prefix("I'm running out of stuff to write", "I'm")` | `true` | +| `//seq.has_prefix("I'm running out of stuff to write", "to write")` | `{}` which is equal to `false` | ## `//str.has_suffix(s <: string, suffix <: string) <: bool` diff --git a/examples/grpc/grpc-proto.arrai b/examples/grpc/grpc-proto.arrai index ed194a84..f3415502 100644 --- a/examples/grpc/grpc-proto.arrai +++ b/examples/grpc/grpc-proto.arrai @@ -2,7 +2,7 @@ let grpc = //{./grpc}; let wrap = "wrap" <: app.attrs.patterns; let proto = //{./proto-util}(wrap); -let endpoints = app.endpoints where !//str.has_prefix(.@item.name, "enum "); +let endpoints = app.endpoints where !//seq.has_prefix(.@item.name, "enum "); //archive.tar.tar({ app.name + ".proto": $` // THIS IS AUTOGENERATED BY sysl // From 81aa4cf8ae18693d47c1155eb64fd2ec6f289b1f Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 13 May 2020 10:17:42 +0800 Subject: [PATCH 018/106] #234 Updated test cases as func changes in str. --- internal/shell/shell_cmd_test.go | 2 +- internal/shell/shell_test.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/shell/shell_cmd_test.go b/internal/shell/shell_cmd_test.go index d1faca5b..c2cbd475 100644 --- a/internal/shell/shell_cmd_test.go +++ b/internal/shell/shell_cmd_test.go @@ -14,7 +14,7 @@ func TestIsCommand(t *testing.T) { t.Parallel() assert.True(t, isCommand("/hi")) - assert.False(t, isCommand("//str.join")) + assert.False(t, isCommand("//seq.join")) } func TestTryRunCommand(t *testing.T) { diff --git a/internal/shell/shell_test.go b/internal/shell/shell_test.go index 5d29eb56..aad69aa7 100644 --- a/internal/shell/shell_test.go +++ b/internal/shell/shell_test.go @@ -134,10 +134,10 @@ func TestGetLastToken(t *testing.T) { assert.Equal(t, "//str", getLastToken([]rune("//str"))) assert.Equal(t, "//", getLastToken([]rune("//"))) assert.Equal(t, "///", getLastToken([]rune("///"))) - assert.Equal(t, "//", getLastToken([]rune("//str.contains(//"))) - assert.Equal(t, "//arch", getLastToken([]rune("//str.contains(//arch"))) - assert.Equal(t, "tuple.", getLastToken([]rune("//str.contains(tuple."))) - assert.Equal(t, "", getLastToken([]rune("//str.contains("))) + assert.Equal(t, "//", getLastToken([]rune("//seq.contains(//"))) + assert.Equal(t, "//arch", getLastToken([]rune("//seq.contains(//arch"))) + assert.Equal(t, "tuple.", getLastToken([]rune("//seq.contains(tuple."))) + assert.Equal(t, "", getLastToken([]rune("//seq.contains("))) assert.Equal(t, "", getLastToken([]rune(""))) } @@ -147,7 +147,7 @@ func TestTabCompletionStdlib(t *testing.T) { stdlibNames := stdlib.Names().OrderedNames() assertTabCompletion(t, append(stdlibNames, "{"), 0, "//\t", nil) - assertTabCompletion(t, append(stdlibNames, "{"), 0, "//str.contains(//\t", nil) + assertTabCompletion(t, append(stdlibNames, "{"), 0, "//seq.contains(//\t", nil) prefix := "s" assertTabCompletionWithPrefix(t, prefix, stdlibNames, "//%s\t", nil) From 7d421e349d227035e84454b8647e911a8a7b4791 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 13 May 2020 10:21:50 +0800 Subject: [PATCH 019/106] #234 Updated samples as func is moved from str tp seq. --- examples/grpc/grpc-proto.arrai | 10 +++++----- examples/grpc/grpc.arrai | 2 +- examples/grpc/proto-util.arrai | 14 +++++++------- internal/shell/shell_test.go | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/grpc/grpc-proto.arrai b/examples/grpc/grpc-proto.arrai index f3415502..7569b21b 100644 --- a/examples/grpc/grpc-proto.arrai +++ b/examples/grpc/grpc-proto.arrai @@ -24,8 +24,8 @@ let endpoints = app.endpoints where !//seq.has_prefix(.@item.name, "enum "); ${cond (app.endpoints: $` ${//rel.union((endpoints >> (.params >> cond ( - //str.contains(grpc.type(.), "google.protobuf"): $` - import "${//str.sub(grpc.type(.), ".", "/")}.proto";`, + //seq.contains(grpc.type(.), "google.protobuf"): $` + import "${//seq.sub(grpc.type(.), ".", "/")}.proto";`, ) => .@item )) => .@item)::\i:\n} service ${app.name} { @@ -37,14 +37,14 @@ let endpoints = app.endpoints where !//seq.has_prefix(.@item.name, "enum "); }`:::\n} ${endpoints >> proto.wrapSequence(.).grpcType::\i} ${cond (wrap: endpoints) >> - let retTokens = //str.split(ep.ret("ok"), " "); - let retName = //str.sub(//seq.concat(retTokens -- {"sequence", "of"}), ".", ""); + let retTokens = //seq.split(ep.ret("ok"), " "); + let retName = //seq.sub(//seq.concat(retTokens -- {"sequence", "of"}), ".", ""); let attr = ep.attrs(retName + "_rpcId"); let epi = proto.endpointInfo(ep); $` message ${epi.paramName} { ${ep.params >> - let name = //str.sub(.name, "-", ""); + let name = //seq.sub(.name, "-", ""); $`${grpc.type(.)} req${name} = ${.attrs(name + "_rpcId")};` ::\i} } diff --git a/examples/grpc/grpc.arrai b/examples/grpc/grpc.arrai index 0c17c397..8e3a9e09 100644 --- a/examples/grpc/grpc.arrai +++ b/examples/grpc/grpc.arrai @@ -13,7 +13,7 @@ ), 'sequence': 'repeated ' + type(t.sequence) *: cond ( - //str.contains(t.type_ref, "google-protobuf"): //str.sub(t.type_ref, "-", "."), + //seq.contains(t.type_ref, "google-protobuf"): //seq.sub(t.type_ref, "-", "."), *: t.type_ref, ), ), diff --git a/examples/grpc/proto-util.arrai b/examples/grpc/proto-util.arrai index b090d77c..7b2f78ea 100644 --- a/examples/grpc/proto-util.arrai +++ b/examples/grpc/proto-util.arrai @@ -1,21 +1,21 @@ let grpc = //{./grpc}; \wrap ( - field: \. $`${grpc.type(.)} ${//str.sub(.key, "-", "")} = ${.attrs.rpcId};`, + field: \. $`${grpc.type(.)} ${//seq.sub(.key, "-", "")} = ${.attrs.rpcId};`, imports: \fields - fields where(//str.contains(grpc.type(.@item), "google.protobuf")) >> - $`import "${//str.sub(grpc.type(.), ".", "/")}.proto";`, + fields where(//seq.contains(grpc.type(.@item), "google.protobuf")) >> + $`import "${//seq.sub(grpc.type(.), ".", "/")}.proto";`, endpointInfo: \ep - let method = //str.sub(//str.title(//str.lower(ep.name)), "-", ""); + let method = //seq.sub(//str.title(//str.lower(ep.name)), "-", ""); let paramName = cond ( wrap: method + "Request", *: $"${ep.params >> grpc.type(.)::, }", ); let streamRes = cond ( - ep.attrs.stream: //str.sub(ep.ret("ok"), "sequence of", "stream"), - *: //str.sub(ep.ret("ok"), "sequence of ", "") + "s", + ep.attrs.stream: //seq.sub(ep.ret("ok"), "sequence of", "stream"), + *: //seq.sub(ep.ret("ok"), "sequence of ", "") + "s", ); let responseName = cond (wrap: method + "Response", *: streamRes); ( @@ -26,7 +26,7 @@ let grpc = //{./grpc}; ), wrapSequence: \ep - let type = //str.sub(ep.ret("ok"), "sequence of ", ""); + let type = //seq.sub(ep.ret("ok"), "sequence of ", ""); let wrapType = type + "s"; let name = //str.lower(type) + "s"; ( diff --git a/internal/shell/shell_test.go b/internal/shell/shell_test.go index aad69aa7..c06f7cfa 100644 --- a/internal/shell/shell_test.go +++ b/internal/shell/shell_test.go @@ -154,9 +154,9 @@ func TestTabCompletionStdlib(t *testing.T) { assertTabCompletionWithPrefix(t, prefix, stdlibNames, "x(//%s\t", nil) assertTabCompletionWithPrefix(t, prefix, stdlibNames, "x(//%s\t + random)", nil) - lib := "str" - strlib := stdlib.MustGet(lib).(rel.Tuple).Names().OrderedNames() - assertTabCompletionWithPrefix(t, prefix, strlib, "//"+lib+".%s\t", nil) + // lib := "str" + // strlib := stdlib.MustGet(lib).(rel.Tuple).Names().OrderedNames() + // assertTabCompletionWithPrefix(t, prefix, strlib, "//"+lib+".%s\t", nil) } func assertTabCompletionWithPrefix( From e53492a2b468071ea275b3e5154fef964315f3b1 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 13 May 2020 10:22:22 +0800 Subject: [PATCH 020/106] #234 Updated docs as func is moved from str tp seq. --- docs/README.md | 2 +- docs/std-str.md | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/README.md b/docs/README.md index e0df3cff..e57ae483 100644 --- a/docs/README.md +++ b/docs/README.md @@ -417,7 +417,7 @@ External libraries may be accessed via package references. 1. **`//math`:** math functions and constants such as `//math.sin` and `//math.pi`. 2. **`//str`:** string functions such as `//str.upper` and - `//str.join`. + `//seq.join`. 3. **`//fn`:** higher order functions such as `//fn.fix` and `//fn.fixt`. See the [standard library reference](std.md) for full documentation on all packages. 2. **`//{./path}`** provides access to other arrai files relative to the current diff --git a/docs/std-str.md b/docs/std-str.md index 802c721f..b612061f 100644 --- a/docs/std-str.md +++ b/docs/std-str.md @@ -2,7 +2,7 @@ The `str` library contains functions that are used for string manipulations. -## `//str.contains(str <: string, substr <: string) <: bool` +## `//seq.contains(str <: string, substr <: string) <: bool` `contains` checks whether `substr` is contained in `str`. It returns a boolean. @@ -11,10 +11,10 @@ Usage: | example | equals | |:-|:-| -| `//str.contains("the full string which has substring", "substring")` | `true` | -| `//str.contains("just some random sentence", "microwave")` | `{}` which is equal to `false` | +| `//seq.contains("the full string which has substring", "substring")` | `true` | +| `//seq.contains("just some random sentence", "microwave")` | `{}` which is equal to `false` | -## `//str.sub(s <: string, old <: string, new <: string) <: string` +## `//seq.sub(s <: string, old <: string, new <: string) <: string` `sub` replaces occurrences of `old` in `s` with `new`. It returns the modified string. @@ -22,10 +22,10 @@ Usage: | example | equals | |:-|:-| -| `//str.sub("this is the old string", "old string", "new sentence")` | `"this is the new sentence"` | -| `//str.sub("just another sentence", "string", "stuff")` | `"just another sentence"` | +| `//seq.sub("this is the old string", "old string", "new sentence")` | `"this is the new sentence"` | +| `//seq.sub("just another sentence", "string", "stuff")` | `"just another sentence"` | -## `//str.split(s <: string, delimiter <: string) <: array of string` +## `//seq.split(s <: string, delimiter <: string) <: array of string` `split` splits the string `s` based on the provided `delimiter`. It returns an array of strings which are split from the string `s`. @@ -34,8 +34,8 @@ Usage: | example | equals | |:-|:-| -| `//str.split("deliberately adding spaces to demonstrate the split function", " ")` | `["deliberately", "adding", "spaces", "to", "demonstrate", "the", "split", "function"]` | -| `//str.split("this is just a random sentence", "random stuff")` | `["this is just a random sentence"]` | +| `//seq.split("deliberately adding spaces to demonstrate the split function", " ")` | `["deliberately", "adding", "spaces", "to", "demonstrate", "the", "split", "function"]` | +| `//seq.split("this is just a random sentence", "random stuff")` | `["this is just a random sentence"]` | ## `//str.lower(s <: string) <: string` @@ -84,7 +84,7 @@ Usage: | `//seq.has_prefix("I'm running out of stuff to write", "I'm")` | `true` | | `//seq.has_prefix("I'm running out of stuff to write", "to write")` | `{}` which is equal to `false` | -## `//str.has_suffix(s <: string, suffix <: string) <: bool` +## `//seq.has_suffix(s <: string, suffix <: string) <: bool` `has_suffix` checks whether the string `s` is suffixed by `suffix`. It returns a boolean. @@ -92,10 +92,10 @@ Usage: | example | equals | |:-|:-| -| `//str.has_suffix("I'm running out of stuff to write", "I'm")` | `{}` which is equal to `false` | -| `//str.has_suffix("I'm running out of stuff to write", "to write")` | `true` | +| `//seq.has_suffix("I'm running out of stuff to write", "I'm")` | `{}` which is equal to `false` | +| `//seq.has_suffix("I'm running out of stuff to write", "to write")` | `true` | -## `//str.join(s <: array_of_string, delimiter <: string) <: string` +## `//seq.join(s <: array_of_string, delimiter <: string) <: string` `join` returns a concatenated string with each member of `s` delimited by `delimiter` @@ -103,6 +103,6 @@ Usage: | example | equals | |:-|:-| -| `//str.join(["pew", "another pew", "and more pews"], ", ")` | `"pew, another pew, and more pews"` | -| `//str.join(["this", "is", "a", "sentence"], " ")` | `"this is a sentence"` | -| `//str.join(["this", "is", "a", "sentence"], "")` | `"thisisasentence"` | +| `//seq.join(["pew", "another pew", "and more pews"], ", ")` | `"pew, another pew, and more pews"` | +| `//seq.join(["this", "is", "a", "sentence"], " ")` | `"this is a sentence"` | +| `//seq.join(["this", "is", "a", "sentence"], "")` | `"thisisasentence"` | From 04e57e3a9946cfd272ce5438acbc97d7c56d338c Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 13 May 2020 13:35:12 +0800 Subject: [PATCH 021/106] Simplify process and code. --- syntax/std_seq_helper.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/syntax/std_seq_helper.go b/syntax/std_seq_helper.go index 04d6ecb3..d8057d55 100644 --- a/syntax/std_seq_helper.go +++ b/syntax/std_seq_helper.go @@ -10,14 +10,9 @@ func ArrayContains(a rel.Array, b rel.Value) rel.Value { case rel.Array: return arrayContainsArray(a, b) case rel.Value: - arrayEnum, _ := a.ArrayEnumerator() - if arrayEnum != nil { - for arrayEnum.MoveNext() { - if arrayEnum.Current().Equal(b) { - return rel.NewBool(true) - } - } - } + // Convert to array and make it using the same process + bArray, _ := rel.AsArray(rel.NewArray(b)) + return arrayContainsArray(a, bArray) } return rel.NewBool(false) } From 8ba80db27b1987af732c467a5829ab13010b28f9 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 13 May 2020 17:04:15 +0800 Subject: [PATCH 022/106] #234 Update code to support has_prefix and has_suffix. --- syntax/std_seq.go | 22 ++++++---- syntax/std_seq_helper.go | 90 +++++++++++++++++++++++++++++++--------- syntax/std_seq_test.go | 89 +++++++++++++++++++++++++++++++++++---- 3 files changed, 167 insertions(+), 34 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 6932e666..f28402d6 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -95,6 +95,10 @@ func process(apiName string, args ...rel.Value) rel.Value { var ( handlerMapping = map[typeMethod]func(...rel.Value) rel.Value{ // API contains + {reflect.TypeOf(rel.String{}), "contains"}: func(args ...rel.Value) rel.Value { + return rel.NewBool(strings.Contains(mustAsString(args[0]), mustAsString(args[1]))) + }, + {reflect.TypeOf(rel.Array{}), "contains"}: func(args ...rel.Value) rel.Value { return ArrayContains(args[0].(rel.Array), args[1]) }, @@ -102,10 +106,6 @@ var ( {reflect.TypeOf(rel.Bytes{}), "contains"}: func(args ...rel.Value) rel.Value { return nil }, - - {reflect.TypeOf(rel.String{}), "contains"}: func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.Contains(mustAsString(args[0]), mustAsString(args[1]))) - }, // API sub {reflect.TypeOf(rel.String{}), "sub"}: func(args ...rel.Value) rel.Value { return rel.NewString( @@ -164,20 +164,26 @@ var ( return rel.NewBool(strings.HasPrefix(mustAsString(args[0]), mustAsString(args[1]))) }, {reflect.TypeOf(rel.Array{}), "has_prefix"}: func(args ...rel.Value) rel.Value { - return nil + return ArrayPrefix(args[0].(rel.Array), args[1]) + }, + {reflect.TypeOf(rel.GenericSet{}), "has_prefix"}: func(args ...rel.Value) rel.Value { + return rel.NewBool(false) }, {reflect.TypeOf(rel.Bytes{}), "has_prefix"}: func(args ...rel.Value) rel.Value { - return nil + return rel.NewBool(strings.HasPrefix(args[0].String(), args[1].String())) }, // API has_suffix {reflect.TypeOf(rel.String{}), "has_suffix"}: func(args ...rel.Value) rel.Value { return rel.NewBool(strings.HasSuffix(mustAsString(args[0]), mustAsString(args[1]))) }, {reflect.TypeOf(rel.Array{}), "has_suffix"}: func(args ...rel.Value) rel.Value { - return nil + return ArraySuffix(args[0].(rel.Array), args[1]) + }, + {reflect.TypeOf(rel.GenericSet{}), "has_suffix"}: func(args ...rel.Value) rel.Value { + return rel.NewBool(false) }, {reflect.TypeOf(rel.Bytes{}), "has_suffix"}: func(args ...rel.Value) rel.Value { - return nil + return rel.NewBool(strings.HasSuffix(args[0].String(), args[1].String())) }, } ) diff --git a/syntax/std_seq_helper.go b/syntax/std_seq_helper.go index d8057d55..881c03de 100644 --- a/syntax/std_seq_helper.go +++ b/syntax/std_seq_helper.go @@ -6,21 +6,10 @@ import ( // ArrayContains check if array a contains b, and b can be rel.Value or rel.Array. func ArrayContains(a rel.Array, b rel.Value) rel.Value { - switch b := b.(type) { - case rel.Array: - return arrayContainsArray(a, b) - case rel.Value: - // Convert to array and make it using the same process - bArray, _ := rel.AsArray(rel.NewArray(b)) - return arrayContainsArray(a, bArray) - } - return rel.NewBool(false) -} + bArray := convert2Array(b) -func arrayContainsArray(a, b rel.Array) rel.Value { - // Get index of b[0] in a bOffset := 0 - bVals := b.Values() + bVals := bArray.Values() arrayEnum, _ := a.ArrayEnumerator() for arrayEnum.MoveNext() { if bOffset < len(bVals) && arrayEnum.Current().Equal(bVals[bOffset]) { @@ -41,11 +30,74 @@ func arrayContainsArray(a, b rel.Array) rel.Value { // ArraySub substitutes all b in a with c. func ArraySub(a rel.Array, b, c rel.Value) rel.Value { - // bArray := rel.NewArray(b) - // cArray := rel.NewArray(c) - // switch b := b.(type) { - // case rel.Array: - // return arrayContainsArray(a, b) - // case rel.Value: return nil } + +// ArrayPrefix check if a starts with b. +func ArrayPrefix(a rel.Array, b rel.Value) rel.Value { + bArray := convert2Array(b) + + if bArray.Count() == 0 { + return rel.NewBool(false) + } + if a.Count() < bArray.Count() { + return rel.NewBool(false) + } + + bVals := bArray.Values() + bOffset := 0 + arrayEnum, _ := a.ArrayEnumerator() + for arrayEnum.MoveNext() { + if bOffset < bArray.Count() && arrayEnum.Current().Equal(bVals[bOffset]) { + bOffset++ + if bOffset == bArray.Count() { + break + } + } else { + return rel.NewBool(false) + } + } + + return rel.NewBool(true) +} + +// ArraySuffix check if a starts with b. +func ArraySuffix(a rel.Array, b rel.Value) rel.Value { + bArray := convert2Array(b) + + if bArray.Count() == 0 { + return rel.NewBool(false) + } + if a.Count() < bArray.Count() { + return rel.NewBool(false) + } + + aVals := a.Values() + bVals := bArray.Values() + bOffset := bArray.Count() - 1 + + for i := a.Count() - 1; i >= 0; i-- { + if bOffset > -1 && aVals[i].Equal(bVals[bOffset]) { + bOffset-- + if bOffset == -1 { + break + } + } else { + return rel.NewBool(false) + } + } + + return rel.NewBool(true) +} + +func convert2Array(val rel.Value) rel.Array { + switch val := val.(type) { + case rel.Array: + return val + case rel.Value: + valArray, _ := rel.AsArray(rel.NewArray(val)) + return valArray + } + + panic("it support types rel.Array and rel.Value only.") +} diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index 80579670..2f7700bb 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -37,14 +37,17 @@ func TestSeqRepeat(t *testing.T) { AssertCodePanics(t, `//seq.repeat(2, 3.4)`) } -func TestContains(t *testing.T) { +func TestStrContains(t *testing.T) { t.Parallel() - // string AssertCodesEvalToSameValue(t, `true `, `//seq.contains("this is a test", "") `) AssertCodesEvalToSameValue(t, `true `, `//seq.contains("this is a test", "is a test") `) AssertCodesEvalToSameValue(t, `false`, `//seq.contains("this is a test", "is not a test")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains("this is a test", "a is")`) assertExprPanics(t, `//seq.contains(123, 124)`) - // array +} + +func TestArrayContains(t *testing.T) { + t.Parallel() AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],1)`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],3)`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],5)`) @@ -60,11 +63,16 @@ func TestContains(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['B','C','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['A','B','C','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['A','B','C','D','E','F'])`) - // bytes + + // TODO, it requries API contains code change + // AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A', 'A', 'B','C','D','E'],['A','B','C'])`) +} +func TestBytesContains(t *testing.T) { + t.Parallel() } /////////////////// -func TestSub(t *testing.T) { +func TestStrSub(t *testing.T) { t.Parallel() // string AssertCodesEvalToSameValue(t, @@ -77,11 +85,17 @@ func TestSub(t *testing.T) { `"this is still a test"`, `//seq.sub("this is still a test", "doesn't matter", "hello there")`) assertExprPanics(t, `//seq.sub("hello there", "test", 1)`) - // array +} + +func TestArraySub(t *testing.T) { + t.Parallel() // AssertCodesEvalToSameValue(t, // `"this is a test"`, // `//seq.sub(["this", "is", "not", "a", "test"], ["is", "not"], "is")`) - // bytes +} + +func TestBytesSub(t *testing.T) { + t.Parallel() } func TestStrSplit(t *testing.T) { @@ -103,3 +117,64 @@ func TestStrJoin(t *testing.T) { AssertCodesEvalToSameValue(t, `"this" `, `//seq.join(["this"], ",") `) assertExprPanics(t, `//seq.join("this", 2)`) } + +func TestStrPrefix(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("ABCDE", "A")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("ABCDE", "AB")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("ABCDE", "BCD")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("ABCDE", "CD")`) +} + +func TestArrayPrefix(t *testing.T) { + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C','D','E'], 'A')`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C','D','E'], ['A'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C','D','E'], ['A','B'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C','D','E'], ['A','B','C'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C','D','E'], ['A','B','C','D'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E'], [])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix([], [])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E'], 'B')`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E'], ['B'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E'], ['B','C'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E'], ['A','B','C','D','E','F'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix([], ['A','B','C','D','E','F'])`) +} + +func TestBytesPrefix(t *testing.T) { + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('h'))`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('he'))`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('e'))`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('l'))`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('o'))`) +} + +func TestStrSuffix(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("ABCDE", "E")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("ABCDE", "DE")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("ABCDE", "CD")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("ABCDE", "D")`) +} + +func TestArraySuffix(t *testing.T) { + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(['A','B','C','D','E'], 'E')`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(['A','B','C','D','E'], ['E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(['A','B','C','D','E'], ['D','E'])`) + + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E'], [])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix([], [])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix([], ['D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E'], 'D')`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E'], ['D'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E'], ['C','D'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E'], ['A','B','C','D','E','F'])`) +} + +func TestBytesSuffix(t *testing.T) { + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('o'))`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('lo'))`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('e'))`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('ell'))`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('h'))`) +} From 2ff9b6a425e8de69ea99a1622b43797ce88aaafb Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 13 May 2020 17:24:32 +0800 Subject: [PATCH 023/106] #234 Updated test case as funcs have been moved from str to seq. --- internal/shell/shell_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/shell/shell_test.go b/internal/shell/shell_test.go index 56e69ced..cc777571 100644 --- a/internal/shell/shell_test.go +++ b/internal/shell/shell_test.go @@ -157,7 +157,7 @@ func TestTabCompletionStdlib(t *testing.T) { assertTabCompletionWithPrefix(t, prefix, stdlibNames, "x(//%s\t", nil) assertTabCompletionWithPrefix(t, prefix, stdlibNames, "x(//%s\t + random)", nil) - lib := "str" + lib := "seq" strlib := stdlib.MustGet(lib).(rel.Tuple).Names().OrderedNames() assertTabCompletionWithPrefix(t, prefix, strlib, "//"+lib+".%s\t", nil) } From 205024e9707f6eca40f38af27c29cfcd4aadb8e6 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 13 May 2020 17:44:34 +0800 Subject: [PATCH 024/106] #234 Fixed issue found by linter. --- syntax/std_seq_helper.go | 4 ++-- syntax/std_seq_test.go | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/syntax/std_seq_helper.go b/syntax/std_seq_helper.go index 881c03de..bff40d11 100644 --- a/syntax/std_seq_helper.go +++ b/syntax/std_seq_helper.go @@ -76,8 +76,8 @@ func ArraySuffix(a rel.Array, b rel.Value) rel.Value { bVals := bArray.Values() bOffset := bArray.Count() - 1 - for i := a.Count() - 1; i >= 0; i-- { - if bOffset > -1 && aVals[i].Equal(bVals[bOffset]) { + for _, val := range aVals[a.Count()-1:] { + if bOffset > -1 && val.Equal(bVals[bOffset]) { bOffset-- if bOffset == -1 { break diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index 2f7700bb..d0b3ef4a 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -64,7 +64,7 @@ func TestArrayContains(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['A','B','C','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['A','B','C','D','E','F'])`) - // TODO, it requries API contains code change + // TODO, it requires API contains code change // AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A', 'A', 'B','C','D','E'],['A','B','C'])`) } func TestBytesContains(t *testing.T) { @@ -118,6 +118,15 @@ func TestStrJoin(t *testing.T) { assertExprPanics(t, `//seq.join("this", 2)`) } +func TestArrayJoin(t *testing.T) { + t.Parallel() + // AssertCodesEvalToSameValue(t, `""`, `//seq.join([], ",")`) +} + +func TestBytesJoin(t *testing.T) { + t.Parallel() +} + func TestStrPrefix(t *testing.T) { t.Parallel() AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("ABCDE", "A")`) From 72b21b7c38115dc8e92c12eef3b10ceb8b9deb3b Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Thu, 14 May 2020 00:20:51 +0800 Subject: [PATCH 025/106] #234 Change API params order. --- syntax/std_seq.go | 19 +++++++++---------- syntax/std_seq_test.go | 20 +++++++++++++++----- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index f28402d6..0f669475 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -91,7 +91,7 @@ func process(apiName string, args ...rel.Value) rel.Value { return handler(args...) } -// This seq API handlders mapping. +// This is seq API handlders mapping. var ( handlerMapping = map[typeMethod]func(...rel.Value) rel.Value{ // API contains @@ -140,21 +140,20 @@ var ( return nil }, // API join - {reflect.TypeOf(rel.GenericSet{}), "join"}: func(args ...rel.Value) rel.Value { - strs := args[0].(rel.Set) + {reflect.TypeOf(rel.String{}), "join"}: func(args ...rel.Value) rel.Value { + strs := args[1].(rel.Set) toJoin := make([]string, 0, strs.Count()) for i, ok := strs.(rel.Set).ArrayEnumerator(); ok && i.MoveNext(); { toJoin = append(toJoin, mustAsString(i.Current())) } - return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[1])))) + return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[0])))) + }, + {reflect.TypeOf(rel.GenericSet{}), "join"}: func(args ...rel.Value) rel.Value { + // e.g. //seq.join("",["You", "me"]) + return args[1] }, {reflect.TypeOf(rel.Array{}), "join"}: func(args ...rel.Value) rel.Value { - strs := args[0].(rel.Set) - toJoin := make([]string, 0, strs.Count()) - for i, ok := strs.(rel.Set).ArrayEnumerator(); ok && i.MoveNext(); { - toJoin = append(toJoin, mustAsString(i.Current())) - } - return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[1])))) + return nil }, {reflect.TypeOf(rel.Bytes{}), "join"}: func(args ...rel.Value) rel.Value { return nil diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index d0b3ef4a..bd209b78 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -109,18 +109,28 @@ func TestStrSplit(t *testing.T) { assertExprPanics(t, `//seq.split("this is a test", 1)`) } +// TestStrJoin, joiner is string. func TestStrJoin(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, `"" `, `//seq.join([], ",") `) - AssertCodesEvalToSameValue(t, `",," `, `//seq.join(["", "", ""], ",") `) - AssertCodesEvalToSameValue(t, `"this is a test" `, `//seq.join(["this", "is", "a", "test"], " ")`) - AssertCodesEvalToSameValue(t, `"this" `, `//seq.join(["this"], ",") `) + AssertCodesEvalToSameValue(t, `"" `, `//seq.join(",",[]) `) + AssertCodesEvalToSameValue(t, `",," `, `//seq.join(",",["", "", ""]) `) + AssertCodesEvalToSameValue(t, `"this is a test" `, `//seq.join(" ",["this", "is", "a", "test"])`) + AssertCodesEvalToSameValue(t, `"this" `, `//seq.join(",",["this"]) `) + AssertCodesEvalToSameValue(t, `"You and me"`, `//seq.join(" and ",["You", "me"])`) assertExprPanics(t, `//seq.join("this", 2)`) } func TestArrayJoin(t *testing.T) { t.Parallel() - // AssertCodesEvalToSameValue(t, `""`, `//seq.join([], ",")`) + AssertCodesEvalToSameValue(t, `["A","B"]`, `//seq.join([],["A","B"])`) + AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) + // joiner "" is translated to rel.GenericSet + AssertCodesEvalToSameValue(t, `["You", "me"]`, `//seq.join("",["You", "me"])`) + AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join("",[1,2])`) + // AssertCodesEvalToSameValue(t, `["A",",","B"]`, `//seq.join([","],["A","B"])`) + // AssertCodesEvalToSameValue(t, `[1,1,2]`, `//seq.join([1],[1,2])`) + // AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) + // AssertCodesEvalToSameValue(t, `[1]`, `//seq.join([1],[])`) } func TestBytesJoin(t *testing.T) { From 803b028b5caa47cea8702425235f3ec9284100f3 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Thu, 14 May 2020 14:08:11 +0800 Subject: [PATCH 026/106] #234 Fix array match issue. --- syntax/std_seq.go | 4 ++-- syntax/std_seq_helper.go | 49 +++++++++++++++++++++++++++++++++------- syntax/std_seq_test.go | 34 +++++++++++++++++++++------- 3 files changed, 69 insertions(+), 18 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 0f669475..c28a8213 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -149,11 +149,11 @@ var ( return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[0])))) }, {reflect.TypeOf(rel.GenericSet{}), "join"}: func(args ...rel.Value) rel.Value { - // e.g. //seq.join("",["You", "me"]) + // e.g. `//seq.join("",["You", "me"])`, `//seq.join([],[1,2])` return args[1] }, {reflect.TypeOf(rel.Array{}), "join"}: func(args ...rel.Value) rel.Value { - return nil + return ArrayJoin(args[0].(rel.Array), args[1]) }, {reflect.TypeOf(rel.Bytes{}), "join"}: func(args ...rel.Value) rel.Value { return nil diff --git a/syntax/std_seq_helper.go b/syntax/std_seq_helper.go index bff40d11..887fae3c 100644 --- a/syntax/std_seq_helper.go +++ b/syntax/std_seq_helper.go @@ -7,25 +7,31 @@ import ( // ArrayContains check if array a contains b, and b can be rel.Value or rel.Array. func ArrayContains(a rel.Array, b rel.Value) rel.Value { bArray := convert2Array(b) + return rel.NewBool(findFirstSequentialSubArray(a, bArray)) +} +// It is brute force approach, can be imporved later if it is necessary. +func findFirstSequentialSubArray(a, b rel.Array) bool { bOffset := 0 - bVals := bArray.Values() - arrayEnum, _ := a.ArrayEnumerator() - for arrayEnum.MoveNext() { - if bOffset < len(bVals) && arrayEnum.Current().Equal(bVals[bOffset]) { + aVals := a.Values() + bVals := b.Values() + + for i := 0; i < len(aVals); i++ { + if bOffset < len(bVals) && aVals[i].Equal(bVals[bOffset]) { bOffset++ } else { if bOffset > 0 && bOffset < len(bVals) { - return rel.NewBool(false) + bOffset = 0 + i-- } } } if bOffset == len(bVals) { - return rel.NewBool(true) + return true } - return rel.NewBool(false) + return false } // ArraySub substitutes all b in a with c. @@ -33,6 +39,30 @@ func ArraySub(a rel.Array, b, c rel.Value) rel.Value { return nil } +// ArrayJoin joins array a to b, a is joiner and b is joinee. +func ArrayJoin(a rel.Array, b rel.Value) rel.Value { + bArray := convert2Array(b) + if bArray.Count() == 0 { + // if joinee is empty, the final value will be empty + return b + } + if a.Count() == 0 { + return a + } + + var vals []rel.Value + for i, value := range bArray.Values() { + vals = append(vals, value) + if i+1 < bArray.Count() { + for _, value := range a.Values() { + vals = append(vals, value) + } + } + } + + return rel.NewArray(vals...) +} + // ArrayPrefix check if a starts with b. func ArrayPrefix(a rel.Array, b rel.Value) rel.Value { bArray := convert2Array(b) @@ -94,10 +124,13 @@ func convert2Array(val rel.Value) rel.Array { switch val := val.(type) { case rel.Array: return val + case rel.GenericSet: + valArray, _ := rel.AsArray(val) + return valArray case rel.Value: valArray, _ := rel.AsArray(rel.NewArray(val)) return valArray } - panic("it support types rel.Array and rel.Value only.") + panic("it support types rel.Array, rel.GenericSet and rel.Value only.") } diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index bd209b78..330ca3f1 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -64,8 +64,8 @@ func TestArrayContains(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['A','B','C','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['A','B','C','D','E','F'])`) - // TODO, it requires API contains code change - // AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A', 'A', 'B','C','D','E'],['A','B','C'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A', 'A', 'B','C','D','E'],['A','B','C'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A', 'A', 'B','C','D','E'],['B','C'])`) } func TestBytesContains(t *testing.T) { t.Parallel() @@ -122,19 +122,37 @@ func TestStrJoin(t *testing.T) { func TestArrayJoin(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, `["A","B"]`, `//seq.join([],["A","B"])`) - AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) // joiner "" is translated to rel.GenericSet AssertCodesEvalToSameValue(t, `["You", "me"]`, `//seq.join("",["You", "me"])`) AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join("",[1,2])`) - // AssertCodesEvalToSameValue(t, `["A",",","B"]`, `//seq.join([","],["A","B"])`) - // AssertCodesEvalToSameValue(t, `[1,1,2]`, `//seq.join([1],[1,2])`) - // AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) - // AssertCodesEvalToSameValue(t, `[1]`, `//seq.join([1],[])`) + + AssertCodesEvalToSameValue(t, `["A","B"]`, `//seq.join([],["A","B"])`) + AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) + // if joinee is empty, the final value will be empty + AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) + AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) + + AssertCodesEvalToSameValue(t, `["A",",","B"]`, `//seq.join([","],["A","B"])`) + AssertCodesEvalToSameValue(t, `[1,0,2,0,3,0,4,0,5]`, `//seq.join([0], [1,2,3,4,5])`) + AssertCodesEvalToSameValue(t, `['A','A','B','A','C','A','D']`, `//seq.join(['A'], ['A','B','C','D'])`) } func TestBytesJoin(t *testing.T) { t.Parallel() + // joiner "" is translated to rel.GenericSet + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('hello')`, `//seq.join("",//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('hello')`, `//seq.join([],//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) + AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) + + // AssertCodesEvalToSameValue(t, `["A","B"]`, `//seq.join([],["A","B"])`) + // AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) + // // if joinee is empty, the final value will be empty + // AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) + // AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) + + // AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('h'))`) + } func TestStrPrefix(t *testing.T) { From e73afdd9eb7b08f5dee539b6a4b57c5088a29602 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Thu, 14 May 2020 14:17:28 +0800 Subject: [PATCH 027/106] #234 Fixed issue found by golabg linter. --- syntax/std_seq_helper.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/syntax/std_seq_helper.go b/syntax/std_seq_helper.go index 887fae3c..6aa38b57 100644 --- a/syntax/std_seq_helper.go +++ b/syntax/std_seq_helper.go @@ -10,7 +10,7 @@ func ArrayContains(a rel.Array, b rel.Value) rel.Value { return rel.NewBool(findFirstSequentialSubArray(a, bArray)) } -// It is brute force approach, can be imporved later if it is necessary. +// It is brute force approach, can be improved later if it is necessary. func findFirstSequentialSubArray(a, b rel.Array) bool { bOffset := 0 aVals := a.Values() @@ -27,11 +27,7 @@ func findFirstSequentialSubArray(a, b rel.Array) bool { } } - if bOffset == len(bVals) { - return true - } - - return false + return bOffset == len(bVals) } // ArraySub substitutes all b in a with c. @@ -54,9 +50,7 @@ func ArrayJoin(a rel.Array, b rel.Value) rel.Value { for i, value := range bArray.Values() { vals = append(vals, value) if i+1 < bArray.Count() { - for _, value := range a.Values() { - vals = append(vals, value) - } + vals = append(vals, a.Values()...) } } From db49821e6cbcc0e4175ab4f7eb7fd11f053696ad Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Thu, 14 May 2020 14:33:34 +0800 Subject: [PATCH 028/106] #234 Fixed issue found by golang linter. --- syntax/std_seq_helper.go | 2 +- syntax/std_seq_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/syntax/std_seq_helper.go b/syntax/std_seq_helper.go index 6aa38b57..3e39a4c1 100644 --- a/syntax/std_seq_helper.go +++ b/syntax/std_seq_helper.go @@ -46,7 +46,7 @@ func ArrayJoin(a rel.Array, b rel.Value) rel.Value { return a } - var vals []rel.Value + var vals []rel.Value = nil for i, value := range bArray.Values() { vals = append(vals, value) if i+1 < bArray.Count() { diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index 330ca3f1..4080854d 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -140,8 +140,8 @@ func TestArrayJoin(t *testing.T) { func TestBytesJoin(t *testing.T) { t.Parallel() // joiner "" is translated to rel.GenericSet - AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('hello')`, `//seq.join("",//unicode.utf8.encode('hello'))`) - AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('hello')`, `//seq.join([],//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) }`, `//seq.join("",//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) }`, `//seq.join([],{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) })`) AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) From 4d0c0cfba88719d76415f90027e9e656a7398b1b Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Thu, 14 May 2020 15:10:18 +0800 Subject: [PATCH 029/106] #234 Check in one more test case. --- syntax/std_seq_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index 4080854d..f353e73d 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -66,6 +66,8 @@ func TestArrayContains(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A', 'A', 'B','C','D','E'],['A','B','C'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A', 'A', 'B','C','D','E'],['B','C'])`) + + AssertCodesEvalToSameValue(t, `true`, `//seq.contains([['A', 'B'], ['B','C'],['D','E']],[['B','C']])`) } func TestBytesContains(t *testing.T) { t.Parallel() From 4d43f8916f9988d6f22a672a71988e72160d4700 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Thu, 14 May 2020 17:37:46 +0800 Subject: [PATCH 030/106] #234 Check in code to implement array substitute. --- syntax/std_seq_helper.go | 62 +++++++++++++++++++++++++++------------- syntax/std_seq_test.go | 25 ++++++++++++++-- 2 files changed, 65 insertions(+), 22 deletions(-) diff --git a/syntax/std_seq_helper.go b/syntax/std_seq_helper.go index 3e39a4c1..7e57458c 100644 --- a/syntax/std_seq_helper.go +++ b/syntax/std_seq_helper.go @@ -7,32 +7,30 @@ import ( // ArrayContains check if array a contains b, and b can be rel.Value or rel.Array. func ArrayContains(a rel.Array, b rel.Value) rel.Value { bArray := convert2Array(b) - return rel.NewBool(findFirstSequentialSubArray(a, bArray)) + return rel.NewBool(indexSubArray(a.Values(), bArray.Values()) > -1) } -// It is brute force approach, can be improved later if it is necessary. -func findFirstSequentialSubArray(a, b rel.Array) bool { - bOffset := 0 - aVals := a.Values() - bVals := b.Values() - - for i := 0; i < len(aVals); i++ { - if bOffset < len(bVals) && aVals[i].Equal(bVals[bOffset]) { - bOffset++ - } else { - if bOffset > 0 && bOffset < len(bVals) { - bOffset = 0 - i-- +// ArraySub substitutes all old in a with new. +func ArraySub(a rel.Array, old, new rel.Value) rel.Value { + oldArray := convert2Array(old) + + var finalVals []rel.Value = nil + for start, absoluteIndex := 0, 0; start < a.Count(); { + relativeIndex := indexSubArray(a.Values()[start:], oldArray.Values()) + if relativeIndex >= 0 { + absoluteIndex = relativeIndex + start + if absoluteIndex-start > 0 { + finalVals = append(finalVals, a.Values()[start:absoluteIndex]...) } + finalVals = append(finalVals, new) + start = absoluteIndex + oldArray.Count() + } else { + finalVals = append(finalVals, a.Values()[absoluteIndex+1:]...) + break } } - return bOffset == len(bVals) -} - -// ArraySub substitutes all b in a with c. -func ArraySub(a rel.Array, b, c rel.Value) rel.Value { - return nil + return rel.NewArray(finalVals...) } // ArrayJoin joins array a to b, a is joiner and b is joinee. @@ -128,3 +126,27 @@ func convert2Array(val rel.Value) rel.Array { panic("it support types rel.Array, rel.GenericSet and rel.Value only.") } + +// It is brute force approach, can be improved later if it is necessary. +func indexSubArray(a, b []rel.Value) int { + aOffset, bOffset := 0, 0 + + for ; aOffset < len(a); aOffset++ { + if bOffset < len(b) && a[aOffset].Equal(b[bOffset]) { + bOffset++ + } else { + if bOffset > 0 && bOffset < len(b) { + bOffset = 0 + aOffset-- + } + } + if bOffset == len(b) { + break + } + } + + if aOffset < len(a) { + return aOffset + } + return -1 +} diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index f353e73d..ec1ac3d1 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -1,6 +1,8 @@ package syntax -import "testing" +import ( + "testing" +) func TestSeqConcat(t *testing.T) { t.Parallel() @@ -77,12 +79,18 @@ func TestBytesContains(t *testing.T) { func TestStrSub(t *testing.T) { t.Parallel() // string + AssertCodesEvalToSameValue(t, + `"this is not a test"`, + `//seq.sub("this is not a test", "aaa", "is")`) AssertCodesEvalToSameValue(t, `"this is a test"`, `//seq.sub("this is not a test", "is not", "is")`) AssertCodesEvalToSameValue(t, `"this is a test"`, `//seq.sub("this is not a test", "not ", "")`) + AssertCodesEvalToSameValue(t, + `"t1his is not1 a t1est1"`, + `//seq.sub("this is not a test", "t", "t1")`) AssertCodesEvalToSameValue(t, `"this is still a test"`, `//seq.sub("this is still a test", "doesn't matter", "hello there")`) @@ -91,9 +99,22 @@ func TestStrSub(t *testing.T) { func TestArraySub(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, + `['T', 'B', 'T', 'C', 'D', 'E']`, + `//seq.sub(['A', 'B', 'A', 'C', 'D', 'E'], 'A', 'T')`) + AssertCodesEvalToSameValue(t, + `['T', 'B', 'T']`, + `//seq.sub(['A', 'B', 'A'], 'A', 'T')`) + AssertCodesEvalToSameValue(t, + `['T', 'B', 'T']`, + `//seq.sub(['A', 'B', 'A'], 'A', 'T')`) // AssertCodesEvalToSameValue(t, - // `"this is a test"`, + // `["this", "is", "a", "test"]`, // `//seq.sub(["this", "is", "not", "a", "test"], ["is", "not"], "is")`) + + // data := []string{"A", "B", "C"} + // data1 := data[1:1] + // fmt.Print(data1) } func TestBytesSub(t *testing.T) { From e58c0c8d033af31f692335ab18e0030b0d4f5c78 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 15 May 2020 00:48:42 +0800 Subject: [PATCH 031/106] #234 Fixed issue found by golang linter. --- syntax/std_seq_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index ec1ac3d1..b863f04f 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -163,8 +163,10 @@ func TestArrayJoin(t *testing.T) { func TestBytesJoin(t *testing.T) { t.Parallel() // joiner "" is translated to rel.GenericSet - AssertCodesEvalToSameValue(t, `{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) }`, `//seq.join("",//unicode.utf8.encode('hello'))`) - AssertCodesEvalToSameValue(t, `{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) }`, `//seq.join([],{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) })`) + AssertCodesEvalToSameValue(t, `{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) }`, + `//seq.join("",//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) }`, + `//seq.join([],{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) })`) AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) From 37b1059d7d9ad3f65df5a0f28bf884c476f7670c Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 15 May 2020 01:14:15 +0800 Subject: [PATCH 032/106] #234 Fixed issue found by golang linter. --- syntax/std_seq_helper.go | 7 ++++--- syntax/std_seq_test.go | 28 +++++++++++++++++----------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/syntax/std_seq_helper.go b/syntax/std_seq_helper.go index 7e57458c..7e7a0ce9 100644 --- a/syntax/std_seq_helper.go +++ b/syntax/std_seq_helper.go @@ -13,8 +13,9 @@ func ArrayContains(a rel.Array, b rel.Value) rel.Value { // ArraySub substitutes all old in a with new. func ArraySub(a rel.Array, old, new rel.Value) rel.Value { oldArray := convert2Array(old) + newArray := convert2Array(new) - var finalVals []rel.Value = nil + var finalVals []rel.Value = make([]rel.Value, 0, a.Count()) for start, absoluteIndex := 0, 0; start < a.Count(); { relativeIndex := indexSubArray(a.Values()[start:], oldArray.Values()) if relativeIndex >= 0 { @@ -22,7 +23,7 @@ func ArraySub(a rel.Array, old, new rel.Value) rel.Value { if absoluteIndex-start > 0 { finalVals = append(finalVals, a.Values()[start:absoluteIndex]...) } - finalVals = append(finalVals, new) + finalVals = append(finalVals, newArray.Values()...) start = absoluteIndex + oldArray.Count() } else { finalVals = append(finalVals, a.Values()[absoluteIndex+1:]...) @@ -44,7 +45,7 @@ func ArrayJoin(a rel.Array, b rel.Value) rel.Value { return a } - var vals []rel.Value = nil + var vals []rel.Value = make([]rel.Value, 0, a.Count()) for i, value := range bArray.Values() { vals = append(vals, value) if i+1 < bArray.Count() { diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index b863f04f..dee611f1 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -55,8 +55,11 @@ func TestArrayContains(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],5)`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'A')`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['A'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'E')`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'C')`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['C'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['A','B','C'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['B','C'])`) @@ -97,24 +100,27 @@ func TestStrSub(t *testing.T) { assertExprPanics(t, `//seq.sub("hello there", "test", 1)`) } +// TODO: unify the input syntax func TestArraySub(t *testing.T) { t.Parallel() AssertCodesEvalToSameValue(t, `['T', 'B', 'T', 'C', 'D', 'E']`, `//seq.sub(['A', 'B', 'A', 'C', 'D', 'E'], 'A', 'T')`) AssertCodesEvalToSameValue(t, - `['T', 'B', 'T']`, - `//seq.sub(['A', 'B', 'A'], 'A', 'T')`) + `['T', 'B', 'T', 'C', 'D', 'E']`, + `//seq.sub(['A', 'B', 'A', 'C', 'D', 'E'], ['A'], ['T'])`) + AssertCodesEvalToSameValue(t, + `[['A', 'B'], ['T','C'],['A','D']]`, + `//seq.sub([['A', 'B'], ['A','C'],['A','D']], [['A','C']], [['T','C']])`) + AssertCodesEvalToSameValue(t, + `[2, 2, 3]`, + `//seq.sub([1, 2, 3], 1, 2)`) + AssertCodesEvalToSameValue(t, + `[2, 2, 3]`, + `//seq.sub([1, 2, 3], [1], [2])`) AssertCodesEvalToSameValue(t, - `['T', 'B', 'T']`, - `//seq.sub(['A', 'B', 'A'], 'A', 'T')`) - // AssertCodesEvalToSameValue(t, - // `["this", "is", "a", "test"]`, - // `//seq.sub(["this", "is", "not", "a", "test"], ["is", "not"], "is")`) - - // data := []string{"A", "B", "C"} - // data1 := data[1:1] - // fmt.Print(data1) + `[[1,1], [4,4], [3,3]]`, + `//seq.sub([[1,1], [2,2], [3,3]], [[2,2]], [[4,4]])`) } func TestBytesSub(t *testing.T) { From aff29e93874d82dcbab6de3b7f982f07fe1001ae Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 15 May 2020 01:23:32 +0800 Subject: [PATCH 033/106] #234 Fixed issue found by golang linter. --- syntax/std_seq_helper.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syntax/std_seq_helper.go b/syntax/std_seq_helper.go index 7e7a0ce9..cc7efcce 100644 --- a/syntax/std_seq_helper.go +++ b/syntax/std_seq_helper.go @@ -15,7 +15,7 @@ func ArraySub(a rel.Array, old, new rel.Value) rel.Value { oldArray := convert2Array(old) newArray := convert2Array(new) - var finalVals []rel.Value = make([]rel.Value, 0, a.Count()) + finalVals := make([]rel.Value, 0, a.Count()) for start, absoluteIndex := 0, 0; start < a.Count(); { relativeIndex := indexSubArray(a.Values()[start:], oldArray.Values()) if relativeIndex >= 0 { @@ -45,7 +45,7 @@ func ArrayJoin(a rel.Array, b rel.Value) rel.Value { return a } - var vals []rel.Value = make([]rel.Value, 0, a.Count()) + vals := make([]rel.Value, 0, a.Count()) for i, value := range bArray.Values() { vals = append(vals, value) if i+1 < bArray.Count() { From d110124de303270fad32ec394cbdd94f76e257c2 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 15 May 2020 01:32:32 +0800 Subject: [PATCH 034/106] #234 Check in more test cases. --- syntax/std_seq_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index dee611f1..687a854a 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -138,6 +138,14 @@ func TestStrSplit(t *testing.T) { assertExprPanics(t, `//seq.split("this is a test", 1)`) } +func TestArraySplit(t *testing.T) { + t.Parallel() +} + +func TestBytesSplit(t *testing.T) { + t.Parallel() +} + // TestStrJoin, joiner is string. func TestStrJoin(t *testing.T) { t.Parallel() From 698e7f82082460c8cae68cfa6eef6399152f587e Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 15 May 2020 01:34:29 +0800 Subject: [PATCH 035/106] #234 Check in more test cases. --- syntax/std_seq_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index 687a854a..dc7be1b6 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -191,7 +191,6 @@ func TestBytesJoin(t *testing.T) { // AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) // AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('h'))`) - } func TestStrPrefix(t *testing.T) { From 6f59c9d7c2e210d955f23a7ae161ff2cfb53d545 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 15 May 2020 01:48:49 +0800 Subject: [PATCH 036/106] #234 Check in more test cases. --- syntax/std_seq_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index dc7be1b6..f49ac63c 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -140,6 +140,10 @@ func TestStrSplit(t *testing.T) { func TestArraySplit(t *testing.T) { t.Parallel() + // TODO + // AssertCodesEvalToSameValue(t, + // `[['B'],['C', 'D', 'E']]`, + // `//seq.sub(['A', 'B', 'A', 'C', 'D', 'E'], 'A')`) } func TestBytesSplit(t *testing.T) { @@ -171,6 +175,8 @@ func TestArrayJoin(t *testing.T) { AssertCodesEvalToSameValue(t, `["A",",","B"]`, `//seq.join([","],["A","B"])`) AssertCodesEvalToSameValue(t, `[1,0,2,0,3,0,4,0,5]`, `//seq.join([0], [1,2,3,4,5])`) + // TODO + //AssertCodesEvalToSameValue(t, `[1, 2, 0, 3, 4, 0, 5, 6]`, `//seq.join([0], [[1, 2], [3, 4], [5, 6]])`) AssertCodesEvalToSameValue(t, `['A','A','B','A','C','A','D']`, `//seq.join(['A'], ['A','B','C','D'])`) } From cc72e5116bee298d470b3db1e2a25da18a9c4479 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 15 May 2020 11:27:28 +0800 Subject: [PATCH 037/106] #230 Updated API signature to place the subject param in the last. --- syntax/std_seq.go | 35 ++++++--- syntax/std_seq_helper.go | 153 --------------------------------------- syntax/std_seq_test.go | 97 ++++++++++++++----------- 3 files changed, 78 insertions(+), 207 deletions(-) delete mode 100644 syntax/std_seq_helper.go diff --git a/syntax/std_seq.go b/syntax/std_seq.go index c28a8213..d1765d9f 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -87,7 +87,9 @@ func stdSeq() rel.Attr { } func process(apiName string, args ...rel.Value) rel.Value { - handler := handlerMapping[typeMethod{reflect.TypeOf(args[0]), apiName}] + // As https://github.com/arr-ai/arrai/issues/230, the subject parameter is placed in the last param in API signature, + // so get handler by the last param. + handler := handlerMapping[typeMethod{reflect.TypeOf(args[len(args)-1]), apiName}] return handler(args...) } @@ -96,44 +98,43 @@ var ( handlerMapping = map[typeMethod]func(...rel.Value) rel.Value{ // API contains {reflect.TypeOf(rel.String{}), "contains"}: func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.Contains(mustAsString(args[0]), mustAsString(args[1]))) + return rel.NewBool(strings.Contains(mustAsString(args[1]), mustAsString(args[0]))) }, {reflect.TypeOf(rel.Array{}), "contains"}: func(args ...rel.Value) rel.Value { - return ArrayContains(args[0].(rel.Array), args[1]) + return ArrayContains(args[1].(rel.Array), args[0]) }, {reflect.TypeOf(rel.Bytes{}), "contains"}: func(args ...rel.Value) rel.Value { - return nil + return BytesContain(args[1].(rel.Bytes), args[0].(rel.Bytes)) }, // API sub {reflect.TypeOf(rel.String{}), "sub"}: func(args ...rel.Value) rel.Value { return rel.NewString( []rune( strings.ReplaceAll( + mustAsString(args[2]), mustAsString(args[0]), mustAsString(args[1]), - mustAsString(args[2]), ), ), ) }, {reflect.TypeOf(rel.Array{}), "sub"}: func(args ...rel.Value) rel.Value { - return ArraySub(args[0].(rel.Array), args[1], args[2]) + return ArraySub(args[2].(rel.Array), args[0], args[1]) }, {reflect.TypeOf(rel.Bytes{}), "sub"}: func(args ...rel.Value) rel.Value { return nil }, // API split + {reflect.TypeOf(rel.GenericSet{}), "split"}: func(args ...rel.Value) rel.Value { + return strSplit(args...) + }, {reflect.TypeOf(rel.String{}), "split"}: func(args ...rel.Value) rel.Value { - splitted := strings.Split(mustAsString(args[0]), mustAsString(args[1])) - vals := make([]rel.Value, 0, len(splitted)) - for _, s := range splitted { - vals = append(vals, rel.NewString([]rune(s))) - } - return rel.NewArray(vals...) + return strSplit(args...) }, {reflect.TypeOf(rel.Array{}), "split"}: func(args ...rel.Value) rel.Value { + // return ArraySplit(a, b) return nil }, {reflect.TypeOf(rel.Bytes{}), "split"}: func(args ...rel.Value) rel.Value { @@ -191,3 +192,13 @@ type typeMethod struct { t reflect.Type apiName string } + +// String funcs +func strSplit(args ...rel.Value) rel.Value { + splitted := strings.Split(mustAsString(args[1]), mustAsString(args[0])) + vals := make([]rel.Value, 0, len(splitted)) + for _, s := range splitted { + vals = append(vals, rel.NewString([]rune(s))) + } + return rel.NewArray(vals...) +} diff --git a/syntax/std_seq_helper.go b/syntax/std_seq_helper.go deleted file mode 100644 index cc7efcce..00000000 --- a/syntax/std_seq_helper.go +++ /dev/null @@ -1,153 +0,0 @@ -package syntax - -import ( - "github.com/arr-ai/arrai/rel" -) - -// ArrayContains check if array a contains b, and b can be rel.Value or rel.Array. -func ArrayContains(a rel.Array, b rel.Value) rel.Value { - bArray := convert2Array(b) - return rel.NewBool(indexSubArray(a.Values(), bArray.Values()) > -1) -} - -// ArraySub substitutes all old in a with new. -func ArraySub(a rel.Array, old, new rel.Value) rel.Value { - oldArray := convert2Array(old) - newArray := convert2Array(new) - - finalVals := make([]rel.Value, 0, a.Count()) - for start, absoluteIndex := 0, 0; start < a.Count(); { - relativeIndex := indexSubArray(a.Values()[start:], oldArray.Values()) - if relativeIndex >= 0 { - absoluteIndex = relativeIndex + start - if absoluteIndex-start > 0 { - finalVals = append(finalVals, a.Values()[start:absoluteIndex]...) - } - finalVals = append(finalVals, newArray.Values()...) - start = absoluteIndex + oldArray.Count() - } else { - finalVals = append(finalVals, a.Values()[absoluteIndex+1:]...) - break - } - } - - return rel.NewArray(finalVals...) -} - -// ArrayJoin joins array a to b, a is joiner and b is joinee. -func ArrayJoin(a rel.Array, b rel.Value) rel.Value { - bArray := convert2Array(b) - if bArray.Count() == 0 { - // if joinee is empty, the final value will be empty - return b - } - if a.Count() == 0 { - return a - } - - vals := make([]rel.Value, 0, a.Count()) - for i, value := range bArray.Values() { - vals = append(vals, value) - if i+1 < bArray.Count() { - vals = append(vals, a.Values()...) - } - } - - return rel.NewArray(vals...) -} - -// ArrayPrefix check if a starts with b. -func ArrayPrefix(a rel.Array, b rel.Value) rel.Value { - bArray := convert2Array(b) - - if bArray.Count() == 0 { - return rel.NewBool(false) - } - if a.Count() < bArray.Count() { - return rel.NewBool(false) - } - - bVals := bArray.Values() - bOffset := 0 - arrayEnum, _ := a.ArrayEnumerator() - for arrayEnum.MoveNext() { - if bOffset < bArray.Count() && arrayEnum.Current().Equal(bVals[bOffset]) { - bOffset++ - if bOffset == bArray.Count() { - break - } - } else { - return rel.NewBool(false) - } - } - - return rel.NewBool(true) -} - -// ArraySuffix check if a starts with b. -func ArraySuffix(a rel.Array, b rel.Value) rel.Value { - bArray := convert2Array(b) - - if bArray.Count() == 0 { - return rel.NewBool(false) - } - if a.Count() < bArray.Count() { - return rel.NewBool(false) - } - - aVals := a.Values() - bVals := bArray.Values() - bOffset := bArray.Count() - 1 - - for _, val := range aVals[a.Count()-1:] { - if bOffset > -1 && val.Equal(bVals[bOffset]) { - bOffset-- - if bOffset == -1 { - break - } - } else { - return rel.NewBool(false) - } - } - - return rel.NewBool(true) -} - -func convert2Array(val rel.Value) rel.Array { - switch val := val.(type) { - case rel.Array: - return val - case rel.GenericSet: - valArray, _ := rel.AsArray(val) - return valArray - case rel.Value: - valArray, _ := rel.AsArray(rel.NewArray(val)) - return valArray - } - - panic("it support types rel.Array, rel.GenericSet and rel.Value only.") -} - -// It is brute force approach, can be improved later if it is necessary. -func indexSubArray(a, b []rel.Value) int { - aOffset, bOffset := 0, 0 - - for ; aOffset < len(a); aOffset++ { - if bOffset < len(b) && a[aOffset].Equal(b[bOffset]) { - bOffset++ - } else { - if bOffset > 0 && bOffset < len(b) { - bOffset = 0 - aOffset-- - } - } - if bOffset == len(b) { - break - } - } - - if aOffset < len(a) { - return aOffset - } - return -1 -} diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index f49ac63c..4f0762b6 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -41,41 +41,51 @@ func TestSeqRepeat(t *testing.T) { func TestStrContains(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, `true `, `//seq.contains("this is a test", "") `) - AssertCodesEvalToSameValue(t, `true `, `//seq.contains("this is a test", "is a test") `) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains("this is a test", "is not a test")`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains("this is a test", "a is")`) - assertExprPanics(t, `//seq.contains(123, 124)`) + AssertCodesEvalToSameValue(t, `true `, `//seq.contains("", "this is a test") `) + AssertCodesEvalToSameValue(t, `true `, `//seq.contains("is a test", "this is a test") `) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains("is not a test", "this is a test")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains("a is", "this is a test")`) + assertExprPanics(t, `//seq.contains(124, 123)`) } func TestArrayContains(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],1)`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],3)`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5],5)`) - - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'A')`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['A'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'E')`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],'C')`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['C'])`) - - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['A','B','C'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['B','C'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(1, [1,2,3,4,5])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(3, [1,2,3,4,5])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(5, [1,2,3,4,5])`) + + AssertCodesEvalToSameValue(t, `true`, `//seq.contains('A',['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains('E',['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['E'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains('C',['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['C'],['A','B','C','D','E'])`) + + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['B','C'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['C','D','E'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['B','C','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['A','B','C','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E'],['A','B','C','D','E','F'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['B','C','E'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','E'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E','F'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A', 'A', 'B','C','D','E'],['A','B','C'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A', 'A', 'B','C','D','E'],['B','C'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C'], ['A', 'A', 'B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['B','C'],['A', 'A', 'B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains([['A', 'B'], ['B','C'],['D','E']],[['B','C']])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains([['B','C']],[['A', 'B'], ['B','C'],['D','E']])`) } + func TestBytesContains(t *testing.T) { t.Parallel() + // hello bytes - 104 101 108 108 111 + AssertCodesEvalToSameValue(t, `true`, + `//seq.contains({ |@, @byte| (0, 104)},//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true`, + `//seq.contains({ |@, @byte| (0, 111)},//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true`, + `//seq.contains({ |@, @byte| (0, 108),(0, 108)},//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true`, + `//seq.contains(//unicode.utf8.encode('h'),//unicode.utf8.encode('hello'))`) } /////////////////// @@ -84,63 +94,66 @@ func TestStrSub(t *testing.T) { // string AssertCodesEvalToSameValue(t, `"this is not a test"`, - `//seq.sub("this is not a test", "aaa", "is")`) + `//seq.sub("aaa", "is", "this is not a test")`) AssertCodesEvalToSameValue(t, `"this is a test"`, - `//seq.sub("this is not a test", "is not", "is")`) + `//seq.sub("is not", "is", "this is not a test")`) AssertCodesEvalToSameValue(t, `"this is a test"`, - `//seq.sub("this is not a test", "not ", "")`) + `//seq.sub("not ", "","this is not a test")`) AssertCodesEvalToSameValue(t, `"t1his is not1 a t1est1"`, - `//seq.sub("this is not a test", "t", "t1")`) + `//seq.sub("t", "t1","this is not a test")`) AssertCodesEvalToSameValue(t, `"this is still a test"`, - `//seq.sub("this is still a test", "doesn't matter", "hello there")`) + `//seq.sub( "doesn't matter", "hello there","this is still a test")`) assertExprPanics(t, `//seq.sub("hello there", "test", 1)`) } -// TODO: unify the input syntax func TestArraySub(t *testing.T) { t.Parallel() AssertCodesEvalToSameValue(t, `['T', 'B', 'T', 'C', 'D', 'E']`, - `//seq.sub(['A', 'B', 'A', 'C', 'D', 'E'], 'A', 'T')`) + `//seq.sub('A', 'T', ['A', 'B', 'A', 'C', 'D', 'E'])`) AssertCodesEvalToSameValue(t, `['T', 'B', 'T', 'C', 'D', 'E']`, - `//seq.sub(['A', 'B', 'A', 'C', 'D', 'E'], ['A'], ['T'])`) + `//seq.sub(['A'], ['T'], ['A', 'B', 'A', 'C', 'D', 'E'])`) AssertCodesEvalToSameValue(t, `[['A', 'B'], ['T','C'],['A','D']]`, - `//seq.sub([['A', 'B'], ['A','C'],['A','D']], [['A','C']], [['T','C']])`) + `//seq.sub([['A','C']], [['T','C']], [['A', 'B'], ['A','C'],['A','D']])`) AssertCodesEvalToSameValue(t, `[2, 2, 3]`, - `//seq.sub([1, 2, 3], 1, 2)`) + `//seq.sub(1, 2, [1, 2, 3])`) AssertCodesEvalToSameValue(t, `[2, 2, 3]`, - `//seq.sub([1, 2, 3], [1], [2])`) + `//seq.sub([1], [2], [1, 2, 3])`) AssertCodesEvalToSameValue(t, `[[1,1], [4,4], [3,3]]`, - `//seq.sub([[1,1], [2,2], [3,3]], [[2,2]], [[4,4]])`) + `//seq.sub([[2,2]], [[4,4]], [[1,1], [2,2], [3,3]])`) } func TestBytesSub(t *testing.T) { t.Parallel() + /// } func TestStrSplit(t *testing.T) { t.Parallel() AssertCodesEvalToSameValue(t, `["t", "h", "i", "s", " ", "i", "s", " ", "a", " ", "t", "e", "s", "t"]`, - `//seq.split("this is a test", "")`) - AssertCodesEvalToSameValue(t, `["this", "is", "a", "test"]`, `//seq.split("this is a test", " ") `) - AssertCodesEvalToSameValue(t, `["this is a test"] `, `//seq.split("this is a test", ",") `) - AssertCodesEvalToSameValue(t, `["th", " ", " a test"] `, `//seq.split("this is a test", "is")`) - assertExprPanics(t, `//seq.split("this is a test", 1)`) + `//seq.split('',"this is a test")`) + AssertCodesEvalToSameValue(t, `["this", "is", "a", "test"]`, `//seq.split(" ","this is a test") `) + AssertCodesEvalToSameValue(t, `["this is a test"] `, `//seq.split(",","this is a test") `) + AssertCodesEvalToSameValue(t, `["th", " ", " a test"] `, `//seq.split("is","this is a test")`) + assertExprPanics(t, `//seq.split(1, "this is a test")`) } func TestArraySplit(t *testing.T) { t.Parallel() // TODO + AssertCodesEvalToSameValue(t, + `['A', 'B', 'A', 'C', 'D', 'E']`, + `//seq.sub(['A', 'B', 'A', 'C', 'D', 'E'], 'T')`) // AssertCodesEvalToSameValue(t, // `[['B'],['C', 'D', 'E']]`, // `//seq.sub(['A', 'B', 'A', 'C', 'D', 'E'], 'A')`) From 325b5778a2f40ace5fac982d73bdb7ebd0427320 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 15 May 2020 18:11:55 +0800 Subject: [PATCH 038/106] #230 Refactor code for API signature change. --- syntax/std_seq.go | 202 +++++++++++---------------- syntax/std_seq_array_helper.go | 159 ++++++++++++++++++++++ syntax/std_seq_bytes_helper.go | 63 +++++++++ syntax/std_seq_contains_test.go | 52 +++++++ syntax/std_seq_join_test.go | 53 ++++++++ syntax/std_seq_prefix_test.go | 34 +++++ syntax/std_seq_split_test.go | 26 ++++ syntax/std_seq_sub_test.go | 56 ++++++++ syntax/std_seq_suffix_test.go | 33 +++++ syntax/std_seq_test.go | 234 -------------------------------- 10 files changed, 554 insertions(+), 358 deletions(-) create mode 100644 syntax/std_seq_array_helper.go create mode 100644 syntax/std_seq_bytes_helper.go create mode 100644 syntax/std_seq_contains_test.go create mode 100644 syntax/std_seq_join_test.go create mode 100644 syntax/std_seq_prefix_test.go create mode 100644 syntax/std_seq_split_test.go create mode 100644 syntax/std_seq_sub_test.go create mode 100644 syntax/std_seq_suffix_test.go diff --git a/syntax/std_seq.go b/syntax/std_seq.go index d1765d9f..4f558dff 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -2,7 +2,6 @@ package syntax import ( "fmt" - "reflect" "strings" "github.com/arr-ai/arrai/rel" @@ -66,139 +65,94 @@ func stdSeq() rel.Attr { rel.NewNativeFunctionAttr("concat", stdSeqConcat), rel.NewNativeFunctionAttr("repeat", stdSeqRepeat), createNestedFuncAttr("contains", 2, func(args ...rel.Value) rel.Value { - return process("contains", args...) + switch args[1].(type) { + case rel.String: + return rel.NewBool(strings.Contains(mustAsString(args[1]), mustAsString(args[0]))) + case rel.Array: + return ArrayContains(args[1].(rel.Array), args[0]) + case rel.Bytes: + return BytesContain(args[1].(rel.Bytes), args[0].(rel.Bytes)) + } + + return rel.NewBool(false) }), createNestedFuncAttr("split", 2, func(args ...rel.Value) rel.Value { - return process("split", args...) + switch args[1].(type) { + case rel.String: + splitted := strings.Split(mustAsString(args[1]), mustAsString(args[0])) + vals := make([]rel.Value, 0, len(splitted)) + for _, s := range splitted { + vals = append(vals, rel.NewString([]rune(s))) + } + return rel.NewArray(vals...) + case rel.Array: + return nil + case rel.Bytes: + return nil + } + + return nil }), createNestedFuncAttr("sub", 3, func(args ...rel.Value) rel.Value { - return process("sub", args...) + switch args[1].(type) { + case rel.String: + return rel.NewString( + []rune( + strings.ReplaceAll( + mustAsString(args[2]), + mustAsString(args[0]), + mustAsString(args[1]), + ), + ), + ) + case rel.Array: + return ArraySub(args[2].(rel.Array), args[0], args[1]) + case rel.Bytes: + return BytesSub(args[2].(rel.Bytes), args[0].(rel.Bytes), args[1].(rel.Bytes)) + } + + return nil }), createNestedFuncAttr("has_prefix", 2, func(args ...rel.Value) rel.Value { - return process("has_prefix", args...) + switch args[1].(type) { + case rel.String: + return rel.NewBool(strings.HasPrefix(mustAsString(args[1]), mustAsString(args[0]))) + case rel.Array: + return ArrayPrefix(args[1].(rel.Array), args[0]) + case rel.Bytes: + return rel.NewBool(strings.HasPrefix(args[1].String(), args[0].String())) + } + + return rel.NewBool(false) }), createNestedFuncAttr("has_suffix", 2, func(args ...rel.Value) rel.Value { - return process("has_suffix", args...) + switch args[1].(type) { + case rel.String: + return rel.NewBool(strings.HasSuffix(mustAsString(args[1]), mustAsString(args[0]))) + case rel.Array: + return ArraySuffix(args[1].(rel.Array), args[0]) + case rel.Bytes: + return rel.NewBool(strings.HasSuffix(args[1].String(), args[0].String())) + } + + return rel.NewBool(false) }), createNestedFuncAttr("join", 2, func(args ...rel.Value) rel.Value { - return process("join", args...) - }), - ) -} - -func process(apiName string, args ...rel.Value) rel.Value { - // As https://github.com/arr-ai/arrai/issues/230, the subject parameter is placed in the last param in API signature, - // so get handler by the last param. - handler := handlerMapping[typeMethod{reflect.TypeOf(args[len(args)-1]), apiName}] - return handler(args...) -} - -// This is seq API handlders mapping. -var ( - handlerMapping = map[typeMethod]func(...rel.Value) rel.Value{ - // API contains - {reflect.TypeOf(rel.String{}), "contains"}: func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.Contains(mustAsString(args[1]), mustAsString(args[0]))) - }, - - {reflect.TypeOf(rel.Array{}), "contains"}: func(args ...rel.Value) rel.Value { - return ArrayContains(args[1].(rel.Array), args[0]) - }, - - {reflect.TypeOf(rel.Bytes{}), "contains"}: func(args ...rel.Value) rel.Value { - return BytesContain(args[1].(rel.Bytes), args[0].(rel.Bytes)) - }, - // API sub - {reflect.TypeOf(rel.String{}), "sub"}: func(args ...rel.Value) rel.Value { - return rel.NewString( - []rune( - strings.ReplaceAll( - mustAsString(args[2]), - mustAsString(args[0]), - mustAsString(args[1]), - ), - ), - ) - }, - {reflect.TypeOf(rel.Array{}), "sub"}: func(args ...rel.Value) rel.Value { - return ArraySub(args[2].(rel.Array), args[0], args[1]) - }, - {reflect.TypeOf(rel.Bytes{}), "sub"}: func(args ...rel.Value) rel.Value { - return nil - }, - // API split - {reflect.TypeOf(rel.GenericSet{}), "split"}: func(args ...rel.Value) rel.Value { - return strSplit(args...) - }, - {reflect.TypeOf(rel.String{}), "split"}: func(args ...rel.Value) rel.Value { - return strSplit(args...) - }, - {reflect.TypeOf(rel.Array{}), "split"}: func(args ...rel.Value) rel.Value { - // return ArraySplit(a, b) - return nil - }, - {reflect.TypeOf(rel.Bytes{}), "split"}: func(args ...rel.Value) rel.Value { - return nil - }, - // API join - {reflect.TypeOf(rel.String{}), "join"}: func(args ...rel.Value) rel.Value { - strs := args[1].(rel.Set) - toJoin := make([]string, 0, strs.Count()) - for i, ok := strs.(rel.Set).ArrayEnumerator(); ok && i.MoveNext(); { - toJoin = append(toJoin, mustAsString(i.Current())) + switch args[1].(type) { + case rel.Set: + strs := args[1].(rel.Set) + toJoin := make([]string, 0, strs.Count()) + for i, ok := strs.(rel.Set).ArrayEnumerator(); ok && i.MoveNext(); { + toJoin = append(toJoin, mustAsString(i.Current())) + } + return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[0])))) + case rel.Array: + return ArrayContains(args[1].(rel.Array), args[0]) + case rel.Bytes: + return nil } - return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[0])))) - }, - {reflect.TypeOf(rel.GenericSet{}), "join"}: func(args ...rel.Value) rel.Value { - // e.g. `//seq.join("",["You", "me"])`, `//seq.join([],[1,2])` - return args[1] - }, - {reflect.TypeOf(rel.Array{}), "join"}: func(args ...rel.Value) rel.Value { - return ArrayJoin(args[0].(rel.Array), args[1]) - }, - {reflect.TypeOf(rel.Bytes{}), "join"}: func(args ...rel.Value) rel.Value { - return nil - }, - // API has_prefix - {reflect.TypeOf(rel.String{}), "has_prefix"}: func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.HasPrefix(mustAsString(args[0]), mustAsString(args[1]))) - }, - {reflect.TypeOf(rel.Array{}), "has_prefix"}: func(args ...rel.Value) rel.Value { - return ArrayPrefix(args[0].(rel.Array), args[1]) - }, - {reflect.TypeOf(rel.GenericSet{}), "has_prefix"}: func(args ...rel.Value) rel.Value { - return rel.NewBool(false) - }, - {reflect.TypeOf(rel.Bytes{}), "has_prefix"}: func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.HasPrefix(args[0].String(), args[1].String())) - }, - // API has_suffix - {reflect.TypeOf(rel.String{}), "has_suffix"}: func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.HasSuffix(mustAsString(args[0]), mustAsString(args[1]))) - }, - {reflect.TypeOf(rel.Array{}), "has_suffix"}: func(args ...rel.Value) rel.Value { - return ArraySuffix(args[0].(rel.Array), args[1]) - }, - {reflect.TypeOf(rel.GenericSet{}), "has_suffix"}: func(args ...rel.Value) rel.Value { - return rel.NewBool(false) - }, - {reflect.TypeOf(rel.Bytes{}), "has_suffix"}: func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.HasSuffix(args[0].String(), args[1].String())) - }, - } -) -type typeMethod struct { - t reflect.Type - apiName string -} - -// String funcs -func strSplit(args ...rel.Value) rel.Value { - splitted := strings.Split(mustAsString(args[1]), mustAsString(args[0])) - vals := make([]rel.Value, 0, len(splitted)) - for _, s := range splitted { - vals = append(vals, rel.NewString([]rune(s))) - } - return rel.NewArray(vals...) + panic("couldn't find hanlder for subject sequence, the supported subject sequence are string, array and byte array.") + }), + ) } diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go new file mode 100644 index 00000000..76020a19 --- /dev/null +++ b/syntax/std_seq_array_helper.go @@ -0,0 +1,159 @@ +package syntax + +import ( + "github.com/arr-ai/arrai/rel" +) + +// ArrayContains check if array a contains b, and b can be rel.Value or rel.Array. +func ArrayContains(a rel.Array, b rel.Value) rel.Value { + bArray := convert2Array(b) + return rel.NewBool(indexSubArray(a.Values(), bArray.Values()) > -1) +} + +// ArraySub substitutes all old in a with new. +func ArraySub(a rel.Array, old, new rel.Value) rel.Value { + // Convert to array to facilitate process + oldArray := convert2Array(old) + newArray := convert2Array(new) + + finalVals := make([]rel.Value, 0, a.Count()) + for start, absoluteIndex := 0, 0; start < a.Count(); { + relativeIndex := indexSubArray(a.Values()[start:], oldArray.Values()) + if relativeIndex >= 0 { + absoluteIndex = relativeIndex + start + if absoluteIndex-start > 0 { + finalVals = append(finalVals, a.Values()[start:absoluteIndex]...) + } + finalVals = append(finalVals, newArray.Values()...) + start = absoluteIndex + oldArray.Count() + } else { + finalVals = append(finalVals, a.Values()[absoluteIndex+1:]...) + break + } + } + + return rel.NewArray(finalVals...) +} + +// ArraySplit split a by b. +func ArraySplit(a rel.Array, b rel.Value) rel.Value { + return nil +} + +// ArrayJoin joins array a to b, a is joiner and b is joinee. +func ArrayJoin(a rel.Array, b rel.Value) rel.Value { + bArray := convert2Array(b) + if bArray.Count() == 0 { + // if joinee is empty, the final value will be empty + return b + } + if a.Count() == 0 { + return a + } + + vals := make([]rel.Value, 0, a.Count()) + for i, value := range bArray.Values() { + vals = append(vals, value) + if i+1 < bArray.Count() { + vals = append(vals, a.Values()...) + } + } + + return rel.NewArray(vals...) +} + +// ArrayPrefix check if a starts with b. +func ArrayPrefix(a rel.Array, b rel.Value) rel.Value { + bArray := convert2Array(b) + + if bArray.Count() == 0 { + return rel.NewBool(false) + } + if a.Count() < bArray.Count() { + return rel.NewBool(false) + } + + bVals := bArray.Values() + bOffset := 0 + arrayEnum, _ := a.ArrayEnumerator() + for arrayEnum.MoveNext() { + if bOffset < bArray.Count() && arrayEnum.Current().Equal(bVals[bOffset]) { + bOffset++ + if bOffset == bArray.Count() { + break + } + } else { + return rel.NewBool(false) + } + } + + return rel.NewBool(true) +} + +// ArraySuffix check if a starts with b. +func ArraySuffix(a rel.Array, b rel.Value) rel.Value { + bArray := convert2Array(b) + + if bArray.Count() == 0 { + return rel.NewBool(false) + } + if a.Count() < bArray.Count() { + return rel.NewBool(false) + } + + aVals := a.Values() + bVals := bArray.Values() + bOffset := bArray.Count() - 1 + + for _, val := range aVals[a.Count()-1:] { + if bOffset > -1 && val.Equal(bVals[bOffset]) { + bOffset-- + if bOffset == -1 { + break + } + } else { + return rel.NewBool(false) + } + } + + return rel.NewBool(true) +} + +func convert2Array(val rel.Value) rel.Array { + switch val := val.(type) { + case rel.Array: + return val + case rel.GenericSet: + valArray, _ := rel.AsArray(val) + return valArray + case rel.Value: + valArray, _ := rel.AsArray(rel.NewArray(val)) + return valArray + } + + panic("it support types rel.Array, rel.GenericSet and rel.Value only.") +} + +// It is brute force approach, can be improved later if it is necessary. +func indexSubArray(a, b []rel.Value) int { + aOffset, bOffset := 0, 0 + + for ; aOffset < len(a); aOffset++ { + if bOffset < len(b) && a[aOffset].Equal(b[bOffset]) { + bOffset++ + } else { + if bOffset > 0 && bOffset < len(b) { + bOffset = 0 + aOffset-- + } + } + if bOffset == len(b) { + break + } + } + + if aOffset < len(a) { + return aOffset + } + return -1 +} diff --git a/syntax/std_seq_bytes_helper.go b/syntax/std_seq_bytes_helper.go new file mode 100644 index 00000000..2ece0458 --- /dev/null +++ b/syntax/std_seq_bytes_helper.go @@ -0,0 +1,63 @@ +package syntax + +import ( + "github.com/arr-ai/arrai/rel" +) + +// BytesContain check if a contains b. +func BytesContain(a, b rel.Bytes) rel.Value { + return rel.NewBool(indexSubBytes(a.Bytes(), b.Bytes()) > -1) +} + +// BytesSub substitute all old in a with new. +func BytesSub(a, old, new rel.Bytes) rel.Value { + finalVals := make([]byte, 0, a.Count()) + + for start, absoluteIndex := 0, 0; start < a.Count(); { + relativeIndex := indexSubBytes(a.Bytes()[start:], old.Bytes()) + if relativeIndex >= 0 { + absoluteIndex = relativeIndex + start + if absoluteIndex-start > 0 { + finalVals = append(finalVals, a.Bytes()[start:absoluteIndex]...) + } + finalVals = append(finalVals, new.Bytes()...) + start = absoluteIndex + old.Count() + } else { + finalVals = append(finalVals, a.Bytes()[absoluteIndex+1:]...) + break + } + } + + return rel.NewBytes(finalVals) +} + +// It is brute force approach, can be improved later if it is necessary. +func indexSubBytes(a, b []byte) int { + aOffset, bOffset := 0, 0 + + for ; aOffset < len(a); aOffset++ { + if bOffset < len(b) && a[aOffset] == b[bOffset] { + bOffset++ + } else { + if bOffset > 0 && bOffset < len(b) { + bOffset = 0 + aOffset-- + } + } + if bOffset == len(b) { + break + } + } + + if aOffset < len(a) { + return aOffset + } + return -1 +} + +// func convert2Bytes(val rel.Value) rel.Bytes { +// // switch val := val.(type) { +// return nil + +// // panic("it support types rel.Array, rel.GenericSet and rel.Value only.") +// } diff --git a/syntax/std_seq_contains_test.go b/syntax/std_seq_contains_test.go new file mode 100644 index 00000000..7d24b799 --- /dev/null +++ b/syntax/std_seq_contains_test.go @@ -0,0 +1,52 @@ +package syntax + +import "testing" + +func TestStrContains(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, `true `, `//seq.contains("", "this is a test") `) + AssertCodesEvalToSameValue(t, `true `, `//seq.contains("is a test", "this is a test") `) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains("is not a test", "this is a test")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains("a is", "this is a test")`) + assertExprPanics(t, `//seq.contains(124, 123)`) +} + +func TestArrayContains(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(1, [1,2,3,4,5])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(3, [1,2,3,4,5])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(5, [1,2,3,4,5])`) + + AssertCodesEvalToSameValue(t, `true`, `//seq.contains('A',['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains('E',['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['E'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains('C',['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['C'],['A','B','C','D','E'])`) + + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['B','C'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['C','D','E'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['B','C','E'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','E'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E','F'],['A','B','C','D','E'])`) + + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C'], ['A', 'A', 'B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['B','C'],['A', 'A', 'B','C','D','E'])`) + + AssertCodesEvalToSameValue(t, `true`, `//seq.contains([['B','C']],[['A', 'B'], ['B','C'],['D','E']])`) +} + +func TestBytesContains(t *testing.T) { + t.Parallel() + // hello bytes - 104 101 108 108 111 + AssertCodesEvalToSameValue(t, `true`, + `//seq.contains({ |@, @byte| (0, 104)},//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true`, + `//seq.contains({ |@, @byte| (0, 111)},//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true`, + `//seq.contains({ |@, @byte| (0, 108),(0, 108)},//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true`, + `//seq.contains(//unicode.utf8.encode('h'),//unicode.utf8.encode('hello'))`) +} diff --git a/syntax/std_seq_join_test.go b/syntax/std_seq_join_test.go new file mode 100644 index 00000000..a675ba2d --- /dev/null +++ b/syntax/std_seq_join_test.go @@ -0,0 +1,53 @@ +package syntax + +import "testing" + +// TestStrJoin, joiner is string. +func TestStrJoin(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, `"AB"`, `//seq.join("",['A','B']) `) + AssertCodesEvalToSameValue(t, `"" `, `//seq.join(",",[]) `) + AssertCodesEvalToSameValue(t, `",," `, `//seq.join(",",["", "", ""]) `) + AssertCodesEvalToSameValue(t, `"this is a test" `, `//seq.join(" ",["this", "is", "a", "test"])`) + AssertCodesEvalToSameValue(t, `"this" `, `//seq.join(",",["this"]) `) + AssertCodesEvalToSameValue(t, `"You and me"`, `//seq.join(" and ",["You", "me"])`) + assertExprPanics(t, `//seq.join("this", 2)`) +} + +func TestArrayJoin(t *testing.T) { + t.Parallel() + // joiner "" is translated to rel.GenericSet + // AssertCodesEvalToSameValue(t, `["You", "me"]`, `//seq.join("",["You", "me"])`) + // AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join("",[1,2])`) + + // AssertCodesEvalToSameValue(t, `["A","B"]`, `//seq.join([],["A","B"])`) + // AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) + // // if joinee is empty, the final value will be empty + // AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) + // AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) + + // AssertCodesEvalToSameValue(t, `["A",",","B"]`, `//seq.join([","],["A","B"])`) + // AssertCodesEvalToSameValue(t, `[1,0,2,0,3,0,4,0,5]`, `//seq.join([0], [1,2,3,4,5])`) + // // TODO + // //AssertCodesEvalToSameValue(t, `[1, 2, 0, 3, 4, 0, 5, 6]`, `//seq.join([0], [[1, 2], [3, 4], [5, 6]])`) + // AssertCodesEvalToSameValue(t, `['A','A','B','A','C','A','D']`, `//seq.join(['A'], ['A','B','C','D'])`) +} + +func TestBytesJoin(t *testing.T) { + t.Parallel() + // joiner "" is translated to rel.GenericSet + // AssertCodesEvalToSameValue(t, `{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) }`, + // `//seq.join("",//unicode.utf8.encode('hello'))`) + // AssertCodesEvalToSameValue(t, `{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) }`, + // `//seq.join([],{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) })`) + // AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) + // AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) + + // AssertCodesEvalToSameValue(t, `["A","B"]`, `//seq.join([],["A","B"])`) + // AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) + // // if joinee is empty, the final value will be empty + // AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) + // AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) + + // AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('h'))`) +} diff --git a/syntax/std_seq_prefix_test.go b/syntax/std_seq_prefix_test.go new file mode 100644 index 00000000..ab679cc3 --- /dev/null +++ b/syntax/std_seq_prefix_test.go @@ -0,0 +1,34 @@ +package syntax + +import "testing" + +func TestStrPrefix(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("A","ABCDE")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("AB","ABCDE")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("BCD","ABCDE")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("CD","ABCDE")`) +} + +func TestArrayPrefix(t *testing.T) { + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix('A',['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C','D'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix([],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix([], [])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix('B',['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['B'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['B','C'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],[])`) +} + +func TestBytesPrefix(t *testing.T) { + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('h'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('he'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('e'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('l'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('o'),//unicode.utf8.encode('hello'))`) +} diff --git a/syntax/std_seq_split_test.go b/syntax/std_seq_split_test.go new file mode 100644 index 00000000..4f2abb7c --- /dev/null +++ b/syntax/std_seq_split_test.go @@ -0,0 +1,26 @@ +package syntax + +import "testing" + +func TestStrSplit(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, + `["t", "h", "i", "s", " ", "i", "s", " ", "a", " ", "t", "e", "s", "t"]`, + `//seq.split('',"this is a test")`) + AssertCodesEvalToSameValue(t, `["this", "is", "a", "test"]`, `//seq.split(" ","this is a test") `) + AssertCodesEvalToSameValue(t, `["this is a test"] `, `//seq.split(",","this is a test") `) + AssertCodesEvalToSameValue(t, `["th", " ", " a test"] `, `//seq.split("is","this is a test")`) + assertExprPanics(t, `//seq.split(1, "this is a test")`) +} + +func TestArraySplit(t *testing.T) { + t.Parallel() + // TODO + // AssertCodesEvalToSameValue(t, + // `[['B'],['C', 'D', 'E']]`, + // `//seq.sub(['A', 'B', 'A', 'C', 'D', 'E'], 'A')`) +} + +func TestBytesSplit(t *testing.T) { + t.Parallel() +} diff --git a/syntax/std_seq_sub_test.go b/syntax/std_seq_sub_test.go new file mode 100644 index 00000000..756356a9 --- /dev/null +++ b/syntax/std_seq_sub_test.go @@ -0,0 +1,56 @@ +package syntax + +// import "testing" + +// /////////////////// +// func TestStrSub(t *testing.T) { +// t.Parallel() +// // string +// AssertCodesEvalToSameValue(t, +// `"this is not a test"`, +// `//seq.sub("aaa", "is", "this is not a test")`) +// AssertCodesEvalToSameValue(t, +// `"this is a test"`, +// `//seq.sub("is not", "is", "this is not a test")`) +// AssertCodesEvalToSameValue(t, +// `"this is a test"`, +// `//seq.sub("not ", "","this is not a test")`) +// AssertCodesEvalToSameValue(t, +// `"t1his is not1 a t1est1"`, +// `//seq.sub("t", "t1","this is not a test")`) +// AssertCodesEvalToSameValue(t, +// `"this is still a test"`, +// `//seq.sub( "doesn't matter", "hello there","this is still a test")`) +// assertExprPanics(t, `//seq.sub("hello there", "test", 1)`) +// } + +// func TestArraySub(t *testing.T) { +// t.Parallel() +// AssertCodesEvalToSameValue(t, +// `['T', 'B', 'T', 'C', 'D', 'E']`, +// `//seq.sub('A', 'T', ['A', 'B', 'A', 'C', 'D', 'E'])`) +// AssertCodesEvalToSameValue(t, +// `['T', 'B', 'T', 'C', 'D', 'E']`, +// `//seq.sub(['A'], ['T'], ['A', 'B', 'A', 'C', 'D', 'E'])`) +// AssertCodesEvalToSameValue(t, +// `[['A', 'B'], ['T','C'],['A','D']]`, +// `//seq.sub([['A','C']], [['T','C']], [['A', 'B'], ['A','C'],['A','D']])`) +// AssertCodesEvalToSameValue(t, +// `[2, 2, 3]`, +// `//seq.sub(1, 2, [1, 2, 3])`) +// AssertCodesEvalToSameValue(t, +// `[2, 2, 3]`, +// `//seq.sub([1], [2], [1, 2, 3])`) +// AssertCodesEvalToSameValue(t, +// `[[1,1], [4,4], [3,3]]`, +// `//seq.sub([[2,2]], [[4,4]], [[1,1], [2,2], [3,3]])`) +// } + +// func TestBytesSub(t *testing.T) { +// t.Parallel() +// // hello bytes - 104 101 108 108 111 +// AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('oello')`, +// `//seq.sub({ |@, @byte| (0, 104)},{ |@, @byte| (0, 111)},//unicode.utf8.encode('hello'))`) +// AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('hehho')`, +// `//seq.sub({ |@, @byte| (0, 108)},{ |@, @byte| (0, 104)},//unicode.utf8.encode('hello'))`) +// } diff --git a/syntax/std_seq_suffix_test.go b/syntax/std_seq_suffix_test.go new file mode 100644 index 00000000..94857932 --- /dev/null +++ b/syntax/std_seq_suffix_test.go @@ -0,0 +1,33 @@ +package syntax + +import "testing" + +func TestStrSuffix(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("E","ABCDE")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("DE","ABCDE")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("CD", "ABCDE")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("D","ABCDE")`) +} + +func TestArraySuffix(t *testing.T) { + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix('E',['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(['E'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix( ['D','E'],['A','B','C','D','E'])`) + + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix([],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix([], [])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['D','E'],[])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix('D',['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['D'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['C','D'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) +} + +func TestBytesSuffix(t *testing.T) { + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode('o'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode('lo'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('e'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('ell'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('h'),//unicode.utf8.encode('hello'))`) +} diff --git a/syntax/std_seq_test.go b/syntax/std_seq_test.go index 4f0762b6..ca888100 100644 --- a/syntax/std_seq_test.go +++ b/syntax/std_seq_test.go @@ -38,237 +38,3 @@ func TestSeqRepeat(t *testing.T) { AssertCodePanics(t, `//seq.repeat(2, 3.4)`) } - -func TestStrContains(t *testing.T) { - t.Parallel() - AssertCodesEvalToSameValue(t, `true `, `//seq.contains("", "this is a test") `) - AssertCodesEvalToSameValue(t, `true `, `//seq.contains("is a test", "this is a test") `) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains("is not a test", "this is a test")`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains("a is", "this is a test")`) - assertExprPanics(t, `//seq.contains(124, 123)`) -} - -func TestArrayContains(t *testing.T) { - t.Parallel() - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(1, [1,2,3,4,5])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(3, [1,2,3,4,5])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(5, [1,2,3,4,5])`) - - AssertCodesEvalToSameValue(t, `true`, `//seq.contains('A',['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains('E',['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['E'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains('C',['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['C'],['A','B','C','D','E'])`) - - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['B','C'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['C','D','E'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['B','C','E'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','E'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E','F'],['A','B','C','D','E'])`) - - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C'], ['A', 'A', 'B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['B','C'],['A', 'A', 'B','C','D','E'])`) - - AssertCodesEvalToSameValue(t, `true`, `//seq.contains([['B','C']],[['A', 'B'], ['B','C'],['D','E']])`) -} - -func TestBytesContains(t *testing.T) { - t.Parallel() - // hello bytes - 104 101 108 108 111 - AssertCodesEvalToSameValue(t, `true`, - `//seq.contains({ |@, @byte| (0, 104)},//unicode.utf8.encode('hello'))`) - AssertCodesEvalToSameValue(t, `true`, - `//seq.contains({ |@, @byte| (0, 111)},//unicode.utf8.encode('hello'))`) - AssertCodesEvalToSameValue(t, `true`, - `//seq.contains({ |@, @byte| (0, 108),(0, 108)},//unicode.utf8.encode('hello'))`) - AssertCodesEvalToSameValue(t, `true`, - `//seq.contains(//unicode.utf8.encode('h'),//unicode.utf8.encode('hello'))`) -} - -/////////////////// -func TestStrSub(t *testing.T) { - t.Parallel() - // string - AssertCodesEvalToSameValue(t, - `"this is not a test"`, - `//seq.sub("aaa", "is", "this is not a test")`) - AssertCodesEvalToSameValue(t, - `"this is a test"`, - `//seq.sub("is not", "is", "this is not a test")`) - AssertCodesEvalToSameValue(t, - `"this is a test"`, - `//seq.sub("not ", "","this is not a test")`) - AssertCodesEvalToSameValue(t, - `"t1his is not1 a t1est1"`, - `//seq.sub("t", "t1","this is not a test")`) - AssertCodesEvalToSameValue(t, - `"this is still a test"`, - `//seq.sub( "doesn't matter", "hello there","this is still a test")`) - assertExprPanics(t, `//seq.sub("hello there", "test", 1)`) -} - -func TestArraySub(t *testing.T) { - t.Parallel() - AssertCodesEvalToSameValue(t, - `['T', 'B', 'T', 'C', 'D', 'E']`, - `//seq.sub('A', 'T', ['A', 'B', 'A', 'C', 'D', 'E'])`) - AssertCodesEvalToSameValue(t, - `['T', 'B', 'T', 'C', 'D', 'E']`, - `//seq.sub(['A'], ['T'], ['A', 'B', 'A', 'C', 'D', 'E'])`) - AssertCodesEvalToSameValue(t, - `[['A', 'B'], ['T','C'],['A','D']]`, - `//seq.sub([['A','C']], [['T','C']], [['A', 'B'], ['A','C'],['A','D']])`) - AssertCodesEvalToSameValue(t, - `[2, 2, 3]`, - `//seq.sub(1, 2, [1, 2, 3])`) - AssertCodesEvalToSameValue(t, - `[2, 2, 3]`, - `//seq.sub([1], [2], [1, 2, 3])`) - AssertCodesEvalToSameValue(t, - `[[1,1], [4,4], [3,3]]`, - `//seq.sub([[2,2]], [[4,4]], [[1,1], [2,2], [3,3]])`) -} - -func TestBytesSub(t *testing.T) { - t.Parallel() - /// -} - -func TestStrSplit(t *testing.T) { - t.Parallel() - AssertCodesEvalToSameValue(t, - `["t", "h", "i", "s", " ", "i", "s", " ", "a", " ", "t", "e", "s", "t"]`, - `//seq.split('',"this is a test")`) - AssertCodesEvalToSameValue(t, `["this", "is", "a", "test"]`, `//seq.split(" ","this is a test") `) - AssertCodesEvalToSameValue(t, `["this is a test"] `, `//seq.split(",","this is a test") `) - AssertCodesEvalToSameValue(t, `["th", " ", " a test"] `, `//seq.split("is","this is a test")`) - assertExprPanics(t, `//seq.split(1, "this is a test")`) -} - -func TestArraySplit(t *testing.T) { - t.Parallel() - // TODO - AssertCodesEvalToSameValue(t, - `['A', 'B', 'A', 'C', 'D', 'E']`, - `//seq.sub(['A', 'B', 'A', 'C', 'D', 'E'], 'T')`) - // AssertCodesEvalToSameValue(t, - // `[['B'],['C', 'D', 'E']]`, - // `//seq.sub(['A', 'B', 'A', 'C', 'D', 'E'], 'A')`) -} - -func TestBytesSplit(t *testing.T) { - t.Parallel() -} - -// TestStrJoin, joiner is string. -func TestStrJoin(t *testing.T) { - t.Parallel() - AssertCodesEvalToSameValue(t, `"" `, `//seq.join(",",[]) `) - AssertCodesEvalToSameValue(t, `",," `, `//seq.join(",",["", "", ""]) `) - AssertCodesEvalToSameValue(t, `"this is a test" `, `//seq.join(" ",["this", "is", "a", "test"])`) - AssertCodesEvalToSameValue(t, `"this" `, `//seq.join(",",["this"]) `) - AssertCodesEvalToSameValue(t, `"You and me"`, `//seq.join(" and ",["You", "me"])`) - assertExprPanics(t, `//seq.join("this", 2)`) -} - -func TestArrayJoin(t *testing.T) { - t.Parallel() - // joiner "" is translated to rel.GenericSet - AssertCodesEvalToSameValue(t, `["You", "me"]`, `//seq.join("",["You", "me"])`) - AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join("",[1,2])`) - - AssertCodesEvalToSameValue(t, `["A","B"]`, `//seq.join([],["A","B"])`) - AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) - // if joinee is empty, the final value will be empty - AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) - AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) - - AssertCodesEvalToSameValue(t, `["A",",","B"]`, `//seq.join([","],["A","B"])`) - AssertCodesEvalToSameValue(t, `[1,0,2,0,3,0,4,0,5]`, `//seq.join([0], [1,2,3,4,5])`) - // TODO - //AssertCodesEvalToSameValue(t, `[1, 2, 0, 3, 4, 0, 5, 6]`, `//seq.join([0], [[1, 2], [3, 4], [5, 6]])`) - AssertCodesEvalToSameValue(t, `['A','A','B','A','C','A','D']`, `//seq.join(['A'], ['A','B','C','D'])`) -} - -func TestBytesJoin(t *testing.T) { - t.Parallel() - // joiner "" is translated to rel.GenericSet - AssertCodesEvalToSameValue(t, `{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) }`, - `//seq.join("",//unicode.utf8.encode('hello'))`) - AssertCodesEvalToSameValue(t, `{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) }`, - `//seq.join([],{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) })`) - AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) - AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) - - // AssertCodesEvalToSameValue(t, `["A","B"]`, `//seq.join([],["A","B"])`) - // AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) - // // if joinee is empty, the final value will be empty - // AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) - // AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) - - // AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('h'))`) -} - -func TestStrPrefix(t *testing.T) { - t.Parallel() - AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("ABCDE", "A")`) - AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("ABCDE", "AB")`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("ABCDE", "BCD")`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("ABCDE", "CD")`) -} - -func TestArrayPrefix(t *testing.T) { - AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C','D','E'], 'A')`) - AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C','D','E'], ['A'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C','D','E'], ['A','B'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C','D','E'], ['A','B','C'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C','D','E'], ['A','B','C','D'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E'], [])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix([], [])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E'], 'B')`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E'], ['B'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E'], ['B','C'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E'], ['A','B','C','D','E','F'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix([], ['A','B','C','D','E','F'])`) -} - -func TestBytesPrefix(t *testing.T) { - AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('h'))`) - AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('he'))`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('e'))`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('l'))`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('o'))`) -} - -func TestStrSuffix(t *testing.T) { - t.Parallel() - AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("ABCDE", "E")`) - AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("ABCDE", "DE")`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("ABCDE", "CD")`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("ABCDE", "D")`) -} - -func TestArraySuffix(t *testing.T) { - AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(['A','B','C','D','E'], 'E')`) - AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(['A','B','C','D','E'], ['E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(['A','B','C','D','E'], ['D','E'])`) - - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E'], [])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix([], [])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix([], ['D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E'], 'D')`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E'], ['D'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E'], ['C','D'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E'], ['A','B','C','D','E','F'])`) -} - -func TestBytesSuffix(t *testing.T) { - AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('o'))`) - AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('lo'))`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('e'))`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('ell'))`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('h'))`) -} From c07d672026d78de633cd0f0fcc042abb572dc9a5 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 15 May 2020 21:18:00 +0800 Subject: [PATCH 039/106] #234 Removed unused test case. --- syntax/std_seq_contains_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/syntax/std_seq_contains_test.go b/syntax/std_seq_contains_test.go index 7d24b799..8c1df4f6 100644 --- a/syntax/std_seq_contains_test.go +++ b/syntax/std_seq_contains_test.go @@ -8,7 +8,6 @@ func TestStrContains(t *testing.T) { AssertCodesEvalToSameValue(t, `true `, `//seq.contains("is a test", "this is a test") `) AssertCodesEvalToSameValue(t, `false`, `//seq.contains("is not a test", "this is a test")`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains("a is", "this is a test")`) - assertExprPanics(t, `//seq.contains(124, 123)`) } func TestArrayContains(t *testing.T) { From 1c516d1a107fd3e9783c6c3e46e1f25040e3dedc Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 15 May 2020 22:02:21 +0800 Subject: [PATCH 040/106] #234 Removed unused test case. --- syntax/std_seq.go | 69 ++++++++++++++----------- syntax/std_seq_sub_test.go | 100 ++++++++++++++++++------------------- 2 files changed, 90 insertions(+), 79 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 4f558dff..2bc93781 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -2,6 +2,7 @@ package syntax import ( "fmt" + "reflect" "strings" "github.com/arr-ai/arrai/rel" @@ -76,6 +77,30 @@ func stdSeq() rel.Attr { return rel.NewBool(false) }), + createNestedFuncAttr("has_prefix", 2, func(args ...rel.Value) rel.Value { + switch args[1].(type) { + case rel.String: + return rel.NewBool(strings.HasPrefix(mustAsString(args[1]), mustAsString(args[0]))) + case rel.Array: + return ArrayPrefix(args[1].(rel.Array), args[0]) + case rel.Bytes: + return rel.NewBool(strings.HasPrefix(args[1].String(), args[0].String())) + } + + return rel.NewBool(false) + }), + createNestedFuncAttr("has_suffix", 2, func(args ...rel.Value) rel.Value { + switch args[1].(type) { + case rel.String: + return rel.NewBool(strings.HasSuffix(mustAsString(args[1]), mustAsString(args[0]))) + case rel.Array: + return ArraySuffix(args[1].(rel.Array), args[0]) + case rel.Bytes: + return rel.NewBool(strings.HasSuffix(args[1].String(), args[0].String())) + } + + return rel.NewBool(false) + }), createNestedFuncAttr("split", 2, func(args ...rel.Value) rel.Value { switch args[1].(type) { case rel.String: @@ -91,10 +116,12 @@ func stdSeq() rel.Attr { return nil } - return nil + panic(fmt.Errorf("expected subject sequence types are %s, %s and %s, but the actual type is %s", + reflect.TypeOf(rel.String{}), reflect.TypeOf(rel.Array{}), reflect.TypeOf(rel.Bytes{}), + reflect.TypeOf(args[2]))) }), createNestedFuncAttr("sub", 3, func(args ...rel.Value) rel.Value { - switch args[1].(type) { + switch args[2].(type) { case rel.String: return rel.NewString( []rune( @@ -111,31 +138,9 @@ func stdSeq() rel.Attr { return BytesSub(args[2].(rel.Bytes), args[0].(rel.Bytes), args[1].(rel.Bytes)) } - return nil - }), - createNestedFuncAttr("has_prefix", 2, func(args ...rel.Value) rel.Value { - switch args[1].(type) { - case rel.String: - return rel.NewBool(strings.HasPrefix(mustAsString(args[1]), mustAsString(args[0]))) - case rel.Array: - return ArrayPrefix(args[1].(rel.Array), args[0]) - case rel.Bytes: - return rel.NewBool(strings.HasPrefix(args[1].String(), args[0].String())) - } - - return rel.NewBool(false) - }), - createNestedFuncAttr("has_suffix", 2, func(args ...rel.Value) rel.Value { - switch args[1].(type) { - case rel.String: - return rel.NewBool(strings.HasSuffix(mustAsString(args[1]), mustAsString(args[0]))) - case rel.Array: - return ArraySuffix(args[1].(rel.Array), args[0]) - case rel.Bytes: - return rel.NewBool(strings.HasSuffix(args[1].String(), args[0].String())) - } - - return rel.NewBool(false) + panic(fmt.Errorf("expected subject sequence types are %s, %s and %s, but the actual type is %s", + reflect.TypeOf(rel.String{}), reflect.TypeOf(rel.Array{}), reflect.TypeOf(rel.Bytes{}), + reflect.TypeOf(args[2]))) }), createNestedFuncAttr("join", 2, func(args ...rel.Value) rel.Value { switch args[1].(type) { @@ -152,7 +157,15 @@ func stdSeq() rel.Attr { return nil } - panic("couldn't find hanlder for subject sequence, the supported subject sequence are string, array and byte array.") + panic(fmt.Errorf("expected subject sequence types are %s, %s and %s, but the actual type is %s", + reflect.TypeOf(rel.String{}), reflect.TypeOf(rel.Array{}), reflect.TypeOf(rel.Bytes{}), + reflect.TypeOf(args[2]))) }), ) } + +func subjectSeqPanic(args ...rel.Value) { + panic(fmt.Errorf("expected subject sequence types are %s, %s and %s, but the actual type is %s", + reflect.TypeOf(rel.String{}), reflect.TypeOf(rel.Array{}), reflect.TypeOf(rel.Bytes{}), + reflect.TypeOf(args[2]))) +} diff --git a/syntax/std_seq_sub_test.go b/syntax/std_seq_sub_test.go index 756356a9..dcfbddc3 100644 --- a/syntax/std_seq_sub_test.go +++ b/syntax/std_seq_sub_test.go @@ -1,56 +1,54 @@ package syntax -// import "testing" +import "testing" -// /////////////////// -// func TestStrSub(t *testing.T) { -// t.Parallel() -// // string -// AssertCodesEvalToSameValue(t, -// `"this is not a test"`, -// `//seq.sub("aaa", "is", "this is not a test")`) -// AssertCodesEvalToSameValue(t, -// `"this is a test"`, -// `//seq.sub("is not", "is", "this is not a test")`) -// AssertCodesEvalToSameValue(t, -// `"this is a test"`, -// `//seq.sub("not ", "","this is not a test")`) -// AssertCodesEvalToSameValue(t, -// `"t1his is not1 a t1est1"`, -// `//seq.sub("t", "t1","this is not a test")`) -// AssertCodesEvalToSameValue(t, -// `"this is still a test"`, -// `//seq.sub( "doesn't matter", "hello there","this is still a test")`) -// assertExprPanics(t, `//seq.sub("hello there", "test", 1)`) -// } +func TestStrSub(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, + `"this is not a test"`, + `//seq.sub("aaa", "is", "this is not a test")`) + AssertCodesEvalToSameValue(t, + `"this is a test"`, + `//seq.sub("is not", "is", "this is not a test")`) + AssertCodesEvalToSameValue(t, + `"this is a test"`, + `//seq.sub("not ", "","this is not a test")`) + AssertCodesEvalToSameValue(t, + `"t1his is not1 a t1est1"`, + `//seq.sub("t", "t1","this is not a test")`) + AssertCodesEvalToSameValue(t, + `"this is still a test"`, + `//seq.sub( "doesn't matter", "hello there","this is still a test")`) + assertExprPanics(t, `//seq.sub("hello there", "test", 1)`) +} -// func TestArraySub(t *testing.T) { -// t.Parallel() -// AssertCodesEvalToSameValue(t, -// `['T', 'B', 'T', 'C', 'D', 'E']`, -// `//seq.sub('A', 'T', ['A', 'B', 'A', 'C', 'D', 'E'])`) -// AssertCodesEvalToSameValue(t, -// `['T', 'B', 'T', 'C', 'D', 'E']`, -// `//seq.sub(['A'], ['T'], ['A', 'B', 'A', 'C', 'D', 'E'])`) -// AssertCodesEvalToSameValue(t, -// `[['A', 'B'], ['T','C'],['A','D']]`, -// `//seq.sub([['A','C']], [['T','C']], [['A', 'B'], ['A','C'],['A','D']])`) -// AssertCodesEvalToSameValue(t, -// `[2, 2, 3]`, -// `//seq.sub(1, 2, [1, 2, 3])`) -// AssertCodesEvalToSameValue(t, -// `[2, 2, 3]`, -// `//seq.sub([1], [2], [1, 2, 3])`) -// AssertCodesEvalToSameValue(t, -// `[[1,1], [4,4], [3,3]]`, -// `//seq.sub([[2,2]], [[4,4]], [[1,1], [2,2], [3,3]])`) -// } +func TestArraySub(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, + `['T', 'B', 'T', 'C', 'D', 'E']`, + `//seq.sub('A', 'T', ['A', 'B', 'A', 'C', 'D', 'E'])`) + AssertCodesEvalToSameValue(t, + `['T', 'B', 'T', 'C', 'D', 'E']`, + `//seq.sub(['A'], ['T'], ['A', 'B', 'A', 'C', 'D', 'E'])`) + AssertCodesEvalToSameValue(t, + `[['A', 'B'], ['T','C'],['A','D']]`, + `//seq.sub([['A','C']], [['T','C']], [['A', 'B'], ['A','C'],['A','D']])`) + AssertCodesEvalToSameValue(t, + `[2, 2, 3]`, + `//seq.sub(1, 2, [1, 2, 3])`) + AssertCodesEvalToSameValue(t, + `[2, 2, 3]`, + `//seq.sub([1], [2], [1, 2, 3])`) + AssertCodesEvalToSameValue(t, + `[[1,1], [4,4], [3,3]]`, + `//seq.sub([[2,2]], [[4,4]], [[1,1], [2,2], [3,3]])`) +} -// func TestBytesSub(t *testing.T) { -// t.Parallel() -// // hello bytes - 104 101 108 108 111 -// AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('oello')`, -// `//seq.sub({ |@, @byte| (0, 104)},{ |@, @byte| (0, 111)},//unicode.utf8.encode('hello'))`) -// AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('hehho')`, -// `//seq.sub({ |@, @byte| (0, 108)},{ |@, @byte| (0, 104)},//unicode.utf8.encode('hello'))`) -// } +func TestBytesSub(t *testing.T) { + t.Parallel() + // hello bytes - 104 101 108 108 111 + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('oello')`, + `//seq.sub({ |@, @byte| (0, 104)},{ |@, @byte| (0, 111)},//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('hehho')`, + `//seq.sub({ |@, @byte| (0, 108)},{ |@, @byte| (0, 104)},//unicode.utf8.encode('hello'))`) +} From c6299c74487e1f53c3ce04f2488d3a3ea51a3ef7 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Sat, 16 May 2020 01:01:00 +0800 Subject: [PATCH 041/106] #234 Updated join implementation. --- syntax/std_seq.go | 33 ++++++++++++------------ syntax/std_seq_array_helper.go | 42 ++++++++++++++++++++++-------- syntax/std_seq_join_test.go | 47 +++++++++++++++------------------- 3 files changed, 70 insertions(+), 52 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 2bc93781..c1be5657 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -143,29 +143,30 @@ func stdSeq() rel.Attr { reflect.TypeOf(args[2]))) }), createNestedFuncAttr("join", 2, func(args ...rel.Value) rel.Value { - switch args[1].(type) { - case rel.Set: - strs := args[1].(rel.Set) - toJoin := make([]string, 0, strs.Count()) - for i, ok := strs.(rel.Set).ArrayEnumerator(); ok && i.MoveNext(); { - toJoin = append(toJoin, mustAsString(i.Current())) - } - return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[0])))) + switch a1 := args[1].(type) { case rel.Array: - return ArrayContains(args[1].(rel.Array), args[0]) + switch a1.Values()[0].(type) { + case rel.String: + return strJoin(args...) + case rel.Value: + return ArrayJoin(a1, args[0]) + } case rel.Bytes: return nil } - - panic(fmt.Errorf("expected subject sequence types are %s, %s and %s, but the actual type is %s", - reflect.TypeOf(rel.String{}), reflect.TypeOf(rel.Array{}), reflect.TypeOf(rel.Bytes{}), + // return ArrayContains(args[1].(rel.Array), args[0]) + panic(fmt.Errorf("expected subject sequence types are %s and %s, but the actual type is %s", + reflect.TypeOf(rel.Array{}), reflect.TypeOf(rel.Bytes{}), reflect.TypeOf(args[2]))) }), ) } -func subjectSeqPanic(args ...rel.Value) { - panic(fmt.Errorf("expected subject sequence types are %s, %s and %s, but the actual type is %s", - reflect.TypeOf(rel.String{}), reflect.TypeOf(rel.Array{}), reflect.TypeOf(rel.Bytes{}), - reflect.TypeOf(args[2]))) +func strJoin(args ...rel.Value) rel.Value { + strs := args[1].(rel.Set) + toJoin := make([]string, 0, strs.Count()) + for i, ok := strs.(rel.Set).ArrayEnumerator(); ok && i.MoveNext(); { + toJoin = append(toJoin, mustAsString(i.Current())) + } + return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[0])))) } diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index 76020a19..1a458af6 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -40,22 +40,25 @@ func ArraySplit(a rel.Array, b rel.Value) rel.Value { return nil } -// ArrayJoin joins array a to b, a is joiner and b is joinee. +// ArrayJoin joins array b to a, b is joiner and a is joinee. func ArrayJoin(a rel.Array, b rel.Value) rel.Value { - bArray := convert2Array(b) - if bArray.Count() == 0 { + joiner := convert2Array(b) + if joiner.Count() == 0 || a.Count() == 0 { // if joinee is empty, the final value will be empty - return b - } - if a.Count() == 0 { return a } vals := make([]rel.Value, 0, a.Count()) - for i, value := range bArray.Values() { - vals = append(vals, value) - if i+1 < bArray.Count() { - vals = append(vals, a.Values()...) + for i, value := range a.Values() { + switch vArray := value.(type) { + case rel.Array: + vals = append(vals, generate1LevelArray(vArray)...) + case rel.Value: + vals = append(vals, value) + } + + if i+1 < a.Count() { + vals = append(vals, generate1LevelArray(joiner)...) } } @@ -157,3 +160,22 @@ func indexSubArray(a, b []rel.Value) int { } return -1 } + +// Convert [[1, 2],[3, 4]] to [1, 2, 3, 4] +func generate1LevelArray(source rel.Array) []rel.Value { + if source.Count() == 0 { + return nil + } + + finalArray := make([]rel.Value, 0, source.Count()) + for _, val := range source.Values() { + switch rVal := val.(type) { + case rel.Array: + finalArray = append(finalArray, generate1LevelArray(rVal)...) + case rel.Value: + finalArray = append(finalArray, rVal) + } + } + + return finalArray +} diff --git a/syntax/std_seq_join_test.go b/syntax/std_seq_join_test.go index a675ba2d..45eb46a1 100644 --- a/syntax/std_seq_join_test.go +++ b/syntax/std_seq_join_test.go @@ -2,35 +2,40 @@ package syntax import "testing" -// TestStrJoin, joiner is string. func TestStrJoin(t *testing.T) { t.Parallel() AssertCodesEvalToSameValue(t, `"AB"`, `//seq.join("",['A','B']) `) - AssertCodesEvalToSameValue(t, `"" `, `//seq.join(",",[]) `) - AssertCodesEvalToSameValue(t, `",," `, `//seq.join(",",["", "", ""]) `) + AssertCodesEvalToSameValue(t, `"Youme"`, `//seq.join("",["You", "me"])`) + AssertCodesEvalToSameValue(t, `"AB"`, `//seq.join([],["A","B"])`) + AssertCodesEvalToSameValue(t, `"AB"`, `//seq.join([],['A','B'])`) AssertCodesEvalToSameValue(t, `"this is a test" `, `//seq.join(" ",["this", "is", "a", "test"])`) AssertCodesEvalToSameValue(t, `"this" `, `//seq.join(",",["this"]) `) AssertCodesEvalToSameValue(t, `"You and me"`, `//seq.join(" and ",["You", "me"])`) assertExprPanics(t, `//seq.join("this", 2)`) + + // Following cases are not supported to make sure code is clear and simple. + // Or it has to check array element is rel.String or rel.Number. + // And they are imapcted by https://github.com/arr-ai/arrai/issues/268 too. + // AssertCodesEvalToSameValue(t, `"" `, `//seq.join(",",[]) `) + // AssertCodesEvalToSameValue(t, `",," `, `//seq.join(",",["", "", ""]) `) } func TestArrayJoin(t *testing.T) { t.Parallel() // joiner "" is translated to rel.GenericSet - // AssertCodesEvalToSameValue(t, `["You", "me"]`, `//seq.join("",["You", "me"])`) - // AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join("",[1,2])`) - - // AssertCodesEvalToSameValue(t, `["A","B"]`, `//seq.join([],["A","B"])`) - // AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) - // // if joinee is empty, the final value will be empty + AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) + AssertCodesEvalToSameValue(t, `[1,0,2,0,3,0,4,0,5]`, `//seq.join([0], [1,2,3,4,5])`) + AssertCodesEvalToSameValue(t, `[1, 2, 0, 3, 4, 0, 5, 6]`, `//seq.join([0], [[1, 2], [3, 4], [5, 6]])`) + AssertCodesEvalToSameValue(t, `[1, 2, 10, 11, 3, 4, 10, 11, 5, 6]`, `//seq.join([10,11], [[1, 2], [3, 4], [5, 6]])`) + AssertCodesEvalToSameValue(t, `[1, 2, 3, 4, 0, 5, 6, 7, 8]`, `//seq.join([0], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])`) + + AssertCodesEvalToSameValue(t, `[1, 2, 10, 11, 3, 4, 10, 11, 5, 6]`, `//seq.join([[10],[11]], [[1, 2], [3, 4], [5, 6]])`) + AssertCodesEvalToSameValue(t, `[1, 2, 3, 4, 0, 1, 5, 6, 7, 8]`, `//seq.join([[0],[1]], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])`) + + // Following cases are not supported to make sure code is clear and simple. + // Or it has to check array element is rel.String or rel.Number. + // And they are imapcted by https://github.com/arr-ai/arrai/issues/268 too. // AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) - // AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) - - // AssertCodesEvalToSameValue(t, `["A",",","B"]`, `//seq.join([","],["A","B"])`) - // AssertCodesEvalToSameValue(t, `[1,0,2,0,3,0,4,0,5]`, `//seq.join([0], [1,2,3,4,5])`) - // // TODO - // //AssertCodesEvalToSameValue(t, `[1, 2, 0, 3, 4, 0, 5, 6]`, `//seq.join([0], [[1, 2], [3, 4], [5, 6]])`) - // AssertCodesEvalToSameValue(t, `['A','A','B','A','C','A','D']`, `//seq.join(['A'], ['A','B','C','D'])`) } func TestBytesJoin(t *testing.T) { @@ -40,14 +45,4 @@ func TestBytesJoin(t *testing.T) { // `//seq.join("",//unicode.utf8.encode('hello'))`) // AssertCodesEvalToSameValue(t, `{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) }`, // `//seq.join([],{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) })`) - // AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) - // AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) - - // AssertCodesEvalToSameValue(t, `["A","B"]`, `//seq.join([],["A","B"])`) - // AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) - // // if joinee is empty, the final value will be empty - // AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) - // AssertCodesEvalToSameValue(t, `[]`, `//seq.join(['A'],[])`) - - // AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('h'))`) } From 88802ffe256721ea55804ff1ebcf099f9260b472 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Sat, 16 May 2020 01:03:58 +0800 Subject: [PATCH 042/106] #234 Fixed issue found by golang linter. --- syntax/std_seq_join_test.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/syntax/std_seq_join_test.go b/syntax/std_seq_join_test.go index 45eb46a1..c72adc47 100644 --- a/syntax/std_seq_join_test.go +++ b/syntax/std_seq_join_test.go @@ -26,11 +26,15 @@ func TestArrayJoin(t *testing.T) { AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) AssertCodesEvalToSameValue(t, `[1,0,2,0,3,0,4,0,5]`, `//seq.join([0], [1,2,3,4,5])`) AssertCodesEvalToSameValue(t, `[1, 2, 0, 3, 4, 0, 5, 6]`, `//seq.join([0], [[1, 2], [3, 4], [5, 6]])`) - AssertCodesEvalToSameValue(t, `[1, 2, 10, 11, 3, 4, 10, 11, 5, 6]`, `//seq.join([10,11], [[1, 2], [3, 4], [5, 6]])`) - AssertCodesEvalToSameValue(t, `[1, 2, 3, 4, 0, 5, 6, 7, 8]`, `//seq.join([0], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])`) + AssertCodesEvalToSameValue(t, `[1, 2, 10, 11, 3, 4, 10, 11, 5, 6]`, + `//seq.join([10,11], [[1, 2], [3, 4], [5, 6]])`) + AssertCodesEvalToSameValue(t, `[1, 2, 3, 4, 0, 5, 6, 7, 8]`, + `//seq.join([0], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])`) - AssertCodesEvalToSameValue(t, `[1, 2, 10, 11, 3, 4, 10, 11, 5, 6]`, `//seq.join([[10],[11]], [[1, 2], [3, 4], [5, 6]])`) - AssertCodesEvalToSameValue(t, `[1, 2, 3, 4, 0, 1, 5, 6, 7, 8]`, `//seq.join([[0],[1]], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])`) + AssertCodesEvalToSameValue(t, `[1, 2, 10, 11, 3, 4, 10, 11, 5, 6]`, + `//seq.join([[10],[11]], [[1, 2], [3, 4], [5, 6]])`) + AssertCodesEvalToSameValue(t, `[1, 2, 3, 4, 0, 1, 5, 6, 7, 8]`, + `//seq.join([[0],[1]], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])`) // Following cases are not supported to make sure code is clear and simple. // Or it has to check array element is rel.String or rel.Number. From fb5a92e44ef131b0c52b990d740b71e398b9c6b9 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Sun, 17 May 2020 11:12:13 +0800 Subject: [PATCH 043/106] #268 Change API signature. --- docs/std-str.md | 38 +++++++++++++++++----------------- examples/grpc/grpc-proto.arrai | 12 +++++------ examples/grpc/grpc.arrai | 2 +- examples/grpc/proto-util.arrai | 14 ++++++------- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/docs/std-str.md b/docs/std-str.md index b612061f..410f216e 100644 --- a/docs/std-str.md +++ b/docs/std-str.md @@ -2,7 +2,7 @@ The `str` library contains functions that are used for string manipulations. -## `//seq.contains(str <: string, substr <: string) <: bool` +## `//seq.contains(substr <: string, str <: string) <: bool` `contains` checks whether `substr` is contained in `str`. It returns a boolean. @@ -11,10 +11,10 @@ Usage: | example | equals | |:-|:-| -| `//seq.contains("the full string which has substring", "substring")` | `true` | -| `//seq.contains("just some random sentence", "microwave")` | `{}` which is equal to `false` | +| `//seq.contains("substring", "the full string which has substring")` | `true` | +| `//seq.contains("microwave", "just some random sentence")` | `{}` which is equal to `false` | -## `//seq.sub(s <: string, old <: string, new <: string) <: string` +## `//seq.sub(old <: string, new <: string, s <: string) <: string` `sub` replaces occurrences of `old` in `s` with `new`. It returns the modified string. @@ -22,10 +22,10 @@ Usage: | example | equals | |:-|:-| -| `//seq.sub("this is the old string", "old string", "new sentence")` | `"this is the new sentence"` | -| `//seq.sub("just another sentence", "string", "stuff")` | `"just another sentence"` | +| `//seq.sub("old string", "new sentence", "this is the old string")` | `"this is the new sentence"` | +| `//seq.sub("string", "stuff", "just another sentence")` | `"just another sentence"` | -## `//seq.split(s <: string, delimiter <: string) <: array of string` +## `//seq.split(delimiter <: string, s <: string) <: array of string` `split` splits the string `s` based on the provided `delimiter`. It returns an array of strings which are split from the string `s`. @@ -34,8 +34,8 @@ Usage: | example | equals | |:-|:-| -| `//seq.split("deliberately adding spaces to demonstrate the split function", " ")` | `["deliberately", "adding", "spaces", "to", "demonstrate", "the", "split", "function"]` | -| `//seq.split("this is just a random sentence", "random stuff")` | `["this is just a random sentence"]` | +| `//seq.split(" ", "deliberately adding spaces to demonstrate the split function")` | `["deliberately", "adding", "spaces", "to", "demonstrate", "the", "split", "function"]` | +| `//seq.split("random stuff", "this is just a random sentence")` | `["this is just a random sentence"]` | ## `//str.lower(s <: string) <: string` @@ -73,7 +73,7 @@ Usage: | `//str.title("laser noises pew pew pew")` | `"Laser Noises Pew Pew Pew"` | | `//str.title("pew")` | `"Pew"` | -## `//seq.has_prefix(s <: string, prefix <: string) <: bool` +## `//seq.has_prefix(prefix <: string, s <: string) <: bool` `has_prefix` checks whether the string `s` is prefixed by `prefix`. It returns a boolean. @@ -81,10 +81,10 @@ Usage: | example | equals | |:-|:-| -| `//seq.has_prefix("I'm running out of stuff to write", "I'm")` | `true` | -| `//seq.has_prefix("I'm running out of stuff to write", "to write")` | `{}` which is equal to `false` | +| `//seq.has_prefix("I'm", "I'm running out of stuff to write")` | `true` | +| `//seq.has_prefix("to write", "I'm running out of stuff to write")` | `{}` which is equal to `false` | -## `//seq.has_suffix(s <: string, suffix <: string) <: bool` +## `//seq.has_suffix(suffix <: string, s <: string) <: bool` `has_suffix` checks whether the string `s` is suffixed by `suffix`. It returns a boolean. @@ -92,10 +92,10 @@ Usage: | example | equals | |:-|:-| -| `//seq.has_suffix("I'm running out of stuff to write", "I'm")` | `{}` which is equal to `false` | -| `//seq.has_suffix("I'm running out of stuff to write", "to write")` | `true` | +| `//seq.has_suffix("I'm", "I'm running out of stuff to write")` | `{}` which is equal to `false` | +| `//seq.has_suffix("to write", "I'm running out of stuff to write")` | `true` | -## `//seq.join(s <: array_of_string, delimiter <: string) <: string` +## `//seq.join(delimiter <: string, s <: array_of_string) <: string` `join` returns a concatenated string with each member of `s` delimited by `delimiter` @@ -103,6 +103,6 @@ Usage: | example | equals | |:-|:-| -| `//seq.join(["pew", "another pew", "and more pews"], ", ")` | `"pew, another pew, and more pews"` | -| `//seq.join(["this", "is", "a", "sentence"], " ")` | `"this is a sentence"` | -| `//seq.join(["this", "is", "a", "sentence"], "")` | `"thisisasentence"` | +| `//seq.join(", ", ["pew", "another pew", "and more pews"])` | `"pew, another pew, and more pews"` | +| `//seq.join(" ", ["this", "is", "a", "sentence"])` | `"this is a sentence"` | +| `//seq.join(["", "this", "is", "a", "sentence"])` | `"thisisasentence"` | diff --git a/examples/grpc/grpc-proto.arrai b/examples/grpc/grpc-proto.arrai index 7569b21b..db1923af 100644 --- a/examples/grpc/grpc-proto.arrai +++ b/examples/grpc/grpc-proto.arrai @@ -2,7 +2,7 @@ let grpc = //{./grpc}; let wrap = "wrap" <: app.attrs.patterns; let proto = //{./proto-util}(wrap); -let endpoints = app.endpoints where !//seq.has_prefix(.@item.name, "enum "); +let endpoints = app.endpoints where !//seq.has_prefix("enum ", .@item.name); //archive.tar.tar({ app.name + ".proto": $` // THIS IS AUTOGENERATED BY sysl // @@ -24,8 +24,8 @@ let endpoints = app.endpoints where !//seq.has_prefix(.@item.name, "enum "); ${cond (app.endpoints: $` ${//rel.union((endpoints >> (.params >> cond ( - //seq.contains(grpc.type(.), "google.protobuf"): $` - import "${//seq.sub(grpc.type(.), ".", "/")}.proto";`, + //seq.contains("google.protobuf", grpc.type(.)): $` + import "${//seq.sub(".", "/", grpc.type(.))}.proto";`, ) => .@item )) => .@item)::\i:\n} service ${app.name} { @@ -37,14 +37,14 @@ let endpoints = app.endpoints where !//seq.has_prefix(.@item.name, "enum "); }`:::\n} ${endpoints >> proto.wrapSequence(.).grpcType::\i} ${cond (wrap: endpoints) >> - let retTokens = //seq.split(ep.ret("ok"), " "); - let retName = //seq.sub(//seq.concat(retTokens -- {"sequence", "of"}), ".", ""); + let retTokens = //seq.split(" ", ep.ret("ok")); + let retName = //seq.sub(".", "", //seq.concat(retTokens -- {"sequence", "of"})); let attr = ep.attrs(retName + "_rpcId"); let epi = proto.endpointInfo(ep); $` message ${epi.paramName} { ${ep.params >> - let name = //seq.sub(.name, "-", ""); + let name = //seq.sub("-", "", .name); $`${grpc.type(.)} req${name} = ${.attrs(name + "_rpcId")};` ::\i} } diff --git a/examples/grpc/grpc.arrai b/examples/grpc/grpc.arrai index 8e3a9e09..b43578f2 100644 --- a/examples/grpc/grpc.arrai +++ b/examples/grpc/grpc.arrai @@ -13,7 +13,7 @@ ), 'sequence': 'repeated ' + type(t.sequence) *: cond ( - //seq.contains(t.type_ref, "google-protobuf"): //seq.sub(t.type_ref, "-", "."), + //seq.contains("google-protobuf", t.type_ref): //seq.sub("-", ".", t.type_ref), *: t.type_ref, ), ), diff --git a/examples/grpc/proto-util.arrai b/examples/grpc/proto-util.arrai index 7b2f78ea..816fd9be 100644 --- a/examples/grpc/proto-util.arrai +++ b/examples/grpc/proto-util.arrai @@ -1,21 +1,21 @@ let grpc = //{./grpc}; \wrap ( - field: \. $`${grpc.type(.)} ${//seq.sub(.key, "-", "")} = ${.attrs.rpcId};`, + field: \. $`${grpc.type(.)} ${//seq.sub("-", "", .key)} = ${.attrs.rpcId};`, imports: \fields - fields where(//seq.contains(grpc.type(.@item), "google.protobuf")) >> - $`import "${//seq.sub(grpc.type(.), ".", "/")}.proto";`, + fields where(//seq.contains("google.protobuf", grpc.type(.@item))) >> + $`import "${//seq.sub(".", "/", grpc.type(.))}.proto";`, endpointInfo: \ep - let method = //seq.sub(//str.title(//str.lower(ep.name)), "-", ""); + let method = //seq.sub("-", "", //str.title(//str.lower(ep.name))); let paramName = cond ( wrap: method + "Request", *: $"${ep.params >> grpc.type(.)::, }", ); let streamRes = cond ( - ep.attrs.stream: //seq.sub(ep.ret("ok"), "sequence of", "stream"), - *: //seq.sub(ep.ret("ok"), "sequence of ", "") + "s", + ep.attrs.stream: //seq.sub("sequence of", "stream", ep.ret("ok")), + *: //seq.sub("sequence of ", "", ep.ret("ok")) + "s", ); let responseName = cond (wrap: method + "Response", *: streamRes); ( @@ -26,7 +26,7 @@ let grpc = //{./grpc}; ), wrapSequence: \ep - let type = //seq.sub(ep.ret("ok"), "sequence of ", ""); + let type = //seq.sub("sequence of ", "", ep.ret("ok")); let wrapType = type + "s"; let name = //str.lower(type) + "s"; ( From d427d086438689363cee34b42c80a92104a5de8c Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Sun, 17 May 2020 12:38:32 +0800 Subject: [PATCH 044/106] #234 Fixed byte join API. --- syntax/std_seq.go | 49 +++++++++++++++++++--------------- syntax/std_seq_bytes_helper.go | 20 +++++++++----- syntax/std_seq_join_test.go | 11 +++++--- syntax/std_seq_split_test.go | 2 +- 4 files changed, 48 insertions(+), 34 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index c1be5657..77096767 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -72,7 +72,7 @@ func stdSeq() rel.Attr { case rel.Array: return ArrayContains(args[1].(rel.Array), args[0]) case rel.Bytes: - return BytesContain(args[1].(rel.Bytes), args[0].(rel.Bytes)) + return rel.NewBool(strings.Contains(args[1].String(), args[0].String())) } return rel.NewBool(false) @@ -101,25 +101,6 @@ func stdSeq() rel.Attr { return rel.NewBool(false) }), - createNestedFuncAttr("split", 2, func(args ...rel.Value) rel.Value { - switch args[1].(type) { - case rel.String: - splitted := strings.Split(mustAsString(args[1]), mustAsString(args[0])) - vals := make([]rel.Value, 0, len(splitted)) - for _, s := range splitted { - vals = append(vals, rel.NewString([]rune(s))) - } - return rel.NewArray(vals...) - case rel.Array: - return nil - case rel.Bytes: - return nil - } - - panic(fmt.Errorf("expected subject sequence types are %s, %s and %s, but the actual type is %s", - reflect.TypeOf(rel.String{}), reflect.TypeOf(rel.Array{}), reflect.TypeOf(rel.Bytes{}), - reflect.TypeOf(args[2]))) - }), createNestedFuncAttr("sub", 3, func(args ...rel.Value) rel.Value { switch args[2].(type) { case rel.String: @@ -135,7 +116,31 @@ func stdSeq() rel.Attr { case rel.Array: return ArraySub(args[2].(rel.Array), args[0], args[1]) case rel.Bytes: - return BytesSub(args[2].(rel.Bytes), args[0].(rel.Bytes), args[1].(rel.Bytes)) + return rel.NewBytes([]byte(strings.ReplaceAll(args[2].String(), args[0].String(), args[1].String()))) + } + + panic(fmt.Errorf("expected subject sequence types are %s, %s and %s, but the actual type is %s", + reflect.TypeOf(rel.String{}), reflect.TypeOf(rel.Array{}), reflect.TypeOf(rel.Bytes{}), + reflect.TypeOf(args[2]))) + }), + createNestedFuncAttr("split", 2, func(args ...rel.Value) rel.Value { + switch args[1].(type) { + case rel.String: + splitted := strings.Split(mustAsString(args[1]), mustAsString(args[0])) + vals := make([]rel.Value, 0, len(splitted)) + for _, s := range splitted { + vals = append(vals, rel.NewString([]rune(s))) + } + return rel.NewArray(vals...) + case rel.Array: + return nil + case rel.Bytes: + splitted := strings.Split(args[1].String(), args[0].String()) + vals := make([]byte, 0, len(splitted)) + for _, s := range splitted { + vals = append(vals, []byte(s)...) + } + return rel.NewBytes(vals) } panic(fmt.Errorf("expected subject sequence types are %s, %s and %s, but the actual type is %s", @@ -152,7 +157,7 @@ func stdSeq() rel.Attr { return ArrayJoin(a1, args[0]) } case rel.Bytes: - return nil + return BytesJoin(args[1].(rel.Bytes), args[0].(rel.Bytes)) } // return ArrayContains(args[1].(rel.Array), args[0]) panic(fmt.Errorf("expected subject sequence types are %s and %s, but the actual type is %s", diff --git a/syntax/std_seq_bytes_helper.go b/syntax/std_seq_bytes_helper.go index 2ece0458..925a2e86 100644 --- a/syntax/std_seq_bytes_helper.go +++ b/syntax/std_seq_bytes_helper.go @@ -4,6 +4,19 @@ import ( "github.com/arr-ai/arrai/rel" ) +// BytesJoin join b to a +func BytesJoin(a, b rel.Bytes) rel.Value { + result := make([]byte, 0, a.Count()) + for index, e := range a.Bytes() { + if index > 0 && index < a.Count() { + result = append(result, b.Bytes()...) + } + result = append(result, e) + } + + return rel.NewBytes(result) +} + // BytesContain check if a contains b. func BytesContain(a, b rel.Bytes) rel.Value { return rel.NewBool(indexSubBytes(a.Bytes(), b.Bytes()) > -1) @@ -54,10 +67,3 @@ func indexSubBytes(a, b []byte) int { } return -1 } - -// func convert2Bytes(val rel.Value) rel.Bytes { -// // switch val := val.(type) { -// return nil - -// // panic("it support types rel.Array, rel.GenericSet and rel.Value only.") -// } diff --git a/syntax/std_seq_join_test.go b/syntax/std_seq_join_test.go index c72adc47..c5041b13 100644 --- a/syntax/std_seq_join_test.go +++ b/syntax/std_seq_join_test.go @@ -45,8 +45,11 @@ func TestArrayJoin(t *testing.T) { func TestBytesJoin(t *testing.T) { t.Parallel() // joiner "" is translated to rel.GenericSet - // AssertCodesEvalToSameValue(t, `{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) }`, - // `//seq.join("",//unicode.utf8.encode('hello'))`) - // AssertCodesEvalToSameValue(t, `{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) }`, - // `//seq.join([],{ |@, @byte| (0, 104), (1, 101), (2, 108), (3, 108), (4, 111) })`) + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('hhehlhlho')`, + `//seq.join({ |@, @byte| (0, 104)},//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, + `{ |@, @byte| (0, 104), (1, 108), (2, 111), (3, 101), (4, 108), (5, 111),`+ + `(6, 108), (7, 108), (8, 111), (9, 108), (10, 108), (11, 111), (12, 111) }`, + `//seq.join({ |@, @byte| (0, 108), (1, 111)},{ |@, @byte| (0, 104), (1, 101),`+ + ` (2, 108), (3, 108), (4, 111) })`) } diff --git a/syntax/std_seq_split_test.go b/syntax/std_seq_split_test.go index 4f2abb7c..c76d1fbc 100644 --- a/syntax/std_seq_split_test.go +++ b/syntax/std_seq_split_test.go @@ -6,7 +6,7 @@ func TestStrSplit(t *testing.T) { t.Parallel() AssertCodesEvalToSameValue(t, `["t", "h", "i", "s", " ", "i", "s", " ", "a", " ", "t", "e", "s", "t"]`, - `//seq.split('',"this is a test")`) + `//seq.split("","this is a test")`) AssertCodesEvalToSameValue(t, `["this", "is", "a", "test"]`, `//seq.split(" ","this is a test") `) AssertCodesEvalToSameValue(t, `["this is a test"] `, `//seq.split(",","this is a test") `) AssertCodesEvalToSameValue(t, `["th", " ", " a test"] `, `//seq.split("is","this is a test")`) From 30147a22c8ef3a3c8f42729b3e8635cf27a598aa Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Sun, 17 May 2020 12:41:41 +0800 Subject: [PATCH 045/106] #234 Check in one more test case for byte join. --- syntax/std_seq_join_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/syntax/std_seq_join_test.go b/syntax/std_seq_join_test.go index c5041b13..4bc89f2e 100644 --- a/syntax/std_seq_join_test.go +++ b/syntax/std_seq_join_test.go @@ -52,4 +52,6 @@ func TestBytesJoin(t *testing.T) { `(6, 108), (7, 108), (8, 111), (9, 108), (10, 108), (11, 111), (12, 111) }`, `//seq.join({ |@, @byte| (0, 108), (1, 111)},{ |@, @byte| (0, 104), (1, 101),`+ ` (2, 108), (3, 108), (4, 111) })`) + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('hateatlatlato')`, + `//seq.join(//unicode.utf8.encode('at'),//unicode.utf8.encode('hello'))`) } From 63358e55e026b6a44af734aff4dd119940264c96 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Sun, 17 May 2020 16:05:23 +0800 Subject: [PATCH 046/106] #234 Check bytes split test case. --- syntax/std_seq.go | 7 +--- syntax/std_seq_bytes_helper.go | 59 ++++++++-------------------------- syntax/std_seq_split_test.go | 10 ++++++ 3 files changed, 25 insertions(+), 51 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 77096767..50585b43 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -135,12 +135,7 @@ func stdSeq() rel.Attr { case rel.Array: return nil case rel.Bytes: - splitted := strings.Split(args[1].String(), args[0].String()) - vals := make([]byte, 0, len(splitted)) - for _, s := range splitted { - vals = append(vals, []byte(s)...) - } - return rel.NewBytes(vals) + return BytesSplit(args[1].(rel.Bytes), args[0]) } panic(fmt.Errorf("expected subject sequence types are %s, %s and %s, but the actual type is %s", diff --git a/syntax/std_seq_bytes_helper.go b/syntax/std_seq_bytes_helper.go index 925a2e86..2cc0af36 100644 --- a/syntax/std_seq_bytes_helper.go +++ b/syntax/std_seq_bytes_helper.go @@ -1,6 +1,8 @@ package syntax import ( + "strings" + "github.com/arr-ai/arrai/rel" ) @@ -17,53 +19,20 @@ func BytesJoin(a, b rel.Bytes) rel.Value { return rel.NewBytes(result) } -// BytesContain check if a contains b. -func BytesContain(a, b rel.Bytes) rel.Value { - return rel.NewBool(indexSubBytes(a.Bytes(), b.Bytes()) > -1) -} - -// BytesSub substitute all old in a with new. -func BytesSub(a, old, new rel.Bytes) rel.Value { - finalVals := make([]byte, 0, a.Count()) - - for start, absoluteIndex := 0, 0; start < a.Count(); { - relativeIndex := indexSubBytes(a.Bytes()[start:], old.Bytes()) - if relativeIndex >= 0 { - absoluteIndex = relativeIndex + start - if absoluteIndex-start > 0 { - finalVals = append(finalVals, a.Bytes()[start:absoluteIndex]...) - } - finalVals = append(finalVals, new.Bytes()...) - start = absoluteIndex + old.Count() - } else { - finalVals = append(finalVals, a.Bytes()[absoluteIndex+1:]...) - break - } - } +// BytesSplit split a by b +func BytesSplit(a rel.Bytes, b rel.Value) rel.Value { + var splitted []string - return rel.NewBytes(finalVals) -} - -// It is brute force approach, can be improved later if it is necessary. -func indexSubBytes(a, b []byte) int { - aOffset, bOffset := 0, 0 - - for ; aOffset < len(a); aOffset++ { - if bOffset < len(b) && a[aOffset] == b[bOffset] { - bOffset++ - } else { - if bOffset > 0 && bOffset < len(b) { - bOffset = 0 - aOffset-- - } - } - if bOffset == len(b) { - break - } + switch b := b.(type) { + case rel.Bytes: + splitted = strings.Split(a.String(), b.String()) + case rel.GenericSet: + splitted = strings.Split(a.String(), mustAsString(b)) } - if aOffset < len(a) { - return aOffset + vals := make([]rel.Value, 0, len(splitted)) + for _, s := range splitted { + vals = append(vals, rel.NewBytes([]byte(s)).(rel.Value)) } - return -1 + return rel.NewArray(vals...) } diff --git a/syntax/std_seq_split_test.go b/syntax/std_seq_split_test.go index c76d1fbc..ec05d44e 100644 --- a/syntax/std_seq_split_test.go +++ b/syntax/std_seq_split_test.go @@ -23,4 +23,14 @@ func TestArraySplit(t *testing.T) { func TestBytesSplit(t *testing.T) { t.Parallel() + // hello bytes - 104 101 108 108 111 + AssertCodesEvalToSameValue(t, + `[//unicode.utf8.encode('y'),//unicode.utf8.encode('e'),//unicode.utf8.encode('s')]`, + `//seq.split(//unicode.utf8.encode(""),//unicode.utf8.encode("yes"))`) + AssertCodesEvalToSameValue(t, + `[//unicode.utf8.encode("this"), //unicode.utf8.encode("is"), //unicode.utf8.encode("a"), //unicode.utf8.encode("test")]`, + `//seq.split(//unicode.utf8.encode(" "),//unicode.utf8.encode("this is a test"))`) + AssertCodesEvalToSameValue(t, + `[//unicode.utf8.encode("this is a test")]`, + `//seq.split(//unicode.utf8.encode("get"),//unicode.utf8.encode("this is a test"))`) } From b02b5b4012847d81742ef3ed7e5363f32ee8f8a6 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Sun, 17 May 2020 16:13:30 +0800 Subject: [PATCH 047/106] #234 Fixed issue found by golang linter. --- syntax/std_seq_split_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/syntax/std_seq_split_test.go b/syntax/std_seq_split_test.go index ec05d44e..abac111a 100644 --- a/syntax/std_seq_split_test.go +++ b/syntax/std_seq_split_test.go @@ -28,7 +28,8 @@ func TestBytesSplit(t *testing.T) { `[//unicode.utf8.encode('y'),//unicode.utf8.encode('e'),//unicode.utf8.encode('s')]`, `//seq.split(//unicode.utf8.encode(""),//unicode.utf8.encode("yes"))`) AssertCodesEvalToSameValue(t, - `[//unicode.utf8.encode("this"), //unicode.utf8.encode("is"), //unicode.utf8.encode("a"), //unicode.utf8.encode("test")]`, + `[//unicode.utf8.encode("this"), //unicode.utf8.encode("is"),`+ + ` //unicode.utf8.encode("a"), //unicode.utf8.encode("test")]`, `//seq.split(//unicode.utf8.encode(" "),//unicode.utf8.encode("this is a test"))`) AssertCodesEvalToSameValue(t, `[//unicode.utf8.encode("this is a test")]`, From 333a73a90ff08b707f82dd409ee0e53543650250 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Sun, 17 May 2020 18:01:09 +0800 Subject: [PATCH 048/106] #234 Implemented array split feature. --- syntax/std_seq.go | 2 +- syntax/std_seq_array_helper.go | 27 ++++++++++++++++++++++++++- syntax/std_seq_split_test.go | 32 ++++++++++++++++++++++++++++---- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 50585b43..2454caa5 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -133,7 +133,7 @@ func stdSeq() rel.Attr { } return rel.NewArray(vals...) case rel.Array: - return nil + return ArraySplit(args[1].(rel.Array), args[0]) case rel.Bytes: return BytesSplit(args[1].(rel.Bytes), args[0]) } diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index 1a458af6..dfb825b0 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -37,7 +37,32 @@ func ArraySub(a rel.Array, old, new rel.Value) rel.Value { // ArraySplit split a by b. func ArraySplit(a rel.Array, b rel.Value) rel.Value { - return nil + delimiter := convert2Array(b) + var result []rel.Value + + if delimiter.Count() == 0 { + for _, e := range a.Values() { + result = append(result, rel.NewArray(e)) + } + } else { + for start, absoluteIndex := 0, 0; start < a.Count(); { + relativeIndex := indexSubArray(a.Values()[start:], delimiter.Values()) + if relativeIndex >= 0 { + absoluteIndex = relativeIndex + start + if start != absoluteIndex { + result = append(result, rel.NewArray(a.Values()[start:absoluteIndex]...)) + } + start = absoluteIndex + delimiter.Count() + } else { + if start == 0 || start < a.Count() { + result = append(result, rel.NewArray(a.Values()[start:]...)) + } + break + } + } + } + + return rel.NewArray(result...) } // ArrayJoin joins array b to a, b is joiner and a is joinee. diff --git a/syntax/std_seq_split_test.go b/syntax/std_seq_split_test.go index abac111a..d788e99f 100644 --- a/syntax/std_seq_split_test.go +++ b/syntax/std_seq_split_test.go @@ -15,10 +15,34 @@ func TestStrSplit(t *testing.T) { func TestArraySplit(t *testing.T) { t.Parallel() - // TODO - // AssertCodesEvalToSameValue(t, - // `[['B'],['C', 'D', 'E']]`, - // `//seq.sub(['A', 'B', 'A', 'C', 'D', 'E'], 'A')`) + AssertCodesEvalToSameValue(t, + `[['A'],['B'],['A']]`, + `//seq.split([],['A', 'B', 'A'])`) + AssertCodesEvalToSameValue(t, + `[['B'],['C', 'D', 'E']]`, + `//seq.split('A',['A', 'B', 'A', 'C', 'D', 'E'])`) + AssertCodesEvalToSameValue(t, + `[['B'],['C'], ['D', 'E']]`, + `//seq.split('A',['B', 'A', 'C', 'A', 'D', 'E'])`) + AssertCodesEvalToSameValue(t, + `[['A', 'B', 'C']]`, + `//seq.split(['F'],['A', 'B', 'C'])`) + AssertCodesEvalToSameValue(t, + `[[['A','B'], ['C','D'], ['E','F']]]`, + `//seq.split([['F','F']],[['A','B'], ['C','D'], ['E','F']])`) + + AssertCodesEvalToSameValue(t, + `[[1],[3]]`, + `//seq.split(2,[1, 2, 3])`) + AssertCodesEvalToSameValue(t, + `[[1],[3]]`, + `//seq.split([2],[1, 2, 3])`) + AssertCodesEvalToSameValue(t, + `[[[1,2]],[[5,6]]]`, + `//seq.split([[3,4]],[[1,2],[3,4],[5,6]])`) + AssertCodesEvalToSameValue(t, + `[[[1,2]], [[3,4]]]`, + `//seq.split([],[[1,2], [3,4]])`) } func TestBytesSplit(t *testing.T) { From f56532eb00a83f29a1c1d4c541eda04cdb78ed07 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Sun, 17 May 2020 18:09:29 +0800 Subject: [PATCH 049/106] #234 Removed unnecessary comments. --- syntax/std_seq.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 2454caa5..02ec35df 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -154,7 +154,7 @@ func stdSeq() rel.Attr { case rel.Bytes: return BytesJoin(args[1].(rel.Bytes), args[0].(rel.Bytes)) } - // return ArrayContains(args[1].(rel.Array), args[0]) + panic(fmt.Errorf("expected subject sequence types are %s and %s, but the actual type is %s", reflect.TypeOf(rel.Array{}), reflect.TypeOf(rel.Bytes{}), reflect.TypeOf(args[2]))) From b659c0ab9f69d3a4b96bebc25926c62af6b7f462 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Sun, 17 May 2020 18:19:46 +0800 Subject: [PATCH 050/106] #234 Updated documents. --- docs/std-seq.md | 73 ++++++++++++++++++++++++++++++++++++++++++++++++- docs/std-str.md | 69 ---------------------------------------------- 2 files changed, 72 insertions(+), 70 deletions(-) diff --git a/docs/std-seq.md b/docs/std-seq.md index a5fcb56c..d4ff69d5 100644 --- a/docs/std-seq.md +++ b/docs/std-seq.md @@ -1,6 +1,6 @@ # seq -The `seq` library contains functions that are used for string manipulations. +The `seq` library contains functions that are used for string, array and bytes manipulations. ## `//seq.concat(seqs <: array) <: array`
`concat(seqs <: string) <: string` @@ -19,3 +19,74 @@ the concatenation of the sequences in the array. | example | equals | |:-|:-| | `//seq.repeat(2, "hots")` | `"hotshots"` | + + +## `//seq.has_prefix(prefix <: string, s <: string) <: bool` + +`has_prefix` checks whether the string `s` is prefixed by `prefix`. It returns a boolean. + +Usage: + +| example | equals | +|:-|:-| +| `//seq.has_prefix("I'm", "I'm running out of stuff to write")` | `true` | +| `//seq.has_prefix("to write", "I'm running out of stuff to write")` | `{}` which is equal to `false` | + +## `//seq.has_suffix(suffix <: string, s <: string) <: bool` + +`has_suffix` checks whether the string `s` is suffixed by `suffix`. It returns a boolean. + +Usage: + +| example | equals | +|:-|:-| +| `//seq.has_suffix("I'm", "I'm running out of stuff to write")` | `{}` which is equal to `false` | +| `//seq.has_suffix("to write", "I'm running out of stuff to write")` | `true` | + +## `//seq.join(delimiter <: string, s <: array_of_string) <: string` + +`join` returns a concatenated string with each member of `s` delimited by `delimiter` + +Usage: + +| example | equals | +|:-|:-| +| `//seq.join(", ", ["pew", "another pew", "and more pews"])` | `"pew, another pew, and more pews"` | +| `//seq.join(" ", ["this", "is", "a", "sentence"])` | `"this is a sentence"` | +| `//seq.join(["", "this", "is", "a", "sentence"])` | `"thisisasentence"` | + +## `//seq.contains(substr <: string, str <: string) <: bool` + +`contains` checks whether `substr` is contained in `str`. It returns a +boolean. + +Usage: + +| example | equals | +|:-|:-| +| `//seq.contains("substring", "the full string which has substring")` | `true` | +| `//seq.contains("microwave", "just some random sentence")` | `{}` which is equal to `false` | + +## `//seq.sub(old <: string, new <: string, s <: string) <: string` + +`sub` replaces occurrences of `old` in `s` with `new`. It returns the modified string. + +Usage: + +| example | equals | +|:-|:-| +| `//seq.sub("old string", "new sentence", "this is the old string")` | `"this is the new sentence"` | +| `//seq.sub("string", "stuff", "just another sentence")` | `"just another sentence"` | + +## `//seq.split(delimiter <: string, s <: string) <: array of string` + +`split` splits the string `s` based on the provided `delimiter`. It returns an array of strings +which are split from the string `s`. + +Usage: + +| example | equals | +|:-|:-| +| `//seq.split(" ", "deliberately adding spaces to demonstrate the split function")` | `["deliberately", "adding", "spaces", "to", "demonstrate", "the", "split", "function"]` | +| `//seq.split("random stuff", "this is just a random sentence")` | `["this is just a random sentence"]` | + diff --git a/docs/std-str.md b/docs/std-str.md index 410f216e..4e97dfd7 100644 --- a/docs/std-str.md +++ b/docs/std-str.md @@ -2,41 +2,6 @@ The `str` library contains functions that are used for string manipulations. -## `//seq.contains(substr <: string, str <: string) <: bool` - -`contains` checks whether `substr` is contained in `str`. It returns a -boolean. - -Usage: - -| example | equals | -|:-|:-| -| `//seq.contains("substring", "the full string which has substring")` | `true` | -| `//seq.contains("microwave", "just some random sentence")` | `{}` which is equal to `false` | - -## `//seq.sub(old <: string, new <: string, s <: string) <: string` - -`sub` replaces occurrences of `old` in `s` with `new`. It returns the modified string. - -Usage: - -| example | equals | -|:-|:-| -| `//seq.sub("old string", "new sentence", "this is the old string")` | `"this is the new sentence"` | -| `//seq.sub("string", "stuff", "just another sentence")` | `"just another sentence"` | - -## `//seq.split(delimiter <: string, s <: string) <: array of string` - -`split` splits the string `s` based on the provided `delimiter`. It returns an array of strings -which are split from the string `s`. - -Usage: - -| example | equals | -|:-|:-| -| `//seq.split(" ", "deliberately adding spaces to demonstrate the split function")` | `["deliberately", "adding", "spaces", "to", "demonstrate", "the", "split", "function"]` | -| `//seq.split("random stuff", "this is just a random sentence")` | `["this is just a random sentence"]` | - ## `//str.lower(s <: string) <: string` `lower` returns the string `s` with all of the character converted to lowercase. @@ -72,37 +37,3 @@ Usage: |:-|:-| | `//str.title("laser noises pew pew pew")` | `"Laser Noises Pew Pew Pew"` | | `//str.title("pew")` | `"Pew"` | - -## `//seq.has_prefix(prefix <: string, s <: string) <: bool` - -`has_prefix` checks whether the string `s` is prefixed by `prefix`. It returns a boolean. - -Usage: - -| example | equals | -|:-|:-| -| `//seq.has_prefix("I'm", "I'm running out of stuff to write")` | `true` | -| `//seq.has_prefix("to write", "I'm running out of stuff to write")` | `{}` which is equal to `false` | - -## `//seq.has_suffix(suffix <: string, s <: string) <: bool` - -`has_suffix` checks whether the string `s` is suffixed by `suffix`. It returns a boolean. - -Usage: - -| example | equals | -|:-|:-| -| `//seq.has_suffix("I'm", "I'm running out of stuff to write")` | `{}` which is equal to `false` | -| `//seq.has_suffix("to write", "I'm running out of stuff to write")` | `true` | - -## `//seq.join(delimiter <: string, s <: array_of_string) <: string` - -`join` returns a concatenated string with each member of `s` delimited by `delimiter` - -Usage: - -| example | equals | -|:-|:-| -| `//seq.join(", ", ["pew", "another pew", "and more pews"])` | `"pew, another pew, and more pews"` | -| `//seq.join(" ", ["this", "is", "a", "sentence"])` | `"this is a sentence"` | -| `//seq.join(["", "this", "is", "a", "sentence"])` | `"thisisasentence"` | From be8e898ce07e746871198b93e279a3098265fb1f Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Sun, 17 May 2020 18:26:51 +0800 Subject: [PATCH 051/106] #234 Updated panic error messages. --- syntax/std_seq_array_helper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index dfb825b0..7dd04d5b 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -159,7 +159,7 @@ func convert2Array(val rel.Value) rel.Array { return valArray } - panic("it support types rel.Array, rel.GenericSet and rel.Value only.") + panic("it supports types rel.Array, rel.GenericSet and rel.Value only.") } // It is brute force approach, can be improved later if it is necessary. From 6d7647c6284e7119785381f048b8b2e60f00d8a9 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 18 May 2020 10:58:31 +0800 Subject: [PATCH 052/106] #234 Updated method signature as review comments. --- syntax/std_seq.go | 16 ++++++++-------- syntax/std_seq_array_helper.go | 26 +++++++++++++------------- syntax/std_seq_bytes_helper.go | 8 ++++---- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 02ec35df..a950a758 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -70,7 +70,7 @@ func stdSeq() rel.Attr { case rel.String: return rel.NewBool(strings.Contains(mustAsString(args[1]), mustAsString(args[0]))) case rel.Array: - return ArrayContains(args[1].(rel.Array), args[0]) + return arrayContains(args[1].(rel.Array), args[0]) case rel.Bytes: return rel.NewBool(strings.Contains(args[1].String(), args[0].String())) } @@ -82,7 +82,7 @@ func stdSeq() rel.Attr { case rel.String: return rel.NewBool(strings.HasPrefix(mustAsString(args[1]), mustAsString(args[0]))) case rel.Array: - return ArrayPrefix(args[1].(rel.Array), args[0]) + return arrayPrefix(args[1].(rel.Array), args[0]) case rel.Bytes: return rel.NewBool(strings.HasPrefix(args[1].String(), args[0].String())) } @@ -94,7 +94,7 @@ func stdSeq() rel.Attr { case rel.String: return rel.NewBool(strings.HasSuffix(mustAsString(args[1]), mustAsString(args[0]))) case rel.Array: - return ArraySuffix(args[1].(rel.Array), args[0]) + return arraySuffix(args[1].(rel.Array), args[0]) case rel.Bytes: return rel.NewBool(strings.HasSuffix(args[1].String(), args[0].String())) } @@ -114,7 +114,7 @@ func stdSeq() rel.Attr { ), ) case rel.Array: - return ArraySub(args[2].(rel.Array), args[0], args[1]) + return arraySub(args[2].(rel.Array), args[0], args[1]) case rel.Bytes: return rel.NewBytes([]byte(strings.ReplaceAll(args[2].String(), args[0].String(), args[1].String()))) } @@ -133,9 +133,9 @@ func stdSeq() rel.Attr { } return rel.NewArray(vals...) case rel.Array: - return ArraySplit(args[1].(rel.Array), args[0]) + return arraySplit(args[1].(rel.Array), args[0]) case rel.Bytes: - return BytesSplit(args[1].(rel.Bytes), args[0]) + return bytesSplit(args[1].(rel.Bytes), args[0]) } panic(fmt.Errorf("expected subject sequence types are %s, %s and %s, but the actual type is %s", @@ -149,10 +149,10 @@ func stdSeq() rel.Attr { case rel.String: return strJoin(args...) case rel.Value: - return ArrayJoin(a1, args[0]) + return arrayJoin(a1, args[0]) } case rel.Bytes: - return BytesJoin(args[1].(rel.Bytes), args[0].(rel.Bytes)) + return bytesJoin(args[1].(rel.Bytes), args[0].(rel.Bytes)) } panic(fmt.Errorf("expected subject sequence types are %s and %s, but the actual type is %s", diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index 7dd04d5b..6a6b1998 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -4,14 +4,14 @@ import ( "github.com/arr-ai/arrai/rel" ) -// ArrayContains check if array a contains b, and b can be rel.Value or rel.Array. -func ArrayContains(a rel.Array, b rel.Value) rel.Value { +// arrayContains check if array a contains b, and b can be rel.Value or rel.Array. +func arrayContains(a rel.Array, b rel.Value) rel.Value { bArray := convert2Array(b) return rel.NewBool(indexSubArray(a.Values(), bArray.Values()) > -1) } -// ArraySub substitutes all old in a with new. -func ArraySub(a rel.Array, old, new rel.Value) rel.Value { +// arraySub substitutes all old in a with new. +func arraySub(a rel.Array, old, new rel.Value) rel.Value { // Convert to array to facilitate process oldArray := convert2Array(old) newArray := convert2Array(new) @@ -35,12 +35,12 @@ func ArraySub(a rel.Array, old, new rel.Value) rel.Value { return rel.NewArray(finalVals...) } -// ArraySplit split a by b. -func ArraySplit(a rel.Array, b rel.Value) rel.Value { +// arraySplit split a by b. +func arraySplit(a rel.Array, b rel.Value) rel.Value { delimiter := convert2Array(b) var result []rel.Value - if delimiter.Count() == 0 { + if !delimiter.IsTrue() { for _, e := range a.Values() { result = append(result, rel.NewArray(e)) } @@ -65,8 +65,8 @@ func ArraySplit(a rel.Array, b rel.Value) rel.Value { return rel.NewArray(result...) } -// ArrayJoin joins array b to a, b is joiner and a is joinee. -func ArrayJoin(a rel.Array, b rel.Value) rel.Value { +// arrayJoin joins array b to a, b is joiner and a is joinee. +func arrayJoin(a rel.Array, b rel.Value) rel.Value { joiner := convert2Array(b) if joiner.Count() == 0 || a.Count() == 0 { // if joinee is empty, the final value will be empty @@ -90,8 +90,8 @@ func ArrayJoin(a rel.Array, b rel.Value) rel.Value { return rel.NewArray(vals...) } -// ArrayPrefix check if a starts with b. -func ArrayPrefix(a rel.Array, b rel.Value) rel.Value { +// arrayPrefix check if a starts with b. +func arrayPrefix(a rel.Array, b rel.Value) rel.Value { bArray := convert2Array(b) if bArray.Count() == 0 { @@ -118,8 +118,8 @@ func ArrayPrefix(a rel.Array, b rel.Value) rel.Value { return rel.NewBool(true) } -// ArraySuffix check if a starts with b. -func ArraySuffix(a rel.Array, b rel.Value) rel.Value { +// arraySuffix check if a ends with b. +func arraySuffix(a rel.Array, b rel.Value) rel.Value { bArray := convert2Array(b) if bArray.Count() == 0 { diff --git a/syntax/std_seq_bytes_helper.go b/syntax/std_seq_bytes_helper.go index 2cc0af36..0dc2b1bc 100644 --- a/syntax/std_seq_bytes_helper.go +++ b/syntax/std_seq_bytes_helper.go @@ -6,8 +6,8 @@ import ( "github.com/arr-ai/arrai/rel" ) -// BytesJoin join b to a -func BytesJoin(a, b rel.Bytes) rel.Value { +// bytesJoin join b to a +func bytesJoin(a, b rel.Bytes) rel.Value { result := make([]byte, 0, a.Count()) for index, e := range a.Bytes() { if index > 0 && index < a.Count() { @@ -19,8 +19,8 @@ func BytesJoin(a, b rel.Bytes) rel.Value { return rel.NewBytes(result) } -// BytesSplit split a by b -func BytesSplit(a rel.Bytes, b rel.Value) rel.Value { +// bytesSplit split a by b +func bytesSplit(a rel.Bytes, b rel.Value) rel.Value { var splitted []string switch b := b.(type) { From 8708e894d89cb26f5fceaf413b4acf5731b4d47b Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 18 May 2020 11:07:55 +0800 Subject: [PATCH 053/106] #234 Updated method signature as review comments. --- syntax/std_seq_array_helper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index 6a6b1998..feefb06f 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -40,7 +40,7 @@ func arraySplit(a rel.Array, b rel.Value) rel.Value { delimiter := convert2Array(b) var result []rel.Value - if !delimiter.IsTrue() { + if delimiter.Count() == 0 { for _, e := range a.Values() { result = append(result, rel.NewArray(e)) } From 3dfdf424e23bd8a438e0ec91d5ea1b9a61c41269 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 18 May 2020 15:27:23 +0800 Subject: [PATCH 054/106] #234 Updated code to make variables are meaningful as review comment. --- syntax/std_seq.go | 6 +- syntax/std_seq_array_helper.go | 141 +++++++++++++++++---------------- syntax/std_seq_bytes_helper.go | 28 +++---- 3 files changed, 88 insertions(+), 87 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index a950a758..140ea151 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -70,7 +70,7 @@ func stdSeq() rel.Attr { case rel.String: return rel.NewBool(strings.Contains(mustAsString(args[1]), mustAsString(args[0]))) case rel.Array: - return arrayContains(args[1].(rel.Array), args[0]) + return arrayContain(args[1].(rel.Array), args[0]) case rel.Bytes: return rel.NewBool(strings.Contains(args[1].String(), args[0].String())) } @@ -82,7 +82,7 @@ func stdSeq() rel.Attr { case rel.String: return rel.NewBool(strings.HasPrefix(mustAsString(args[1]), mustAsString(args[0]))) case rel.Array: - return arrayPrefix(args[1].(rel.Array), args[0]) + return arrayHasPrefix(args[1].(rel.Array), args[0]) case rel.Bytes: return rel.NewBool(strings.HasPrefix(args[1].String(), args[0].String())) } @@ -94,7 +94,7 @@ func stdSeq() rel.Attr { case rel.String: return rel.NewBool(strings.HasSuffix(mustAsString(args[1]), mustAsString(args[0]))) case rel.Array: - return arraySuffix(args[1].(rel.Array), args[0]) + return arrayHasSuffix(args[1].(rel.Array), args[0]) case rel.Bytes: return rel.NewBool(strings.HasSuffix(args[1].String(), args[0].String())) } diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index feefb06f..3052fcfc 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -4,58 +4,58 @@ import ( "github.com/arr-ai/arrai/rel" ) -// arrayContains check if array a contains b, and b can be rel.Value or rel.Array. -func arrayContains(a rel.Array, b rel.Value) rel.Value { - bArray := convert2Array(b) - return rel.NewBool(indexSubArray(a.Values(), bArray.Values()) > -1) +// Checks if array subject contains sub. +func arrayContain(subject rel.Array, sub rel.Value) rel.Value { + subArray := convert2Array(sub) + return rel.NewBool(search(subject.Values(), subArray.Values()) > -1) } -// arraySub substitutes all old in a with new. -func arraySub(a rel.Array, old, new rel.Value) rel.Value { +// Substitutes all old in subject with new. +func arraySub(subject rel.Array, old, new rel.Value) rel.Value { // Convert to array to facilitate process oldArray := convert2Array(old) newArray := convert2Array(new) - finalVals := make([]rel.Value, 0, a.Count()) - for start, absoluteIndex := 0, 0; start < a.Count(); { - relativeIndex := indexSubArray(a.Values()[start:], oldArray.Values()) + result := make([]rel.Value, 0, subject.Count()) + for start, absoluteIndex := 0, 0; start < subject.Count(); { + relativeIndex := search(subject.Values()[start:], oldArray.Values()) if relativeIndex >= 0 { absoluteIndex = relativeIndex + start if absoluteIndex-start > 0 { - finalVals = append(finalVals, a.Values()[start:absoluteIndex]...) + result = append(result, subject.Values()[start:absoluteIndex]...) } - finalVals = append(finalVals, newArray.Values()...) + result = append(result, newArray.Values()...) start = absoluteIndex + oldArray.Count() } else { - finalVals = append(finalVals, a.Values()[absoluteIndex+1:]...) + result = append(result, subject.Values()[absoluteIndex+1:]...) break } } - return rel.NewArray(finalVals...) + return rel.NewArray(result...) } -// arraySplit split a by b. -func arraySplit(a rel.Array, b rel.Value) rel.Value { - delimiter := convert2Array(b) +// Splits array subject by delimiter. +func arraySplit(subject rel.Array, delimiter rel.Value) rel.Value { + delimiterArray := convert2Array(delimiter) var result []rel.Value - if delimiter.Count() == 0 { - for _, e := range a.Values() { + if delimiterArray.Count() == 0 { + for _, e := range subject.Values() { result = append(result, rel.NewArray(e)) } } else { - for start, absoluteIndex := 0, 0; start < a.Count(); { - relativeIndex := indexSubArray(a.Values()[start:], delimiter.Values()) + for start, absoluteIndex := 0, 0; start < subject.Count(); { + relativeIndex := search(subject.Values()[start:], delimiterArray.Values()) if relativeIndex >= 0 { absoluteIndex = relativeIndex + start if start != absoluteIndex { - result = append(result, rel.NewArray(a.Values()[start:absoluteIndex]...)) + result = append(result, rel.NewArray(subject.Values()[start:absoluteIndex]...)) } - start = absoluteIndex + delimiter.Count() + start = absoluteIndex + delimiterArray.Count() } else { - if start == 0 || start < a.Count() { - result = append(result, rel.NewArray(a.Values()[start:]...)) + if start == 0 || start < subject.Count() { + result = append(result, rel.NewArray(subject.Values()[start:]...)) } break } @@ -65,49 +65,49 @@ func arraySplit(a rel.Array, b rel.Value) rel.Value { return rel.NewArray(result...) } -// arrayJoin joins array b to a, b is joiner and a is joinee. -func arrayJoin(a rel.Array, b rel.Value) rel.Value { - joiner := convert2Array(b) - if joiner.Count() == 0 || a.Count() == 0 { +// Joins array joiner to subject. +func arrayJoin(subject rel.Array, joiner rel.Value) rel.Value { + joinerArray := convert2Array(joiner) + if joinerArray.Count() == 0 || subject.Count() == 0 { // if joinee is empty, the final value will be empty - return a + return subject } - vals := make([]rel.Value, 0, a.Count()) - for i, value := range a.Values() { + result := make([]rel.Value, 0, subject.Count()) + for i, value := range subject.Values() { switch vArray := value.(type) { case rel.Array: - vals = append(vals, generate1LevelArray(vArray)...) + result = append(result, generate1LevelArray(vArray)...) case rel.Value: - vals = append(vals, value) + result = append(result, value) } - if i+1 < a.Count() { - vals = append(vals, generate1LevelArray(joiner)...) + if i+1 < subject.Count() { + result = append(result, generate1LevelArray(joinerArray)...) } } - return rel.NewArray(vals...) + return rel.NewArray(result...) } -// arrayPrefix check if a starts with b. -func arrayPrefix(a rel.Array, b rel.Value) rel.Value { - bArray := convert2Array(b) +// Check if array subject starts with prefix. +func arrayHasPrefix(subject rel.Array, prefix rel.Value) rel.Value { + prefixArray := convert2Array(prefix) - if bArray.Count() == 0 { + if prefixArray.Count() == 0 { return rel.NewBool(false) } - if a.Count() < bArray.Count() { + if subject.Count() < prefixArray.Count() { return rel.NewBool(false) } - bVals := bArray.Values() - bOffset := 0 - arrayEnum, _ := a.ArrayEnumerator() + prefixVals := prefixArray.Values() + prefixOffset := 0 + arrayEnum, _ := subject.ArrayEnumerator() for arrayEnum.MoveNext() { - if bOffset < bArray.Count() && arrayEnum.Current().Equal(bVals[bOffset]) { - bOffset++ - if bOffset == bArray.Count() { + if prefixOffset < prefixArray.Count() && arrayEnum.Current().Equal(prefixVals[prefixOffset]) { + prefixOffset++ + if prefixOffset == prefixArray.Count() { break } } else { @@ -118,25 +118,25 @@ func arrayPrefix(a rel.Array, b rel.Value) rel.Value { return rel.NewBool(true) } -// arraySuffix check if a ends with b. -func arraySuffix(a rel.Array, b rel.Value) rel.Value { - bArray := convert2Array(b) +// Check if array subject ends with suffix. +func arrayHasSuffix(subject rel.Array, suffix rel.Value) rel.Value { + suffixArray := convert2Array(suffix) - if bArray.Count() == 0 { + if suffixArray.Count() == 0 { return rel.NewBool(false) } - if a.Count() < bArray.Count() { + if subject.Count() < suffixArray.Count() { return rel.NewBool(false) } - aVals := a.Values() - bVals := bArray.Values() - bOffset := bArray.Count() - 1 + subjectVals := subject.Values() + suffixVals := suffixArray.Values() + suffixOffset := suffixArray.Count() - 1 - for _, val := range aVals[a.Count()-1:] { - if bOffset > -1 && val.Equal(bVals[bOffset]) { - bOffset-- - if bOffset == -1 { + for _, val := range subjectVals[subject.Count()-1:] { + if suffixOffset > -1 && val.Equal(suffixVals[suffixOffset]) { + suffixOffset-- + if suffixOffset == -1 { break } } else { @@ -162,26 +162,27 @@ func convert2Array(val rel.Value) rel.Array { panic("it supports types rel.Array, rel.GenericSet and rel.Value only.") } +// Searches array sub in subject and return the first indedx if found, or return -1. // It is brute force approach, can be improved later if it is necessary. -func indexSubArray(a, b []rel.Value) int { - aOffset, bOffset := 0, 0 +func search(subject, sub []rel.Value) int { + subjectOffset, subOffset := 0, 0 - for ; aOffset < len(a); aOffset++ { - if bOffset < len(b) && a[aOffset].Equal(b[bOffset]) { - bOffset++ + for ; subjectOffset < len(subject); subjectOffset++ { + if subOffset < len(sub) && subject[subjectOffset].Equal(sub[subOffset]) { + subOffset++ } else { - if bOffset > 0 && bOffset < len(b) { - bOffset = 0 - aOffset-- + if subOffset > 0 && subOffset < len(sub) { + subOffset = 0 + subjectOffset-- } } - if bOffset == len(b) { + if subOffset == len(sub) { break } } - if aOffset < len(a) { - return aOffset + if subjectOffset < len(subject) { + return subjectOffset } return -1 } diff --git a/syntax/std_seq_bytes_helper.go b/syntax/std_seq_bytes_helper.go index 0dc2b1bc..f9f4501b 100644 --- a/syntax/std_seq_bytes_helper.go +++ b/syntax/std_seq_bytes_helper.go @@ -6,12 +6,12 @@ import ( "github.com/arr-ai/arrai/rel" ) -// bytesJoin join b to a -func bytesJoin(a, b rel.Bytes) rel.Value { - result := make([]byte, 0, a.Count()) - for index, e := range a.Bytes() { - if index > 0 && index < a.Count() { - result = append(result, b.Bytes()...) +// Joins byte array joiner to subject. +func bytesJoin(subject, joiner rel.Bytes) rel.Value { + result := make([]byte, 0, subject.Count()) + for index, e := range subject.Bytes() { + if index > 0 && index < subject.Count() { + result = append(result, joiner.Bytes()...) } result = append(result, e) } @@ -19,20 +19,20 @@ func bytesJoin(a, b rel.Bytes) rel.Value { return rel.NewBytes(result) } -// bytesSplit split a by b -func bytesSplit(a rel.Bytes, b rel.Value) rel.Value { +// Splits byte array subject by delimiter. +func bytesSplit(subject rel.Bytes, delimiter rel.Value) rel.Value { var splitted []string - switch b := b.(type) { + switch delimiter := delimiter.(type) { case rel.Bytes: - splitted = strings.Split(a.String(), b.String()) + splitted = strings.Split(subject.String(), delimiter.String()) case rel.GenericSet: - splitted = strings.Split(a.String(), mustAsString(b)) + splitted = strings.Split(subject.String(), mustAsString(delimiter)) } - vals := make([]rel.Value, 0, len(splitted)) + result := make([]rel.Value, 0, len(splitted)) for _, s := range splitted { - vals = append(vals, rel.NewBytes([]byte(s)).(rel.Value)) + result = append(result, rel.NewBytes([]byte(s)).(rel.Value)) } - return rel.NewArray(vals...) + return rel.NewArray(result...) } From 9e12034a59ea64b254cbd69535ead9077da553cc Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 18 May 2020 15:34:14 +0800 Subject: [PATCH 055/106] #234 Updated code to user IsTrue as review comment. --- rel/value_set_array.go | 4 ++-- syntax/std_seq_array_helper.go | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/rel/value_set_array.go b/rel/value_set_array.go index 2ae8c957..bde56167 100644 --- a/rel/value_set_array.go +++ b/rel/value_set_array.go @@ -141,10 +141,10 @@ func (a Array) Kind() int { return arrayKind } -// Bool returns true iff the tuple has attributes. +// IsTrue returns true if the tuple has attributes. func (a Array) IsTrue() bool { if len(a.values) == 0 { - panic("Empty array not allowed (should be == None)") + return false } return true } diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index 3052fcfc..e9d0f62f 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -40,7 +40,7 @@ func arraySplit(subject rel.Array, delimiter rel.Value) rel.Value { delimiterArray := convert2Array(delimiter) var result []rel.Value - if delimiterArray.Count() == 0 { + if !delimiterArray.IsTrue() { for _, e := range subject.Values() { result = append(result, rel.NewArray(e)) } @@ -68,8 +68,7 @@ func arraySplit(subject rel.Array, delimiter rel.Value) rel.Value { // Joins array joiner to subject. func arrayJoin(subject rel.Array, joiner rel.Value) rel.Value { joinerArray := convert2Array(joiner) - if joinerArray.Count() == 0 || subject.Count() == 0 { - // if joinee is empty, the final value will be empty + if !joinerArray.IsTrue() || !subject.IsTrue() { return subject } @@ -94,7 +93,7 @@ func arrayJoin(subject rel.Array, joiner rel.Value) rel.Value { func arrayHasPrefix(subject rel.Array, prefix rel.Value) rel.Value { prefixArray := convert2Array(prefix) - if prefixArray.Count() == 0 { + if !prefixArray.IsTrue() { return rel.NewBool(false) } if subject.Count() < prefixArray.Count() { @@ -122,7 +121,7 @@ func arrayHasPrefix(subject rel.Array, prefix rel.Value) rel.Value { func arrayHasSuffix(subject rel.Array, suffix rel.Value) rel.Value { suffixArray := convert2Array(suffix) - if suffixArray.Count() == 0 { + if !suffixArray.IsTrue() { return rel.NewBool(false) } if subject.Count() < suffixArray.Count() { @@ -189,7 +188,7 @@ func search(subject, sub []rel.Value) int { // Convert [[1, 2],[3, 4]] to [1, 2, 3, 4] func generate1LevelArray(source rel.Array) []rel.Value { - if source.Count() == 0 { + if !source.IsTrue() { return nil } From 71601ff11f4d51278e8dfceef0cdf6640f05c4a3 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 18 May 2020 15:38:22 +0800 Subject: [PATCH 056/106] #234 Updated code to user IsTrue as review comment. --- rel/value_set_array.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rel/value_set_array.go b/rel/value_set_array.go index bde56167..f4c4cc02 100644 --- a/rel/value_set_array.go +++ b/rel/value_set_array.go @@ -143,10 +143,7 @@ func (a Array) Kind() int { // IsTrue returns true if the tuple has attributes. func (a Array) IsTrue() bool { - if len(a.values) == 0 { - return false - } - return true + return len(a.values) > 0 } // Less returns true iff v is not a number or tuple, or v is a tuple and t From 3e36ad5a3204831bd0e3850012d2c26c0ad1cb81 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 18 May 2020 15:58:34 +0800 Subject: [PATCH 057/106] #234 Updated code as review comment. --- docs/std-seq.md | 2 +- syntax/std_seq.go | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/std-seq.md b/docs/std-seq.md index d4ff69d5..e5cbc5d7 100644 --- a/docs/std-seq.md +++ b/docs/std-seq.md @@ -1,6 +1,6 @@ # seq -The `seq` library contains functions that are used for string, array and bytes manipulations. +The `seq` library contains functions that are used for manipulating sequenced data structures. ## `//seq.concat(seqs <: array) <: array`
`concat(seqs <: string) <: string` diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 140ea151..8afb8222 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -119,9 +119,7 @@ func stdSeq() rel.Attr { return rel.NewBytes([]byte(strings.ReplaceAll(args[2].String(), args[0].String(), args[1].String()))) } - panic(fmt.Errorf("expected subject sequence types are %s, %s and %s, but the actual type is %s", - reflect.TypeOf(rel.String{}), reflect.TypeOf(rel.Array{}), reflect.TypeOf(rel.Bytes{}), - reflect.TypeOf(args[2]))) + panic(fmt.Errorf(sharedError, reflect.TypeOf(args[2]))) }), createNestedFuncAttr("split", 2, func(args ...rel.Value) rel.Value { switch args[1].(type) { @@ -155,9 +153,7 @@ func stdSeq() rel.Attr { return bytesJoin(args[1].(rel.Bytes), args[0].(rel.Bytes)) } - panic(fmt.Errorf("expected subject sequence types are %s and %s, but the actual type is %s", - reflect.TypeOf(rel.Array{}), reflect.TypeOf(rel.Bytes{}), - reflect.TypeOf(args[2]))) + panic(fmt.Errorf(sharedError, reflect.TypeOf(args[2]))) }), ) } @@ -170,3 +166,5 @@ func strJoin(args ...rel.Value) rel.Value { } return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[0])))) } + +var sharedError = "expected subject sequence types are rel.String, rel.Array and rel.Bytes, but the actual type is %s" From 93f148aa964bd3e61f358e446e1dba1e625e56c0 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 18 May 2020 16:24:10 +0800 Subject: [PATCH 058/106] #234 Removed code flattern the array. --- syntax/std_seq_array_helper.go | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index e9d0f62f..3cdb0f8f 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -76,13 +76,13 @@ func arrayJoin(subject rel.Array, joiner rel.Value) rel.Value { for i, value := range subject.Values() { switch vArray := value.(type) { case rel.Array: - result = append(result, generate1LevelArray(vArray)...) + result = append(result, vArray.Values()...) case rel.Value: result = append(result, value) } if i+1 < subject.Count() { - result = append(result, generate1LevelArray(joinerArray)...) + result = append(result, joinerArray.Values()...) } } @@ -185,22 +185,3 @@ func search(subject, sub []rel.Value) int { } return -1 } - -// Convert [[1, 2],[3, 4]] to [1, 2, 3, 4] -func generate1LevelArray(source rel.Array) []rel.Value { - if !source.IsTrue() { - return nil - } - - finalArray := make([]rel.Value, 0, source.Count()) - for _, val := range source.Values() { - switch rVal := val.(type) { - case rel.Array: - finalArray = append(finalArray, generate1LevelArray(rVal)...) - case rel.Value: - finalArray = append(finalArray, rVal) - } - } - - return finalArray -} From 6def4243c9c6b8e63547199ad61dfa2b8dad8ee4 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 18 May 2020 16:32:34 +0800 Subject: [PATCH 059/106] #234 Removed code flattern the array. --- syntax/std_seq_array_helper.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index 3cdb0f8f..fb66a9fd 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -74,16 +74,15 @@ func arrayJoin(subject rel.Array, joiner rel.Value) rel.Value { result := make([]rel.Value, 0, subject.Count()) for i, value := range subject.Values() { + if i > 0 { + result = append(result, joinerArray.Values()...) + } switch vArray := value.(type) { case rel.Array: result = append(result, vArray.Values()...) case rel.Value: result = append(result, value) } - - if i+1 < subject.Count() { - result = append(result, joinerArray.Values()...) - } } return rel.NewArray(result...) From 780647ec3e85c464ea9dc2f57af7a849115a2677 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 18 May 2020 16:51:24 +0800 Subject: [PATCH 060/106] #234 Changed test case as it is not required to flatten array. --- syntax/std_seq_join_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/syntax/std_seq_join_test.go b/syntax/std_seq_join_test.go index 4bc89f2e..47f160bd 100644 --- a/syntax/std_seq_join_test.go +++ b/syntax/std_seq_join_test.go @@ -26,14 +26,15 @@ func TestArrayJoin(t *testing.T) { AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) AssertCodesEvalToSameValue(t, `[1,0,2,0,3,0,4,0,5]`, `//seq.join([0], [1,2,3,4,5])`) AssertCodesEvalToSameValue(t, `[1, 2, 0, 3, 4, 0, 5, 6]`, `//seq.join([0], [[1, 2], [3, 4], [5, 6]])`) + AssertCodesEvalToSameValue(t, `[2, [3, 4], 0, 5, 6]`, `//seq.join([0], [[2, [3, 4]], [5, 6]])`) AssertCodesEvalToSameValue(t, `[1, 2, 10, 11, 3, 4, 10, 11, 5, 6]`, `//seq.join([10,11], [[1, 2], [3, 4], [5, 6]])`) - AssertCodesEvalToSameValue(t, `[1, 2, 3, 4, 0, 5, 6, 7, 8]`, + AssertCodesEvalToSameValue(t, `[[1, 2], [3, 4], 0, [5, 6], [7, 8]]`, `//seq.join([0], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])`) - AssertCodesEvalToSameValue(t, `[1, 2, 10, 11, 3, 4, 10, 11, 5, 6]`, + AssertCodesEvalToSameValue(t, `[1, 2, [10], [11], 3, 4, [10], [11], 5, 6]`, `//seq.join([[10],[11]], [[1, 2], [3, 4], [5, 6]])`) - AssertCodesEvalToSameValue(t, `[1, 2, 3, 4, 0, 1, 5, 6, 7, 8]`, + AssertCodesEvalToSameValue(t, `[[1, 2], [3, 4], [0], [1], [5, 6], [7, 8]]`, `//seq.join([[0],[1]], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])`) // Following cases are not supported to make sure code is clear and simple. From a9fc5c20d2bfaf4adb965ba0c8806973ffd7a2cd Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 18 May 2020 17:52:48 +0800 Subject: [PATCH 061/106] #234 Added more empty params cases. --- syntax/std_seq.go | 12 ++++++++++++ syntax/std_seq_array_helper.go | 4 ++-- syntax/std_seq_contains_test.go | 9 +++++++++ syntax/std_seq_prefix_test.go | 15 +++++++++++++-- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 8afb8222..0a65c9c3 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -84,6 +84,12 @@ func stdSeq() rel.Attr { case rel.Array: return arrayHasPrefix(args[1].(rel.Array), args[0]) case rel.Bytes: + switch args[0].(type) { + case rel.GenericSet: + if len(args[1].String()) > 0 { + return rel.NewBool(true) + } + } return rel.NewBool(strings.HasPrefix(args[1].String(), args[0].String())) } @@ -96,6 +102,12 @@ func stdSeq() rel.Attr { case rel.Array: return arrayHasSuffix(args[1].(rel.Array), args[0]) case rel.Bytes: + switch args[0].(type) { + case rel.GenericSet: + if len(args[1].String()) > 0 { + return rel.NewBool(true) + } + } return rel.NewBool(strings.HasSuffix(args[1].String(), args[0].String())) } diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index fb66a9fd..e5b0fef2 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -92,8 +92,8 @@ func arrayJoin(subject rel.Array, joiner rel.Value) rel.Value { func arrayHasPrefix(subject rel.Array, prefix rel.Value) rel.Value { prefixArray := convert2Array(prefix) - if !prefixArray.IsTrue() { - return rel.NewBool(false) + if !prefixArray.IsTrue() && subject.IsTrue() { + return rel.NewBool(true) } if subject.Count() < prefixArray.Count() { return rel.NewBool(false) diff --git a/syntax/std_seq_contains_test.go b/syntax/std_seq_contains_test.go index 8c1df4f6..e5e6dcca 100644 --- a/syntax/std_seq_contains_test.go +++ b/syntax/std_seq_contains_test.go @@ -4,6 +4,10 @@ import "testing" func TestStrContains(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.contains("", "A")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains("", "")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains("A", "")`) + AssertCodesEvalToSameValue(t, `true `, `//seq.contains("", "this is a test") `) AssertCodesEvalToSameValue(t, `true `, `//seq.contains("is a test", "this is a test") `) AssertCodesEvalToSameValue(t, `false`, `//seq.contains("is not a test", "this is a test")`) @@ -12,6 +16,11 @@ func TestStrContains(t *testing.T) { func TestArrayContains(t *testing.T) { t.Parallel() + + AssertCodesEvalToSameValue(t, `false`, `//seq.contains(1, [])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains([], [])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains([], [1])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(1, [1,2,3,4,5])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(3, [1,2,3,4,5])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(5, [1,2,3,4,5])`) diff --git a/syntax/std_seq_prefix_test.go b/syntax/std_seq_prefix_test.go index ab679cc3..081b7329 100644 --- a/syntax/std_seq_prefix_test.go +++ b/syntax/std_seq_prefix_test.go @@ -8,6 +8,12 @@ func TestStrPrefix(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("AB","ABCDE")`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("BCD","ABCDE")`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("CD","ABCDE")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("CD","")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("","ABCD")`) + + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("","")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("A","")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("","A")`) } func TestArrayPrefix(t *testing.T) { @@ -16,13 +22,14 @@ func TestArrayPrefix(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C','D'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix([],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix([], [])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix('B',['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['B'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['B','C'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) + + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix([], [])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],[])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix([],['A','B','C','D','E'])`) } func TestBytesPrefix(t *testing.T) { @@ -31,4 +38,8 @@ func TestBytesPrefix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('e'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('l'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('o'),//unicode.utf8.encode('hello'))`) + + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode(''),//unicode.utf8.encode(''))`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('o'),//unicode.utf8.encode(''))`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode(''),//unicode.utf8.encode('hello'))`) } From 77844ce34c0e6cb25baecab76b1f997135707cdf Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 18 May 2020 23:03:37 +0800 Subject: [PATCH 062/106] #234 Added more test cases. --- syntax/std_seq.go | 6 ++++++ syntax/std_seq_array_helper.go | 4 ++-- syntax/std_seq_contains_test.go | 7 +++++++ syntax/std_seq_prefix_test.go | 3 +++ syntax/std_seq_suffix_test.go | 19 ++++++++++++++++--- 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 0a65c9c3..8008ffe4 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -72,6 +72,12 @@ func stdSeq() rel.Attr { case rel.Array: return arrayContain(args[1].(rel.Array), args[0]) case rel.Bytes: + switch args[0].(type) { + case rel.GenericSet: + if len(args[1].String()) > 0 { + return rel.NewBool(true) + } + } return rel.NewBool(strings.Contains(args[1].String(), args[0].String())) } diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index e5b0fef2..87e2a331 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -120,8 +120,8 @@ func arrayHasPrefix(subject rel.Array, prefix rel.Value) rel.Value { func arrayHasSuffix(subject rel.Array, suffix rel.Value) rel.Value { suffixArray := convert2Array(suffix) - if !suffixArray.IsTrue() { - return rel.NewBool(false) + if !suffixArray.IsTrue() && subject.IsTrue() { + return rel.NewBool(true) } if subject.Count() < suffixArray.Count() { return rel.NewBool(false) diff --git a/syntax/std_seq_contains_test.go b/syntax/std_seq_contains_test.go index e5e6dcca..3b5c6083 100644 --- a/syntax/std_seq_contains_test.go +++ b/syntax/std_seq_contains_test.go @@ -57,4 +57,11 @@ func TestBytesContains(t *testing.T) { `//seq.contains({ |@, @byte| (0, 108),(0, 108)},//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(//unicode.utf8.encode('h'),//unicode.utf8.encode('hello'))`) + + AssertCodesEvalToSameValue(t, `false`, + `//seq.contains(//unicode.utf8.encode('A'),//unicode.utf8.encode(''))`) + AssertCodesEvalToSameValue(t, `false`, + `//seq.contains(//unicode.utf8.encode(''),//unicode.utf8.encode(''))`) + AssertCodesEvalToSameValue(t, `true`, + `//seq.contains(//unicode.utf8.encode(''),//unicode.utf8.encode('hello'))`) } diff --git a/syntax/std_seq_prefix_test.go b/syntax/std_seq_prefix_test.go index 081b7329..ade02b89 100644 --- a/syntax/std_seq_prefix_test.go +++ b/syntax/std_seq_prefix_test.go @@ -17,6 +17,7 @@ func TestStrPrefix(t *testing.T) { } func TestArrayPrefix(t *testing.T) { + t.Parallel() AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix('A',['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B'],['A','B','C','D','E'])`) @@ -33,6 +34,8 @@ func TestArrayPrefix(t *testing.T) { } func TestBytesPrefix(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('h'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('he'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('e'),//unicode.utf8.encode('hello'))`) diff --git a/syntax/std_seq_suffix_test.go b/syntax/std_seq_suffix_test.go index 94857932..1c613d2b 100644 --- a/syntax/std_seq_suffix_test.go +++ b/syntax/std_seq_suffix_test.go @@ -8,26 +8,39 @@ func TestStrSuffix(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("DE","ABCDE")`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("CD", "ABCDE")`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("D","ABCDE")`) + + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("D","")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("","")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("","ABCDE")`) } func TestArraySuffix(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix('E',['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(['E'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix( ['D','E'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix([],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix([], [])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['D','E'],[])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix('D',['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['D'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['C','D'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) + + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix([],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix([], [])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['D','E'],[])`) } func TestBytesSuffix(t *testing.T) { + t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode('o'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode('lo'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('e'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('ell'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('h'),//unicode.utf8.encode('hello'))`) + + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode(''),//unicode.utf8.encode(''))`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('o'),//unicode.utf8.encode(''))`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode(''),//unicode.utf8.encode('hello'))`) } From 61fbbc33c66f9f2244d6405f7b02756c3a00fe6b Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 00:15:03 +0800 Subject: [PATCH 063/106] #234 Added more test cases. --- syntax/std_seq.go | 9 +++++++++ syntax/std_seq_split_test.go | 33 +++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 8008ffe4..3d2706d4 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -152,6 +152,15 @@ func stdSeq() rel.Attr { return arraySplit(args[1].(rel.Array), args[0]) case rel.Bytes: return bytesSplit(args[1].(rel.Bytes), args[0]) + case rel.GenericSet: + switch args[0].(type) { + case rel.String: + return rel.NewArray(args[1]) + case rel.Array, rel.Bytes: + return rel.NewArray(rel.NewArray()) + case rel.GenericSet: + return args[1] + } } panic(fmt.Errorf("expected subject sequence types are %s, %s and %s, but the actual type is %s", diff --git a/syntax/std_seq_split_test.go b/syntax/std_seq_split_test.go index d788e99f..5c893149 100644 --- a/syntax/std_seq_split_test.go +++ b/syntax/std_seq_split_test.go @@ -4,20 +4,21 @@ import "testing" func TestStrSplit(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, - `["t", "h", "i", "s", " ", "i", "s", " ", "a", " ", "t", "e", "s", "t"]`, - `//seq.split("","this is a test")`) + AssertCodesEvalToSameValue(t, `["this", "is", "a", "test"]`, `//seq.split(" ","this is a test") `) AssertCodesEvalToSameValue(t, `["this is a test"] `, `//seq.split(",","this is a test") `) AssertCodesEvalToSameValue(t, `["th", " ", " a test"] `, `//seq.split("is","this is a test")`) assertExprPanics(t, `//seq.split(1, "this is a test")`) + + AssertCodesEvalToSameValue(t, + `["t", "h", "i", "s", " ", "i", "s", " ", "a", " ", "t", "e", "s", "t"]`, + `//seq.split("","this is a test")`) + AssertCodesEvalToSameValue(t, `""`, `//seq.split("","") `) + AssertCodesEvalToSameValue(t, `[""]`, `//seq.split(",","") `) } func TestArraySplit(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, - `[['A'],['B'],['A']]`, - `//seq.split([],['A', 'B', 'A'])`) AssertCodesEvalToSameValue(t, `[['B'],['C', 'D', 'E']]`, `//seq.split('A',['A', 'B', 'A', 'C', 'D', 'E'])`) @@ -43,6 +44,16 @@ func TestArraySplit(t *testing.T) { AssertCodesEvalToSameValue(t, `[[[1,2]], [[3,4]]]`, `//seq.split([],[[1,2], [3,4]])`) + + AssertCodesEvalToSameValue(t, + `[['A'],['B'],['A']]`, + `//seq.split([],['A', 'B', 'A'])`) + AssertCodesEvalToSameValue(t, + `[]`, + `//seq.split([],[])`) + AssertCodesEvalToSameValue(t, + `[[]]`, + `//seq.split(['A'],[])`) } func TestBytesSplit(t *testing.T) { @@ -58,4 +69,14 @@ func TestBytesSplit(t *testing.T) { AssertCodesEvalToSameValue(t, `[//unicode.utf8.encode("this is a test")]`, `//seq.split(//unicode.utf8.encode("get"),//unicode.utf8.encode("this is a test"))`) + + AssertCodesEvalToSameValue(t, + `//unicode.utf8.encode("")`, + `//seq.split(//unicode.utf8.encode(""),//unicode.utf8.encode(""))`) + AssertCodesEvalToSameValue(t, + `[//unicode.utf8.encode("A"),//unicode.utf8.encode("B"),//unicode.utf8.encode("C")]`, + `//seq.split(//unicode.utf8.encode(""),//unicode.utf8.encode("ABC"))`) + AssertCodesEvalToSameValue(t, + `[//unicode.utf8.encode("")]`, + `//seq.split(//unicode.utf8.encode(","),//unicode.utf8.encode(""))`) } From 7569d8ea0a627407d2455db193f4fff3b0a68ecc Mon Sep 17 00:00:00 2001 From: "Zhang, Eric" <59079351+ericzhang6222@users.noreply.github.com> Date: Tue, 19 May 2020 09:48:17 +0800 Subject: [PATCH 064/106] Update syntax/std_seq_array_helper.go Co-authored-by: Marcelo Cantos --- syntax/std_seq_array_helper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index 87e2a331..a5477e48 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -5,7 +5,7 @@ import ( ) // Checks if array subject contains sub. -func arrayContain(subject rel.Array, sub rel.Value) rel.Value { +func arrayContains(subject rel.Array, sub rel.Value) rel.Value { subArray := convert2Array(sub) return rel.NewBool(search(subject.Values(), subArray.Values()) > -1) } From 7d2575e4a0d447a1b3ecfe1f1b0ac54ba276c96d Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 09:51:30 +0800 Subject: [PATCH 065/106] #234 Updated method signature from arrayContain to arrayContains. --- syntax/std_seq.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 3d2706d4..5e2f56e7 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -70,7 +70,7 @@ func stdSeq() rel.Attr { case rel.String: return rel.NewBool(strings.Contains(mustAsString(args[1]), mustAsString(args[0]))) case rel.Array: - return arrayContain(args[1].(rel.Array), args[0]) + return arrayContains(args[1].(rel.Array), args[0]) case rel.Bytes: switch args[0].(type) { case rel.GenericSet: From 9a5114e6ff1cbe0ebef084d73a0a58b06376a81d Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 10:16:29 +0800 Subject: [PATCH 066/106] #234 Updated args order in helper go files to match arr.ai functions. --- syntax/std_seq.go | 16 ++++++++-------- syntax/std_seq_array_helper.go | 12 ++++++------ syntax/std_seq_bytes_helper.go | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 5e2f56e7..4d79fb11 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -70,7 +70,7 @@ func stdSeq() rel.Attr { case rel.String: return rel.NewBool(strings.Contains(mustAsString(args[1]), mustAsString(args[0]))) case rel.Array: - return arrayContains(args[1].(rel.Array), args[0]) + return arrayContains(args[0], args[1].(rel.Array)) case rel.Bytes: switch args[0].(type) { case rel.GenericSet: @@ -88,7 +88,7 @@ func stdSeq() rel.Attr { case rel.String: return rel.NewBool(strings.HasPrefix(mustAsString(args[1]), mustAsString(args[0]))) case rel.Array: - return arrayHasPrefix(args[1].(rel.Array), args[0]) + return arrayHasPrefix(args[0], args[1].(rel.Array)) case rel.Bytes: switch args[0].(type) { case rel.GenericSet: @@ -106,7 +106,7 @@ func stdSeq() rel.Attr { case rel.String: return rel.NewBool(strings.HasSuffix(mustAsString(args[1]), mustAsString(args[0]))) case rel.Array: - return arrayHasSuffix(args[1].(rel.Array), args[0]) + return arrayHasSuffix(args[0], args[1].(rel.Array)) case rel.Bytes: switch args[0].(type) { case rel.GenericSet: @@ -132,7 +132,7 @@ func stdSeq() rel.Attr { ), ) case rel.Array: - return arraySub(args[2].(rel.Array), args[0], args[1]) + return arraySub(args[0], args[1], args[2].(rel.Array)) case rel.Bytes: return rel.NewBytes([]byte(strings.ReplaceAll(args[2].String(), args[0].String(), args[1].String()))) } @@ -149,9 +149,9 @@ func stdSeq() rel.Attr { } return rel.NewArray(vals...) case rel.Array: - return arraySplit(args[1].(rel.Array), args[0]) + return arraySplit(args[0], args[1].(rel.Array)) case rel.Bytes: - return bytesSplit(args[1].(rel.Bytes), args[0]) + return bytesSplit(args[0], args[1].(rel.Bytes)) case rel.GenericSet: switch args[0].(type) { case rel.String: @@ -174,10 +174,10 @@ func stdSeq() rel.Attr { case rel.String: return strJoin(args...) case rel.Value: - return arrayJoin(a1, args[0]) + return arrayJoin(args[0], a1) } case rel.Bytes: - return bytesJoin(args[1].(rel.Bytes), args[0].(rel.Bytes)) + return bytesJoin(args[0].(rel.Bytes), args[1].(rel.Bytes)) } panic(fmt.Errorf(sharedError, reflect.TypeOf(args[2]))) diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index a5477e48..7420b759 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -5,13 +5,13 @@ import ( ) // Checks if array subject contains sub. -func arrayContains(subject rel.Array, sub rel.Value) rel.Value { +func arrayContains(sub rel.Value, subject rel.Array) rel.Value { subArray := convert2Array(sub) return rel.NewBool(search(subject.Values(), subArray.Values()) > -1) } // Substitutes all old in subject with new. -func arraySub(subject rel.Array, old, new rel.Value) rel.Value { +func arraySub(old, new rel.Value, subject rel.Array) rel.Value { // Convert to array to facilitate process oldArray := convert2Array(old) newArray := convert2Array(new) @@ -36,7 +36,7 @@ func arraySub(subject rel.Array, old, new rel.Value) rel.Value { } // Splits array subject by delimiter. -func arraySplit(subject rel.Array, delimiter rel.Value) rel.Value { +func arraySplit(delimiter rel.Value, subject rel.Array) rel.Value { delimiterArray := convert2Array(delimiter) var result []rel.Value @@ -66,7 +66,7 @@ func arraySplit(subject rel.Array, delimiter rel.Value) rel.Value { } // Joins array joiner to subject. -func arrayJoin(subject rel.Array, joiner rel.Value) rel.Value { +func arrayJoin(joiner rel.Value, subject rel.Array) rel.Value { joinerArray := convert2Array(joiner) if !joinerArray.IsTrue() || !subject.IsTrue() { return subject @@ -89,7 +89,7 @@ func arrayJoin(subject rel.Array, joiner rel.Value) rel.Value { } // Check if array subject starts with prefix. -func arrayHasPrefix(subject rel.Array, prefix rel.Value) rel.Value { +func arrayHasPrefix(prefix rel.Value, subject rel.Array) rel.Value { prefixArray := convert2Array(prefix) if !prefixArray.IsTrue() && subject.IsTrue() { @@ -117,7 +117,7 @@ func arrayHasPrefix(subject rel.Array, prefix rel.Value) rel.Value { } // Check if array subject ends with suffix. -func arrayHasSuffix(subject rel.Array, suffix rel.Value) rel.Value { +func arrayHasSuffix(suffix rel.Value, subject rel.Array) rel.Value { suffixArray := convert2Array(suffix) if !suffixArray.IsTrue() && subject.IsTrue() { diff --git a/syntax/std_seq_bytes_helper.go b/syntax/std_seq_bytes_helper.go index f9f4501b..f79f1c11 100644 --- a/syntax/std_seq_bytes_helper.go +++ b/syntax/std_seq_bytes_helper.go @@ -7,7 +7,7 @@ import ( ) // Joins byte array joiner to subject. -func bytesJoin(subject, joiner rel.Bytes) rel.Value { +func bytesJoin(joiner, subject rel.Bytes) rel.Value { result := make([]byte, 0, subject.Count()) for index, e := range subject.Bytes() { if index > 0 && index < subject.Count() { @@ -20,7 +20,7 @@ func bytesJoin(subject, joiner rel.Bytes) rel.Value { } // Splits byte array subject by delimiter. -func bytesSplit(subject rel.Bytes, delimiter rel.Value) rel.Value { +func bytesSplit(delimiter rel.Value, subject rel.Bytes) rel.Value { var splitted []string switch delimiter := delimiter.(type) { From 8be0447c729f5a9f43ce5903e09622797d281ea0 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 10:47:13 +0800 Subject: [PATCH 067/106] #234 Used if statement replacing switch. --- syntax/std_seq.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 4d79fb11..bd6d9310 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -72,8 +72,7 @@ func stdSeq() rel.Attr { case rel.Array: return arrayContains(args[0], args[1].(rel.Array)) case rel.Bytes: - switch args[0].(type) { - case rel.GenericSet: + if _, isSet := args[0].(rel.GenericSet); isSet { if len(args[1].String()) > 0 { return rel.NewBool(true) } @@ -90,8 +89,7 @@ func stdSeq() rel.Attr { case rel.Array: return arrayHasPrefix(args[0], args[1].(rel.Array)) case rel.Bytes: - switch args[0].(type) { - case rel.GenericSet: + if _, isSet := args[0].(rel.GenericSet); isSet { if len(args[1].String()) > 0 { return rel.NewBool(true) } @@ -108,8 +106,7 @@ func stdSeq() rel.Attr { case rel.Array: return arrayHasSuffix(args[0], args[1].(rel.Array)) case rel.Bytes: - switch args[0].(type) { - case rel.GenericSet: + if _, isSet := args[0].(rel.GenericSet); isSet { if len(args[1].String()) > 0 { return rel.NewBool(true) } From c7841d8428833d926f846043341efdfda3c00f3b Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 11:00:31 +0800 Subject: [PATCH 068/106] #234 Change test case result to [] for split. --- syntax/std_seq_split_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/syntax/std_seq_split_test.go b/syntax/std_seq_split_test.go index 5c893149..3355b98d 100644 --- a/syntax/std_seq_split_test.go +++ b/syntax/std_seq_split_test.go @@ -13,7 +13,12 @@ func TestStrSplit(t *testing.T) { AssertCodesEvalToSameValue(t, `["t", "h", "i", "s", " ", "i", "s", " ", "a", " ", "t", "e", "s", "t"]`, `//seq.split("","this is a test")`) - AssertCodesEvalToSameValue(t, `""`, `//seq.split("","") `) + + // As https://github.com/arr-ai/arrai/issues/268, `{}`, `[]` and `""` means empty set in arr.ai + // And the intent for //seq.split is to return an array, so it should be expressed as such. + // `""` -> empty string, `[]` -> empty array and `{}` -> empty set + AssertCodesEvalToSameValue(t, `[]`, `//seq.split("","") `) + AssertCodesEvalToSameValue(t, `[""]`, `//seq.split(",","") `) } From df83a23872d52165e594d7065d35df9a542de80c Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 14:22:33 +0800 Subject: [PATCH 069/106] #234 Added edge test case for sub API. --- syntax/std_seq.go | 22 +++++++++- syntax/std_seq_array_helper.go | 33 ++++++++++----- syntax/std_seq_sub_test.go | 74 ++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 12 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index bd6d9310..9d0cad97 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -131,7 +131,27 @@ func stdSeq() rel.Attr { case rel.Array: return arraySub(args[0], args[1], args[2].(rel.Array)) case rel.Bytes: - return rel.NewBytes([]byte(strings.ReplaceAll(args[2].String(), args[0].String(), args[1].String()))) + _, arg0IsSet := args[0].(rel.GenericSet) + _, arg1IsSet := args[1].(rel.GenericSet) + if !arg0IsSet && arg1IsSet { + return rel.NewBytes([]byte(strings.ReplaceAll(args[2].String(), + args[0].String(), ""))) + } else if arg0IsSet && !arg1IsSet { + return rel.NewBytes([]byte(strings.ReplaceAll(args[2].String(), + "", args[1].String()))) + } + return rel.NewBytes([]byte(strings.ReplaceAll(args[2].String(), + args[0].String(), args[1].String()))) + case rel.GenericSet: + _, arg0IsSet := args[0].(rel.GenericSet) + _, arg1IsSet := args[1].(rel.GenericSet) + if arg0IsSet && arg1IsSet { + return args[2] + } else if arg0IsSet && !arg1IsSet { + return args[1] + } else if !arg0IsSet && arg1IsSet { + return args[2] + } } panic(fmt.Errorf(sharedError, reflect.TypeOf(args[2]))) diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index 7420b759..fe024668 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -16,19 +16,30 @@ func arraySub(old, new rel.Value, subject rel.Array) rel.Value { oldArray := convert2Array(old) newArray := convert2Array(new) + if !oldArray.IsTrue() && !new.IsTrue() { + return subject + } + result := make([]rel.Value, 0, subject.Count()) - for start, absoluteIndex := 0, 0; start < subject.Count(); { - relativeIndex := search(subject.Values()[start:], oldArray.Values()) - if relativeIndex >= 0 { - absoluteIndex = relativeIndex + start - if absoluteIndex-start > 0 { - result = append(result, subject.Values()[start:absoluteIndex]...) + if !old.IsTrue() { + for _, e := range subject.Values() { + result = append(append(result, newArray.Values()...), e) + } + result = append(result, newArray.Values()...) + } else { + for start, absoluteIndex := 0, 0; start < subject.Count(); { + relativeIndex := search(subject.Values()[start:], oldArray.Values()) + if relativeIndex >= 0 { + absoluteIndex = relativeIndex + start + if absoluteIndex-start > 0 { + result = append(result, subject.Values()[start:absoluteIndex]...) + } + result = append(result, newArray.Values()...) + start = absoluteIndex + oldArray.Count() + } else { + result = append(result, subject.Values()[absoluteIndex+1:]...) + break } - result = append(result, newArray.Values()...) - start = absoluteIndex + oldArray.Count() - } else { - result = append(result, subject.Values()[absoluteIndex+1:]...) - break } } diff --git a/syntax/std_seq_sub_test.go b/syntax/std_seq_sub_test.go index dcfbddc3..5d5faca7 100644 --- a/syntax/std_seq_sub_test.go +++ b/syntax/std_seq_sub_test.go @@ -4,6 +4,9 @@ import "testing" func TestStrSub(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, + `" BC"`, + `//seq.sub( "A", " ","ABC")`) AssertCodesEvalToSameValue(t, `"this is not a test"`, `//seq.sub("aaa", "is", "this is not a test")`) @@ -20,6 +23,26 @@ func TestStrSub(t *testing.T) { `"this is still a test"`, `//seq.sub( "doesn't matter", "hello there","this is still a test")`) assertExprPanics(t, `//seq.sub("hello there", "test", 1)`) + ///////////////// + AssertCodesEvalToSameValue(t, + `""`, + `//seq.sub( "","", "")`) + AssertCodesEvalToSameValue(t, + `"A"`, + `//seq.sub( "","A", "")`) + AssertCodesEvalToSameValue(t, + `""`, + `//seq.sub( "A","", "")`) + + AssertCodesEvalToSameValue(t, + `"ABC"`, + `//seq.sub( "","", "ABC")`) + AssertCodesEvalToSameValue(t, + `"EAEBECE"`, + `//seq.sub( "", "E","ABC")`) + AssertCodesEvalToSameValue(t, + `"BC"`, + `//seq.sub( "A", "","ABC")`) } func TestArraySub(t *testing.T) { @@ -42,6 +65,42 @@ func TestArraySub(t *testing.T) { AssertCodesEvalToSameValue(t, `[[1,1], [4,4], [3,3]]`, `//seq.sub([[2,2]], [[4,4]], [[1,1], [2,2], [3,3]])`) + +} + +func TestArraySubEdgeCases(t *testing.T) { + ///////////////// + AssertCodesEvalToSameValue(t, + `[]`, + `//seq.sub( [],[], [])`) + AssertCodesEvalToSameValue(t, + `[1]`, + `//seq.sub( [],[1], [])`) + AssertCodesEvalToSameValue(t, + `[1,2]`, + `//seq.sub( [],[1,2], [])`) + AssertCodesEvalToSameValue(t, + `[[1,2]]`, + `//seq.sub( [],[[1,2]], [])`) + AssertCodesEvalToSameValue(t, + `[]`, + `//seq.sub( [1],[], [])`) + + AssertCodesEvalToSameValue(t, + `[1,2,3]`, + `//seq.sub( [],[], [1,2,3])`) + AssertCodesEvalToSameValue(t, + `[4,1,4,2,4,3,4]`, + `//seq.sub( [], [4],[1,2,3])`) + AssertCodesEvalToSameValue(t, + `[4,[1,2],4,[3,4],4]`, + `//seq.sub( [], [4],[[1,2],[3,4]])`) + AssertCodesEvalToSameValue(t, + `[[4],[1,2],[4],[3,4],[4]]`, + `//seq.sub( [], [[4]],[[1,2],[3,4]])`) + AssertCodesEvalToSameValue(t, + `[1,3]`, + `//seq.sub([2], [],[1,2,3])`) } func TestBytesSub(t *testing.T) { @@ -51,4 +110,19 @@ func TestBytesSub(t *testing.T) { `//seq.sub({ |@, @byte| (0, 104)},{ |@, @byte| (0, 111)},//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('hehho')`, `//seq.sub({ |@, @byte| (0, 108)},{ |@, @byte| (0, 104)},//unicode.utf8.encode('hello'))`) + /////////////////// + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('')`, + `//seq.sub(//unicode.utf8.encode(''),//unicode.utf8.encode(''),//unicode.utf8.encode(''))`) + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('')`, + `//seq.sub(//unicode.utf8.encode('a'),//unicode.utf8.encode(''),//unicode.utf8.encode(''))`) + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('a')`, + `//seq.sub(//unicode.utf8.encode(''),//unicode.utf8.encode('a'),//unicode.utf8.encode(''))`) + + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('hello')`, + `//seq.sub(//unicode.utf8.encode(''),//unicode.utf8.encode(''),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('ello')`, + `//seq.sub(//unicode.utf8.encode('h'),//unicode.utf8.encode(''),//unicode.utf8.encode('hello'))`) + + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('thtetltltot')`, + `//seq.sub(//unicode.utf8.encode(''),//unicode.utf8.encode('t'),//unicode.utf8.encode('hello'))`) } From f4cfb50d19dda09d3fdf30c77395152e603e6a95 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 14:35:28 +0800 Subject: [PATCH 070/106] #234 Added edge test case for APIs. --- syntax/std_seq_contains_test.go | 7 +++++++ syntax/std_seq_prefix_test.go | 7 +++++++ syntax/std_seq_suffix_test.go | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/syntax/std_seq_contains_test.go b/syntax/std_seq_contains_test.go index 3b5c6083..d7e48e48 100644 --- a/syntax/std_seq_contains_test.go +++ b/syntax/std_seq_contains_test.go @@ -4,6 +4,8 @@ import "testing" func TestStrContains(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.contains("A", "A")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains("", "A")`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains("", "")`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains("A", "")`) @@ -16,6 +18,8 @@ func TestStrContains(t *testing.T) { func TestArrayContains(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5], [1,2,3,4,5])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains([[1,2],[3,4],[5]], [[1,2],[3,4],[5]])`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains(1, [])`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains([], [])`) @@ -49,6 +53,9 @@ func TestArrayContains(t *testing.T) { func TestBytesContains(t *testing.T) { t.Parallel() // hello bytes - 104 101 108 108 111 + AssertCodesEvalToSameValue(t, `true`, + `//seq.contains(//unicode.utf8.encode('hello'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains({ |@, @byte| (0, 104)},//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `true`, diff --git a/syntax/std_seq_prefix_test.go b/syntax/std_seq_prefix_test.go index ade02b89..d4cbaf6c 100644 --- a/syntax/std_seq_prefix_test.go +++ b/syntax/std_seq_prefix_test.go @@ -4,6 +4,8 @@ import "testing" func TestStrPrefix(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("ABC","ABC")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("A","ABCDE")`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("AB","ABCDE")`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("BCD","ABCDE")`) @@ -18,6 +20,9 @@ func TestStrPrefix(t *testing.T) { func TestArrayPrefix(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix('A',['A'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B'],['A','B'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix('A',['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B'],['A','B','C','D','E'])`) @@ -27,6 +32,7 @@ func TestArrayPrefix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['B'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['B','C'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix([], [])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],[])`) @@ -35,6 +41,7 @@ func TestArrayPrefix(t *testing.T) { func TestBytesPrefix(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('h'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('he'),//unicode.utf8.encode('hello'))`) diff --git a/syntax/std_seq_suffix_test.go b/syntax/std_seq_suffix_test.go index 1c613d2b..4d53a526 100644 --- a/syntax/std_seq_suffix_test.go +++ b/syntax/std_seq_suffix_test.go @@ -4,6 +4,8 @@ import "testing" func TestStrSuffix(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("ABCDE","ABCDE")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("E","ABCDE")`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("DE","ABCDE")`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("CD", "ABCDE")`) @@ -16,6 +18,7 @@ func TestStrSuffix(t *testing.T) { func TestArraySuffix(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(['A','B'],['A','B'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix('E',['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(['E'],['A','B','C','D','E'])`) @@ -33,6 +36,7 @@ func TestArraySuffix(t *testing.T) { func TestBytesSuffix(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode('o'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode('lo'),//unicode.utf8.encode('hello'))`) From cf9c8ff9a1c5edbb4da99a34460dd27b15e97e0c Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 14:48:39 +0800 Subject: [PATCH 071/106] #234 Fixed incorrect test case for APIs. --- syntax/std_seq.go | 12 ++++++++++++ syntax/std_seq_contains_test.go | 6 +++--- syntax/std_seq_prefix_test.go | 6 +++--- syntax/std_seq_suffix_test.go | 6 +++--- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 9d0cad97..abefdc5e 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -78,6 +78,10 @@ func stdSeq() rel.Attr { } } return rel.NewBool(strings.Contains(args[1].String(), args[0].String())) + case rel.GenericSet: + if _, isSet := args[0].(rel.GenericSet); isSet { + return rel.NewBool(true) + } } return rel.NewBool(false) @@ -95,6 +99,10 @@ func stdSeq() rel.Attr { } } return rel.NewBool(strings.HasPrefix(args[1].String(), args[0].String())) + case rel.GenericSet: + if _, isSet := args[0].(rel.GenericSet); isSet { + return rel.NewBool(true) + } } return rel.NewBool(false) @@ -112,6 +120,10 @@ func stdSeq() rel.Attr { } } return rel.NewBool(strings.HasSuffix(args[1].String(), args[0].String())) + case rel.GenericSet: + if _, isSet := args[0].(rel.GenericSet); isSet { + return rel.NewBool(true) + } } return rel.NewBool(false) diff --git a/syntax/std_seq_contains_test.go b/syntax/std_seq_contains_test.go index d7e48e48..d95fefd5 100644 --- a/syntax/std_seq_contains_test.go +++ b/syntax/std_seq_contains_test.go @@ -7,8 +7,8 @@ func TestStrContains(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.contains("A", "A")`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains("", "A")`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains("", "")`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains("A", "")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains("", "")`) AssertCodesEvalToSameValue(t, `true `, `//seq.contains("", "this is a test") `) AssertCodesEvalToSameValue(t, `true `, `//seq.contains("is a test", "this is a test") `) @@ -22,8 +22,8 @@ func TestArrayContains(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.contains([[1,2],[3,4],[5]], [[1,2],[3,4],[5]])`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains(1, [])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains([], [])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains([], [1])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains([], [])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(1, [1,2,3,4,5])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(3, [1,2,3,4,5])`) @@ -67,7 +67,7 @@ func TestBytesContains(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.contains(//unicode.utf8.encode('A'),//unicode.utf8.encode(''))`) - AssertCodesEvalToSameValue(t, `false`, + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(//unicode.utf8.encode(''),//unicode.utf8.encode(''))`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(//unicode.utf8.encode(''),//unicode.utf8.encode('hello'))`) diff --git a/syntax/std_seq_prefix_test.go b/syntax/std_seq_prefix_test.go index d4cbaf6c..45919ce6 100644 --- a/syntax/std_seq_prefix_test.go +++ b/syntax/std_seq_prefix_test.go @@ -13,7 +13,7 @@ func TestStrPrefix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("CD","")`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("","ABCD")`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("","")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("","")`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("A","")`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("","A")`) } @@ -34,7 +34,7 @@ func TestArrayPrefix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix([], [])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix([], [])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],[])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix([],['A','B','C','D','E'])`) } @@ -49,7 +49,7 @@ func TestBytesPrefix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('l'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('o'),//unicode.utf8.encode('hello'))`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode(''),//unicode.utf8.encode(''))`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode(''),//unicode.utf8.encode(''))`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('o'),//unicode.utf8.encode(''))`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode(''),//unicode.utf8.encode('hello'))`) } diff --git a/syntax/std_seq_suffix_test.go b/syntax/std_seq_suffix_test.go index 4d53a526..5173053f 100644 --- a/syntax/std_seq_suffix_test.go +++ b/syntax/std_seq_suffix_test.go @@ -12,7 +12,7 @@ func TestStrSuffix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("D","ABCDE")`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("D","")`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("","")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("","")`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("","ABCDE")`) } @@ -30,7 +30,7 @@ func TestArraySuffix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix([],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix([], [])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix([], [])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['D','E'],[])`) } @@ -44,7 +44,7 @@ func TestBytesSuffix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('ell'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('h'),//unicode.utf8.encode('hello'))`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode(''),//unicode.utf8.encode(''))`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode(''),//unicode.utf8.encode(''))`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(//unicode.utf8.encode('o'),//unicode.utf8.encode(''))`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode(''),//unicode.utf8.encode('hello'))`) } From 3982a2b47c03b4dabcfd09bf5a250567443fd5d6 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 14:54:22 +0800 Subject: [PATCH 072/106] #234 Fixed incorrect test case for APIs. --- syntax/std_seq_prefix_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/syntax/std_seq_prefix_test.go b/syntax/std_seq_prefix_test.go index 45919ce6..ce96264e 100644 --- a/syntax/std_seq_prefix_test.go +++ b/syntax/std_seq_prefix_test.go @@ -49,6 +49,10 @@ func TestBytesPrefix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('l'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('o'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true `, `//seq.has_prefix(//unicode.utf8.encode('h'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true `, `//seq.has_prefix(//unicode.utf8.encode('he'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true `, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode(''),//unicode.utf8.encode(''))`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('o'),//unicode.utf8.encode(''))`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode(''),//unicode.utf8.encode('hello'))`) From 5473ea8e6969e69f038b3c1bf28f4b93d0d009b4 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 15:37:59 +0800 Subject: [PATCH 073/106] #234 Fixed incorrect test case for APIs. --- syntax/std_seq_array_helper.go | 5 +---- syntax/std_seq_contains_test.go | 24 +++++++++++------------- syntax/std_seq_join_test.go | 8 ++------ syntax/std_seq_prefix_test.go | 9 ++++++--- syntax/std_seq_split_test.go | 12 +++++++----- syntax/std_seq_sub_test.go | 10 ++++------ syntax/std_seq_suffix_test.go | 8 ++++++-- 7 files changed, 37 insertions(+), 39 deletions(-) diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index fe024668..a1a62c7a 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -163,12 +163,9 @@ func convert2Array(val rel.Value) rel.Array { case rel.GenericSet: valArray, _ := rel.AsArray(val) return valArray - case rel.Value: - valArray, _ := rel.AsArray(rel.NewArray(val)) - return valArray } - panic("it supports types rel.Array, rel.GenericSet and rel.Value only.") + panic("it supports types rel.Array and rel.GenericSet only.") } // Searches array sub in subject and return the first indedx if found, or return -1. diff --git a/syntax/std_seq_contains_test.go b/syntax/std_seq_contains_test.go index d95fefd5..29f74e58 100644 --- a/syntax/std_seq_contains_test.go +++ b/syntax/std_seq_contains_test.go @@ -4,16 +4,19 @@ import "testing" func TestStrContains(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, `true`, `//seq.contains("A", "A")`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains("", "A")`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains("A", "")`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains("", "")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains("A", "A")`) AssertCodesEvalToSameValue(t, `true `, `//seq.contains("", "this is a test") `) AssertCodesEvalToSameValue(t, `true `, `//seq.contains("is a test", "this is a test") `) AssertCodesEvalToSameValue(t, `false`, `//seq.contains("is not a test", "this is a test")`) AssertCodesEvalToSameValue(t, `false`, `//seq.contains("a is", "this is a test")`) + + AssertCodesEvalToSameValue(t, `true`, `//seq.contains("", "A")`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains("A", "")`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains("", "")`) + + assertExprPanics(t, `//seq.contains(1, "ABC")`) } func TestArrayContains(t *testing.T) { @@ -21,19 +24,12 @@ func TestArrayContains(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5], [1,2,3,4,5])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains([[1,2],[3,4],[5]], [[1,2],[3,4],[5]])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains(1, [])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains([1], [])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains([], [1])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains([], [])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(1, [1,2,3,4,5])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(3, [1,2,3,4,5])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(5, [1,2,3,4,5])`) - - AssertCodesEvalToSameValue(t, `true`, `//seq.contains('A',['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains('E',['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['E'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains('C',['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['C'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C'],['A','B','C','D','E'])`) @@ -46,8 +42,10 @@ func TestArrayContains(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C'], ['A', 'A', 'B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['B','C'],['A', 'A', 'B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains([['B','C']],[['A', 'B'], ['B','C'],['D','E']])`) + + assertExprPanics(t, `//seq.contains(1, [1,2,3,4,5])`) + assertExprPanics(t, `//seq.contains('A',['A','B','C','D','E'])`) } func TestBytesContains(t *testing.T) { diff --git a/syntax/std_seq_join_test.go b/syntax/std_seq_join_test.go index 47f160bd..35128717 100644 --- a/syntax/std_seq_join_test.go +++ b/syntax/std_seq_join_test.go @@ -13,9 +13,6 @@ func TestStrJoin(t *testing.T) { AssertCodesEvalToSameValue(t, `"You and me"`, `//seq.join(" and ",["You", "me"])`) assertExprPanics(t, `//seq.join("this", 2)`) - // Following cases are not supported to make sure code is clear and simple. - // Or it has to check array element is rel.String or rel.Number. - // And they are imapcted by https://github.com/arr-ai/arrai/issues/268 too. // AssertCodesEvalToSameValue(t, `"" `, `//seq.join(",",[]) `) // AssertCodesEvalToSameValue(t, `",," `, `//seq.join(",",["", "", ""]) `) } @@ -37,10 +34,9 @@ func TestArrayJoin(t *testing.T) { AssertCodesEvalToSameValue(t, `[[1, 2], [3, 4], [0], [1], [5, 6], [7, 8]]`, `//seq.join([[0],[1]], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])`) - // Following cases are not supported to make sure code is clear and simple. - // Or it has to check array element is rel.String or rel.Number. - // And they are imapcted by https://github.com/arr-ai/arrai/issues/268 too. // AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) + + assertExprPanics(t, `//seq.join(1, [1,2,3,4,5])`) } func TestBytesJoin(t *testing.T) { diff --git a/syntax/std_seq_prefix_test.go b/syntax/std_seq_prefix_test.go index ce96264e..eec0fb52 100644 --- a/syntax/std_seq_prefix_test.go +++ b/syntax/std_seq_prefix_test.go @@ -16,19 +16,19 @@ func TestStrPrefix(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("","")`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("A","")`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("","A")`) + + assertExprPanics(t, `//seq.has_prefix(1,"ABC")`) } func TestArrayPrefix(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix('A',['A'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A'],['A'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B'],['A','B'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix('A',['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(['A','B','C','D'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix('B',['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['B'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['B','C'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) @@ -37,6 +37,9 @@ func TestArrayPrefix(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix([], [])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],[])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix([],['A','B','C','D','E'])`) + + assertExprPanics(t, `//seq.has_prefix(1,[1,2,3])`) + assertExprPanics(t, `//seq.has_prefix('A',['A','B','C'])`) } func TestBytesPrefix(t *testing.T) { diff --git a/syntax/std_seq_split_test.go b/syntax/std_seq_split_test.go index 3355b98d..fa9a5d63 100644 --- a/syntax/std_seq_split_test.go +++ b/syntax/std_seq_split_test.go @@ -20,16 +20,18 @@ func TestStrSplit(t *testing.T) { AssertCodesEvalToSameValue(t, `[]`, `//seq.split("","") `) AssertCodesEvalToSameValue(t, `[""]`, `//seq.split(",","") `) + + assertExprPanics(t, `//seq.split(1,"ABC")`) } func TestArraySplit(t *testing.T) { t.Parallel() AssertCodesEvalToSameValue(t, `[['B'],['C', 'D', 'E']]`, - `//seq.split('A',['A', 'B', 'A', 'C', 'D', 'E'])`) + `//seq.split(['A'],['A', 'B', 'A', 'C', 'D', 'E'])`) AssertCodesEvalToSameValue(t, `[['B'],['C'], ['D', 'E']]`, - `//seq.split('A',['B', 'A', 'C', 'A', 'D', 'E'])`) + `//seq.split(['A'],['B', 'A', 'C', 'A', 'D', 'E'])`) AssertCodesEvalToSameValue(t, `[['A', 'B', 'C']]`, `//seq.split(['F'],['A', 'B', 'C'])`) @@ -37,9 +39,6 @@ func TestArraySplit(t *testing.T) { `[[['A','B'], ['C','D'], ['E','F']]]`, `//seq.split([['F','F']],[['A','B'], ['C','D'], ['E','F']])`) - AssertCodesEvalToSameValue(t, - `[[1],[3]]`, - `//seq.split(2,[1, 2, 3])`) AssertCodesEvalToSameValue(t, `[[1],[3]]`, `//seq.split([2],[1, 2, 3])`) @@ -59,6 +58,9 @@ func TestArraySplit(t *testing.T) { AssertCodesEvalToSameValue(t, `[[]]`, `//seq.split(['A'],[])`) + + assertExprPanics(t, `//seq.split(1,[1,2,3])`) + assertExprPanics(t, `//seq.split('A',['A','B'])`) } func TestBytesSplit(t *testing.T) { diff --git a/syntax/std_seq_sub_test.go b/syntax/std_seq_sub_test.go index 5d5faca7..2a845f57 100644 --- a/syntax/std_seq_sub_test.go +++ b/syntax/std_seq_sub_test.go @@ -43,22 +43,18 @@ func TestStrSub(t *testing.T) { AssertCodesEvalToSameValue(t, `"BC"`, `//seq.sub( "A", "","ABC")`) + + assertExprPanics(t, `//seq.sub(1,'B','BCD')`) } func TestArraySub(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, - `['T', 'B', 'T', 'C', 'D', 'E']`, - `//seq.sub('A', 'T', ['A', 'B', 'A', 'C', 'D', 'E'])`) AssertCodesEvalToSameValue(t, `['T', 'B', 'T', 'C', 'D', 'E']`, `//seq.sub(['A'], ['T'], ['A', 'B', 'A', 'C', 'D', 'E'])`) AssertCodesEvalToSameValue(t, `[['A', 'B'], ['T','C'],['A','D']]`, `//seq.sub([['A','C']], [['T','C']], [['A', 'B'], ['A','C'],['A','D']])`) - AssertCodesEvalToSameValue(t, - `[2, 2, 3]`, - `//seq.sub(1, 2, [1, 2, 3])`) AssertCodesEvalToSameValue(t, `[2, 2, 3]`, `//seq.sub([1], [2], [1, 2, 3])`) @@ -66,6 +62,8 @@ func TestArraySub(t *testing.T) { `[[1,1], [4,4], [3,3]]`, `//seq.sub([[2,2]], [[4,4]], [[1,1], [2,2], [3,3]])`) + assertExprPanics(t, `//seq.sub(1,'B',[1,2,3])`) + assertExprPanics(t, `//seq.sub(1,'B',['A','B','C'])`) } func TestArraySubEdgeCases(t *testing.T) { diff --git a/syntax/std_seq_suffix_test.go b/syntax/std_seq_suffix_test.go index 5173053f..f870dfd5 100644 --- a/syntax/std_seq_suffix_test.go +++ b/syntax/std_seq_suffix_test.go @@ -14,17 +14,18 @@ func TestStrSuffix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix("D","")`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("","")`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("","ABCDE")`) + + assertExprPanics(t, `//seq.has_suffix(1,"ABC")`) } func TestArraySuffix(t *testing.T) { t.Parallel() AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(['A','B'],['A','B'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix('E',['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(['E'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(['E'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix( ['D','E'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix('D',['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['D'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['C','D'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) @@ -32,6 +33,9 @@ func TestArraySuffix(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix([],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix([], [])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['D','E'],[])`) + + assertExprPanics(t, `//seq.has_suffix(1,[1,2])`) + assertExprPanics(t, `//seq.has_suffix('A',['A','B'])`) } func TestBytesSuffix(t *testing.T) { From 254d09f48bd7d5169067bb3bff4025f95e0660ff Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 16:34:59 +0800 Subject: [PATCH 074/106] #234 Added more test case for API join. --- syntax/std_seq.go | 15 +++++++++++++++ syntax/std_seq_join_test.go | 31 ++++++++++++++++++++++--------- syntax/std_seq_prefix_test.go | 6 ++++-- syntax/std_seq_suffix_test.go | 3 ++- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index abefdc5e..db4a459d 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -201,12 +201,27 @@ func stdSeq() rel.Attr { case rel.Array: switch a1.Values()[0].(type) { case rel.String: + // if subject is rel.String return strJoin(args...) case rel.Value: + if _, isStr := args[0].(rel.String); isStr { + return strJoin(args...) + } return arrayJoin(args[0], a1) } case rel.Bytes: + if _, isSet := args[0].(rel.GenericSet); isSet { + return args[1] + } return bytesJoin(args[0].(rel.Bytes), args[1].(rel.Bytes)) + case rel.GenericSet: + switch args[0].(type) { + case rel.String: + // if joiner is rel.String + return strJoin(args...) + case rel.Array, rel.GenericSet, rel.Bytes: + return args[1] + } } panic(fmt.Errorf(sharedError, reflect.TypeOf(args[2]))) diff --git a/syntax/std_seq_join_test.go b/syntax/std_seq_join_test.go index 35128717..046ecd4c 100644 --- a/syntax/std_seq_join_test.go +++ b/syntax/std_seq_join_test.go @@ -4,23 +4,26 @@ import "testing" func TestStrJoin(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, `"AB"`, `//seq.join("",['A','B']) `) - AssertCodesEvalToSameValue(t, `"Youme"`, `//seq.join("",["You", "me"])`) - AssertCodesEvalToSameValue(t, `"AB"`, `//seq.join([],["A","B"])`) - AssertCodesEvalToSameValue(t, `"AB"`, `//seq.join([],['A','B'])`) + AssertCodesEvalToSameValue(t, `"this is a test" `, `//seq.join(" ",["this", "is", "a", "test"])`) AssertCodesEvalToSameValue(t, `"this" `, `//seq.join(",",["this"]) `) AssertCodesEvalToSameValue(t, `"You and me"`, `//seq.join(" and ",["You", "me"])`) - assertExprPanics(t, `//seq.join("this", 2)`) + AssertCodesEvalToSameValue(t, `"AB"`, `//seq.join("",['A','B']) `) + + AssertCodesEvalToSameValue(t, `"AB"`, `//seq.join([],["A","B"])`) + AssertCodesEvalToSameValue(t, `"Youme"`, `//seq.join("",["You", "me"])`) + AssertCodesEvalToSameValue(t, `"" `, `//seq.join(",",[]) `) + AssertCodesEvalToSameValue(t, `",," `, `//seq.join(",",["", "", ""]) `) - // AssertCodesEvalToSameValue(t, `"" `, `//seq.join(",",[]) `) - // AssertCodesEvalToSameValue(t, `",," `, `//seq.join(",",["", "", ""]) `) + // It is not supported + // AssertCodesEvalToSameValue(t, `""`, `//seq.join("",[])`) + + assertExprPanics(t, `//seq.join("this", 2)`) } func TestArrayJoin(t *testing.T) { t.Parallel() // joiner "" is translated to rel.GenericSet - AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) AssertCodesEvalToSameValue(t, `[1,0,2,0,3,0,4,0,5]`, `//seq.join([0], [1,2,3,4,5])`) AssertCodesEvalToSameValue(t, `[1, 2, 0, 3, 4, 0, 5, 6]`, `//seq.join([0], [[1, 2], [3, 4], [5, 6]])`) AssertCodesEvalToSameValue(t, `[2, [3, 4], 0, 5, 6]`, `//seq.join([0], [[2, [3, 4]], [5, 6]])`) @@ -34,9 +37,12 @@ func TestArrayJoin(t *testing.T) { AssertCodesEvalToSameValue(t, `[[1, 2], [3, 4], [0], [1], [5, 6], [7, 8]]`, `//seq.join([[0],[1]], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])`) - // AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) + AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) + AssertCodesEvalToSameValue(t, `[]`, `//seq.join([],[])`) + AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) assertExprPanics(t, `//seq.join(1, [1,2,3,4,5])`) + assertExprPanics(t, `//seq.join('A', [1,2])`) } func TestBytesJoin(t *testing.T) { @@ -51,4 +57,11 @@ func TestBytesJoin(t *testing.T) { ` (2, 108), (3, 108), (4, 111) })`) AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('hateatlatlato')`, `//seq.join(//unicode.utf8.encode('at'),//unicode.utf8.encode('hello'))`) + + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('')`, + `//seq.join(//unicode.utf8.encode(''),//unicode.utf8.encode(''))`) + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('hello')`, + `//seq.join(//unicode.utf8.encode(''),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode('')`, + `//seq.join(//unicode.utf8.encode('h'),//unicode.utf8.encode(''))`) } diff --git a/syntax/std_seq_prefix_test.go b/syntax/std_seq_prefix_test.go index eec0fb52..a54106b4 100644 --- a/syntax/std_seq_prefix_test.go +++ b/syntax/std_seq_prefix_test.go @@ -44,7 +44,8 @@ func TestArrayPrefix(t *testing.T) { func TestBytesPrefix(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true`, + `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('h'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode('he'),//unicode.utf8.encode('hello'))`) @@ -54,7 +55,8 @@ func TestBytesPrefix(t *testing.T) { AssertCodesEvalToSameValue(t, `true `, `//seq.has_prefix(//unicode.utf8.encode('h'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `true `, `//seq.has_prefix(//unicode.utf8.encode('he'),//unicode.utf8.encode('hello'))`) - AssertCodesEvalToSameValue(t, `true `, `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true `, + `//seq.has_prefix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix(//unicode.utf8.encode(''),//unicode.utf8.encode(''))`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(//unicode.utf8.encode('o'),//unicode.utf8.encode(''))`) diff --git a/syntax/std_seq_suffix_test.go b/syntax/std_seq_suffix_test.go index f870dfd5..23711864 100644 --- a/syntax/std_seq_suffix_test.go +++ b/syntax/std_seq_suffix_test.go @@ -40,7 +40,8 @@ func TestArraySuffix(t *testing.T) { func TestBytesSuffix(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('hello'))`) + AssertCodesEvalToSameValue(t, `true`, + `//seq.has_suffix(//unicode.utf8.encode('hello'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode('o'),//unicode.utf8.encode('hello'))`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix(//unicode.utf8.encode('lo'),//unicode.utf8.encode('hello'))`) From 26fb4e38382f23eca5781c3b27d049e660310233 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 16:53:14 +0800 Subject: [PATCH 075/106] #234 Removed unnecessary test case. --- syntax/std_seq_join_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/syntax/std_seq_join_test.go b/syntax/std_seq_join_test.go index 046ecd4c..c9f17bbb 100644 --- a/syntax/std_seq_join_test.go +++ b/syntax/std_seq_join_test.go @@ -5,12 +5,13 @@ import "testing" func TestStrJoin(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, `"" `, `//seq.join(",",[]) `) + AssertCodesEvalToSameValue(t, `"this is a test" `, `//seq.join(" ",["this", "is", "a", "test"])`) AssertCodesEvalToSameValue(t, `"this" `, `//seq.join(",",["this"]) `) AssertCodesEvalToSameValue(t, `"You and me"`, `//seq.join(" and ",["You", "me"])`) AssertCodesEvalToSameValue(t, `"AB"`, `//seq.join("",['A','B']) `) - AssertCodesEvalToSameValue(t, `"AB"`, `//seq.join([],["A","B"])`) AssertCodesEvalToSameValue(t, `"Youme"`, `//seq.join("",["You", "me"])`) AssertCodesEvalToSameValue(t, `"" `, `//seq.join(",",[]) `) AssertCodesEvalToSameValue(t, `",," `, `//seq.join(",",["", "", ""]) `) From 076afd7d97b0132233c215209c6d45db39bbc99a Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 16:58:55 +0800 Subject: [PATCH 076/106] #234 Removed unnecessary test case. --- syntax/std_seq_join_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/syntax/std_seq_join_test.go b/syntax/std_seq_join_test.go index c9f17bbb..4bf66ea3 100644 --- a/syntax/std_seq_join_test.go +++ b/syntax/std_seq_join_test.go @@ -5,16 +5,16 @@ import "testing" func TestStrJoin(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, `"" `, `//seq.join(",",[]) `) + AssertCodesEvalToSameValue(t, `""`, `//seq.join(",",[])`) - AssertCodesEvalToSameValue(t, `"this is a test" `, `//seq.join(" ",["this", "is", "a", "test"])`) - AssertCodesEvalToSameValue(t, `"this" `, `//seq.join(",",["this"]) `) + AssertCodesEvalToSameValue(t, `"this is a test" `, `//seq.join(" ",["this", "is", "a", "test"])`) + AssertCodesEvalToSameValue(t, `"this"`, `//seq.join(",",["this"])`) AssertCodesEvalToSameValue(t, `"You and me"`, `//seq.join(" and ",["You", "me"])`) - AssertCodesEvalToSameValue(t, `"AB"`, `//seq.join("",['A','B']) `) + AssertCodesEvalToSameValue(t, `"AB"`, `//seq.join("",['A','B'])`) AssertCodesEvalToSameValue(t, `"Youme"`, `//seq.join("",["You", "me"])`) - AssertCodesEvalToSameValue(t, `"" `, `//seq.join(",",[]) `) - AssertCodesEvalToSameValue(t, `",," `, `//seq.join(",",["", "", ""]) `) + AssertCodesEvalToSameValue(t, `""`, `//seq.join(",",[])`) + AssertCodesEvalToSameValue(t, `",,"`, `//seq.join(",",["", "", ""])`) // It is not supported // AssertCodesEvalToSameValue(t, `""`, `//seq.join("",[])`) From d05356e7e65776a5eaffea1f78d15e0d10d8c14a Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 17:44:05 +0800 Subject: [PATCH 077/106] #234 Fixed issue found by golang CI. --- syntax/std_seq.go | 109 +++++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index db4a459d..30828641 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -66,67 +66,40 @@ func stdSeq() rel.Attr { rel.NewNativeFunctionAttr("concat", stdSeqConcat), rel.NewNativeFunctionAttr("repeat", stdSeqRepeat), createNestedFuncAttr("contains", 2, func(args ...rel.Value) rel.Value { - switch args[1].(type) { - case rel.String: + return includingProcess(func(args ...rel.Value) rel.Value { return rel.NewBool(strings.Contains(mustAsString(args[1]), mustAsString(args[0]))) - case rel.Array: - return arrayContains(args[0], args[1].(rel.Array)) - case rel.Bytes: - if _, isSet := args[0].(rel.GenericSet); isSet { - if len(args[1].String()) > 0 { - return rel.NewBool(true) - } - } - return rel.NewBool(strings.Contains(args[1].String(), args[0].String())) - case rel.GenericSet: - if _, isSet := args[0].(rel.GenericSet); isSet { - return rel.NewBool(true) - } - } - - return rel.NewBool(false) + }, + func(args ...rel.Value) rel.Value { + return arrayContains(args[0], args[1].(rel.Array)) + }, + func(args ...rel.Value) rel.Value { + return rel.NewBool(strings.Contains(args[1].String(), args[0].String())) + }, + args...) }), createNestedFuncAttr("has_prefix", 2, func(args ...rel.Value) rel.Value { - switch args[1].(type) { - case rel.String: + return includingProcess(func(args ...rel.Value) rel.Value { return rel.NewBool(strings.HasPrefix(mustAsString(args[1]), mustAsString(args[0]))) - case rel.Array: - return arrayHasPrefix(args[0], args[1].(rel.Array)) - case rel.Bytes: - if _, isSet := args[0].(rel.GenericSet); isSet { - if len(args[1].String()) > 0 { - return rel.NewBool(true) - } - } - return rel.NewBool(strings.HasPrefix(args[1].String(), args[0].String())) - case rel.GenericSet: - if _, isSet := args[0].(rel.GenericSet); isSet { - return rel.NewBool(true) - } - } - - return rel.NewBool(false) + }, + func(args ...rel.Value) rel.Value { + return arrayHasPrefix(args[0], args[1].(rel.Array)) + }, + func(args ...rel.Value) rel.Value { + return rel.NewBool(strings.HasPrefix(args[1].String(), args[0].String())) + }, + args...) }), createNestedFuncAttr("has_suffix", 2, func(args ...rel.Value) rel.Value { - switch args[1].(type) { - case rel.String: + return includingProcess(func(args ...rel.Value) rel.Value { return rel.NewBool(strings.HasSuffix(mustAsString(args[1]), mustAsString(args[0]))) - case rel.Array: - return arrayHasSuffix(args[0], args[1].(rel.Array)) - case rel.Bytes: - if _, isSet := args[0].(rel.GenericSet); isSet { - if len(args[1].String()) > 0 { - return rel.NewBool(true) - } - } - return rel.NewBool(strings.HasSuffix(args[1].String(), args[0].String())) - case rel.GenericSet: - if _, isSet := args[0].(rel.GenericSet); isSet { - return rel.NewBool(true) - } - } - - return rel.NewBool(false) + }, + func(args ...rel.Value) rel.Value { + return arrayHasSuffix(args[0], args[1].(rel.Array)) + }, + func(args ...rel.Value) rel.Value { + return rel.NewBool(strings.HasSuffix(args[1].String(), args[0].String())) + }, + args...) }), createNestedFuncAttr("sub", 3, func(args ...rel.Value) rel.Value { switch args[2].(type) { @@ -229,6 +202,34 @@ func stdSeq() rel.Attr { ) } +// Shared method for contains, hasPrefix and hasSuffix +func includingProcess( + strHandler, + arrayHandler, + bytesHandler func(...rel.Value) rel.Value, + args ...rel.Value) rel.Value { + + switch args[1].(type) { + case rel.String: + return strHandler(args...) + case rel.Array: + return arrayHandler(args...) + case rel.Bytes: + if _, isSet := args[0].(rel.GenericSet); isSet { + if len(args[1].String()) > 0 { + return rel.NewBool(true) + } + } + return bytesHandler(args...) + case rel.GenericSet: + if _, isSet := args[0].(rel.GenericSet); isSet { + return rel.NewBool(true) + } + } + + return rel.NewBool(false) +} + func strJoin(args ...rel.Value) rel.Value { strs := args[1].(rel.Set) toJoin := make([]string, 0, strs.Count()) From 1bd51761412c7152f332b07587316da1596ace55 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 17:55:46 +0800 Subject: [PATCH 078/106] #234 Reformat test cases as review comment. --- syntax/std_seq_split_test.go | 37 ++++--------- syntax/std_seq_sub_test.go | 104 +++++++++-------------------------- 2 files changed, 36 insertions(+), 105 deletions(-) diff --git a/syntax/std_seq_split_test.go b/syntax/std_seq_split_test.go index fa9a5d63..b8a8f6fb 100644 --- a/syntax/std_seq_split_test.go +++ b/syntax/std_seq_split_test.go @@ -26,38 +26,21 @@ func TestStrSplit(t *testing.T) { func TestArraySplit(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, - `[['B'],['C', 'D', 'E']]`, + AssertCodesEvalToSameValue(t, `[['B'],['C', 'D', 'E']]`, `//seq.split(['A'],['A', 'B', 'A', 'C', 'D', 'E'])`) - AssertCodesEvalToSameValue(t, - `[['B'],['C'], ['D', 'E']]`, + AssertCodesEvalToSameValue(t, `[['B'],['C'], ['D', 'E']]`, `//seq.split(['A'],['B', 'A', 'C', 'A', 'D', 'E'])`) - AssertCodesEvalToSameValue(t, - `[['A', 'B', 'C']]`, + AssertCodesEvalToSameValue(t, `[['A', 'B', 'C']]`, `//seq.split(['F'],['A', 'B', 'C'])`) - AssertCodesEvalToSameValue(t, - `[[['A','B'], ['C','D'], ['E','F']]]`, + AssertCodesEvalToSameValue(t, `[[['A','B'], ['C','D'], ['E','F']]]`, `//seq.split([['F','F']],[['A','B'], ['C','D'], ['E','F']])`) - AssertCodesEvalToSameValue(t, - `[[1],[3]]`, - `//seq.split([2],[1, 2, 3])`) - AssertCodesEvalToSameValue(t, - `[[[1,2]],[[5,6]]]`, - `//seq.split([[3,4]],[[1,2],[3,4],[5,6]])`) - AssertCodesEvalToSameValue(t, - `[[[1,2]], [[3,4]]]`, - `//seq.split([],[[1,2], [3,4]])`) - - AssertCodesEvalToSameValue(t, - `[['A'],['B'],['A']]`, - `//seq.split([],['A', 'B', 'A'])`) - AssertCodesEvalToSameValue(t, - `[]`, - `//seq.split([],[])`) - AssertCodesEvalToSameValue(t, - `[[]]`, - `//seq.split(['A'],[])`) + AssertCodesEvalToSameValue(t, `[[1],[3]]`, `//seq.split([2],[1, 2, 3])`) + AssertCodesEvalToSameValue(t, `[[[1,2]],[[5,6]]]`, `//seq.split([[3,4]],[[1,2],[3,4],[5,6]])`) + AssertCodesEvalToSameValue(t, `[[[1,2]], [[3,4]]]`, `//seq.split([],[[1,2], [3,4]])`) + AssertCodesEvalToSameValue(t, `[['A'],['B'],['A']]`, `//seq.split([],['A', 'B', 'A'])`) + AssertCodesEvalToSameValue(t, `[]`, `//seq.split([],[])`) + AssertCodesEvalToSameValue(t, `[[]]`, `//seq.split(['A'],[])`) assertExprPanics(t, `//seq.split(1,[1,2,3])`) assertExprPanics(t, `//seq.split('A',['A','B'])`) diff --git a/syntax/std_seq_sub_test.go b/syntax/std_seq_sub_test.go index 2a845f57..45d2b327 100644 --- a/syntax/std_seq_sub_test.go +++ b/syntax/std_seq_sub_test.go @@ -4,101 +4,49 @@ import "testing" func TestStrSub(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, - `" BC"`, - `//seq.sub( "A", " ","ABC")`) - AssertCodesEvalToSameValue(t, - `"this is not a test"`, - `//seq.sub("aaa", "is", "this is not a test")`) - AssertCodesEvalToSameValue(t, - `"this is a test"`, - `//seq.sub("is not", "is", "this is not a test")`) - AssertCodesEvalToSameValue(t, - `"this is a test"`, - `//seq.sub("not ", "","this is not a test")`) - AssertCodesEvalToSameValue(t, - `"t1his is not1 a t1est1"`, - `//seq.sub("t", "t1","this is not a test")`) - AssertCodesEvalToSameValue(t, - `"this is still a test"`, + AssertCodesEvalToSameValue(t, `" BC"`, `//seq.sub( "A", " ","ABC")`) + AssertCodesEvalToSameValue(t, `"this is not a test"`, `//seq.sub("aaa", "is", "this is not a test")`) + AssertCodesEvalToSameValue(t, `"this is a test"`, `//seq.sub("is not", "is", "this is not a test")`) + AssertCodesEvalToSameValue(t, `"this is a test"`, `//seq.sub("not ", "","this is not a test")`) + AssertCodesEvalToSameValue(t, `"t1his is not1 a t1est1"`, `//seq.sub("t", "t1","this is not a test")`) + AssertCodesEvalToSameValue(t, `"this is still a test"`, `//seq.sub( "doesn't matter", "hello there","this is still a test")`) assertExprPanics(t, `//seq.sub("hello there", "test", 1)`) ///////////////// - AssertCodesEvalToSameValue(t, - `""`, - `//seq.sub( "","", "")`) - AssertCodesEvalToSameValue(t, - `"A"`, - `//seq.sub( "","A", "")`) - AssertCodesEvalToSameValue(t, - `""`, - `//seq.sub( "A","", "")`) - - AssertCodesEvalToSameValue(t, - `"ABC"`, - `//seq.sub( "","", "ABC")`) - AssertCodesEvalToSameValue(t, - `"EAEBECE"`, - `//seq.sub( "", "E","ABC")`) - AssertCodesEvalToSameValue(t, - `"BC"`, - `//seq.sub( "A", "","ABC")`) + AssertCodesEvalToSameValue(t, `""`, `//seq.sub( "","", "")`) + AssertCodesEvalToSameValue(t, `"A"`, `//seq.sub( "","A", "")`) + AssertCodesEvalToSameValue(t, `""`, `//seq.sub( "A","", "")`) + AssertCodesEvalToSameValue(t, `"ABC"`, `//seq.sub( "","", "ABC")`) + AssertCodesEvalToSameValue(t, `"EAEBECE"`, `//seq.sub( "", "E","ABC")`) + AssertCodesEvalToSameValue(t, `"BC"`, `//seq.sub( "A", "","ABC")`) assertExprPanics(t, `//seq.sub(1,'B','BCD')`) } func TestArraySub(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, - `['T', 'B', 'T', 'C', 'D', 'E']`, + AssertCodesEvalToSameValue(t, `['T', 'B', 'T', 'C', 'D', 'E']`, `//seq.sub(['A'], ['T'], ['A', 'B', 'A', 'C', 'D', 'E'])`) - AssertCodesEvalToSameValue(t, - `[['A', 'B'], ['T','C'],['A','D']]`, + AssertCodesEvalToSameValue(t, `[['A', 'B'], ['T','C'],['A','D']]`, `//seq.sub([['A','C']], [['T','C']], [['A', 'B'], ['A','C'],['A','D']])`) - AssertCodesEvalToSameValue(t, - `[2, 2, 3]`, - `//seq.sub([1], [2], [1, 2, 3])`) - AssertCodesEvalToSameValue(t, - `[[1,1], [4,4], [3,3]]`, - `//seq.sub([[2,2]], [[4,4]], [[1,1], [2,2], [3,3]])`) + AssertCodesEvalToSameValue(t, `[2, 2, 3]`, `//seq.sub([1], [2], [1, 2, 3])`) + AssertCodesEvalToSameValue(t, `[[1,1], [4,4], [3,3]]`, `//seq.sub([[2,2]], [[4,4]], [[1,1], [2,2], [3,3]])`) assertExprPanics(t, `//seq.sub(1,'B',[1,2,3])`) assertExprPanics(t, `//seq.sub(1,'B',['A','B','C'])`) } func TestArraySubEdgeCases(t *testing.T) { - ///////////////// - AssertCodesEvalToSameValue(t, - `[]`, - `//seq.sub( [],[], [])`) - AssertCodesEvalToSameValue(t, - `[1]`, - `//seq.sub( [],[1], [])`) - AssertCodesEvalToSameValue(t, - `[1,2]`, - `//seq.sub( [],[1,2], [])`) - AssertCodesEvalToSameValue(t, - `[[1,2]]`, - `//seq.sub( [],[[1,2]], [])`) - AssertCodesEvalToSameValue(t, - `[]`, - `//seq.sub( [1],[], [])`) - - AssertCodesEvalToSameValue(t, - `[1,2,3]`, - `//seq.sub( [],[], [1,2,3])`) - AssertCodesEvalToSameValue(t, - `[4,1,4,2,4,3,4]`, - `//seq.sub( [], [4],[1,2,3])`) - AssertCodesEvalToSameValue(t, - `[4,[1,2],4,[3,4],4]`, - `//seq.sub( [], [4],[[1,2],[3,4]])`) - AssertCodesEvalToSameValue(t, - `[[4],[1,2],[4],[3,4],[4]]`, - `//seq.sub( [], [[4]],[[1,2],[3,4]])`) - AssertCodesEvalToSameValue(t, - `[1,3]`, - `//seq.sub([2], [],[1,2,3])`) + AssertCodesEvalToSameValue(t, `[]`, `//seq.sub( [],[], [])`) + AssertCodesEvalToSameValue(t, `[1]`, `//seq.sub( [],[1], [])`) + AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.sub( [],[1,2], [])`) + AssertCodesEvalToSameValue(t, `[[1,2]]`, `//seq.sub( [],[[1,2]], [])`) + AssertCodesEvalToSameValue(t, `[]`, `//seq.sub( [1],[], [])`) + AssertCodesEvalToSameValue(t, `[1,2,3]`, `//seq.sub( [],[], [1,2,3])`) + AssertCodesEvalToSameValue(t, `[4,1,4,2,4,3,4]`, `//seq.sub( [], [4],[1,2,3])`) + AssertCodesEvalToSameValue(t, `[4,[1,2],4,[3,4],4]`, `//seq.sub( [], [4],[[1,2],[3,4]])`) + AssertCodesEvalToSameValue(t, `[[4],[1,2],[4],[3,4],[4]]`, `//seq.sub( [], [[4]],[[1,2],[3,4]])`) + AssertCodesEvalToSameValue(t, `[1,3]`, `//seq.sub([2], [],[1,2,3])`) } func TestBytesSub(t *testing.T) { From 57841617ea3603de2d901c9f2de4bb518fdf4117 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 18:01:33 +0800 Subject: [PATCH 079/106] #234 Fixed issue found by golang linter. --- syntax/std_seq.go | 1 - 1 file changed, 1 deletion(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 30828641..568a4a68 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -208,7 +208,6 @@ func includingProcess( arrayHandler, bytesHandler func(...rel.Value) rel.Value, args ...rel.Value) rel.Value { - switch args[1].(type) { case rel.String: return strHandler(args...) From 9ecc2d51bffd578116b2e8d9a3d6db3da6368294 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 18:19:23 +0800 Subject: [PATCH 080/106] #234 Check in more edge test case for split API. --- syntax/std_seq_split_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/syntax/std_seq_split_test.go b/syntax/std_seq_split_test.go index b8a8f6fb..ca622fb1 100644 --- a/syntax/std_seq_split_test.go +++ b/syntax/std_seq_split_test.go @@ -5,6 +5,9 @@ import "testing" func TestStrSplit(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, `[[], "B", "CD"] `, `//seq.split("A","ABACD")`) + AssertCodesEvalToSameValue(t, `["ABAC", []] `, `//seq.split("D","ABACD")`) + AssertCodesEvalToSameValue(t, `["this", "is", "a", "test"]`, `//seq.split(" ","this is a test") `) AssertCodesEvalToSameValue(t, `["this is a test"] `, `//seq.split(",","this is a test") `) AssertCodesEvalToSameValue(t, `["th", " ", " a test"] `, `//seq.split("is","this is a test")`) @@ -60,6 +63,11 @@ func TestBytesSplit(t *testing.T) { `[//unicode.utf8.encode("this is a test")]`, `//seq.split(//unicode.utf8.encode("get"),//unicode.utf8.encode("this is a test"))`) + AssertCodesEvalToSameValue(t, `[[], //unicode.utf8.encode("B"), //unicode.utf8.encode("CD")]`, + `//seq.split(//unicode.utf8.encode("A"),//unicode.utf8.encode("ABACD"))`) + AssertCodesEvalToSameValue(t, `[//unicode.utf8.encode("ABAC"), []]`, + `//seq.split(//unicode.utf8.encode("D"),//unicode.utf8.encode("ABACD"))`) + AssertCodesEvalToSameValue(t, `//unicode.utf8.encode("")`, `//seq.split(//unicode.utf8.encode(""),//unicode.utf8.encode(""))`) From 7835e470cdcb437a972ecc8e4ca0b2422c524d93 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Tue, 19 May 2020 18:43:02 +0800 Subject: [PATCH 081/106] #234 Fixed incorrect split API, -> . --- syntax/std_seq_array_helper.go | 7 +++++++ syntax/std_seq_split_test.go | 11 ++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index a1a62c7a..f1bce849 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -62,8 +62,15 @@ func arraySplit(delimiter rel.Value, subject rel.Array) rel.Value { absoluteIndex = relativeIndex + start if start != absoluteIndex { result = append(result, rel.NewArray(subject.Values()[start:absoluteIndex]...)) + } else if start == absoluteIndex { + // case `//seq.split(['A'],['A', 'B'])` -> `[[], ['B']]` + result = append(result, rel.NewArray()) } start = absoluteIndex + delimiterArray.Count() + if start == subject.Count() { + // case `//seq.split(['B'],['A', 'B'])` -> `[['A'], []]` + result = append(result, rel.NewArray()) + } } else { if start == 0 || start < subject.Count() { result = append(result, rel.NewArray(subject.Values()[start:]...)) diff --git a/syntax/std_seq_split_test.go b/syntax/std_seq_split_test.go index ca622fb1..edee77d9 100644 --- a/syntax/std_seq_split_test.go +++ b/syntax/std_seq_split_test.go @@ -29,8 +29,15 @@ func TestStrSplit(t *testing.T) { func TestArraySplit(t *testing.T) { t.Parallel() - AssertCodesEvalToSameValue(t, `[['B'],['C', 'D', 'E']]`, + AssertCodesEvalToSameValue(t, `[['A'], ['B']]`, + `//seq.split([],['A', 'B'])`) + AssertCodesEvalToSameValue(t, `[[], ['B']]`, + `//seq.split(['A'],['A', 'B'])`) + AssertCodesEvalToSameValue(t, `[['A'], []]`, + `//seq.split(['B'],['A', 'B'])`) + AssertCodesEvalToSameValue(t, `[[],['B'],['C', 'D', 'E']]`, `//seq.split(['A'],['A', 'B', 'A', 'C', 'D', 'E'])`) + AssertCodesEvalToSameValue(t, `[['B'],['C'], ['D', 'E']]`, `//seq.split(['A'],['B', 'A', 'C', 'A', 'D', 'E'])`) AssertCodesEvalToSameValue(t, `[['A', 'B', 'C']]`, @@ -38,6 +45,8 @@ func TestArraySplit(t *testing.T) { AssertCodesEvalToSameValue(t, `[[['A','B'], ['C','D'], ['E','F']]]`, `//seq.split([['F','F']],[['A','B'], ['C','D'], ['E','F']])`) + AssertCodesEvalToSameValue(t, `[[],[2,3]]`, `//seq.split([1],[1, 2, 3])`) + AssertCodesEvalToSameValue(t, `[[1,2],[]]`, `//seq.split([3],[1, 2, 3])`) AssertCodesEvalToSameValue(t, `[[1],[3]]`, `//seq.split([2],[1, 2, 3])`) AssertCodesEvalToSameValue(t, `[[[1,2]],[[5,6]]]`, `//seq.split([[3,4]],[[1,2],[3,4],[5,6]])`) AssertCodesEvalToSameValue(t, `[[[1,2]], [[3,4]]]`, `//seq.split([],[[1,2], [3,4]])`) From 90776923f9db7d70846f633b6249bab9c2a128cf Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 20 May 2020 00:27:06 +0800 Subject: [PATCH 082/106] #234 Updated to make array split algorithm more straightforward. --- syntax/std_seq_array_helper.go | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index f1bce849..c38dd2a8 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -56,25 +56,17 @@ func arraySplit(delimiter rel.Value, subject rel.Array) rel.Value { result = append(result, rel.NewArray(e)) } } else { - for start, absoluteIndex := 0, 0; start < subject.Count(); { - relativeIndex := search(subject.Values()[start:], delimiterArray.Values()) - if relativeIndex >= 0 { - absoluteIndex = relativeIndex + start - if start != absoluteIndex { - result = append(result, rel.NewArray(subject.Values()[start:absoluteIndex]...)) - } else if start == absoluteIndex { - // case `//seq.split(['A'],['A', 'B'])` -> `[[], ['B']]` - result = append(result, rel.NewArray()) - } - start = absoluteIndex + delimiterArray.Count() - if start == subject.Count() { - // case `//seq.split(['B'],['A', 'B'])` -> `[['A'], []]` + subjectVals := subject.Values() + for { + if i := search(subjectVals, delimiterArray.Values()); i >= 0 { + if len(subjectVals[:i]) == 0 { result = append(result, rel.NewArray()) + } else { + result = append(result, rel.NewArray(subjectVals[:i]...)) } + subjectVals = subjectVals[i+delimiterArray.Count():] } else { - if start == 0 || start < subject.Count() { - result = append(result, rel.NewArray(subject.Values()[start:]...)) - } + result = append(result, rel.NewArray(subjectVals[i+delimiterArray.Count():]...)) break } } From 01d08fd6845bb9b17e1868083e72b38530139a25 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 20 May 2020 01:31:02 +0800 Subject: [PATCH 083/106] #234 Updated to make array split algorithm more straightforward. --- syntax/std_seq_array_helper.go | 30 +++++++++++++----------------- syntax/std_seq_split_test.go | 9 +++++++++ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index c38dd2a8..98d153da 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -27,17 +27,13 @@ func arraySub(old, new rel.Value, subject rel.Array) rel.Value { } result = append(result, newArray.Values()...) } else { - for start, absoluteIndex := 0, 0; start < subject.Count(); { - relativeIndex := search(subject.Values()[start:], oldArray.Values()) - if relativeIndex >= 0 { - absoluteIndex = relativeIndex + start - if absoluteIndex-start > 0 { - result = append(result, subject.Values()[start:absoluteIndex]...) - } - result = append(result, newArray.Values()...) - start = absoluteIndex + oldArray.Count() + subjectVals := subject.Values() + for { + if i := search(subjectVals, oldArray.Values()); i >= 0 { + result = append(append(result, subjectVals[:i]...), newArray.Values()...) + subjectVals = subjectVals[i+oldArray.Count():] } else { - result = append(result, subject.Values()[absoluteIndex+1:]...) + result = append(result, subjectVals...) break } } @@ -59,14 +55,10 @@ func arraySplit(delimiter rel.Value, subject rel.Array) rel.Value { subjectVals := subject.Values() for { if i := search(subjectVals, delimiterArray.Values()); i >= 0 { - if len(subjectVals[:i]) == 0 { - result = append(result, rel.NewArray()) - } else { - result = append(result, rel.NewArray(subjectVals[:i]...)) - } + result = append(result, rel.NewArray(subjectVals[:i]...)) subjectVals = subjectVals[i+delimiterArray.Count():] } else { - result = append(result, rel.NewArray(subjectVals[i+delimiterArray.Count():]...)) + result = append(result, rel.NewArray(subjectVals...)) break } } @@ -169,6 +161,9 @@ func convert2Array(val rel.Value) rel.Array { // Searches array sub in subject and return the first indedx if found, or return -1. // It is brute force approach, can be improved later if it is necessary. +// Case: subject=[1,2,3,4], sub=[2], return 1 +// Case: subject=[1,2,3,4], sub=[2,3], return 1 +// Case: subject=[1,2,3,4], sub=[2,5], return -1 func search(subject, sub []rel.Value) int { subjectOffset, subOffset := 0, 0 @@ -187,7 +182,8 @@ func search(subject, sub []rel.Value) int { } if subjectOffset < len(subject) { - return subjectOffset + // see len(sub) > 1 + return (subjectOffset + 1) - len(sub) } return -1 } diff --git a/syntax/std_seq_split_test.go b/syntax/std_seq_split_test.go index edee77d9..a9daece0 100644 --- a/syntax/std_seq_split_test.go +++ b/syntax/std_seq_split_test.go @@ -44,6 +44,15 @@ func TestArraySplit(t *testing.T) { `//seq.split(['F'],['A', 'B', 'C'])`) AssertCodesEvalToSameValue(t, `[[['A','B'], ['C','D'], ['E','F']]]`, `//seq.split([['F','F']],[['A','B'], ['C','D'], ['E','F']])`) + AssertCodesEvalToSameValue(t, `[[['A','B']], [['E','F']]]`, + `//seq.split([['C','D']],[['A','B'], ['C','D'], ['E','F']])`) + AssertCodesEvalToSameValue(t, `[[['A','B']], [['E','F'],['G']]]`, + `//seq.split([['C','D']],[['A','B'], ['C','D'], ['E','F'], ['G']])`) + + AssertCodesEvalToSameValue(t, `[[['A','B']], [['G']]]`, + `//seq.split([['C','D'],['E','F']],[['A','B'], ['C','D'], ['E','F'], ['G']])`) + AssertCodesEvalToSameValue(t, `[[['A','B'], ['C','D'], ['E','F'], ['G']]]`, + `//seq.split([['C','D'],['E','T']],[['A','B'], ['C','D'], ['E','F'], ['G']])`) AssertCodesEvalToSameValue(t, `[[],[2,3]]`, `//seq.split([1],[1, 2, 3])`) AssertCodesEvalToSameValue(t, `[[1,2],[]]`, `//seq.split([3],[1, 2, 3])`) From 3211949ee2c49f4fc8702c48b5d175011c9cb12a Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 20 May 2020 10:11:45 +0800 Subject: [PATCH 084/106] #234 Updated panic error.. --- syntax/std_seq.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 568a4a68..3993b8b0 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -2,7 +2,6 @@ package syntax import ( "fmt" - "reflect" "strings" "github.com/arr-ai/arrai/rel" @@ -139,7 +138,7 @@ func stdSeq() rel.Attr { } } - panic(fmt.Errorf(sharedError, reflect.TypeOf(args[2]))) + panic(fmt.Errorf("sub: unsupported args: %s, %s, %s", args[0], args[1], args[2])) }), createNestedFuncAttr("split", 2, func(args ...rel.Value) rel.Value { switch args[1].(type) { @@ -165,9 +164,7 @@ func stdSeq() rel.Attr { } } - panic(fmt.Errorf("expected subject sequence types are %s, %s and %s, but the actual type is %s", - reflect.TypeOf(rel.String{}), reflect.TypeOf(rel.Array{}), reflect.TypeOf(rel.Bytes{}), - reflect.TypeOf(args[2]))) + panic(fmt.Errorf("split: unsupported args: %s, %s", args[0], args[1])) }), createNestedFuncAttr("join", 2, func(args ...rel.Value) rel.Value { switch a1 := args[1].(type) { @@ -197,7 +194,7 @@ func stdSeq() rel.Attr { } } - panic(fmt.Errorf(sharedError, reflect.TypeOf(args[2]))) + panic(fmt.Errorf("join: unsupported args: %s, %s", args[0], args[1])) }), ) } @@ -237,5 +234,3 @@ func strJoin(args ...rel.Value) rel.Value { } return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[0])))) } - -var sharedError = "expected subject sequence types are rel.String, rel.Array and rel.Bytes, but the actual type is %s" From 07ba2e556e5aa015b0a4fb02a32a6166cf633058 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 20 May 2020 10:54:25 +0800 Subject: [PATCH 085/106] #234 Updated docs and added more test cases.. --- docs/std-seq.md | 74 +++++++++++++++++++++++++++++++++++ syntax/std_seq_prefix_test.go | 3 ++ syntax/std_seq_suffix_test.go | 2 + 3 files changed, 79 insertions(+) diff --git a/docs/std-seq.md b/docs/std-seq.md index e5cbc5d7..110898fd 100644 --- a/docs/std-seq.md +++ b/docs/std-seq.md @@ -2,6 +2,8 @@ The `seq` library contains functions that are used for manipulating sequenced data structures. +## String + ## `//seq.concat(seqs <: array) <: array`
`concat(seqs <: string) <: string` `concat` takes an array of sequences `seqs` and returns a sequence that is @@ -90,3 +92,75 @@ Usage: | `//seq.split(" ", "deliberately adding spaces to demonstrate the split function")` | `["deliberately", "adding", "spaces", "to", "demonstrate", "the", "split", "function"]` | | `//seq.split("random stuff", "this is just a random sentence")` | `["this is just a random sentence"]` | +## Array + +## `//seq.has_prefix(prefix <: array, subject <: array) <: bool` + +`has_prefix` checks whether the array `subject` is prefixed by array `prefix`. It returns a boolean. + +Usage: + +| example | equals | +|:-|:-| +| `//seq.has_prefix(['A'],['A','B','C'])` | `true` | +| `//seq.has_prefix([1, 2],[1, 2, 3])` | `true` | +| `//seq.has_prefix([[1, 2]],[[1, 2], [3]])` | `true` | + +## `//seq.has_suffix(suffix <: array, subject <: array) <: bool` + +`has_suffix` checks whether the array `subject` is suffixed by array `suffix`. It returns a boolean. + +Usage: + +| example | equals | +|:-|:-| +| `//seq.has_suffix(['E'],['A','B','C','D','E'])` | `true` | +| `//seq.has_suffix([[3, 4]],[[1 ,2], [3, 4]])` | `true` | + +## `//seq.join(joiner <: array, subject <: array) <: array` + +`join` returns a concatenated array with each member of `subject` delimited by `joiner` + +Usage: + +| example | equals | +|:-|:-| +| `//seq.join([0], [1,2,3,4,5])` | `[1,0,2,0,3,0,4,0,5]` | +| `//seq.join([0], [[2, [3, 4]], [5, 6]])` | `[2, [3, 4], 0, 5, 6]` | +| `//seq.join([[0],[1]], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])` | `[[1, 2], [3, 4], [0], [1], [5, 6], [7, 8]]` | + +## `//seq.contains(sub <: array, subject <: array) <: bool` + +`contains` checks whether array `sub` is contained in array `subject`. It returns a boolean. + +Usage: + +| example | equals | +|:-|:-| +| `//seq.contains([1,2,3,4,5], [1,2,3,4,5])` | `true` | +| `//seq.contains([['B','C']],[['A', 'B'], ['B','C'],['D','E']])` | `true` | + +## `//seq.sub(old <: array, new <: array, subject <: array) <: array` + +`new` replaces occurrences of array `old` in array `subject` with array `new`. It returns the modified array. + +Usage: + +| example | equals | +|:-|:-| +| `//seq.sub([1], [2], [1, 2, 3])` | `[2, 2, 3]` | +| `//seq.sub([[2,2]], [[4,4]], [[1,1], [2,2], [3,3]])`| `[[1,1], [4,4], [3,3]]` | + +## `//seq.split(delimiter <: array, subject <: array) <: array` + +`split` splits the array `subject` based on the provided array `delimiter`. It returns an array +which are split from the array `subject`. + +Usage: + +| example | equals | +|:-|:-| +| `//seq.split([1],[1, 2, 3])` | `[[],[2,3]]` | +| `//seq.split([3],[1, 2, 3])` | `[[1,2],[]]` | +| `//seq.split(['A'],['B', 'A', 'C', 'A', 'D', 'E'])` | `[['B'],['C'], ['D', 'E']]` | +| `//seq.split([['C','D'],['E','F']],[['A','B'], ['C','D'], ['E','F'], ['G']])`) | `[[['A','B']], [['G']]]` | diff --git a/syntax/std_seq_prefix_test.go b/syntax/std_seq_prefix_test.go index a54106b4..57caca0a 100644 --- a/syntax/std_seq_prefix_test.go +++ b/syntax/std_seq_prefix_test.go @@ -34,6 +34,9 @@ func TestArrayPrefix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix([1, 2],[1, 2, 3])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix([[1, 2]],[[1, 2], [3]])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix([], [])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],[])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix([],['A','B','C','D','E'])`) diff --git a/syntax/std_seq_suffix_test.go b/syntax/std_seq_suffix_test.go index 23711864..cdb9fee9 100644 --- a/syntax/std_seq_suffix_test.go +++ b/syntax/std_seq_suffix_test.go @@ -30,6 +30,8 @@ func TestArraySuffix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['C','D'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix([[3, 4]],[[1 ,2], [3, 4]])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix([],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix([], [])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['D','E'],[])`) From 520d461459c382680806db4e196783aaeb41e8c1 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Wed, 20 May 2020 14:02:54 +0800 Subject: [PATCH 086/106] #234 Updated docs.' --- docs/std-seq.md | 113 ++++++++++++------------------------------------ 1 file changed, 27 insertions(+), 86 deletions(-) diff --git a/docs/std-seq.md b/docs/std-seq.md index 110898fd..0fc55355 100644 --- a/docs/std-seq.md +++ b/docs/std-seq.md @@ -2,9 +2,7 @@ The `seq` library contains functions that are used for manipulating sequenced data structures. -## String - -## `//seq.concat(seqs <: array) <: array`
`concat(seqs <: string) <: string` +## `//seq.concat(seqs <: array) <: array`
`//seq.concat(seqs <: string) <: string` `concat` takes an array of sequences `seqs` and returns a sequence that is the concatenation of the sequences in the array. @@ -14,7 +12,7 @@ the concatenation of the sequences in the array. | `//seq.concat(["ba", "na", "na"])` | `"banana"` | | `//seq.concat([[1, 2], [3, 4, 5]])` | `[1, 2, 3, 4, 5]` | -## `//seq.repeat(n <: number, seq <: array) <: array`
`repeat(n <: number, seq <: string) <: string` +## `//seq.repeat(n <: number, seq <: array) <: array`
`//seq.repeat(n <: number, seq <: string) <: string` `repeat` returns a sequence that contains `seq` repeated `n` times. @@ -23,9 +21,9 @@ the concatenation of the sequences in the array. | `//seq.repeat(2, "hots")` | `"hotshots"` | -## `//seq.has_prefix(prefix <: string, s <: string) <: bool` +## `//seq.has_prefix(prefix <: array, subject <: array) <: bool`
`//seq.has_prefix(prefix <: string, subject <: string) <: bool` -`has_prefix` checks whether the string `s` is prefixed by `prefix`. It returns a boolean. +`has_prefix` checks whether the sequence `subject` is prefixed by sequence `prefix`. It returns a boolean. Usage: @@ -33,133 +31,76 @@ Usage: |:-|:-| | `//seq.has_prefix("I'm", "I'm running out of stuff to write")` | `true` | | `//seq.has_prefix("to write", "I'm running out of stuff to write")` | `{}` which is equal to `false` | - -## `//seq.has_suffix(suffix <: string, s <: string) <: bool` - -`has_suffix` checks whether the string `s` is suffixed by `suffix`. It returns a boolean. - -Usage: - -| example | equals | -|:-|:-| -| `//seq.has_suffix("I'm", "I'm running out of stuff to write")` | `{}` which is equal to `false` | -| `//seq.has_suffix("to write", "I'm running out of stuff to write")` | `true` | - -## `//seq.join(delimiter <: string, s <: array_of_string) <: string` - -`join` returns a concatenated string with each member of `s` delimited by `delimiter` - -Usage: - -| example | equals | -|:-|:-| -| `//seq.join(", ", ["pew", "another pew", "and more pews"])` | `"pew, another pew, and more pews"` | -| `//seq.join(" ", ["this", "is", "a", "sentence"])` | `"this is a sentence"` | -| `//seq.join(["", "this", "is", "a", "sentence"])` | `"thisisasentence"` | - -## `//seq.contains(substr <: string, str <: string) <: bool` - -`contains` checks whether `substr` is contained in `str`. It returns a -boolean. - -Usage: - -| example | equals | -|:-|:-| -| `//seq.contains("substring", "the full string which has substring")` | `true` | -| `//seq.contains("microwave", "just some random sentence")` | `{}` which is equal to `false` | - -## `//seq.sub(old <: string, new <: string, s <: string) <: string` - -`sub` replaces occurrences of `old` in `s` with `new`. It returns the modified string. - -Usage: - -| example | equals | -|:-|:-| -| `//seq.sub("old string", "new sentence", "this is the old string")` | `"this is the new sentence"` | -| `//seq.sub("string", "stuff", "just another sentence")` | `"just another sentence"` | - -## `//seq.split(delimiter <: string, s <: string) <: array of string` - -`split` splits the string `s` based on the provided `delimiter`. It returns an array of strings -which are split from the string `s`. - -Usage: - -| example | equals | -|:-|:-| -| `//seq.split(" ", "deliberately adding spaces to demonstrate the split function")` | `["deliberately", "adding", "spaces", "to", "demonstrate", "the", "split", "function"]` | -| `//seq.split("random stuff", "this is just a random sentence")` | `["this is just a random sentence"]` | - -## Array - -## `//seq.has_prefix(prefix <: array, subject <: array) <: bool` - -`has_prefix` checks whether the array `subject` is prefixed by array `prefix`. It returns a boolean. - -Usage: - -| example | equals | -|:-|:-| | `//seq.has_prefix(['A'],['A','B','C'])` | `true` | | `//seq.has_prefix([1, 2],[1, 2, 3])` | `true` | | `//seq.has_prefix([[1, 2]],[[1, 2], [3]])` | `true` | -## `//seq.has_suffix(suffix <: array, subject <: array) <: bool` -`has_suffix` checks whether the array `subject` is suffixed by array `suffix`. It returns a boolean. +## `//seq.has_suffix(suffix <: array, subject <: array) <: bool`
`//seq.has_suffix(suffix <: string, subject <: string) <: bool` + +`has_suffix` checks whether the sequence `subject` is suffixed by sequence `suffix`. It returns a boolean. Usage: | example | equals | |:-|:-| +| `//seq.has_suffix("I'm", "I'm running out of stuff to write")` | `{}` which is equal to `false` | +| `//seq.has_suffix("to write", "I'm running out of stuff to write")` | `true` | | `//seq.has_suffix(['E'],['A','B','C','D','E'])` | `true` | | `//seq.has_suffix([[3, 4]],[[1 ,2], [3, 4]])` | `true` | -## `//seq.join(joiner <: array, subject <: array) <: array` +## `//seq.join(joiner <: array, subject <: array) <: array`
`//seq.join(joiner <: string, subject <: array_of_string) <: string` -`join` returns a concatenated array with each member of `subject` delimited by `joiner` +`join` returns a concatenated sequence with each member of sequence `subject` delimited by sequence `joiner` Usage: | example | equals | |:-|:-| +| `//seq.join(", ", ["pew", "another pew", "and more pews"])` | `"pew, another pew, and more pews"` | +| `//seq.join(" ", ["this", "is", "a", "sentence"])` | `"this is a sentence"` | +| `//seq.join(["", "this", "is", "a", "sentence"])` | `"thisisasentence"` | | `//seq.join([0], [1,2,3,4,5])` | `[1,0,2,0,3,0,4,0,5]` | | `//seq.join([0], [[2, [3, 4]], [5, 6]])` | `[2, [3, 4], 0, 5, 6]` | | `//seq.join([[0],[1]], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])` | `[[1, 2], [3, 4], [0], [1], [5, 6], [7, 8]]` | -## `//seq.contains(sub <: array, subject <: array) <: bool` +## `//seq.contains(sub <: array, subject <: array) <: bool`
`//seq.contains(sub <: string, subject <: string) <: bool` -`contains` checks whether array `sub` is contained in array `subject`. It returns a boolean. +`contains` checks whether sequence `sub` is contained in sequence `subject`. It returns a boolean. Usage: | example | equals | |:-|:-| +| `//seq.contains("substring", "the full string which has substring")` | `true` | +| `//seq.contains("microwave", "just some random sentence")` | `{}` which is equal to `false` | | `//seq.contains([1,2,3,4,5], [1,2,3,4,5])` | `true` | | `//seq.contains([['B','C']],[['A', 'B'], ['B','C'],['D','E']])` | `true` | -## `//seq.sub(old <: array, new <: array, subject <: array) <: array` -`new` replaces occurrences of array `old` in array `subject` with array `new`. It returns the modified array. +## `//seq.sub(old <: array, new <: array, subject <: array) <: array`
`//seq.sub(old <: string, new <: string, subject <: string) <: string` + +`sub` replaces occurrences of sequence `old` in sequence `subject` with sequence `new`. It returns the modified sequence. Usage: | example | equals | |:-|:-| +| `//seq.sub("old string", "new sentence", "this is the old string")` | `"this is the new sentence"` | +| `//seq.sub("string", "stuff", "just another sentence")` | `"just another sentence"` | | `//seq.sub([1], [2], [1, 2, 3])` | `[2, 2, 3]` | | `//seq.sub([[2,2]], [[4,4]], [[1,1], [2,2], [3,3]])`| `[[1,1], [4,4], [3,3]]` | -## `//seq.split(delimiter <: array, subject <: array) <: array` +## `//seq.split(delimiter <: array, subject <: array) <: array`
`//seq.split(delimiter <: string, subject <: string) <: array of string` -`split` splits the array `subject` based on the provided array `delimiter`. It returns an array -which are split from the array `subject`. +`split` splits sequence `subject` based on the provided sequence `delimiter`. It returns an array of sequence which are split from the sequence `subject`. Usage: | example | equals | |:-|:-| +| `//seq.split(" ", "deliberately adding spaces to demonstrate the split function")` | `["deliberately", "adding", "spaces", "to", "demonstrate", "the", "split", "function"]` | +| `//seq.split("random stuff", "this is just a random sentence")` | `["this is just a random sentence"]` | | `//seq.split([1],[1, 2, 3])` | `[[],[2,3]]` | | `//seq.split([3],[1, 2, 3])` | `[[1,2],[]]` | | `//seq.split(['A'],['B', 'A', 'C', 'A', 'D', 'E'])` | `[['B'],['C'], ['D', 'E']]` | From b503c071dae51dd1fed738abd0735c2d811a2f67 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Thu, 21 May 2020 16:44:24 +0800 Subject: [PATCH 087/106] #234 Removed unnecessary code as review comment. --- syntax/std_seq_array_helper.go | 7 ------- syntax/std_seq_join_test.go | 3 +++ syntax/std_seq_sub_test.go | 1 + 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index 98d153da..715908d1 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -16,10 +16,6 @@ func arraySub(old, new rel.Value, subject rel.Array) rel.Value { oldArray := convert2Array(old) newArray := convert2Array(new) - if !oldArray.IsTrue() && !new.IsTrue() { - return subject - } - result := make([]rel.Value, 0, subject.Count()) if !old.IsTrue() { for _, e := range subject.Values() { @@ -70,9 +66,6 @@ func arraySplit(delimiter rel.Value, subject rel.Array) rel.Value { // Joins array joiner to subject. func arrayJoin(joiner rel.Value, subject rel.Array) rel.Value { joinerArray := convert2Array(joiner) - if !joinerArray.IsTrue() || !subject.IsTrue() { - return subject - } result := make([]rel.Value, 0, subject.Count()) for i, value := range subject.Values() { diff --git a/syntax/std_seq_join_test.go b/syntax/std_seq_join_test.go index 4bf66ea3..7abb4e30 100644 --- a/syntax/std_seq_join_test.go +++ b/syntax/std_seq_join_test.go @@ -42,6 +42,9 @@ func TestArrayJoin(t *testing.T) { AssertCodesEvalToSameValue(t, `[]`, `//seq.join([],[])`) AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) + AssertCodesEvalToSameValue(t, `[1, 2, 3, 4]`, `//seq.join([], [[1, 2], [3, 4]])`) + AssertCodesEvalToSameValue(t, `[[1, 2], 3, 4]`, `//seq.join([], [[[1, 2]], [3, 4]])`) + assertExprPanics(t, `//seq.join(1, [1,2,3,4,5])`) assertExprPanics(t, `//seq.join('A', [1,2])`) } diff --git a/syntax/std_seq_sub_test.go b/syntax/std_seq_sub_test.go index 45d2b327..53df3344 100644 --- a/syntax/std_seq_sub_test.go +++ b/syntax/std_seq_sub_test.go @@ -43,6 +43,7 @@ func TestArraySubEdgeCases(t *testing.T) { AssertCodesEvalToSameValue(t, `[[1,2]]`, `//seq.sub( [],[[1,2]], [])`) AssertCodesEvalToSameValue(t, `[]`, `//seq.sub( [1],[], [])`) AssertCodesEvalToSameValue(t, `[1,2,3]`, `//seq.sub( [],[], [1,2,3])`) + AssertCodesEvalToSameValue(t, `[[1,2],3]`, `//seq.sub( [],[], [[1,2],3])`) AssertCodesEvalToSameValue(t, `[4,1,4,2,4,3,4]`, `//seq.sub( [], [4],[1,2,3])`) AssertCodesEvalToSameValue(t, `[4,[1,2],4,[3,4],4]`, `//seq.sub( [], [4],[[1,2],[3,4]])`) AssertCodesEvalToSameValue(t, `[[4],[1,2],[4],[3,4],[4]]`, `//seq.sub( [], [[4]],[[1,2],[3,4]])`) From 1cf57250087e8ed6777023e48661dbe5ef69f27d Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Thu, 21 May 2020 16:58:00 +0800 Subject: [PATCH 088/106] #234 Added more test case as review comment. --- syntax/std_seq_join_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/syntax/std_seq_join_test.go b/syntax/std_seq_join_test.go index 7abb4e30..61d3d42d 100644 --- a/syntax/std_seq_join_test.go +++ b/syntax/std_seq_join_test.go @@ -38,12 +38,14 @@ func TestArrayJoin(t *testing.T) { AssertCodesEvalToSameValue(t, `[[1, 2], [3, 4], [0], [1], [5, 6], [7, 8]]`, `//seq.join([[0],[1]], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])`) + // Test cases the delimiter is [] AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) AssertCodesEvalToSameValue(t, `[]`, `//seq.join([],[])`) AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) AssertCodesEvalToSameValue(t, `[1, 2, 3, 4]`, `//seq.join([], [[1, 2], [3, 4]])`) AssertCodesEvalToSameValue(t, `[[1, 2], 3, 4]`, `//seq.join([], [[[1, 2]], [3, 4]])`) + AssertCodesEvalToSameValue(t, `[[1, 2], [3, 4], 5]`, `//seq.join([], [[[1, 2]], [[3,4], 5]])`) assertExprPanics(t, `//seq.join(1, [1,2,3,4,5])`) assertExprPanics(t, `//seq.join('A', [1,2])`) From 7d851f126eae53773fba76b4c367fe7f785c19af Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Thu, 21 May 2020 18:07:53 +0800 Subject: [PATCH 089/106] #234 Unpack args... to local variables and make code is more readable. --- syntax/std_seq.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 3993b8b0..0ebf8b9b 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -66,25 +66,31 @@ func stdSeq() rel.Attr { rel.NewNativeFunctionAttr("repeat", stdSeqRepeat), createNestedFuncAttr("contains", 2, func(args ...rel.Value) rel.Value { return includingProcess(func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.Contains(mustAsString(args[1]), mustAsString(args[0]))) + sub, subject := args[0], args[1] + return rel.NewBool(strings.Contains(mustAsString(subject), mustAsString(sub))) }, func(args ...rel.Value) rel.Value { - return arrayContains(args[0], args[1].(rel.Array)) + sub, subject := args[0], args[1] + return arrayContains(sub, subject.(rel.Array)) }, func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.Contains(args[1].String(), args[0].String())) + sub, subject := args[0], args[1] + return rel.NewBool(strings.Contains(subject.String(), sub.String())) }, args...) }), createNestedFuncAttr("has_prefix", 2, func(args ...rel.Value) rel.Value { return includingProcess(func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.HasPrefix(mustAsString(args[1]), mustAsString(args[0]))) + sub, subject := args[0], args[1] + return rel.NewBool(strings.HasPrefix(mustAsString(subject), mustAsString(sub))) }, func(args ...rel.Value) rel.Value { - return arrayHasPrefix(args[0], args[1].(rel.Array)) + sub, subject := args[0], args[1] + return arrayHasPrefix(sub, subject.(rel.Array)) }, func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.HasPrefix(args[1].String(), args[0].String())) + sub, subject := args[0], args[1] + return rel.NewBool(strings.HasPrefix(subject.String(), sub.String())) }, args...) }), From fd68a4321853477f8ba2bce2592f0b63732c91f2 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 22 May 2020 10:10:29 +0800 Subject: [PATCH 090/106] #234 Unpack args... to local variables to make code is more readable. --- syntax/std_seq.go | 116 +++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 54 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 0ebf8b9b..2c4d66d8 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -7,13 +7,13 @@ import ( "github.com/arr-ai/arrai/rel" ) -func stdSeqConcat(arg rel.Value) rel.Value { - if set, is := arg.(rel.Set); is { +func stdSeqConcat(seq rel.Value) rel.Value { + if set, is := seq.(rel.Set); is { if !set.IsTrue() { return rel.None } } - values := arg.(rel.Array).Values() + values := seq.(rel.Array).Values() if len(values) == 0 { return rel.None } @@ -96,111 +96,117 @@ func stdSeq() rel.Attr { }), createNestedFuncAttr("has_suffix", 2, func(args ...rel.Value) rel.Value { return includingProcess(func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.HasSuffix(mustAsString(args[1]), mustAsString(args[0]))) + suffix, subject := args[0], args[1] + return rel.NewBool(strings.HasSuffix(mustAsString(subject), mustAsString(suffix))) }, func(args ...rel.Value) rel.Value { - return arrayHasSuffix(args[0], args[1].(rel.Array)) + suffix, subject := args[0], args[1] + return arrayHasSuffix(suffix, subject.(rel.Array)) }, func(args ...rel.Value) rel.Value { - return rel.NewBool(strings.HasSuffix(args[1].String(), args[0].String())) + suffix, subject := args[0], args[1] + return rel.NewBool(strings.HasSuffix(subject.String(), suffix.String())) }, args...) }), createNestedFuncAttr("sub", 3, func(args ...rel.Value) rel.Value { - switch args[2].(type) { + old, new, subject := args[0], args[1], args[2] + switch subject.(type) { case rel.String: return rel.NewString( []rune( strings.ReplaceAll( - mustAsString(args[2]), - mustAsString(args[0]), - mustAsString(args[1]), + mustAsString(subject), + mustAsString(old), + mustAsString(new), ), ), ) case rel.Array: - return arraySub(args[0], args[1], args[2].(rel.Array)) + return arraySub(old, new, subject.(rel.Array)) case rel.Bytes: - _, arg0IsSet := args[0].(rel.GenericSet) - _, arg1IsSet := args[1].(rel.GenericSet) - if !arg0IsSet && arg1IsSet { - return rel.NewBytes([]byte(strings.ReplaceAll(args[2].String(), - args[0].String(), ""))) - } else if arg0IsSet && !arg1IsSet { - return rel.NewBytes([]byte(strings.ReplaceAll(args[2].String(), - "", args[1].String()))) + _, oldIsSet := old.(rel.GenericSet) + _, newIsSet := new.(rel.GenericSet) + if !oldIsSet && newIsSet { + return rel.NewBytes([]byte(strings.ReplaceAll(subject.String(), + old.String(), ""))) + } else if oldIsSet && !newIsSet { + return rel.NewBytes([]byte(strings.ReplaceAll(subject.String(), + "", new.String()))) } - return rel.NewBytes([]byte(strings.ReplaceAll(args[2].String(), - args[0].String(), args[1].String()))) + return rel.NewBytes([]byte(strings.ReplaceAll(subject.String(), + old.String(), new.String()))) case rel.GenericSet: - _, arg0IsSet := args[0].(rel.GenericSet) - _, arg1IsSet := args[1].(rel.GenericSet) - if arg0IsSet && arg1IsSet { - return args[2] - } else if arg0IsSet && !arg1IsSet { - return args[1] - } else if !arg0IsSet && arg1IsSet { - return args[2] + _, oldIsSet := old.(rel.GenericSet) + _, newIsSet := new.(rel.GenericSet) + if oldIsSet && newIsSet { + return subject + } else if oldIsSet && !newIsSet { + return new + } else if !oldIsSet && newIsSet { + return subject } } - panic(fmt.Errorf("sub: unsupported args: %s, %s, %s", args[0], args[1], args[2])) + panic(fmt.Errorf("sub: unsupported args: %s, %s, %s", old, new, subject)) }), createNestedFuncAttr("split", 2, func(args ...rel.Value) rel.Value { - switch args[1].(type) { + delimiter, subject := args[0], args[1] + switch subject.(type) { case rel.String: - splitted := strings.Split(mustAsString(args[1]), mustAsString(args[0])) + splitted := strings.Split(mustAsString(subject), mustAsString(delimiter)) vals := make([]rel.Value, 0, len(splitted)) for _, s := range splitted { vals = append(vals, rel.NewString([]rune(s))) } return rel.NewArray(vals...) case rel.Array: - return arraySplit(args[0], args[1].(rel.Array)) + return arraySplit(delimiter, subject.(rel.Array)) case rel.Bytes: - return bytesSplit(args[0], args[1].(rel.Bytes)) + return bytesSplit(delimiter, subject.(rel.Bytes)) case rel.GenericSet: - switch args[0].(type) { + switch delimiter.(type) { case rel.String: - return rel.NewArray(args[1]) + return rel.NewArray(subject) case rel.Array, rel.Bytes: return rel.NewArray(rel.NewArray()) case rel.GenericSet: - return args[1] + return subject } } - panic(fmt.Errorf("split: unsupported args: %s, %s", args[0], args[1])) + panic(fmt.Errorf("split: unsupported args: %s, %s", delimiter, subject)) }), createNestedFuncAttr("join", 2, func(args ...rel.Value) rel.Value { - switch a1 := args[1].(type) { + joiner, subject := args[0], args[1] + switch subject := subject.(type) { case rel.Array: - switch a1.Values()[0].(type) { + switch subject.Values()[0].(type) { case rel.String: // if subject is rel.String return strJoin(args...) case rel.Value: - if _, isStr := args[0].(rel.String); isStr { + if _, isStr := joiner.(rel.String); isStr { return strJoin(args...) } - return arrayJoin(args[0], a1) + return arrayJoin(joiner, subject) } case rel.Bytes: - if _, isSet := args[0].(rel.GenericSet); isSet { - return args[1] + if _, isSet := joiner.(rel.GenericSet); isSet { + return subject } - return bytesJoin(args[0].(rel.Bytes), args[1].(rel.Bytes)) + return bytesJoin(joiner.(rel.Bytes), subject) case rel.GenericSet: - switch args[0].(type) { + switch joiner.(type) { case rel.String: // if joiner is rel.String return strJoin(args...) case rel.Array, rel.GenericSet, rel.Bytes: - return args[1] + return subject } } - panic(fmt.Errorf("join: unsupported args: %s, %s", args[0], args[1])) + panic(fmt.Errorf("join: unsupported args: %s, %s", joiner, subject)) }), ) } @@ -211,20 +217,21 @@ func includingProcess( arrayHandler, bytesHandler func(...rel.Value) rel.Value, args ...rel.Value) rel.Value { - switch args[1].(type) { + sub, subject := args[0], args[1] + switch subject.(type) { case rel.String: return strHandler(args...) case rel.Array: return arrayHandler(args...) case rel.Bytes: - if _, isSet := args[0].(rel.GenericSet); isSet { - if len(args[1].String()) > 0 { + if _, isSet := sub.(rel.GenericSet); isSet { + if len(subject.String()) > 0 { return rel.NewBool(true) } } return bytesHandler(args...) case rel.GenericSet: - if _, isSet := args[0].(rel.GenericSet); isSet { + if _, isSet := sub.(rel.GenericSet); isSet { return rel.NewBool(true) } } @@ -233,10 +240,11 @@ func includingProcess( } func strJoin(args ...rel.Value) rel.Value { - strs := args[1].(rel.Set) + joiner, subject := args[0], args[1] + strs := subject.(rel.Set) toJoin := make([]string, 0, strs.Count()) for i, ok := strs.(rel.Set).ArrayEnumerator(); ok && i.MoveNext(); { toJoin = append(toJoin, mustAsString(i.Current())) } - return rel.NewString([]rune(strings.Join(toJoin, mustAsString(args[0])))) + return rel.NewString([]rune(strings.Join(toJoin, mustAsString(joiner)))) } From b230d307ac346de6bda44c9ead71004a3b448eed Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 22 May 2020 11:12:39 +0800 Subject: [PATCH 091/106] #234 Make sure the param subject in array join API has array type element only. --- syntax/std_seq_array_helper.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/syntax/std_seq_array_helper.go b/syntax/std_seq_array_helper.go index 715908d1..191e87c6 100644 --- a/syntax/std_seq_array_helper.go +++ b/syntax/std_seq_array_helper.go @@ -64,6 +64,11 @@ func arraySplit(delimiter rel.Value, subject rel.Array) rel.Value { } // Joins array joiner to subject. +// The type of subject element must be rel.Array, it can help to make sure the API output is clear and will not confuse. +// For example: +// `//seq.join([0], [1, 2])`, it can return [1, 0, 2] or [1, 2], +// `//seq.join([], [1, [2, 3]])`, it can return [1, 2, 3] or [1, [2, 3]]. +// All of the results make sense. It can see the output can't be sure in above cases, it is not good. func arrayJoin(joiner rel.Value, subject rel.Array) rel.Value { joinerArray := convert2Array(joiner) @@ -76,7 +81,7 @@ func arrayJoin(joiner rel.Value, subject rel.Array) rel.Value { case rel.Array: result = append(result, vArray.Values()...) case rel.Value: - result = append(result, value) + panic("the type of subject element must be rel.Array") } } From 2980a3b4daa8feb3a842cdc990c08f6d1dfa4508 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 22 May 2020 11:13:27 +0800 Subject: [PATCH 092/106] #234 Added more test cases. --- syntax/std_seq_contains_test.go | 1 + syntax/std_seq_join_test.go | 12 ++++++++++-- syntax/std_seq_suffix_test.go | 3 +++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/syntax/std_seq_contains_test.go b/syntax/std_seq_contains_test.go index 29f74e58..a46f3b87 100644 --- a/syntax/std_seq_contains_test.go +++ b/syntax/std_seq_contains_test.go @@ -21,6 +21,7 @@ func TestStrContains(t *testing.T) { func TestArrayContains(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2], [1,2,3,4,5])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5], [1,2,3,4,5])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains([[1,2],[3,4],[5]], [[1,2],[3,4],[5]])`) diff --git a/syntax/std_seq_join_test.go b/syntax/std_seq_join_test.go index 61d3d42d..aba7909a 100644 --- a/syntax/std_seq_join_test.go +++ b/syntax/std_seq_join_test.go @@ -25,7 +25,6 @@ func TestStrJoin(t *testing.T) { func TestArrayJoin(t *testing.T) { t.Parallel() // joiner "" is translated to rel.GenericSet - AssertCodesEvalToSameValue(t, `[1,0,2,0,3,0,4,0,5]`, `//seq.join([0], [1,2,3,4,5])`) AssertCodesEvalToSameValue(t, `[1, 2, 0, 3, 4, 0, 5, 6]`, `//seq.join([0], [[1, 2], [3, 4], [5, 6]])`) AssertCodesEvalToSameValue(t, `[2, [3, 4], 0, 5, 6]`, `//seq.join([0], [[2, [3, 4]], [5, 6]])`) AssertCodesEvalToSameValue(t, `[1, 2, 10, 11, 3, 4, 10, 11, 5, 6]`, @@ -38,8 +37,13 @@ func TestArrayJoin(t *testing.T) { AssertCodesEvalToSameValue(t, `[[1, 2], [3, 4], [0], [1], [5, 6], [7, 8]]`, `//seq.join([[0],[1]], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])`) + AssertCodesEvalToSameValue(t, `['AA', 'AB', 'BB', 'AB', 'CC', 'DD']`, + `//seq.join(['AB'], [['AA'], ['BB'], ['CC' , 'DD']])`) + AssertCodesEvalToSameValue(t, `['AA', 'AB', 'BB', ['CC', 'DD']]`, + `//seq.join(['AB'], [['AA'], ['BB' ,['CC' , 'DD']]])`) + // Test cases the delimiter is [] - AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[1,2])`) + AssertCodesEvalToSameValue(t, `[1,2]`, `//seq.join([],[[1],[2]])`) AssertCodesEvalToSameValue(t, `[]`, `//seq.join([],[])`) AssertCodesEvalToSameValue(t, `[]`, `//seq.join([1],[])`) @@ -49,6 +53,10 @@ func TestArrayJoin(t *testing.T) { assertExprPanics(t, `//seq.join(1, [1,2,3,4,5])`) assertExprPanics(t, `//seq.join('A', [1,2])`) + assertExprPanics(t, `//seq.join([],[1,2])`) + assertExprPanics(t, `//seq.join([1],[1,2])`) + assertExprPanics(t, `//seq.join([0], [1,2,3,4,5])`) + assertExprPanics(t, `//seq.join(['A'], ['AT','BB', 'CD'])`) } func TestBytesJoin(t *testing.T) { diff --git a/syntax/std_seq_suffix_test.go b/syntax/std_seq_suffix_test.go index cdb9fee9..6775ef31 100644 --- a/syntax/std_seq_suffix_test.go +++ b/syntax/std_seq_suffix_test.go @@ -30,6 +30,9 @@ func TestArraySuffix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['C','D'],['A','B','C','D','E'])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['A','B','C','D','E','F'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix([3, 4],[1 ,2, 3, 4])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix([3, 4],[[1 ,2], 3, 4])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix([3, 4],[3, 4])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix([[3, 4]],[[1 ,2], [3, 4]])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix([],['A','B','C','D','E'])`) From ea9149cb8eb39b69dce7609ff1f539af45ef9c18 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 22 May 2020 11:18:55 +0800 Subject: [PATCH 093/106] #234 Fixed issue found by golang linter. --- syntax/std_seq.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 2c4d66d8..f9e5909e 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -178,8 +178,8 @@ func stdSeq() rel.Attr { panic(fmt.Errorf("split: unsupported args: %s, %s", delimiter, subject)) }), createNestedFuncAttr("join", 2, func(args ...rel.Value) rel.Value { - joiner, subject := args[0], args[1] - switch subject := subject.(type) { + joiner, subjectParam := args[0], args[1] + switch subject := subjectParam.(type) { case rel.Array: switch subject.Values()[0].(type) { case rel.String: @@ -206,7 +206,7 @@ func stdSeq() rel.Attr { } } - panic(fmt.Errorf("join: unsupported args: %s, %s", joiner, subject)) + panic(fmt.Errorf("join: unsupported args: %s, %s", joiner, subjectParam)) }), ) } From 8c81c1f170787583123c111a272b8a306ddfc080 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 22 May 2020 11:22:53 +0800 Subject: [PATCH 094/106] #234 Fixed issue found by golang linter. --- syntax/std_seq.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index f9e5909e..1b92d155 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -111,7 +111,7 @@ func stdSeq() rel.Attr { }), createNestedFuncAttr("sub", 3, func(args ...rel.Value) rel.Value { old, new, subject := args[0], args[1], args[2] - switch subject.(type) { + switch subject := subject.(type) { case rel.String: return rel.NewString( []rune( @@ -123,7 +123,7 @@ func stdSeq() rel.Attr { ), ) case rel.Array: - return arraySub(old, new, subject.(rel.Array)) + return arraySub(old, new, subject) case rel.Bytes: _, oldIsSet := old.(rel.GenericSet) _, newIsSet := new.(rel.GenericSet) @@ -152,7 +152,7 @@ func stdSeq() rel.Attr { }), createNestedFuncAttr("split", 2, func(args ...rel.Value) rel.Value { delimiter, subject := args[0], args[1] - switch subject.(type) { + switch subject := subject.(type) { case rel.String: splitted := strings.Split(mustAsString(subject), mustAsString(delimiter)) vals := make([]rel.Value, 0, len(splitted)) @@ -161,9 +161,9 @@ func stdSeq() rel.Attr { } return rel.NewArray(vals...) case rel.Array: - return arraySplit(delimiter, subject.(rel.Array)) + return arraySplit(delimiter, subject) case rel.Bytes: - return bytesSplit(delimiter, subject.(rel.Bytes)) + return bytesSplit(delimiter, subject) case rel.GenericSet: switch delimiter.(type) { case rel.String: @@ -178,8 +178,8 @@ func stdSeq() rel.Attr { panic(fmt.Errorf("split: unsupported args: %s, %s", delimiter, subject)) }), createNestedFuncAttr("join", 2, func(args ...rel.Value) rel.Value { - joiner, subjectParam := args[0], args[1] - switch subject := subjectParam.(type) { + joiner, subject := args[0], args[1] + switch subject := subject.(type) { case rel.Array: switch subject.Values()[0].(type) { case rel.String: @@ -206,7 +206,7 @@ func stdSeq() rel.Attr { } } - panic(fmt.Errorf("join: unsupported args: %s, %s", joiner, subjectParam)) + panic(fmt.Errorf("join: unsupported args: %s, %s", joiner, subject)) }), ) } From 40175e849e20b7d837910c227844495e201dd55c Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 22 May 2020 11:31:09 +0800 Subject: [PATCH 095/106] #234 Fixed issue found by golang linter. --- syntax/std_seq_contains_test.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/syntax/std_seq_contains_test.go b/syntax/std_seq_contains_test.go index a46f3b87..7bbe2781 100644 --- a/syntax/std_seq_contains_test.go +++ b/syntax/std_seq_contains_test.go @@ -21,6 +21,22 @@ func TestStrContains(t *testing.T) { func TestArrayContains(t *testing.T) { t.Parallel() + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A'],['A', 'D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['E'],['A',C','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['C'],[B','C','D'])`) + + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['L','M','N'],['L','M','N','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['B','C'],['T','B','C','X','Y'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['C','D','E'],['1','3','C','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A1','B1','C1','D1','E1'],['A1','B1','C1','D1','E1'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['B1','C2','E3'],['A','B1','C2','D','E3'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A2','B3','C4','E5'],['A','B','C','D','E'])`) + AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E','F'],['A','B','C','D','E'])`) + + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A1','B2','C3'], ['A', 'A1', 'B2','C3','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['B4','C5'],['A', 'A', 'B4','C5','D','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains([['B1','C1']],[['A', 'B'], ['B1','C1'],['D','E']])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2], [1,2,3,4,5])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains([1,2,3,4,5], [1,2,3,4,5])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains([[1,2],[3,4],[5]], [[1,2],[3,4],[5]])`) @@ -29,22 +45,6 @@ func TestArrayContains(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.contains([], [1])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains([], [])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['E'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['C'],['A','B','C','D','E'])`) - - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['B','C'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['C','D','E'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C','D','E'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['B','C','E'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','E'],['A','B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `false`, `//seq.contains(['A','B','C','D','E','F'],['A','B','C','D','E'])`) - - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A','B','C'], ['A', 'A', 'B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['B','C'],['A', 'A', 'B','C','D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains([['B','C']],[['A', 'B'], ['B','C'],['D','E']])`) - assertExprPanics(t, `//seq.contains(1, [1,2,3,4,5])`) assertExprPanics(t, `//seq.contains('A',['A','B','C','D','E'])`) } From 3741ef45f4ca1696def48ac4f9474cb32eb792f1 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 22 May 2020 11:40:50 +0800 Subject: [PATCH 096/106] #234 Fixed incorrect test case. --- syntax/std_seq_contains_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/syntax/std_seq_contains_test.go b/syntax/std_seq_contains_test.go index 7bbe2781..b4f8a620 100644 --- a/syntax/std_seq_contains_test.go +++ b/syntax/std_seq_contains_test.go @@ -22,8 +22,8 @@ func TestStrContains(t *testing.T) { func TestArrayContains(t *testing.T) { t.Parallel() AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A'],['A', 'D','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['E'],['A',C','E'])`) - AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['C'],[B','C','D'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['E'],['A','C','E'])`) + AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['C'],['B','C','D'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['L','M','N'],['L','M','N','D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['B','C'],['T','B','C','X','Y'])`) From 9dbc9bea11fa74748fe769d721a004beaae1b29c Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 22 May 2020 12:22:36 +0800 Subject: [PATCH 097/106] #234 Changed code to make it is more readable. --- syntax/std_seq.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 1b92d155..f72d1324 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -75,7 +75,7 @@ func stdSeq() rel.Attr { }, func(args ...rel.Value) rel.Value { sub, subject := args[0], args[1] - return rel.NewBool(strings.Contains(subject.String(), sub.String())) + return rel.NewBool(strings.Contains(asString(subject), asString(sub))) }, args...) }), @@ -90,7 +90,7 @@ func stdSeq() rel.Attr { }, func(args ...rel.Value) rel.Value { sub, subject := args[0], args[1] - return rel.NewBool(strings.HasPrefix(subject.String(), sub.String())) + return rel.NewBool(strings.HasPrefix(asString(subject), asString(sub))) }, args...) }), @@ -105,7 +105,7 @@ func stdSeq() rel.Attr { }, func(args ...rel.Value) rel.Value { suffix, subject := args[0], args[1] - return rel.NewBool(strings.HasSuffix(subject.String(), suffix.String())) + return rel.NewBool(strings.HasSuffix(asString(subject), asString(suffix))) }, args...) }), @@ -224,11 +224,6 @@ func includingProcess( case rel.Array: return arrayHandler(args...) case rel.Bytes: - if _, isSet := sub.(rel.GenericSet); isSet { - if len(subject.String()) > 0 { - return rel.NewBool(true) - } - } return bytesHandler(args...) case rel.GenericSet: if _, isSet := sub.(rel.GenericSet); isSet { @@ -248,3 +243,13 @@ func strJoin(args ...rel.Value) rel.Value { } return rel.NewString([]rune(strings.Join(toJoin, mustAsString(joiner)))) } + +func asString(val rel.Value) string { + switch val := val.(type) { + case rel.Bytes: + return val.String() + case rel.Set: + return mustAsString(val) + } + panic("value can't be converted to a string") +} From a5d4bd45a5c15a460533f56d3d9d993570e8255c Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Fri, 22 May 2020 16:22:08 +0800 Subject: [PATCH 098/106] #234 Corrected std-seq.md sample. --- docs/std-seq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/std-seq.md b/docs/std-seq.md index 0fc55355..1bba4f0c 100644 --- a/docs/std-seq.md +++ b/docs/std-seq.md @@ -60,7 +60,7 @@ Usage: | `//seq.join(", ", ["pew", "another pew", "and more pews"])` | `"pew, another pew, and more pews"` | | `//seq.join(" ", ["this", "is", "a", "sentence"])` | `"this is a sentence"` | | `//seq.join(["", "this", "is", "a", "sentence"])` | `"thisisasentence"` | -| `//seq.join([0], [1,2,3,4,5])` | `[1,0,2,0,3,0,4,0,5]` | +| `//seq.join([0], [[1, 2], [3, 4], [5, 6]]` | `[1, 2, 0, 3, 4, 0, 5, 6]` | `//seq.join([0], [[2, [3, 4]], [5, 6]])` | `[2, [3, 4], 0, 5, 6]` | | `//seq.join([[0],[1]], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])` | `[[1, 2], [3, 4], [0], [1], [5, 6], [7, 8]]` | From bf5e66ef49fef3b328b162fac1fec3794b718bda Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Sun, 24 May 2020 14:33:12 +0800 Subject: [PATCH 099/106] #234 Fixed syntax error. --- rel/value_set_array.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rel/value_set_array.go b/rel/value_set_array.go index 8af98478..33c96219 100644 --- a/rel/value_set_array.go +++ b/rel/value_set_array.go @@ -158,11 +158,7 @@ func (a Array) Kind() int { // IsTrue returns true if the tuple has attributes. func (a Array) IsTrue() bool { -<<<<<<< HEAD - return len(a.values) > 0 -======= return a.count > 0 ->>>>>>> master } // Less returns true iff v is not a number or tuple, or v is a tuple and t From 5f912d087fe04c81c60fde12a5398effd1b5ad94 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 25 May 2020 12:30:29 +0800 Subject: [PATCH 100/106] #234 Updated code as code review comments. --- docs/std-seq.md | 2 +- syntax/std_seq.go | 8 ++++---- syntax/std_seq_contains_test.go | 2 +- syntax/std_seq_split_test.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/std-seq.md b/docs/std-seq.md index 1bba4f0c..ea7e75e6 100644 --- a/docs/std-seq.md +++ b/docs/std-seq.md @@ -30,7 +30,7 @@ Usage: | example | equals | |:-|:-| | `//seq.has_prefix("I'm", "I'm running out of stuff to write")` | `true` | -| `//seq.has_prefix("to write", "I'm running out of stuff to write")` | `{}` which is equal to `false` | +| `//seq.has_prefix("to write", "I'm running out of stuff to write")` | `false` | | `//seq.has_prefix(['A'],['A','B','C'])` | `true` | | `//seq.has_prefix([1, 2],[1, 2, 3])` | `true` | | `//seq.has_prefix([[1, 2]],[[1, 2], [3]])` | `true` | diff --git a/syntax/std_seq.go b/syntax/std_seq.go index f72d1324..0d21291f 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -64,7 +64,7 @@ func stdSeq() rel.Attr { return rel.NewTupleAttr("seq", rel.NewNativeFunctionAttr("concat", stdSeqConcat), rel.NewNativeFunctionAttr("repeat", stdSeqRepeat), - createNestedFuncAttr("contains", 2, func(args ...rel.Value) rel.Value { + createNestedFuncAttr("contains", 2, func(args ...rel.Value) rel.Value { //nolint:dupl return includingProcess(func(args ...rel.Value) rel.Value { sub, subject := args[0], args[1] return rel.NewBool(strings.Contains(mustAsString(subject), mustAsString(sub))) @@ -79,7 +79,7 @@ func stdSeq() rel.Attr { }, args...) }), - createNestedFuncAttr("has_prefix", 2, func(args ...rel.Value) rel.Value { + createNestedFuncAttr("has_prefix", 2, func(args ...rel.Value) rel.Value { //nolint:dupl return includingProcess(func(args ...rel.Value) rel.Value { sub, subject := args[0], args[1] return rel.NewBool(strings.HasPrefix(mustAsString(subject), mustAsString(sub))) @@ -94,7 +94,7 @@ func stdSeq() rel.Attr { }, args...) }), - createNestedFuncAttr("has_suffix", 2, func(args ...rel.Value) rel.Value { + createNestedFuncAttr("has_suffix", 2, func(args ...rel.Value) rel.Value { //nolint:dupl return includingProcess(func(args ...rel.Value) rel.Value { suffix, subject := args[0], args[1] return rel.NewBool(strings.HasSuffix(mustAsString(subject), mustAsString(suffix))) @@ -226,7 +226,7 @@ func includingProcess( case rel.Bytes: return bytesHandler(args...) case rel.GenericSet: - if _, isSet := sub.(rel.GenericSet); isSet { + if emptySet, isSet := sub.(rel.GenericSet); isSet && !emptySet.IsTrue() { return rel.NewBool(true) } } diff --git a/syntax/std_seq_contains_test.go b/syntax/std_seq_contains_test.go index b4f8a620..6a2a7807 100644 --- a/syntax/std_seq_contains_test.go +++ b/syntax/std_seq_contains_test.go @@ -19,7 +19,7 @@ func TestStrContains(t *testing.T) { assertExprPanics(t, `//seq.contains(1, "ABC")`) } -func TestArrayContains(t *testing.T) { +func TestArrayContains(t *testing.T) { //nolint:dupl t.Parallel() AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['A'],['A', 'D','E'])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains(['E'],['A','C','E'])`) diff --git a/syntax/std_seq_split_test.go b/syntax/std_seq_split_test.go index a9daece0..e22a144e 100644 --- a/syntax/std_seq_split_test.go +++ b/syntax/std_seq_split_test.go @@ -27,7 +27,7 @@ func TestStrSplit(t *testing.T) { assertExprPanics(t, `//seq.split(1,"ABC")`) } -func TestArraySplit(t *testing.T) { +func TestArraySplit(t *testing.T) { //nolint:dupl t.Parallel() AssertCodesEvalToSameValue(t, `[['A'], ['B']]`, `//seq.split([],['A', 'B'])`) From c2bf772d0b64e64866eb05d3457de8d50ef23fe2 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 25 May 2020 13:41:10 +0800 Subject: [PATCH 101/106] #234 Fixed code conflicts, use new test API AssertCodeErrors. --- syntax/std_seq_contains_test.go | 6 +++--- syntax/std_seq_join_test.go | 14 +++++++------- syntax/std_seq_prefix_test.go | 6 +++--- syntax/std_seq_split_test.go | 8 ++++---- syntax/std_seq_sub_test.go | 8 ++++---- syntax/std_seq_suffix_test.go | 6 +++--- syntax/std_str_test.go | 6 ------ syntax/test_helpers.go | 4 ++++ 8 files changed, 28 insertions(+), 30 deletions(-) diff --git a/syntax/std_seq_contains_test.go b/syntax/std_seq_contains_test.go index 6a2a7807..1ce8b86d 100644 --- a/syntax/std_seq_contains_test.go +++ b/syntax/std_seq_contains_test.go @@ -16,7 +16,7 @@ func TestStrContains(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.contains("A", "")`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains("", "")`) - assertExprPanics(t, `//seq.contains(1, "ABC")`) + AssertCodeErrors(t, `//seq.contains(1, "ABC")`, "") } func TestArrayContains(t *testing.T) { //nolint:dupl @@ -45,8 +45,8 @@ func TestArrayContains(t *testing.T) { //nolint:dupl AssertCodesEvalToSameValue(t, `true`, `//seq.contains([], [1])`) AssertCodesEvalToSameValue(t, `true`, `//seq.contains([], [])`) - assertExprPanics(t, `//seq.contains(1, [1,2,3,4,5])`) - assertExprPanics(t, `//seq.contains('A',['A','B','C','D','E'])`) + AssertCodeErrors(t, `//seq.contains(1, [1,2,3,4,5])`, "") + AssertCodeErrors(t, `//seq.contains('A',['A','B','C','D','E'])`, "") } func TestBytesContains(t *testing.T) { diff --git a/syntax/std_seq_join_test.go b/syntax/std_seq_join_test.go index aba7909a..51a8ac0c 100644 --- a/syntax/std_seq_join_test.go +++ b/syntax/std_seq_join_test.go @@ -19,7 +19,7 @@ func TestStrJoin(t *testing.T) { // It is not supported // AssertCodesEvalToSameValue(t, `""`, `//seq.join("",[])`) - assertExprPanics(t, `//seq.join("this", 2)`) + AssertCodeErrors(t, `//seq.join("this", 2)`, "") } func TestArrayJoin(t *testing.T) { @@ -51,12 +51,12 @@ func TestArrayJoin(t *testing.T) { AssertCodesEvalToSameValue(t, `[[1, 2], 3, 4]`, `//seq.join([], [[[1, 2]], [3, 4]])`) AssertCodesEvalToSameValue(t, `[[1, 2], [3, 4], 5]`, `//seq.join([], [[[1, 2]], [[3,4], 5]])`) - assertExprPanics(t, `//seq.join(1, [1,2,3,4,5])`) - assertExprPanics(t, `//seq.join('A', [1,2])`) - assertExprPanics(t, `//seq.join([],[1,2])`) - assertExprPanics(t, `//seq.join([1],[1,2])`) - assertExprPanics(t, `//seq.join([0], [1,2,3,4,5])`) - assertExprPanics(t, `//seq.join(['A'], ['AT','BB', 'CD'])`) + AssertCodeErrors(t, `//seq.join(1, [1,2,3,4,5])`, "") + AssertCodeErrors(t, `//seq.join('A', [1,2])`, "") + AssertCodeErrors(t, `//seq.join([],[1,2])`, "") + AssertCodeErrors(t, `//seq.join([1],[1,2])`, "") + AssertCodeErrors(t, `//seq.join([0], [1,2,3,4,5])`, "") + AssertCodeErrors(t, `//seq.join(['A'], ['AT','BB', 'CD'])`, "") } func TestBytesJoin(t *testing.T) { diff --git a/syntax/std_seq_prefix_test.go b/syntax/std_seq_prefix_test.go index 57caca0a..dd4395d5 100644 --- a/syntax/std_seq_prefix_test.go +++ b/syntax/std_seq_prefix_test.go @@ -17,7 +17,7 @@ func TestStrPrefix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix("A","")`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix("","A")`) - assertExprPanics(t, `//seq.has_prefix(1,"ABC")`) + AssertCodeErrors(t, `//seq.has_prefix(1,"ABC")`, "") } func TestArrayPrefix(t *testing.T) { @@ -41,8 +41,8 @@ func TestArrayPrefix(t *testing.T) { AssertCodesEvalToSameValue(t, `false`, `//seq.has_prefix(['A','B','C','D','E','F'],[])`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_prefix([],['A','B','C','D','E'])`) - assertExprPanics(t, `//seq.has_prefix(1,[1,2,3])`) - assertExprPanics(t, `//seq.has_prefix('A',['A','B','C'])`) + AssertCodeErrors(t, `//seq.has_prefix(1,[1,2,3])`, "") + AssertCodeErrors(t, `//seq.has_prefix('A',['A','B','C'])`, "") } func TestBytesPrefix(t *testing.T) { diff --git a/syntax/std_seq_split_test.go b/syntax/std_seq_split_test.go index e22a144e..42b94684 100644 --- a/syntax/std_seq_split_test.go +++ b/syntax/std_seq_split_test.go @@ -11,7 +11,7 @@ func TestStrSplit(t *testing.T) { AssertCodesEvalToSameValue(t, `["this", "is", "a", "test"]`, `//seq.split(" ","this is a test") `) AssertCodesEvalToSameValue(t, `["this is a test"] `, `//seq.split(",","this is a test") `) AssertCodesEvalToSameValue(t, `["th", " ", " a test"] `, `//seq.split("is","this is a test")`) - assertExprPanics(t, `//seq.split(1, "this is a test")`) + AssertCodeErrors(t, `//seq.split(1, "this is a test")`, "") AssertCodesEvalToSameValue(t, `["t", "h", "i", "s", " ", "i", "s", " ", "a", " ", "t", "e", "s", "t"]`, @@ -24,7 +24,7 @@ func TestStrSplit(t *testing.T) { AssertCodesEvalToSameValue(t, `[""]`, `//seq.split(",","") `) - assertExprPanics(t, `//seq.split(1,"ABC")`) + AssertCodeErrors(t, `//seq.split(1,"ABC")`, "") } func TestArraySplit(t *testing.T) { //nolint:dupl @@ -63,8 +63,8 @@ func TestArraySplit(t *testing.T) { //nolint:dupl AssertCodesEvalToSameValue(t, `[]`, `//seq.split([],[])`) AssertCodesEvalToSameValue(t, `[[]]`, `//seq.split(['A'],[])`) - assertExprPanics(t, `//seq.split(1,[1,2,3])`) - assertExprPanics(t, `//seq.split('A',['A','B'])`) + AssertCodeErrors(t, `//seq.split(1,[1,2,3])`, "") + AssertCodeErrors(t, `//seq.split('A',['A','B'])`, "") } func TestBytesSplit(t *testing.T) { diff --git a/syntax/std_seq_sub_test.go b/syntax/std_seq_sub_test.go index 53df3344..9107d111 100644 --- a/syntax/std_seq_sub_test.go +++ b/syntax/std_seq_sub_test.go @@ -11,7 +11,7 @@ func TestStrSub(t *testing.T) { AssertCodesEvalToSameValue(t, `"t1his is not1 a t1est1"`, `//seq.sub("t", "t1","this is not a test")`) AssertCodesEvalToSameValue(t, `"this is still a test"`, `//seq.sub( "doesn't matter", "hello there","this is still a test")`) - assertExprPanics(t, `//seq.sub("hello there", "test", 1)`) + AssertCodeErrors(t, `//seq.sub("hello there", "test", 1)`, "") ///////////////// AssertCodesEvalToSameValue(t, `""`, `//seq.sub( "","", "")`) AssertCodesEvalToSameValue(t, `"A"`, `//seq.sub( "","A", "")`) @@ -20,7 +20,7 @@ func TestStrSub(t *testing.T) { AssertCodesEvalToSameValue(t, `"EAEBECE"`, `//seq.sub( "", "E","ABC")`) AssertCodesEvalToSameValue(t, `"BC"`, `//seq.sub( "A", "","ABC")`) - assertExprPanics(t, `//seq.sub(1,'B','BCD')`) + AssertCodeErrors(t, `//seq.sub(1,'B','BCD')`, "") } func TestArraySub(t *testing.T) { @@ -32,8 +32,8 @@ func TestArraySub(t *testing.T) { AssertCodesEvalToSameValue(t, `[2, 2, 3]`, `//seq.sub([1], [2], [1, 2, 3])`) AssertCodesEvalToSameValue(t, `[[1,1], [4,4], [3,3]]`, `//seq.sub([[2,2]], [[4,4]], [[1,1], [2,2], [3,3]])`) - assertExprPanics(t, `//seq.sub(1,'B',[1,2,3])`) - assertExprPanics(t, `//seq.sub(1,'B',['A','B','C'])`) + AssertCodeErrors(t, `//seq.sub(1,'B',[1,2,3])`, "") + AssertCodeErrors(t, `//seq.sub(1,'B',['A','B','C'])`, "") } func TestArraySubEdgeCases(t *testing.T) { diff --git a/syntax/std_seq_suffix_test.go b/syntax/std_seq_suffix_test.go index 6775ef31..dc1b4a44 100644 --- a/syntax/std_seq_suffix_test.go +++ b/syntax/std_seq_suffix_test.go @@ -15,7 +15,7 @@ func TestStrSuffix(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("","")`) AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix("","ABCDE")`) - assertExprPanics(t, `//seq.has_suffix(1,"ABC")`) + AssertCodeErrors(t, `//seq.has_suffix(1,"ABC")`, "") } func TestArraySuffix(t *testing.T) { @@ -39,8 +39,8 @@ func TestArraySuffix(t *testing.T) { AssertCodesEvalToSameValue(t, `true`, `//seq.has_suffix([], [])`) AssertCodesEvalToSameValue(t, `false`, `//seq.has_suffix(['D','E'],[])`) - assertExprPanics(t, `//seq.has_suffix(1,[1,2])`) - assertExprPanics(t, `//seq.has_suffix('A',['A','B'])`) + AssertCodeErrors(t, `//seq.has_suffix(1,[1,2])`, "") + AssertCodeErrors(t, `//seq.has_suffix('A',['A','B'])`, "") } func TestBytesSuffix(t *testing.T) { diff --git a/syntax/std_str_test.go b/syntax/std_str_test.go index 36cf5e20..eb39fcd7 100644 --- a/syntax/std_str_test.go +++ b/syntax/std_str_test.go @@ -2,8 +2,6 @@ package syntax import ( "testing" - - "github.com/stretchr/testify/assert" ) func TestStrLower(t *testing.T) { @@ -32,7 +30,3 @@ func TestStrTitle(t *testing.T) { AssertCodesEvalToSameValue(t, `"This Is A Test"`, `//str.title("this is a test")`) AssertCodeErrors(t, `//str.title(123)`, "") } - -func assertExprPanics(t *testing.T, code string) { - assert.Panics(t, func() { AssertCodesEvalToSameValue(t, `"doesn't matter"`, code) }) -} diff --git a/syntax/test_helpers.go b/syntax/test_helpers.go index ae051fc7..f44f9eb9 100644 --- a/syntax/test_helpers.go +++ b/syntax/test_helpers.go @@ -2,6 +2,7 @@ package syntax import ( "errors" + "fmt" "testing" "github.com/arr-ai/arrai/rel" @@ -84,6 +85,9 @@ func AssertCodeErrors(t *testing.T, code, errString string) bool { if assert.NoError(t, err, "parsing code: %s", code) { codeExpr := pc.CompileExpr(ast) _, err := codeExpr.Eval(rel.EmptyScope) + if err == nil { + panic(fmt.Sprintf("the code `%s` didn't generate any error", code)) + } assert.EqualError(t, errors.New(err.Error()[:len(errString)]), errString) } return false From d1c33346a491788a27f01e23341476f5bd9164c4 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 25 May 2020 14:46:59 +0800 Subject: [PATCH 102/106] #234 Updated code as review comments. --- docs/README.md | 2 +- docs/std-seq.md | 52 ++++++++++++++---------------- syntax/std_seq.go | 80 +++++++++++++++++++++++++---------------------- 3 files changed, 67 insertions(+), 67 deletions(-) diff --git a/docs/README.md b/docs/README.md index 84df7c75..3c520a58 100644 --- a/docs/README.md +++ b/docs/README.md @@ -417,7 +417,7 @@ External libraries may be accessed via package references. 1. **`//math`:** math functions and constants such as `//math.sin` and `//math.pi`. 2. **`//str`:** string functions such as `//str.upper` and - `//seq.join`. + `//str.lower`. 3. **`//fn`:** higher order functions such as `//fn.fix` and `//fn.fixt`. See the [standard library reference](std.md) for full documentation on all packages. 2. **`//{./path}`** provides access to other arrai files relative to the current diff --git a/docs/std-seq.md b/docs/std-seq.md index ea7e75e6..6bf264cd 100644 --- a/docs/std-seq.md +++ b/docs/std-seq.md @@ -1,32 +1,35 @@ # seq -The `seq` library contains functions that are used for manipulating sequenced data structures. +The `seq` library contains functions that are used for manipulating sequenced data structures including string and array. ## `//seq.concat(seqs <: array) <: array`
`//seq.concat(seqs <: string) <: string` `concat` takes an array of sequences `seqs` and returns a sequence that is the concatenation of the sequences in the array. +Usage: | example | equals | |:-|:-| | `//seq.concat(["ba", "na", "na"])` | `"banana"` | | `//seq.concat([[1, 2], [3, 4, 5]])` | `[1, 2, 3, 4, 5]` | -## `//seq.repeat(n <: number, seq <: array) <: array`
`//seq.repeat(n <: number, seq <: string) <: string` +## `//seq.contains(sub <: array, subject <: array) <: bool`
`//seq.contains(sub <: string, subject <: string) <: bool` -`repeat` returns a sequence that contains `seq` repeated `n` times. +`contains` checks whether sequence `sub` is contained in sequence `subject` and returns true if it is, or false otherwise. +Usage: | example | equals | |:-|:-| -| `//seq.repeat(2, "hots")` | `"hotshots"` | - +| `//seq.contains("substring", "the full string which has substring")` | `true` | +| `//seq.contains("microwave", "just some random sentence")` | `false` | +| `//seq.contains([1,2,3,4,5], [1,2,3,4,5])` | `true` | +| `//seq.contains([['B','C']],[['A', 'B'], ['B','C'],['D','E']])` | `true` | ## `//seq.has_prefix(prefix <: array, subject <: array) <: bool`
`//seq.has_prefix(prefix <: string, subject <: string) <: bool` -`has_prefix` checks whether the sequence `subject` is prefixed by sequence `prefix`. It returns a boolean. +`has_prefix` checks whether the sequence `subject` is prefixed by sequence `prefix` and returns true if it is, or false otherwise. Usage: - | example | equals | |:-|:-| | `//seq.has_prefix("I'm", "I'm running out of stuff to write")` | `true` | @@ -38,13 +41,12 @@ Usage: ## `//seq.has_suffix(suffix <: array, subject <: array) <: bool`
`//seq.has_suffix(suffix <: string, subject <: string) <: bool` -`has_suffix` checks whether the sequence `subject` is suffixed by sequence `suffix`. It returns a boolean. +`has_suffix` checks whether the sequence `subject` is suffixed by sequence `suffix` and returns true if it is, or false otherwise. Usage: - | example | equals | |:-|:-| -| `//seq.has_suffix("I'm", "I'm running out of stuff to write")` | `{}` which is equal to `false` | +| `//seq.has_suffix("I'm", "I'm running out of stuff to write")` | `false` | | `//seq.has_suffix("to write", "I'm running out of stuff to write")` | `true` | | `//seq.has_suffix(['E'],['A','B','C','D','E'])` | `true` | | `//seq.has_suffix([[3, 4]],[[1 ,2], [3, 4]])` | `true` | @@ -54,7 +56,6 @@ Usage: `join` returns a concatenated sequence with each member of sequence `subject` delimited by sequence `joiner` Usage: - | example | equals | |:-|:-| | `//seq.join(", ", ["pew", "another pew", "and more pews"])` | `"pew, another pew, and more pews"` | @@ -64,26 +65,25 @@ Usage: | `//seq.join([0], [[2, [3, 4]], [5, 6]])` | `[2, [3, 4], 0, 5, 6]` | | `//seq.join([[0],[1]], [[[1, 2], [3, 4]],[[5, 6],[7, 8]]])` | `[[1, 2], [3, 4], [0], [1], [5, 6], [7, 8]]` | -## `//seq.contains(sub <: array, subject <: array) <: bool`
`//seq.contains(sub <: string, subject <: string) <: bool` +## `//seq.split(delimiter <: array, subject <: array) <: array`
`//seq.split(delimiter <: string, subject <: string) <: array of string` -`contains` checks whether sequence `sub` is contained in sequence `subject`. It returns a boolean. +`split` splits sequence `subject` based on the provided sequence `delimiter`. It returns an array of sequence which are split from the sequence `subject`. Usage: - | example | equals | |:-|:-| -| `//seq.contains("substring", "the full string which has substring")` | `true` | -| `//seq.contains("microwave", "just some random sentence")` | `{}` which is equal to `false` | -| `//seq.contains([1,2,3,4,5], [1,2,3,4,5])` | `true` | -| `//seq.contains([['B','C']],[['A', 'B'], ['B','C'],['D','E']])` | `true` | - +| `//seq.split(" ", "deliberately adding spaces to demonstrate the split function")` | `["deliberately", "adding", "spaces", "to", "demonstrate", "the", "split", "function"]` | +| `//seq.split("random stuff", "this is just a random sentence")` | `["this is just a random sentence"]` | +| `//seq.split([1],[1, 2, 3])` | `[[],[2,3]]` | +| `//seq.split([3],[1, 2, 3])` | `[[1,2],[]]` | +| `//seq.split(['A'],['B', 'A', 'C', 'A', 'D', 'E'])` | `[['B'],['C'], ['D', 'E']]` | +| `//seq.split([['C','D'],['E','F']],[['A','B'], ['C','D'], ['E','F'], ['G']])`) | `[[['A','B']], [['G']]]` | ## `//seq.sub(old <: array, new <: array, subject <: array) <: array`
`//seq.sub(old <: string, new <: string, subject <: string) <: string` `sub` replaces occurrences of sequence `old` in sequence `subject` with sequence `new`. It returns the modified sequence. Usage: - | example | equals | |:-|:-| | `//seq.sub("old string", "new sentence", "this is the old string")` | `"this is the new sentence"` | @@ -91,17 +91,11 @@ Usage: | `//seq.sub([1], [2], [1, 2, 3])` | `[2, 2, 3]` | | `//seq.sub([[2,2]], [[4,4]], [[1,1], [2,2], [3,3]])`| `[[1,1], [4,4], [3,3]]` | -## `//seq.split(delimiter <: array, subject <: array) <: array`
`//seq.split(delimiter <: string, subject <: string) <: array of string` +## `//seq.repeat(n <: number, seq <: array) <: array`
`//seq.repeat(n <: number, seq <: string) <: string` -`split` splits sequence `subject` based on the provided sequence `delimiter`. It returns an array of sequence which are split from the sequence `subject`. +`repeat` returns a sequence that contains `seq` repeated `n` times. Usage: - | example | equals | |:-|:-| -| `//seq.split(" ", "deliberately adding spaces to demonstrate the split function")` | `["deliberately", "adding", "spaces", "to", "demonstrate", "the", "split", "function"]` | -| `//seq.split("random stuff", "this is just a random sentence")` | `["this is just a random sentence"]` | -| `//seq.split([1],[1, 2, 3])` | `[[],[2,3]]` | -| `//seq.split([3],[1, 2, 3])` | `[[1,2],[]]` | -| `//seq.split(['A'],['B', 'A', 'C', 'A', 'D', 'E'])` | `[['B'],['C'], ['D', 'E']]` | -| `//seq.split([['C','D'],['E','F']],[['A','B'], ['C','D'], ['E','F'], ['G']])`) | `[[['A','B']], [['G']]]` | +| `//seq.repeat(2, "hots")` | `"hotshots"` | \ No newline at end of file diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 0d21291f..09da24e1 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -65,49 +65,55 @@ func stdSeq() rel.Attr { rel.NewNativeFunctionAttr("concat", stdSeqConcat), rel.NewNativeFunctionAttr("repeat", stdSeqRepeat), createNestedFuncAttr("contains", 2, func(args ...rel.Value) rel.Value { //nolint:dupl - return includingProcess(func(args ...rel.Value) rel.Value { - sub, subject := args[0], args[1] + sub, subject := args[0], args[1] + switch subject.(type) { + case rel.String: return rel.NewBool(strings.Contains(mustAsString(subject), mustAsString(sub))) - }, - func(args ...rel.Value) rel.Value { - sub, subject := args[0], args[1] - return arrayContains(sub, subject.(rel.Array)) - }, - func(args ...rel.Value) rel.Value { - sub, subject := args[0], args[1] - return rel.NewBool(strings.Contains(asString(subject), asString(sub))) - }, - args...) + case rel.Array: + return arrayContains(sub, subject.(rel.Array)) + case rel.Bytes: + return rel.NewBool(strings.Contains(asString(subject), asString(sub))) + case rel.GenericSet: + if emptySet, isSet := sub.(rel.GenericSet); isSet && !emptySet.IsTrue() { + return rel.NewBool(true) + } + } + + return rel.NewBool(false) }), createNestedFuncAttr("has_prefix", 2, func(args ...rel.Value) rel.Value { //nolint:dupl - return includingProcess(func(args ...rel.Value) rel.Value { - sub, subject := args[0], args[1] - return rel.NewBool(strings.HasPrefix(mustAsString(subject), mustAsString(sub))) - }, - func(args ...rel.Value) rel.Value { - sub, subject := args[0], args[1] - return arrayHasPrefix(sub, subject.(rel.Array)) - }, - func(args ...rel.Value) rel.Value { - sub, subject := args[0], args[1] - return rel.NewBool(strings.HasPrefix(asString(subject), asString(sub))) - }, - args...) + prefix, subject := args[0], args[1] + switch subject.(type) { + case rel.String: + return rel.NewBool(strings.HasPrefix(mustAsString(subject), mustAsString(prefix))) + case rel.Array: + return arrayHasPrefix(prefix, subject.(rel.Array)) + case rel.Bytes: + return rel.NewBool(strings.HasPrefix(asString(subject), asString(prefix))) + case rel.GenericSet: + if emptySet, isSet := prefix.(rel.GenericSet); isSet && !emptySet.IsTrue() { + return rel.NewBool(true) + } + } + + return rel.NewBool(false) }), createNestedFuncAttr("has_suffix", 2, func(args ...rel.Value) rel.Value { //nolint:dupl - return includingProcess(func(args ...rel.Value) rel.Value { - suffix, subject := args[0], args[1] + suffix, subject := args[0], args[1] + switch subject.(type) { + case rel.String: return rel.NewBool(strings.HasSuffix(mustAsString(subject), mustAsString(suffix))) - }, - func(args ...rel.Value) rel.Value { - suffix, subject := args[0], args[1] - return arrayHasSuffix(suffix, subject.(rel.Array)) - }, - func(args ...rel.Value) rel.Value { - suffix, subject := args[0], args[1] - return rel.NewBool(strings.HasSuffix(asString(subject), asString(suffix))) - }, - args...) + case rel.Array: + return arrayHasSuffix(suffix, subject.(rel.Array)) + case rel.Bytes: + return rel.NewBool(strings.HasSuffix(asString(subject), asString(suffix))) + case rel.GenericSet: + if emptySet, isSet := suffix.(rel.GenericSet); isSet && !emptySet.IsTrue() { + return rel.NewBool(true) + } + } + + return rel.NewBool(false) }), createNestedFuncAttr("sub", 3, func(args ...rel.Value) rel.Value { old, new, subject := args[0], args[1], args[2] From 9d719db9895cca371d49a6f94d0f08ae67bd6d55 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 25 May 2020 14:48:06 +0800 Subject: [PATCH 103/106] #234 Updated code as review comments. --- syntax/std_seq.go | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 09da24e1..3e01ceb3 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -217,29 +217,6 @@ func stdSeq() rel.Attr { ) } -// Shared method for contains, hasPrefix and hasSuffix -func includingProcess( - strHandler, - arrayHandler, - bytesHandler func(...rel.Value) rel.Value, - args ...rel.Value) rel.Value { - sub, subject := args[0], args[1] - switch subject.(type) { - case rel.String: - return strHandler(args...) - case rel.Array: - return arrayHandler(args...) - case rel.Bytes: - return bytesHandler(args...) - case rel.GenericSet: - if emptySet, isSet := sub.(rel.GenericSet); isSet && !emptySet.IsTrue() { - return rel.NewBool(true) - } - } - - return rel.NewBool(false) -} - func strJoin(args ...rel.Value) rel.Value { joiner, subject := args[0], args[1] strs := subject.(rel.Set) From e1997a0d6730ef36d2b27b3eb899d75ed55dd7fc Mon Sep 17 00:00:00 2001 From: Bruno Logerfo Date: Mon, 25 May 2020 06:49:02 +0000 Subject: [PATCH 104/106] Fixed final line endings with Logerfo/newline-action. --- docs/std-seq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/std-seq.md b/docs/std-seq.md index 6bf264cd..42c912fb 100644 --- a/docs/std-seq.md +++ b/docs/std-seq.md @@ -98,4 +98,4 @@ Usage: Usage: | example | equals | |:-|:-| -| `//seq.repeat(2, "hots")` | `"hotshots"` | \ No newline at end of file +| `//seq.repeat(2, "hots")` | `"hotshots"` | From 8996b5c870b14bd43239e7f42909a779d6762e98 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 25 May 2020 14:50:52 +0800 Subject: [PATCH 105/106] #234 Updated code as review comments. --- syntax/std_seq.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 3e01ceb3..0866d1ae 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -74,9 +74,8 @@ func stdSeq() rel.Attr { case rel.Bytes: return rel.NewBool(strings.Contains(asString(subject), asString(sub))) case rel.GenericSet: - if emptySet, isSet := sub.(rel.GenericSet); isSet && !emptySet.IsTrue() { - return rel.NewBool(true) - } + emptySet, isSet := sub.(rel.GenericSet) + return rel.NewBool(isSet && !emptySet.IsTrue()) } return rel.NewBool(false) @@ -91,9 +90,8 @@ func stdSeq() rel.Attr { case rel.Bytes: return rel.NewBool(strings.HasPrefix(asString(subject), asString(prefix))) case rel.GenericSet: - if emptySet, isSet := prefix.(rel.GenericSet); isSet && !emptySet.IsTrue() { - return rel.NewBool(true) - } + emptySet, isSet := prefix.(rel.GenericSet) + return rel.NewBool(isSet && !emptySet.IsTrue()) } return rel.NewBool(false) @@ -108,9 +106,8 @@ func stdSeq() rel.Attr { case rel.Bytes: return rel.NewBool(strings.HasSuffix(asString(subject), asString(suffix))) case rel.GenericSet: - if emptySet, isSet := suffix.(rel.GenericSet); isSet && !emptySet.IsTrue() { - return rel.NewBool(true) - } + emptySet, isSet := suffix.(rel.GenericSet) + return rel.NewBool(isSet && !emptySet.IsTrue()) } return rel.NewBool(false) From 5a7c8b941fe567bc286414ef96b58001c7576f06 Mon Sep 17 00:00:00 2001 From: Zhang Eric Date: Mon, 25 May 2020 15:00:05 +0800 Subject: [PATCH 106/106] #234 Fixed issue found by golang CI. --- syntax/std_seq.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/syntax/std_seq.go b/syntax/std_seq.go index 0866d1ae..63f4436f 100644 --- a/syntax/std_seq.go +++ b/syntax/std_seq.go @@ -66,11 +66,11 @@ func stdSeq() rel.Attr { rel.NewNativeFunctionAttr("repeat", stdSeqRepeat), createNestedFuncAttr("contains", 2, func(args ...rel.Value) rel.Value { //nolint:dupl sub, subject := args[0], args[1] - switch subject.(type) { + switch subject := subject.(type) { case rel.String: return rel.NewBool(strings.Contains(mustAsString(subject), mustAsString(sub))) case rel.Array: - return arrayContains(sub, subject.(rel.Array)) + return arrayContains(sub, subject) case rel.Bytes: return rel.NewBool(strings.Contains(asString(subject), asString(sub))) case rel.GenericSet: @@ -82,11 +82,11 @@ func stdSeq() rel.Attr { }), createNestedFuncAttr("has_prefix", 2, func(args ...rel.Value) rel.Value { //nolint:dupl prefix, subject := args[0], args[1] - switch subject.(type) { + switch subject := subject.(type) { case rel.String: return rel.NewBool(strings.HasPrefix(mustAsString(subject), mustAsString(prefix))) case rel.Array: - return arrayHasPrefix(prefix, subject.(rel.Array)) + return arrayHasPrefix(prefix, subject) case rel.Bytes: return rel.NewBool(strings.HasPrefix(asString(subject), asString(prefix))) case rel.GenericSet: @@ -98,11 +98,11 @@ func stdSeq() rel.Attr { }), createNestedFuncAttr("has_suffix", 2, func(args ...rel.Value) rel.Value { //nolint:dupl suffix, subject := args[0], args[1] - switch subject.(type) { + switch subject := subject.(type) { case rel.String: return rel.NewBool(strings.HasSuffix(mustAsString(subject), mustAsString(suffix))) case rel.Array: - return arrayHasSuffix(suffix, subject.(rel.Array)) + return arrayHasSuffix(suffix, subject) case rel.Bytes: return rel.NewBool(strings.HasSuffix(asString(subject), asString(suffix))) case rel.GenericSet: