new syntax to get items from collection: collection[index]. Supported collections are string, list and dict
This commit is contained in:
parent
e5f63c3d9d
commit
03d4c192c2
@ -24,7 +24,7 @@ func TestDictParser(t *testing.T) {
|
||||
/* 1 */ {`{}`, map[any]any{}, nil},
|
||||
/* 2 */ {`{123}`, nil, errors.New(`[1:6] expected ":", got "}"`)},
|
||||
/* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
|
||||
/* 4 */ {`{1:"one",2:"two",3:"three"}.2`, "three", nil},
|
||||
/* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil},
|
||||
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
|
||||
/* 6 */ {`{1:"one"} + {2:"two"}`, map[any]any{1: "one", 2: "two"}, nil},
|
||||
/* 7 */ {`2 in {1:"one", 2:"two"}`, true, nil},
|
||||
|
@ -33,7 +33,7 @@ func TestListParser(t *testing.T) {
|
||||
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
||||
/* 12 */ {`[1,2,3] << 2+2`, []any{int64(1), int64(2), int64(3), int64(4)}, nil},
|
||||
/* 13 */ {`2-1 >> [2,3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
||||
/* 14 */ {`[1,2,3].1`, int64(2), nil},
|
||||
/* 14 */ {`[1,2,3][1]`, int64(2), nil},
|
||||
/* 15 */ {`ls=[1,2,3] but ls.1`, int64(2), nil},
|
||||
/* 16 */ {`ls=[1,2,3] but ls.(-1)`, int64(3), nil},
|
||||
/* 17 */ {`list=["one","two","three"]; list.10`, nil, errors.New(`[1:36] index 10 out of bounds`)},
|
||||
@ -43,6 +43,8 @@ func TestListParser(t *testing.T) {
|
||||
/* 21 */ {`"b" in ["a", "b", "c"]`, true, nil},
|
||||
/* 22 */ {`a=[1,2]; (a)<<3`, []any{1, 2, 3}, nil},
|
||||
/* 23 */ {`a=[1,2]; (a)<<3; 1`, []any{1, 2}, nil},
|
||||
/* 24 */ {`["a","b","c","d"][1]`, "b", nil},
|
||||
/* 25 */ {`["a","b","c","d"][1,1]`, nil, errors.New(`[1:19] one index only is allowed`)},
|
||||
|
||||
// /* 8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil},
|
||||
// /* 9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil},
|
||||
@ -61,8 +63,6 @@ func TestListParser(t *testing.T) {
|
||||
var gotErr error
|
||||
|
||||
ctx := NewSimpleStore()
|
||||
// ctx.SetVar("var1", int64(123))
|
||||
// ctx.SetVar("var2", "abc")
|
||||
ImportMathFuncs(ctx)
|
||||
parser := NewParser(ctx)
|
||||
|
||||
|
@ -83,12 +83,12 @@ func (list *ListType) indexDeepCmp(target any) (index int) {
|
||||
|
||||
// -------- list term
|
||||
func newListTermA(args ...*term) *term {
|
||||
return newListTerm(args)
|
||||
return newListTerm(0, 0, args)
|
||||
}
|
||||
|
||||
func newListTerm(args []*term) *term {
|
||||
func newListTerm(row, col int, args []*term) *term {
|
||||
return &term{
|
||||
tk: *NewValueToken(0, 0, SymList, "[]", args),
|
||||
tk: *NewValueToken(row, col, SymList, "[]", args),
|
||||
parent: nil,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
|
@ -17,7 +17,7 @@ func newDotTerm(tk *Token) (inst *term) {
|
||||
}
|
||||
}
|
||||
|
||||
func verifyIndex(ctx ExprContext, indexTerm *term, maxValue int) (index int, err error) {
|
||||
func verifyDotIndex(ctx ExprContext, indexTerm *term, maxValue int) (index int, err error) {
|
||||
var v int
|
||||
var indexValue any
|
||||
if indexValue, err = indexTerm.compute(ctx); err == nil {
|
||||
@ -51,12 +51,12 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
||||
case *ListType:
|
||||
var index int
|
||||
array := ([]any)(*unboxedValue)
|
||||
if index, err = verifyIndex(ctx, indexTerm, len(array)); err == nil {
|
||||
if index, err = verifyDotIndex(ctx, indexTerm, len(array)); err == nil {
|
||||
v = array[index]
|
||||
}
|
||||
case string:
|
||||
var index int
|
||||
if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil {
|
||||
if index, err = verifyDotIndex(ctx, indexTerm, len(unboxedValue)); err == nil {
|
||||
v = string(unboxedValue[index])
|
||||
}
|
||||
case *DictType:
|
||||
|
91
operator-index.go
Normal file
91
operator-index.go
Normal file
@ -0,0 +1,91 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-index.go
|
||||
package expr
|
||||
|
||||
// -------- index term
|
||||
func newIndexTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priDot,
|
||||
evalFunc: evalIndex,
|
||||
}
|
||||
}
|
||||
|
||||
func verifyKey(indexTerm *term, indexList *ListType) (index any, err error) {
|
||||
if len(*indexList) != 1 {
|
||||
err = indexTerm.Errorf("one index only is allowed")
|
||||
} else {
|
||||
index = (*indexList)[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int, err error) {
|
||||
var v int
|
||||
|
||||
if len(*indexList) != 1 {
|
||||
err = indexTerm.Errorf("one index only is allowed")
|
||||
} else if v, err = toInt((*indexList)[0], "index expression"); err == nil {
|
||||
if v < 0 && v >= -maxValue {
|
||||
v = maxValue + v
|
||||
}
|
||||
if v >= 0 && v < maxValue {
|
||||
index = v
|
||||
} else {
|
||||
err = indexTerm.Errorf("index %d out of bounds", v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalIndex(ctx ExprContext, self *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
var indexList *ListType
|
||||
var ok bool
|
||||
|
||||
if err = self.checkOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if indexList, ok = rightValue.(*ListType); !ok {
|
||||
err = self.Errorf("invalid index expression")
|
||||
return
|
||||
}
|
||||
indexTerm := self.children[1]
|
||||
|
||||
switch unboxedValue := leftValue.(type) {
|
||||
case *ListType:
|
||||
var index int
|
||||
if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil {
|
||||
v = (*unboxedValue)[index]
|
||||
}
|
||||
case string:
|
||||
var index int
|
||||
if index, err = verifyIndex(indexTerm, indexList, len(unboxedValue)); err == nil {
|
||||
v = string(unboxedValue[index])
|
||||
}
|
||||
case *DictType:
|
||||
var ok bool
|
||||
var indexValue any
|
||||
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
|
||||
if v, ok = (*unboxedValue)[indexValue]; !ok {
|
||||
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
|
||||
}
|
||||
}
|
||||
default:
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymIndex, newIndexTerm)
|
||||
}
|
@ -38,23 +38,6 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
|
||||
rightInt, _ := rightValue.(int64)
|
||||
v = leftInt + rightInt
|
||||
}
|
||||
// } else if IsList(leftValue) || IsList(rightValue) {
|
||||
// var leftList, rightList *ListType
|
||||
// var ok bool
|
||||
// if leftList, ok = leftValue.(*ListType); !ok {
|
||||
// leftList = &ListType{leftValue}
|
||||
// }
|
||||
// if rightList, ok = rightValue.(*ListType); !ok {
|
||||
// rightList = &ListType{rightValue}
|
||||
// }
|
||||
// sumList := make(ListType, 0, len(*leftList)+len(*rightList))
|
||||
// for _, item := range *leftList {
|
||||
// sumList = append(sumList, item)
|
||||
// }
|
||||
// for _, item := range *rightList {
|
||||
// sumList = append(sumList, item)
|
||||
// }
|
||||
// v = &sumList
|
||||
} else if IsList(leftValue) && IsList(rightValue) {
|
||||
var leftList, rightList *ListType
|
||||
leftList, _ = leftValue.(*ListType)
|
||||
|
19
parser.go
19
parser.go
@ -155,6 +155,7 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||
}
|
||||
|
||||
func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
||||
r, c := scanner.lastPos()
|
||||
args := make([]*term, 0)
|
||||
lastSym := SymUnknown
|
||||
itemExpected := false
|
||||
@ -179,7 +180,7 @@ func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term
|
||||
if lastSym != SymClosedSquare {
|
||||
err = scanner.Previous().ErrorExpectedGot("]")
|
||||
} else {
|
||||
subtree = newListTerm(args)
|
||||
subtree = newListTerm(r, c, args)
|
||||
}
|
||||
}
|
||||
return
|
||||
@ -301,7 +302,7 @@ func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaul
|
||||
startRow = tk.row
|
||||
startCol = tk.col
|
||||
} else if !defaultCase {
|
||||
filterList = newListTerm(make([]*term, 0))
|
||||
filterList = newListTerm(startRow, startCol, make([]*term, 0))
|
||||
}
|
||||
|
||||
if tk.Sym == SymOpenBrace {
|
||||
@ -402,7 +403,19 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
|
||||
case SymOpenSquare:
|
||||
var listTerm *term
|
||||
if listTerm, err = self.parseList(scanner, allowVarRef); err == nil {
|
||||
err = tree.addTerm(listTerm)
|
||||
var sym = SymUnknown
|
||||
if currentTerm != nil {
|
||||
sym = currentTerm.symbol()
|
||||
}
|
||||
if sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression {
|
||||
indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
||||
indexTerm := newTerm(indexTk)
|
||||
if err = tree.addTerm(indexTerm); err == nil {
|
||||
err = tree.addTerm(listTerm)
|
||||
}
|
||||
} else {
|
||||
err = tree.addTerm(listTerm)
|
||||
}
|
||||
currentTerm = listTerm
|
||||
}
|
||||
case SymOpenBrace:
|
||||
|
@ -74,6 +74,14 @@ func (self *scanner) unreadChar() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (self *scanner) lastPos() (r, c int) {
|
||||
if self.prev != nil {
|
||||
r = self.prev.row
|
||||
c = self.prev.col
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *scanner) Previous() *Token {
|
||||
return self.prev
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user