new syntax to get items from collection: collection[index]. Supported collections are string, list and dict

This commit is contained in:
Celestino Amoroso 2024-05-24 22:51:01 +02:00
parent e5f63c3d9d
commit 03d4c192c2
9 changed files with 126 additions and 30 deletions

View File

@ -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},

View File

@ -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)

View File

@ -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,

View File

@ -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
View 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)
}

View File

@ -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)

View File

@ -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:

View File

@ -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
}

View File

@ -85,6 +85,7 @@ const (
SymFuncDef
SymList
SymDict
SymIndex
SymExpression
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"