From c39970fa7e96dec174db154ce81b81c8f6ee2f5b Mon Sep 17 00:00:00 2001 From: Celestino Amoroso Date: Sat, 18 May 2024 07:47:41 +0200 Subject: [PATCH] new operator 'in' added. It check if an item is member of a list, or if a key is contained in a dictionary --- dict_test.go | 1 + list_test.go | 1 + operand-list.go | 11 +++++++++++ operator-in.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ symbol.go | 2 ++ 5 files changed, 61 insertions(+) create mode 100644 operator-in.go diff --git a/dict_test.go b/dict_test.go index 410a97b..bd93bab 100644 --- a/dict_test.go +++ b/dict_test.go @@ -27,6 +27,7 @@ func TestDictParser(t *testing.T) { /* 4 */ {`{1:"one",2:"two",3:"three"}.2`, "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}, } succeeded := 0 diff --git a/list_test.go b/list_test.go index 56c958b..c084e84 100644 --- a/list_test.go +++ b/list_test.go @@ -40,6 +40,7 @@ func TestListParser(t *testing.T) { /* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil}, /* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil}, /* 20 */ {`#["a", "b", "c"]`, int64(3), nil}, + /* 21 */ {`"b" in ["a", "b", "c"]`, true, nil}, // /* 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}, diff --git a/operand-list.go b/operand-list.go index 7bea937..5b22243 100644 --- a/operand-list.go +++ b/operand-list.go @@ -6,6 +6,7 @@ package expr import ( "fmt" + "reflect" "strings" ) @@ -61,6 +62,16 @@ func newList(listAny []any) (list *ListType) { return } +func (list *ListType) indexDeepCmp(target any) (index int) { + index = -1 + for i, item := range *list { + if reflect.DeepEqual(item, target) { + index = i + break + } + } + return +} // -------- list term func newListTermA(args ...*term) *term { diff --git a/operator-in.go b/operator-in.go new file mode 100644 index 0000000..1931907 --- /dev/null +++ b/operator-in.go @@ -0,0 +1,46 @@ +// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). +// All rights reserved. + +// operator-in.go +package expr + +//-------- in term + +func newInTerm(tk *Token) (inst *term) { + return &term{ + tk: *tk, + children: make([]*term, 0, 2), + position: posInfix, + priority: priRelational, + evalFunc: evalIn, + } +} + +func hasKey(d map[any]any, target any) (ok bool) { + _, ok = d[target] + return +} + +func evalIn(ctx ExprContext, self *term) (v any, err error) { + var leftValue, rightValue any + + if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { + return + } + + if IsList(rightValue) { + list, _ := rightValue.(*ListType) + v = list.indexDeepCmp(leftValue) >= 0 + } else if IsDict(rightValue) { + d, _ := rightValue.(map[any]any) + v = hasKey(d, leftValue) + } else { + err = self.errIncompatibleTypes(leftValue, rightValue) + } + return +} + +// init +func init() { + registerTermConstructor(SymKwIn, newInTerm) +} diff --git a/symbol.go b/symbol.go index 7087031..ac1e35d 100644 --- a/symbol.go +++ b/symbol.go @@ -99,6 +99,7 @@ const ( SymKwBut SymKwFunc SymKwBuiltin + SymKwIn SymKwInclude SymKwNil ) @@ -112,6 +113,7 @@ func init() { "BUILTIN": SymKwBuiltin, "BUT": SymKwBut, "FUNC": SymKwFunc, + "IN": SymKwIn, "INCLUDE": SymKwInclude, "NOT": SymKwNot, "OR": SymKwOr,