woodpecker/vendor/github.com/drone/expr/eval.go
Matt Leung 4c2ff78d20 Add expression parser evaulator for build filter
Vendor github.com/drone/expr
Vendor github.com/drone/expr/parse
2017-09-08 19:39:24 -07:00

158 lines
3.3 KiB
Go

package expr
import (
"bytes"
"path/filepath"
"regexp"
"github.com/drone/expr/parse"
)
// state represents the state of an execution. It's not part of the
// statement so that multiple executions of the same statement
// can execute in parallel.
type state struct {
node parse.Node
vars Row
}
// at marks the state to be on node n, for error reporting.
func (s *state) at(node parse.Node) {
s.node = node
}
// Walk functions step through the major pieces of the template structure,
// generating output as they go.
func (s *state) walk(node parse.BoolExpr) bool {
s.at(node)
switch node := node.(type) {
case *parse.ComparisonExpr:
return s.eval(node)
case *parse.AndExpr:
return s.walk(node.Left) && s.walk(node.Right)
case *parse.OrExpr:
return s.walk(node.Left) || s.walk(node.Right)
case *parse.NotExpr:
return !s.walk(node.Expr)
case *parse.ParenBoolExpr:
return s.walk(node.Expr)
default:
panic("invalid node type")
}
}
func (s *state) eval(node *parse.ComparisonExpr) bool {
switch node.Operator {
case parse.OperatorEq:
return s.evalEq(node)
case parse.OperatorGt:
return s.evalGt(node)
case parse.OperatorGte:
return s.evalGte(node)
case parse.OperatorLt:
return s.evalLt(node)
case parse.OperatorLte:
return s.evalLte(node)
case parse.OperatorNeq:
return !s.evalEq(node)
case parse.OperatorGlob:
return s.evalGlob(node)
case parse.OperatorNotGlob:
return !s.evalGlob(node)
case parse.OperatorRe:
return s.evalRegexp(node)
case parse.OperatorNotRe:
return !s.evalRegexp(node)
case parse.OperatorIn:
return s.evalIn(node)
case parse.OperatorNotIn:
return !s.evalIn(node)
default:
panic("inalid operator type")
}
}
func (s *state) evalEq(node *parse.ComparisonExpr) bool {
return bytes.Equal(
s.toValue(node.Left),
s.toValue(node.Right),
)
}
func (s *state) evalGt(node *parse.ComparisonExpr) bool {
return bytes.Compare(
s.toValue(node.Left),
s.toValue(node.Right),
) == 1
}
func (s *state) evalGte(node *parse.ComparisonExpr) bool {
return bytes.Compare(
s.toValue(node.Left),
s.toValue(node.Right),
) >= 0
}
func (s *state) evalLt(node *parse.ComparisonExpr) bool {
return bytes.Compare(
s.toValue(node.Left),
s.toValue(node.Right),
) == -1
}
func (s *state) evalLte(node *parse.ComparisonExpr) bool {
return bytes.Compare(
s.toValue(node.Left),
s.toValue(node.Right),
) <= 0
}
func (s *state) evalGlob(node *parse.ComparisonExpr) bool {
match, _ := filepath.Match(
string(s.toValue(node.Right)),
string(s.toValue(node.Left)),
)
return match
}
func (s *state) evalRegexp(node *parse.ComparisonExpr) bool {
match, _ := regexp.Match(
string(s.toValue(node.Right)),
s.toValue(node.Left),
)
return match
}
func (s *state) evalIn(node *parse.ComparisonExpr) bool {
left := s.toValue(node.Left)
right, ok := node.Right.(*parse.ArrayLit)
if !ok {
panic("expected array literal")
}
for _, expr := range right.Values {
if bytes.Equal(left, s.toValue(expr)) {
return true
}
}
return false
}
func (s *state) toValue(expr parse.ValExpr) []byte {
switch node := expr.(type) {
case *parse.Field:
return s.vars.Field(node.Name)
case *parse.BasicLit:
return node.Value
default:
panic("invalid expression type")
}
}
// errRecover is the handler that turns panics into returns.
func errRecover(err *error) {
if e := recover(); e != nil {
*err = e.(error)
}
}