Skip to content
This repository was archived by the owner on Dec 23, 2025. It is now read-only.

Commit f388ce2

Browse files
committed
chore: update '!', and example
Change-Id: Icd5ba7a573c31b1ff19f852586092cc001c2fe7b
1 parent bc0fea6 commit f388ce2

File tree

6 files changed

+167
-38
lines changed

6 files changed

+167
-38
lines changed

README.md

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,51 @@ In development
1010

1111
```go
1212
type T struct {
13-
A int `tagexpr:"$<0||$>=100"`
14-
B string `tagexpr:"len($)>1 || regexp('^\\w*$')"`
15-
C bool `tagexpr:"{expr1:(F.G)$>0 && $}{expr2:'C must be true when T.F.G>0'}"`
16-
D []string `tagexpr:"{expr1:len($)>0 && $[0]=='D'} {expr2:sprintf('Invalid D:%s',$)}"`
17-
E map[string]int `tagexpr:"len($)"`
18-
F struct{ G int }
13+
A int `tagexpr:"$<0||$>=100"`
14+
B string `tagexpr:"len($)>1 && regexp('^\\w*$')"`
15+
C bool `tagexpr:"{expr1:(f.g)$>0 && $}{expr2:'C must be true when T.f.g>0'}"`
16+
d []string `tagexpr:"{match:len($)>0 && $[0]=='D'} {msg:sprintf('Invalid d: %v',$)}"`
17+
e map[string]int `tagexpr:"len($)==$['len']"`
18+
f struct {
19+
g int `tagexpr:"$"`
20+
}
1921
}
22+
vm := New("tagexpr")
23+
err := vm.WarmUp(new(T))
24+
if err != nil {
25+
panic(err)
26+
}
27+
t := &T{
28+
A: 107,
29+
B: "abc",
30+
C: true,
31+
d: []string{"x", "y"},
32+
e: map[string]int{"len": 1},
33+
f: struct {
34+
g int `tagexpr:"$"`
35+
}{1},
36+
}
37+
tagExpr, err := vm.Run(t)
38+
if err != nil {
39+
panic(err)
40+
}
41+
fmt.Println(tagExpr.Eval("A.$"))
42+
fmt.Println(tagExpr.Eval("B.$"))
43+
fmt.Println(tagExpr.Eval("C.expr1"))
44+
fmt.Println(tagExpr.Eval("C.expr2"))
45+
if !tagExpr.Eval("d.match").(bool) {
46+
fmt.Println(tagExpr.Eval("d.msg"))
47+
}
48+
fmt.Println(tagExpr.Eval("e.$"))
49+
fmt.Println(tagExpr.Eval("f.g.$"))
50+
// Output:
51+
// true
52+
// true
53+
// true
54+
// C must be true when T.f.g>0
55+
// Invalid d: [x y]
56+
// true
57+
// 1
2058
```
2159

2260
## Syntax

spec_operand.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import (
1111

1212
type groupExprNode struct {
1313
exprBackground
14-
boolPrefix bool
14+
boolPrefix *bool
1515
}
1616

17-
func newGroupExprNode() ExprNode { return &groupExprNode{boolPrefix: true} }
17+
func newGroupExprNode() ExprNode { return &groupExprNode{} }
1818

1919
func readGroupExprNode(expr *string) (grp ExprNode, subExprNode *string) {
2020
s := *expr
@@ -26,11 +26,13 @@ func readGroupExprNode(expr *string) (grp ExprNode, subExprNode *string) {
2626
return nil, nil
2727
}
2828
e := &groupExprNode{}
29-
var boolPrefix = true
30-
for ; i > 0; i-- {
31-
boolPrefix = !boolPrefix
29+
if i > 0 {
30+
var bol = true
31+
for ; i > 0; i-- {
32+
bol = !bol
33+
}
34+
e.boolPrefix = &bol
3235
}
33-
e.boolPrefix = boolPrefix
3436
return e, sptr
3537
}
3638

@@ -39,10 +41,13 @@ func (ge *groupExprNode) Run(currField string, tagExpr *TagExpr) interface{} {
3941
return nil
4042
}
4143
v := ge.rightOperand.Run(currField, tagExpr)
44+
if ge.boolPrefix == nil {
45+
return v
46+
}
4247
if r, ok := v.(bool); ok {
43-
return ge.boolPrefix == r
48+
return *ge.boolPrefix == r
4449
}
45-
return v
50+
return nil
4651
}
4752

4853
type boolExprNode struct {

spec_selector.go

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,18 @@ type selectorExprNode struct {
99
exprBackground
1010
field, name string
1111
subExprs []ExprNode
12+
boolPrefix *bool
1213
}
1314

1415
func (p *Expr) readSelectorExprNode(expr *string) ExprNode {
15-
field, name, subSelector, found := findSelector(expr)
16+
field, name, subSelector, boolPrefix, found := findSelector(expr)
1617
if !found {
1718
return nil
1819
}
1920
operand := &selectorExprNode{
20-
field: field,
21-
name: name,
21+
field: field,
22+
name: name,
23+
boolPrefix: boolPrefix,
2224
}
2325
operand.subExprs = make([]ExprNode, 0, len(subSelector))
2426
for _, s := range subSelector {
@@ -33,31 +35,38 @@ func (p *Expr) readSelectorExprNode(expr *string) ExprNode {
3335
return operand
3436
}
3537

36-
var selectorRegexp = regexp.MustCompile(`^(\([ \t]*[A-Za-z_]+[A-Za-z0-9_\.]*[ \t]*\))?(\$)([\[\+\-\*\/%><\|&!=\^ \t\\]|$)`)
38+
var selectorRegexp = regexp.MustCompile(`^(\!*)(\([ \t]*[A-Za-z_]+[A-Za-z0-9_\.]*[ \t]*\))?(\$)([\[\+\-\*\/%><\|&!=\^ \t\\]|$)`)
3739

38-
func findSelector(expr *string) (field string, name string, subSelector []string, found bool) {
40+
func findSelector(expr *string) (field string, name string, subSelector []string, boolPrefix *bool, found bool) {
3941
raw := *expr
4042
a := selectorRegexp.FindAllStringSubmatch(raw, -1)
4143
if len(a) != 1 {
4244
return
4345
}
4446
r := a[0]
45-
if s0 := r[1]; len(s0) > 0 {
47+
if s0 := r[2]; len(s0) > 0 {
4648
field = strings.TrimSpace(s0[1 : len(s0)-1])
4749
}
48-
name = r[2]
49-
*expr = (*expr)[len(a[0][0])-len(r[3]):]
50+
name = r[3]
51+
*expr = (*expr)[len(a[0][0])-len(r[4]):]
5052
for {
5153
sub := readPairedSymbol(expr, '[', ']')
5254
if sub == nil {
5355
break
5456
}
5557
if *sub == "" || (*sub)[0] == '[' {
5658
*expr = raw
57-
return "", "", nil, false
59+
return "", "", nil, nil, false
5860
}
5961
subSelector = append(subSelector, strings.TrimSpace(*sub))
6062
}
63+
if boolNum := len(r[1]); boolNum > 0 {
64+
bol := true
65+
for i := len(r[1]); i > 0; i-- {
66+
bol = !bol
67+
}
68+
boolPrefix = &bol
69+
}
6170
found = true
6271
return
6372
}
@@ -67,8 +76,16 @@ func (ve *selectorExprNode) Run(currField string, tagExpr *TagExpr) interface{}
6776
for _, e := range ve.subExprs {
6877
subFields = append(subFields, e.Run(currField, tagExpr))
6978
}
70-
if ve.field != "" {
71-
return tagExpr.getValue(ve.field, subFields)
79+
field := ve.field
80+
if field == "" {
81+
field = currField
82+
}
83+
v := tagExpr.getValue(field, subFields)
84+
if ve.boolPrefix == nil {
85+
return v
86+
}
87+
if r, ok := v.(bool); ok {
88+
return *ve.boolPrefix == r
7289
}
73-
return tagExpr.getValue(currField, subFields)
90+
return nil
7491
}

spec_test.go

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package tagexpr
22

33
import (
4+
"fmt"
45
"reflect"
56
"testing"
67
)
@@ -79,35 +80,46 @@ func TestReadDigitalExprNode(t *testing.T) {
7980
}
8081

8182
func TestFindSelector(t *testing.T) {
83+
var falsePtr = new(bool)
84+
var truePtr = new(bool)
85+
*truePtr = true
8286
var cases = []struct {
8387
expr string
8488
field string
8589
name string
8690
subSelector []string
91+
boolPrefix *bool
8792
found bool
8893
last string
8994
}{
9095
{expr: "$", field: "", name: "$", subSelector: nil, found: true, last: ""},
91-
{expr: "()$", field: "", name: "", subSelector: nil, found: false, last: "()$"},
92-
{expr: "(0)$", field: "", name: "", subSelector: nil, found: false, last: "(0)$"},
96+
{expr: "!!$", field: "", name: "$", subSelector: nil, boolPrefix: truePtr, found: true, last: ""},
97+
{expr: "!$", field: "", name: "$", subSelector: nil, boolPrefix: falsePtr, found: true, last: ""},
98+
{expr: "()$", field: "", name: "", subSelector: nil, last: "()$"},
99+
{expr: "(0)$", field: "", name: "", subSelector: nil, last: "(0)$"},
93100
{expr: "(A)$", field: "A", name: "$", subSelector: nil, found: true, last: ""},
101+
{expr: "!(A)$", field: "A", name: "$", subSelector: nil, boolPrefix: falsePtr, found: true, last: ""},
94102
{expr: "(A0)$", field: "A0", name: "$", subSelector: nil, found: true, last: ""},
95-
{expr: "(A0)$(A1)$", field: "", name: "", subSelector: nil, found: false, last: "(A0)$(A1)$"},
103+
{expr: "!!(A0)$", field: "A0", name: "$", subSelector: nil, boolPrefix: truePtr, found: true, last: ""},
104+
{expr: "(A0)$(A1)$", field: "", name: "", subSelector: nil, last: "(A0)$(A1)$"},
96105
{expr: "(A0)$ $(A1)$", field: "A0", name: "$", subSelector: nil, found: true, last: " $(A1)$"},
97-
{expr: "$a", field: "", name: "", subSelector: nil, found: false, last: "$a"},
106+
{expr: "$a", field: "", name: "", subSelector: nil, last: "$a"},
98107
{expr: "$[1]['a']", field: "", name: "$", subSelector: []string{"1", "'a'"}, found: true, last: ""},
99-
{expr: "$[1][]", field: "", name: "", subSelector: nil, found: false, last: "$[1][]"},
100-
{expr: "$[[]]", field: "", name: "", subSelector: nil, found: false, last: "$[[]]"},
101-
{expr: "$[[[]]]", field: "", name: "", subSelector: nil, found: false, last: "$[[[]]]"},
108+
{expr: "$[1][]", field: "", name: "", subSelector: nil, last: "$[1][]"},
109+
{expr: "$[[]]", field: "", name: "", subSelector: nil, last: "$[[]]"},
110+
{expr: "$[[[]]]", field: "", name: "", subSelector: nil, last: "$[[[]]]"},
102111
{expr: "$[(A)$[1]]", field: "", name: "$", subSelector: []string{"(A)$[1]"}, found: true, last: ""},
103112
{expr: "$>0&&$<10", field: "", name: "$", subSelector: nil, found: true, last: ">0&&$<10"},
104113
}
105114
for _, c := range cases {
106115
last := c.expr
107-
field, name, subSelector, found := findSelector(&last)
116+
field, name, subSelector, boolPrefix, found := findSelector(&last)
108117
if found != c.found {
109118
t.Fatalf("%q found: got: %v, want: %v", c.expr, found, c.found)
110119
}
120+
if printBoolPtr(boolPrefix) != printBoolPtr(c.boolPrefix) {
121+
t.Fatalf("%q boolPrefix: got: %v, want: %v", c.expr, printBoolPtr(boolPrefix), printBoolPtr(c.boolPrefix))
122+
}
111123
if field != c.field {
112124
t.Fatalf("%q field: got: %q, want: %q", c.expr, field, c.field)
113125
}
@@ -122,3 +134,10 @@ func TestFindSelector(t *testing.T) {
122134
}
123135
}
124136
}
137+
func printBoolPtr(b *bool) string {
138+
var v interface{} = b
139+
if b != nil {
140+
v = *b
141+
}
142+
return fmt.Sprint(v)
143+
}

tagexpr.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -345,10 +345,6 @@ func (t *TagExpr) getValue(field string, subFields []interface{}) (v interface{}
345345
return v
346346
}
347347
vv := reflect.ValueOf(v)
348-
fmt.Println("=======", subFields, vv.Kind(), vv.Interface())
349-
// if len(subFields) == 0 {
350-
// return v
351-
// }
352348
for _, k := range subFields {
353349
for vv.Kind() == reflect.Ptr {
354350
vv = vv.Elem()

tagexpr_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,60 @@
11
package tagexpr
22

33
import (
4+
"fmt"
45
"reflect"
56
"testing"
67
)
78

9+
func ExampleVMFunc() {
10+
type T struct {
11+
A int `tagexpr:"$<0||$>=100"`
12+
B string `tagexpr:"len($)>1 && regexp('^\\w*$')"`
13+
C bool `tagexpr:"{expr1:(f.g)$>0 && $}{expr2:'C must be true when T.f.g>0'}"`
14+
d []string `tagexpr:"{match:len($)>0 && $[0]=='D'} {msg:sprintf('Invalid d: %v',$)}"`
15+
e map[string]int `tagexpr:"len($)==$['len']"`
16+
f struct {
17+
g int `tagexpr:"$"`
18+
}
19+
}
20+
vm := New("tagexpr")
21+
err := vm.WarmUp(new(T))
22+
if err != nil {
23+
panic(err)
24+
}
25+
t := &T{
26+
A: 107,
27+
B: "abc",
28+
C: true,
29+
d: []string{"x", "y"},
30+
e: map[string]int{"len": 1},
31+
f: struct {
32+
g int `tagexpr:"$"`
33+
}{1},
34+
}
35+
tagExpr, err := vm.Run(t)
36+
if err != nil {
37+
panic(err)
38+
}
39+
fmt.Println(tagExpr.Eval("A.$"))
40+
fmt.Println(tagExpr.Eval("B.$"))
41+
fmt.Println(tagExpr.Eval("C.expr1"))
42+
fmt.Println(tagExpr.Eval("C.expr2"))
43+
if !tagExpr.Eval("d.match").(bool) {
44+
fmt.Println(tagExpr.Eval("d.msg"))
45+
}
46+
fmt.Println(tagExpr.Eval("e.$"))
47+
fmt.Println(tagExpr.Eval("f.g.$"))
48+
// Output:
49+
// true
50+
// true
51+
// true
52+
// C must be true when T.f.g>0
53+
// Invalid d: [x y]
54+
// true
55+
// 1
56+
}
57+
858
func TestVMFunc(t *testing.T) {
959
g := &struct {
1060
_ int
@@ -84,6 +134,8 @@ func TestVMFunc(t *testing.T) {
84134
m map[string][]string
85135
} `tagexpr:"$['h']"`
86136
i string `tagexpr:"(g.s)$[0]+(g.m)$['0'][0]==$"`
137+
j bool `tagexpr:"!$"`
138+
k int `tagexpr:"!$"`
87139
}{
88140
A: 5.0,
89141
b: "x",
@@ -106,6 +158,8 @@ func TestVMFunc(t *testing.T) {
106158
"e.f.$": true,
107159
"g.h.$": "haha",
108160
"i.$": true,
161+
"j.$": true,
162+
"k.$": nil,
109163
},
110164
},
111165
}

0 commit comments

Comments
 (0)