Skip to content

Commit f5595b2

Browse files
committed
feat: support 'range' tpl function #12
Change-Id: I0999d3942850bbe1e1e07841c0a71e9ef31793a2
1 parent 3b9f75d commit f5595b2

14 files changed

+601
-260
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ NOTE: **The `exprName` under the same struct field cannot be the same!**
139139
|`regexp('^\\w*$', (X)$)`|Regular match the struct field X, return boolean|
140140
|`regexp('^\\w*$')`|Regular match the current struct field, return boolean|
141141
|`sprintf('X value: %v', (X)$)`|`fmt.Sprintf`, format the value of struct field X|
142+
|`range(KvExpr, forEachExpr)`|Iterate over an array, slice, or dictionary <br> - `#k` is the element key var <br> - `#v` is the element value var <br> - `##` is the number of elements <br> - e.g. [example](spec_range_test.go)|
142143

143144
<!-- |`(X)$k`|Traverse each element key of the struct field X(type: map, slice, array)|
144145
|`(X)$v`|Traverse each element value of the struct field X(type: map, slice, array)| -->

expr.go

+15-11
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package tagexpr
1616

1717
import (
18+
"context"
1819
"fmt"
1920
)
2021

@@ -44,7 +45,7 @@ func parseExpr(expr string) (*Expr, error) {
4445

4546
// run calculates the value of expression.
4647
func (p *Expr) run(field string, tagExpr *TagExpr) interface{} {
47-
return p.expr.Run(field, tagExpr)
48+
return p.expr.Run(context.Background(), field, tagExpr)
4849
}
4950

5051
func (p *Expr) parseOperand(expr *string) (e ExprNode) {
@@ -130,15 +131,18 @@ func (p *Expr) parseExprNode(expr *string, e ExprNode) (ExprNode, error) {
130131
}
131132
operand := p.readSelectorExprNode(expr)
132133
if operand == nil {
133-
var subExprNode *string
134-
operand, subExprNode = readGroupExprNode(expr)
135-
if operand != nil {
136-
_, err := p.parseExprNode(subExprNode, operand)
137-
if err != nil {
138-
return nil, err
134+
operand = p.readRangeKvExprNode(expr)
135+
if operand == nil {
136+
var subExprNode *string
137+
operand, subExprNode = readGroupExprNode(expr)
138+
if operand != nil {
139+
_, err := p.parseExprNode(subExprNode, operand)
140+
if err != nil {
141+
return nil, err
142+
}
143+
} else {
144+
operand = p.parseOperand(expr)
139145
}
140-
} else {
141-
operand = p.parseOperand(expr)
142146
}
143147
}
144148
if operand == nil {
@@ -252,7 +256,7 @@ type ExprNode interface {
252256
RightOperand() ExprNode
253257
SetLeftOperand(ExprNode)
254258
SetRightOperand(ExprNode)
255-
Run(string, *TagExpr) interface{}
259+
Run(context.Context, string, *TagExpr) interface{}
256260
}
257261

258262
var _ ExprNode = new(exprBackground)
@@ -287,4 +291,4 @@ func (eb *exprBackground) SetRightOperand(right ExprNode) {
287291
eb.rightOperand = right
288292
}
289293

290-
func (*exprBackground) Run(string, *TagExpr) interface{} { return nil }
294+
func (*exprBackground) Run(context.Context, string, *TagExpr) interface{} { return nil }

expr_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,13 @@ func TestExpr(t *testing.T) {
6767
{expr: "20%(7%5)", val: 0.0},
6868
// Relational operator
6969
{expr: "50 == 5", val: false},
70-
{expr: "'50'==50", val: false},
70+
{expr: "'50'==50", val: true},
7171
{expr: "'50'=='50'", val: true},
7272
{expr: "'50' =='5' == true", val: false},
7373
{expr: "50== 50 == false", val: false},
7474
{expr: "50== 50 == true ==true==true", val: true},
7575
{expr: "50 != 5", val: true},
76-
{expr: "'50'!=50", val: true},
76+
{expr: "'50'!=50", val: false},
7777
{expr: "'50'!= '50'", val: false},
7878
{expr: "'50' !='5' != true", val: false},
7979
{expr: "50!= 50 == false", val: true},
@@ -166,7 +166,7 @@ func TestBuiltInFunc(t *testing.T) {
166166
}{
167167
{expr: "len('abc')", val: 3.0},
168168
{expr: "len('abc')+2*2/len('cd')", val: 5.0},
169-
{expr: "len(0)", val: 0},
169+
{expr: "len(0)", val: 0.0},
170170

171171
{expr: "regexp('a\\d','a0')", val: true},
172172
{expr: "regexp('^a\\d$','a0')", val: true},

spec_func.go

+52-39
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package tagexpr
1616

1717
import (
18+
"context"
1819
"fmt"
1920
"reflect"
2021
"regexp"
@@ -44,44 +45,54 @@ func RegFunc(funcName string, fn func(...interface{}) interface{}, force ...bool
4445
return nil
4546
}
4647

47-
func newFunc(funcName string, fn func(...interface{}) interface{}) func(*Expr, *string) ExprNode {
48+
func (p *Expr) parseFuncSign(funcName string, expr *string) (boolOpposite *bool, signOpposite *bool, args []ExprNode, found bool) {
4849
prefix := funcName + "("
4950
length := len(funcName)
50-
return func(p *Expr, expr *string) ExprNode {
51-
last, boolOpposite := getBoolOpposite(expr)
52-
if !strings.HasPrefix(last, prefix) {
53-
return nil
51+
last, boolOpposite, signOpposite := getBoolAndSignOpposite(expr)
52+
if !strings.HasPrefix(last, prefix) {
53+
return
54+
}
55+
*expr = last[length:]
56+
lastStr := *expr
57+
subExprNode := readPairedSymbol(expr, '(', ')')
58+
if subExprNode == nil {
59+
return
60+
}
61+
*subExprNode = "," + *subExprNode
62+
for {
63+
if strings.HasPrefix(*subExprNode, ",") {
64+
*subExprNode = (*subExprNode)[1:]
65+
operand := newGroupExprNode()
66+
_, err := p.parseExprNode(trimLeftSpace(subExprNode), operand)
67+
if err != nil {
68+
*expr = lastStr
69+
return
70+
}
71+
sortPriority(operand.RightOperand())
72+
args = append(args, operand)
73+
} else {
74+
*expr = lastStr
75+
return
76+
}
77+
trimLeftSpace(subExprNode)
78+
if len(*subExprNode) == 0 {
79+
found = true
80+
return
5481
}
55-
*expr = last[length:]
56-
lastStr := *expr
57-
subExprNode := readPairedSymbol(expr, '(', ')')
58-
if subExprNode == nil {
82+
}
83+
}
84+
85+
func newFunc(funcName string, fn func(...interface{}) interface{}) func(*Expr, *string) ExprNode {
86+
return func(p *Expr, expr *string) ExprNode {
87+
boolOpposite, signOpposite, args, found := p.parseFuncSign(funcName, expr)
88+
if !found {
5989
return nil
6090
}
61-
f := &funcExprNode{
91+
return &funcExprNode{
6292
fn: fn,
6393
boolOpposite: boolOpposite,
64-
}
65-
*subExprNode = "," + *subExprNode
66-
for {
67-
if strings.HasPrefix(*subExprNode, ",") {
68-
*subExprNode = (*subExprNode)[1:]
69-
operand := newGroupExprNode()
70-
_, err := p.parseExprNode(trimLeftSpace(subExprNode), operand)
71-
if err != nil {
72-
*expr = lastStr
73-
return nil
74-
}
75-
sortPriority(operand.RightOperand())
76-
f.args = append(f.args, operand)
77-
} else {
78-
*expr = lastStr
79-
return nil
80-
}
81-
trimLeftSpace(subExprNode)
82-
if len(*subExprNode) == 0 {
83-
return f
84-
}
94+
signOpposite: signOpposite,
95+
args: args,
8596
}
8697
}
8798
}
@@ -91,23 +102,25 @@ type funcExprNode struct {
91102
args []ExprNode
92103
fn func(...interface{}) interface{}
93104
boolOpposite *bool
105+
signOpposite *bool
94106
}
95107

96-
func (f *funcExprNode) Run(currField string, tagExpr *TagExpr) interface{} {
108+
func (f *funcExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} {
97109
var args []interface{}
98110
if n := len(f.args); n > 0 {
99111
args = make([]interface{}, n)
100112
for k, v := range f.args {
101-
args[k] = v.Run(currField, tagExpr)
113+
args[k] = v.Run(ctx, currField, tagExpr)
102114
}
103115
}
104-
return realValue(f.fn(args...), f.boolOpposite)
116+
return realValue(f.fn(args...), f.boolOpposite, f.signOpposite)
105117
}
106118

107119
// --------------------------- Built-in function ---------------------------
108120
func init() {
109121
funcList["regexp"] = readRegexpFuncExprNode
110122
funcList["sprintf"] = readSprintfFuncExprNode
123+
funcList["range"] = readRangeFuncExprNode
111124
err := RegFunc("len", func(args ...interface{}) (n interface{}) {
112125
if len(args) != 1 {
113126
return 0
@@ -159,7 +172,7 @@ type regexpFuncExprNode struct {
159172
}
160173

161174
func readRegexpFuncExprNode(p *Expr, expr *string) ExprNode {
162-
last, boolOpposite := getBoolOpposite(expr)
175+
last, boolOpposite, _ := getBoolAndSignOpposite(expr)
163176
if !strings.HasPrefix(last, "regexp(") {
164177
return nil
165178
}
@@ -207,8 +220,8 @@ func readRegexpFuncExprNode(p *Expr, expr *string) ExprNode {
207220
return e
208221
}
209222

210-
func (re *regexpFuncExprNode) Run(currField string, tagExpr *TagExpr) interface{} {
211-
param := re.rightOperand.Run(currField, tagExpr)
223+
func (re *regexpFuncExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} {
224+
param := re.rightOperand.Run(ctx, currField, tagExpr)
212225
switch v := param.(type) {
213226
case string:
214227
bol := re.re.MatchString(v)
@@ -276,12 +289,12 @@ func readSprintfFuncExprNode(p *Expr, expr *string) ExprNode {
276289
}
277290
}
278291

279-
func (se *sprintfFuncExprNode) Run(currField string, tagExpr *TagExpr) interface{} {
292+
func (se *sprintfFuncExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} {
280293
var args []interface{}
281294
if n := len(se.args); n > 0 {
282295
args = make([]interface{}, n)
283296
for i, e := range se.args {
284-
args[i] = e.Run(currField, tagExpr)
297+
args[i] = e.Run(ctx, currField, tagExpr)
285298
}
286299
}
287300
return fmt.Sprintf(se.format, args...)

0 commit comments

Comments
 (0)