forked from arr-ai/arrai
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix arr-ai#128, add the slice feature to all types of Set
- Loading branch information
Showing
12 changed files
with
626 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package rel | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/go-errors/errors" | ||
) | ||
|
||
// SliceExpr is an expression that evaluates to a slice of the setToSlice. | ||
type SliceExpr struct { | ||
setToSlice, start, end, step Expr | ||
include bool | ||
} | ||
|
||
// NewSliceExpr returns a SliceExpr | ||
func NewSliceExpr(setToSlice, start, end, step Expr, include bool) SliceExpr { | ||
return SliceExpr{setToSlice, start, end, step, include} | ||
} | ||
|
||
// Eval evaluates SliceExpr to the slice of the set. | ||
func (s SliceExpr) Eval(local Scope) (Value, error) { | ||
var start, end, step Value | ||
var err error | ||
|
||
if s.start != nil { | ||
start, err = s.start.Eval(local) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
if s.end != nil { | ||
end, err = s.end.Eval(local) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
if s.step != nil { | ||
step, err = s.step.Eval(local) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if _, isNumber := step.(Number); !isNumber { | ||
return nil, errors.Errorf("step %s must be a number", step) | ||
} | ||
} else { | ||
step = Number(1) | ||
} | ||
|
||
set, err := s.setToSlice.Eval(local) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return set.(Set).CallSlice(start, end, int(step.(Number)), s.include), nil | ||
} | ||
|
||
func (s SliceExpr) String() string { | ||
str := strings.Builder{} | ||
switch { | ||
case s.start == nil && s.end == nil: | ||
str.WriteString(";") | ||
case s.start != nil && s.end == nil: | ||
str.WriteString(fmt.Sprintf("%s;", s.start)) | ||
case s.start == nil && s.end != nil: | ||
str.WriteString(fmt.Sprintf(";%s", s.end)) | ||
default: | ||
str.WriteString(fmt.Sprintf("%s;%s", s.start, s.end)) | ||
} | ||
if s.step != nil { | ||
str.WriteString(fmt.Sprintf(";%s", s.step)) | ||
} | ||
return str.String() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package rel | ||
|
||
import "math" | ||
|
||
// resolveArrayIndexes returns an array of indexes to get for array. | ||
func resolveArrayIndexes(start, end Value, step, offset, maxLen int, inclusive bool) []int { | ||
if maxLen == 0 { | ||
return []int{} | ||
} | ||
startIndex, endIndex := initDefaultArrayIndex(start, end, offset, maxLen+offset, step) | ||
|
||
if startIndex == endIndex { | ||
if inclusive { | ||
return []int{startIndex} | ||
} | ||
return []int{} | ||
} | ||
|
||
return getIndexes(startIndex, endIndex, step, inclusive) | ||
} | ||
|
||
// initDefaultArrayIndex initialize the start and end values for arrays. | ||
func initDefaultArrayIndex(start, end Value, minLen, maxLen, step int) (startIndex int, endIndex int) { | ||
if start != nil { | ||
startIndex = resolveIndex(int(start.(Number)), minLen, maxLen) | ||
if startIndex == maxLen { | ||
startIndex-- | ||
} | ||
} else { | ||
if step > 0 { | ||
startIndex = minLen | ||
} else { | ||
startIndex = maxLen - 1 | ||
} | ||
} | ||
|
||
if end != nil { | ||
endIndex = resolveIndex(int(end.(Number)), minLen, maxLen) | ||
} else { | ||
// TODO: apply inclusivity to the undefined end index | ||
if step > 0 { | ||
endIndex = maxLen | ||
} else { | ||
endIndex = minLen - 1 | ||
} | ||
} | ||
return | ||
} | ||
|
||
// resolveIndex solves the edge cases of index values. | ||
func resolveIndex(i, minVal, maxVal int) int { | ||
if i > maxVal { | ||
return maxVal | ||
} else if i < 0 { | ||
if -i > maxVal { | ||
return minVal | ||
} | ||
return maxVal + i | ||
} | ||
return i | ||
} | ||
|
||
// getIndexes returns a range of numbers between start and end with the provided step. | ||
// inclusive decides whether end can be included or not. | ||
func getIndexes(start, end, step int, inclusive bool) []int { | ||
if !isValidRange(start, end, step) { | ||
return []int{} | ||
} | ||
if inclusive { | ||
if step > 0 { | ||
end++ | ||
} else { | ||
end-- | ||
} | ||
} | ||
|
||
length := int(math.Abs(float64(start - end))) | ||
if step != 1 && step != -1 { | ||
length = int(math.Ceil(float64(length) / math.Abs(float64(step)))) | ||
} | ||
indexes := make([]int, 0, length) | ||
for i := 0; i < length; i++ { | ||
indexes = append(indexes, start+step*i) | ||
} | ||
|
||
return indexes | ||
} | ||
|
||
// isValidRange checks whether start, end, and step are valid values. | ||
func isValidRange(start, end, step int) bool { | ||
return step != 0 && ((start > end && step < 0) || (start < end && step > 0)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package rel | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestGetIndexes(t *testing.T) { | ||
t.Parallel() | ||
|
||
assert.Equal(t, []int{1, 2, 3, 4, 5}, getIndexes(1, 6, 1, false)) | ||
assert.Equal(t, []int{1, 2, 3, 4, 5}, getIndexes(1, 5, 1, true)) | ||
assert.Equal(t, []int{2, 4}, getIndexes(2, 5, 2, false)) | ||
assert.Equal(t, []int{1, 3, 5}, getIndexes(1, 5, 2, true)) | ||
assert.Equal(t, []int{5, 3}, getIndexes(5, 1, -2, false)) | ||
assert.Equal(t, []int{5, 3, 1}, getIndexes(5, 1, -2, true)) | ||
assert.Equal(t, []int{}, getIndexes(5, 1, 1, true)) | ||
assert.Equal(t, []int{}, getIndexes(5, 1, 0, false)) | ||
} | ||
|
||
func TestInitDefaultArrayIndex(t *testing.T) { | ||
t.Parallel() | ||
|
||
start, end := initDefaultArrayIndex(Number(10), Number(20), 5, 25, 1) | ||
assert.Equal(t, 10, start) | ||
assert.Equal(t, 20, end) | ||
|
||
start, end = initDefaultArrayIndex(nil, nil, 5, 25, 1) | ||
assert.Equal(t, 5, start) | ||
assert.Equal(t, 25, end) | ||
|
||
start, end = initDefaultArrayIndex(nil, nil, 5, 25, -2) | ||
assert.Equal(t, 24, start) | ||
assert.Equal(t, 4, end) | ||
|
||
start, end = initDefaultArrayIndex(nil, Number(12), 5, 25, -2) | ||
assert.Equal(t, 24, start) | ||
assert.Equal(t, 12, end) | ||
|
||
start, end = initDefaultArrayIndex(nil, Number(12), 5, 25, 2) | ||
assert.Equal(t, 5, start) | ||
assert.Equal(t, 12, end) | ||
|
||
start, end = initDefaultArrayIndex(Number(7), nil, 5, 25, -2) | ||
assert.Equal(t, 7, start) | ||
assert.Equal(t, 4, end) | ||
|
||
start, end = initDefaultArrayIndex(Number(7), nil, 5, 25, 2) | ||
assert.Equal(t, 7, start) | ||
assert.Equal(t, 25, end) | ||
|
||
start, end = initDefaultArrayIndex(nil, Number(42), 5, 25, -2) | ||
assert.Equal(t, 24, start) | ||
assert.Equal(t, 25, end) | ||
|
||
start, end = initDefaultArrayIndex(Number(42), nil, 5, 25, 2) | ||
assert.Equal(t, 24, start) | ||
assert.Equal(t, 25, end) | ||
|
||
start, end = initDefaultArrayIndex(Number(-5), nil, 5, 25, 2) | ||
assert.Equal(t, 20, start) | ||
assert.Equal(t, 25, end) | ||
|
||
start, end = initDefaultArrayIndex(nil, Number(-5), 5, 25, 2) | ||
assert.Equal(t, 5, start) | ||
assert.Equal(t, 20, end) | ||
|
||
start, end = initDefaultArrayIndex(Number(-30), nil, 5, 25, 2) | ||
assert.Equal(t, 5, start) | ||
assert.Equal(t, 25, end) | ||
|
||
start, end = initDefaultArrayIndex(nil, Number(-30), 5, 25, 2) | ||
assert.Equal(t, 5, start) | ||
assert.Equal(t, 5, end) | ||
|
||
start, end = initDefaultArrayIndex(Number(42), Number(-30), 5, 25, 2) | ||
assert.Equal(t, 24, start) | ||
assert.Equal(t, 5, end) | ||
|
||
start, end = initDefaultArrayIndex(Number(-30), Number(42), 5, 25, 2) | ||
assert.Equal(t, 5, start) | ||
assert.Equal(t, 25, end) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.