diff --git a/expr.go b/expr.go index 73765da..4f43938 100644 --- a/expr.go +++ b/expr.go @@ -43,6 +43,7 @@ func parseExpr(expr string) (*Expr, error) { return nil, err } sortPriority(e) + _, p.expr = e.Optimize() return p, nil } func (p *Expr) parseExprNode(expr *string, e ExprNode) error { @@ -262,6 +263,7 @@ type ExprNode interface { SetRightOperand(ExprNode) String() string Run(context.Context, string, *TagExpr) interface{} + Optimize() (bool, ExprNode) } // var _ ExprNode = new(exprBackground) diff --git a/spec_func.go b/spec_func.go index 610fc88..409b7a9 100644 --- a/spec_func.go +++ b/spec_func.go @@ -136,6 +136,10 @@ func (f *funcExprNode) Run(ctx context.Context, currField string, tagExpr *TagEx return realValue(f.fn(args...), f.boolOpposite, f.signOpposite) } +func (f *funcExprNode) Optimize() (bool, ExprNode) { + return false, f +} + // --------------------------- Built-in function --------------------------- func init() { funcList["regexp"] = readRegexpFuncExprNode @@ -282,6 +286,10 @@ func (re *regexpFuncExprNode) Run(ctx context.Context, currField string, tagExpr return false } +func (re *regexpFuncExprNode) Optimize() (bool, ExprNode) { + return false, re +} + type sprintfFuncExprNode struct { exprBackground format string @@ -342,3 +350,7 @@ func (se *sprintfFuncExprNode) Run(ctx context.Context, currField string, tagExp } return fmt.Sprintf(se.format, args...) } + +func (se *sprintfFuncExprNode) Optimize() (bool, ExprNode) { + return false, se +} diff --git a/spec_operand.go b/spec_operand.go index b18e6ce..218d844 100644 --- a/spec_operand.go +++ b/spec_operand.go @@ -57,6 +57,19 @@ func (ge *groupExprNode) Run(ctx context.Context, currField string, tagExpr *Tag return realValue(ge.rightOperand.Run(ctx, currField, tagExpr), ge.boolOpposite, ge.signOpposite) } +func (ge *groupExprNode) Optimize() (bool, ExprNode) { + if ge.rightOperand == nil { + return true, &nilExprNode{val: nil} + } + + optimize, node := ge.rightOperand.Optimize() + if optimize { + ge.rightOperand = node + } + + return false, ge +} + type boolExprNode struct { exprBackground val bool @@ -91,6 +104,10 @@ func (be *boolExprNode) Run(ctx context.Context, currField string, tagExpr *TagE return be.val } +func (be *boolExprNode) Optimize() (bool, ExprNode) { + return false, be +} + type stringExprNode struct { exprBackground val interface{} @@ -115,6 +132,10 @@ func (se *stringExprNode) Run(ctx context.Context, currField string, tagExpr *Ta return se.val } +func (se *stringExprNode) Optimize() (bool, ExprNode) { + return false, se +} + type digitalExprNode struct { exprBackground val interface{} @@ -144,6 +165,10 @@ func (de *digitalExprNode) Run(ctx context.Context, currField string, tagExpr *T return de.val } +func (de *digitalExprNode) Optimize() (bool, ExprNode) { + return true, de +} + type nilExprNode struct { exprBackground val interface{} @@ -153,6 +178,10 @@ func (ne *nilExprNode) String() string { return "" } +func (ne *nilExprNode) Optimize() (bool, ExprNode) { + return false, ne +} + var nilRegexp = regexp.MustCompile(`^nil([\)\],\|&!= \t]{1}|$)`) func readNilExprNode(expr *string) ExprNode { @@ -197,6 +226,10 @@ func (ve *variableExprNode) Run(ctx context.Context, variableName string, _ *Tag } } +func (ve *variableExprNode) Optimize() (bool, ExprNode) { + return false, ve +} + var variableRegex = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*`) func readVariableExprNode(expr *string) ExprNode { diff --git a/spec_operator.go b/spec_operator.go index 72c4fc6..7a26ea4 100644 --- a/spec_operator.go +++ b/spec_operator.go @@ -44,6 +44,40 @@ func (ae *additionExprNode) Run(ctx context.Context, currField string, tagExpr * return v0 } + +func (ae *additionExprNode) Optimize() (bool, ExprNode) { + lsucc, newLeft := ae.leftOperand.Optimize() + if lsucc { + ae.leftOperand = newLeft + } + + rSucc, newRight := ae.rightOperand.Optimize() + if rSucc { + ae.rightOperand = newRight + } + + if lsucc && rSucc { + if _, ok := ae.leftOperand.(*selectorExprNode); !ok { // note + if _, iok := ae.rightOperand.(*selectorExprNode); !iok { + v0 := ae.leftOperand.Run(context.Background(), "", nil) + v1 := ae.rightOperand.Run(context.Background(), "", nil) + + if s0, ok := toFloat64(v0, false); ok { + s1, _ := toFloat64(v1, true) + return true, &digitalExprNode{val: s0 + s1} + } + if s0, ok := toString(v0, false); ok { + s1, _ := toString(v1, true) + return true, &digitalExprNode{val: s0 + s1} + } + return true, &digitalExprNode{val: v0} + } + } + } + + return false, ae +} + type multiplicationExprNode struct{ exprBackground } func (ae *multiplicationExprNode) String() string { @@ -58,6 +92,31 @@ func (ae *multiplicationExprNode) Run(ctx context.Context, currField string, tag return v0 * v1 } + +func (ae *multiplicationExprNode) Optimize() (bool, ExprNode) { + lsucc, newLeft := ae.leftOperand.Optimize() + if lsucc { + ae.leftOperand = newLeft + } + + rSucc, newRight := ae.rightOperand.Optimize() + if rSucc { + ae.rightOperand = newRight + } + + if lsucc && rSucc { + v1, _ := toFloat64(ae.rightOperand.Run(context.Background(), "", nil), true) + if v1 == 0 { + return true, &digitalExprNode{val: math.NaN()} + } + v0, _ := toFloat64(ae.leftOperand.Run(context.Background(), "", nil), true) + return true, &digitalExprNode{val: v0 * v1} + } + + return false, ae +} + + type divisionExprNode struct{ exprBackground } func (de *divisionExprNode) String() string { @@ -75,6 +134,30 @@ func (de *divisionExprNode) Run(ctx context.Context, currField string, tagExpr * return v0 / v1 } + +func (de *divisionExprNode) Optimize() (bool, ExprNode) { + lsucc, newLeft := de.leftOperand.Optimize() + if lsucc { + de.leftOperand = newLeft + } + + rSucc, newRight := de.rightOperand.Optimize() + if rSucc { + de.rightOperand = newRight + } + + if lsucc && rSucc { + v1, _ := toFloat64(de.rightOperand.Run(context.Background(), "", nil), true) + if v1 == 0 { + return true, &digitalExprNode{val: math.NaN()} + } + v0, _ := toFloat64(de.leftOperand.Run(context.Background(), "", nil), true) + return true, &digitalExprNode{val: v0 / v1} + } + + return false, de +} + type subtractionExprNode struct{ exprBackground } func (de *subtractionExprNode) String() string { @@ -89,6 +172,31 @@ func (de *subtractionExprNode) Run(ctx context.Context, currField string, tagExp return v0 - v1 } + +func (de *subtractionExprNode) Optimize() (bool, ExprNode) { + lsucc, newLeft := de.leftOperand.Optimize() + if lsucc { + de.leftOperand = newLeft + } + + rSucc, newRight := de.rightOperand.Optimize() + if rSucc { + de.rightOperand = newRight + } + + if lsucc && rSucc { + if _, ok := de.leftOperand.(*selectorExprNode); !ok { + if _, iok := de.rightOperand.(*selectorExprNode); !iok { + v0, _ := toFloat64(de.leftOperand.Run(context.Background(), "", nil), true) + v1, _ := toFloat64(de.rightOperand.Run(context.Background(), "", nil), true) + return true, &digitalExprNode{val: v0 - v1} + } + } + } + + return false, de +} + type remainderExprNode struct{ exprBackground } func (re *remainderExprNode) String() string { @@ -106,6 +214,10 @@ func (re *remainderExprNode) Run(ctx context.Context, currField string, tagExpr return float64(int64(v0) % int64(v1)) } +func (re *remainderExprNode) Optimize() (bool, ExprNode) { + return false, re +} + type equalExprNode struct{ exprBackground } func (ee *equalExprNode) String() string { @@ -143,6 +255,10 @@ func (ee *equalExprNode) Run(ctx context.Context, currField string, tagExpr *Tag return false } +func (ee *equalExprNode) Optimize() (bool, ExprNode) { + return false, ee +} + type notEqualExprNode struct{ equalExprNode } func (ne *notEqualExprNode) String() string { @@ -155,6 +271,10 @@ func (ne *notEqualExprNode) Run(ctx context.Context, currField string, tagExpr * return !ne.equalExprNode.Run(ctx, currField, tagExpr).(bool) } +func (ne *notEqualExprNode) Optimize() (bool, ExprNode) { + return false, ne +} + type greaterExprNode struct{ exprBackground } func (ge *greaterExprNode) String() string { @@ -180,6 +300,10 @@ func (ge *greaterExprNode) Run(ctx context.Context, currField string, tagExpr *T return false } +func (ge *greaterExprNode) Optimize() (bool, ExprNode) { + return false, ge +} + type greaterEqualExprNode struct{ exprBackground } func (ge *greaterEqualExprNode) String() string { @@ -205,6 +329,10 @@ func (ge *greaterEqualExprNode) Run(ctx context.Context, currField string, tagEx return false } +func (ge *greaterEqualExprNode) Optimize() (bool, ExprNode) { + return false, ge +} + type lessExprNode struct{ exprBackground } func (le *lessExprNode) String() string { @@ -230,6 +358,10 @@ func (le *lessExprNode) Run(ctx context.Context, currField string, tagExpr *TagE return false } +func (le *lessExprNode) Optimize() (bool, ExprNode) { + return false, le +} + type lessEqualExprNode struct{ exprBackground } func (le *lessEqualExprNode) String() string { @@ -255,6 +387,10 @@ func (le *lessEqualExprNode) Run(ctx context.Context, currField string, tagExpr return false } +func (le *lessEqualExprNode) Optimize() (bool, ExprNode) { + return false, le +} + type andExprNode struct{ exprBackground } func (ae *andExprNode) String() string { @@ -272,6 +408,10 @@ func (ae *andExprNode) Run(ctx context.Context, currField string, tagExpr *TagEx return true } +func (ae *andExprNode) Optimize() (bool, ExprNode) { + return false, ae +} + type orExprNode struct{ exprBackground } func (oe *orExprNode) String() string { @@ -288,3 +428,7 @@ func (oe *orExprNode) Run(ctx context.Context, currField string, tagExpr *TagExp } return false } + +func (oe *orExprNode) Optimize() (bool, ExprNode) { + return false, oe +} \ No newline at end of file diff --git a/spec_range.go b/spec_range.go index fecccad..717c6cd 100644 --- a/spec_range.go +++ b/spec_range.go @@ -74,6 +74,10 @@ func (re *rangeKvExprNode) Run(ctx context.Context, _ string, _ *TagExpr) interf return realValue(v, re.boolOpposite, re.signOpposite) } +func (re *rangeKvExprNode) Optimize() (bool, ExprNode) { + return false, re +} + type rangeFuncExprNode struct { exprBackground object ExprNode @@ -148,3 +152,7 @@ func (e *rangeFuncExprNode) Run(ctx context.Context, currField string, tagExpr * } return r } + +func (re *rangeFuncExprNode) Optimize() (bool, ExprNode) { + return false, re +} \ No newline at end of file diff --git a/spec_selector.go b/spec_selector.go index 7e00990..786ffaa 100644 --- a/spec_selector.go +++ b/spec_selector.go @@ -107,3 +107,7 @@ func (se *selectorExprNode) Run(ctx context.Context, currField string, tagExpr * v := tagExpr.getValue(field, subFields) return realValue(v, se.boolOpposite, se.signOpposite) } + +func (se *selectorExprNode) Optimize() (bool, ExprNode) { + return false, se +}