Documentation ¶
Overview ¶
Package gval provides a generic expression language. All functions, infix and prefix operators can be replaced by composing languages into a new one.
The package contains concrete expression languages for common application in text, arithmetic, decimal arithmetic, propositional logic and so on. They can be used as basis for a custom expression language or to evaluate expressions directly.
Example ¶
package main import ( "fmt" "github.com/PaesslerAG/gval" ) func main() { vars := map[string]interface{}{"name": "World"} value, err := gval.Evaluate(`"Hello " + name + "!"`, vars) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: Hello World!
Index ¶
- func Evaluate(expression string, parameter interface{}, opts ...Language) (interface{}, error)
- func EvaluateWithContext(c context.Context, expression string, parameter interface{}, opts ...Language) (interface{}, error)
- type Evaluable
- func (e Evaluable) EvalBool(c context.Context, parameter interface{}) (bool, error)
- func (e Evaluable) EvalFloat64(c context.Context, parameter interface{}) (float64, error)
- func (e Evaluable) EvalInt(c context.Context, parameter interface{}) (int, error)
- func (e Evaluable) EvalString(c context.Context, parameter interface{}) (string, error)
- func (e Evaluable) IsConst() bool
- type Evaluables
- type Language
- func Arithmetic() Language
- func Base() Language
- func Bitmask() Language
- func Constant(name string, value interface{}) Language
- func DecimalArithmetic() Language
- func DefaultExtension(ext func(context.Context, *Parser) (Evaluable, error)) Language
- func Full(extensions ...Language) Language
- func Function(name string, function interface{}) Language
- func Ident() Language
- func InfixBoolOperator(name string, f func(a, b bool) (interface{}, error)) Language
- func InfixDecimalOperator(name string, f func(a, b decimal.Decimal) (interface{}, error)) Language
- func InfixEvalOperator(name string, f func(a, b Evaluable) (Evaluable, error)) Language
- func InfixNumberOperator(name string, f func(a, b float64) (interface{}, error)) Language
- func InfixOperator(name string, f func(a, b interface{}) (interface{}, error)) Language
- func InfixShortCircuit(name string, f func(a interface{}) (interface{}, bool)) Language
- func InfixTextOperator(name string, f func(a, b string) (interface{}, error)) Language
- func Init(ext func(context.Context, *Parser) (Evaluable, error)) Language
- func JSON() Language
- func NewLanguage(bases ...Language) Language
- func Parentheses() Language
- func PostfixOperator(name string, ext func(context.Context, *Parser, Evaluable) (Evaluable, error)) Language
- func Precedence(name string, operatorPrecendence uint8) Language
- func PrefixExtension(r rune, ext func(context.Context, *Parser) (Evaluable, error)) Language
- func PrefixMetaPrefix(r rune, ...) Language
- func PrefixOperator(name string, e Evaluable) Language
- func PropositionalLogic() Language
- func Text() Language
- func VariableSelector(selector func(path Evaluables) Evaluable) Language
- type Parser
- func (p *Parser) Camouflage(unit string, expected ...rune)
- func (*Parser) Const(value interface{}) Evaluable
- func (p *Parser) Expected(unit string, expected ...rune) error
- func (p *Parser) Next() rune
- func (p *Parser) ParseExpression(c context.Context) (eval Evaluable, err error)
- func (p *Parser) ParseNextExpression(c context.Context) (eval Evaluable, err error)
- func (p *Parser) ParseSublanguage(c context.Context, l Language) (Evaluable, error)
- func (p *Parser) Peek() rune
- func (p *Parser) Scan() rune
- func (p *Parser) SetIsIdentRuneFunc(fn func(ch rune, i int) bool)
- func (p *Parser) SetMode(mode uint)
- func (p *Parser) SetWhitespace(chars ...rune)
- func (p *Parser) TokenText() string
- func (p *Parser) Var(path ...Evaluable) Evaluable
- type Selector
Examples ¶
- Package
- Evaluable
- Evaluable.EvalBool
- Evaluable.EvalInt
- Evaluate
- Evaluate (Accessor)
- Evaluate (Arithmetic)
- Evaluate (Array)
- Evaluate (ComplexAccessor)
- Evaluate (DateComparison)
- Evaluate (Encoding)
- Evaluate (FlatAccessor)
- Evaluate (Float64)
- Evaluate (Jsonpath)
- Evaluate (NestedAccessor)
- Evaluate (NestedParameter)
- Evaluate (String)
- Evaluate (Strlen)
- Language
- Parser.ParseSublanguage
- Selector
- VariableSelector
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Evaluate ¶
Evaluate given parameter with given expression in gval full language
Example ¶
package main import ( "fmt" "github.com/PaesslerAG/gval" ) func main() { value, err := gval.Evaluate("foo > 0", map[string]interface{}{ "foo": -1., }) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: false
Example (Accessor) ¶
package main import ( "fmt" "github.com/PaesslerAG/gval" ) type exampleType struct { Hello string } func (e exampleType) World() string { return "world" } func main() { value, err := gval.Evaluate(`foo.Hello + foo.World()`, map[string]interface{}{ "foo": exampleType{Hello: "hello "}, }) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: hello world
Example (Arithmetic) ¶
package main import ( "fmt" "github.com/PaesslerAG/gval" ) func main() { value, err := gval.Evaluate("(requests_made * requests_succeeded / 100) >= 90", map[string]interface{}{ "requests_made": 100, "requests_succeeded": 80, }) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: false
Example (Array) ¶
package main import ( "fmt" "github.com/PaesslerAG/gval" ) func main() { value, err := gval.Evaluate("foo[0]", map[string]interface{}{ "foo": []interface{}{-1.}, }) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: -1
Example (ComplexAccessor) ¶
package main import ( "fmt" "github.com/PaesslerAG/gval" ) func main() { value, err := gval.Evaluate(`foo["b" + "a" + "r"]`, map[string]interface{}{ "foo": map[string]interface{}{"bar": -1.}, }) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: -1
Example (DateComparison) ¶
package main import ( "fmt" "time" "github.com/PaesslerAG/gval" ) func main() { value, err := gval.Evaluate("date(`2014-01-02`) > date(`2014-01-01 23:59:59`)", nil, // define Date comparison because it is not part expression language gval gval.InfixOperator(">", func(a, b interface{}) (interface{}, error) { date1, ok1 := a.(time.Time) date2, ok2 := b.(time.Time) if ok1 && ok2 { return date1.After(date2), nil } return nil, fmt.Errorf("unexpected operands types (%T) > (%T)", a, b) }), ) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: true
Example (Encoding) ¶
package main import ( "fmt" "github.com/PaesslerAG/gval" ) func main() { value, err := gval.Evaluate(`(7 < "47" == true ? "hello world!\n\u263a" : "good bye\n")`+" + ` more text`", nil, gval.Function("strlen", func(args ...interface{}) (interface{}, error) { length := len(args[0].(string)) return (float64)(length), nil })) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: hello world! ☺ more text
Example (FlatAccessor) ¶
package main import ( "fmt" "github.com/PaesslerAG/gval" ) type exampleType struct { Hello string } func (e exampleType) World() string { return "world" } func main() { value, err := gval.Evaluate(`Hello + World()`, exampleType{Hello: "hello "}, ) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: hello world
Example (Float64) ¶
package main import ( "fmt" "github.com/PaesslerAG/gval" ) func main() { value, err := gval.Evaluate("(mem_used / total_mem) * 100", map[string]interface{}{ "total_mem": 1024, "mem_used": 512, }) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: 50
Example (Jsonpath) ¶
package main import ( "fmt" "github.com/PaesslerAG/gval" "github.com/PaesslerAG/jsonpath" ) func main() { value, err := gval.Evaluate(`$["response-time"]`, map[string]interface{}{ "response-time": 100, }, jsonpath.Language(), ) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: 100
Example (NestedAccessor) ¶
package main import ( "fmt" "github.com/PaesslerAG/gval" ) type exampleType struct { Hello string } func (e exampleType) World() string { return "world" } func main() { value, err := gval.Evaluate(`foo.Bar.Hello + foo.Bar.World()`, map[string]interface{}{ "foo": struct{ Bar exampleType }{ Bar: exampleType{Hello: "hello "}, }, }) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: hello world
Example (NestedParameter) ¶
package main import ( "fmt" "github.com/PaesslerAG/gval" ) func main() { value, err := gval.Evaluate("foo.bar > 0", map[string]interface{}{ "foo": map[string]interface{}{"bar": -1.}, }) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: false
Example (String) ¶
package main import ( "fmt" "github.com/PaesslerAG/gval" ) func main() { value, err := gval.Evaluate(`http_response_body == "service is ok"`, map[string]interface{}{ "http_response_body": "service is ok", }) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: true
Example (Strlen) ¶
package main import ( "fmt" "github.com/PaesslerAG/gval" ) func main() { value, err := gval.Evaluate(`strlen("someReallyLongInputString") <= 16`, nil, gval.Function("strlen", func(args ...interface{}) (interface{}, error) { length := len(args[0].(string)) return (float64)(length), nil })) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: false
Types ¶
type Evaluable ¶
Evaluable evaluates given parameter
Example ¶
package main import ( "context" "fmt" "github.com/PaesslerAG/gval" ) func main() { eval, err := gval.Full(gval.Constant("maximum_time", 52)). NewEvaluable("response_time <= maximum_time") if err != nil { fmt.Println(err) } for i := 50; i < 55; i++ { value, err := eval(context.Background(), map[string]interface{}{ "response_time": i, }) if err != nil { fmt.Println(err) } fmt.Println(value) } }
Output: true true true false false
func (Evaluable) EvalBool ¶
EvalBool evaluates given parameter to a bool
Example ¶
package main import ( "context" "fmt" "github.com/PaesslerAG/gval" ) func main() { eval, err := gval.Full().NewEvaluable("1 == x") if err != nil { fmt.Println(err) return } value, err := eval.EvalBool(context.Background(), map[string]interface{}{"x": 1}) if err != nil { fmt.Println(err) } if value { fmt.Print("yeah") } }
Output: yeah
func (Evaluable) EvalFloat64 ¶
EvalFloat64 evaluates given parameter to a float64
func (Evaluable) EvalInt ¶
EvalInt evaluates given parameter to an int
Example ¶
package main import ( "context" "fmt" "github.com/PaesslerAG/gval" ) func main() { eval, err := gval.Full().NewEvaluable("1 + x") if err != nil { fmt.Println(err) return } value, err := eval.EvalInt(context.Background(), map[string]interface{}{"x": 5}) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: 6
func (Evaluable) EvalString ¶
EvalString evaluates given parameter to a string
type Evaluables ¶ added in v1.1.3
type Evaluables []Evaluable
Evaluables is a slice of Evaluable.
func (Evaluables) EvalStrings ¶ added in v1.1.3
func (evs Evaluables) EvalStrings(c context.Context, parameter interface{}) ([]string, error)
EvalStrings evaluates given parameter to a string slice
type Language ¶
type Language struct {
// contains filtered or unexported fields
}
Language is an expression language
Example ¶
package main import ( "context" "fmt" "github.com/PaesslerAG/gval" ) func main() { lang := gval.NewLanguage(gval.JSON(), gval.Arithmetic(), //pipe operator gval.PostfixOperator("|", func(c context.Context, p *gval.Parser, pre gval.Evaluable) (gval.Evaluable, error) { post, err := p.ParseExpression(c) if err != nil { return nil, err } return func(c context.Context, v interface{}) (interface{}, error) { v, err := pre(c, v) if err != nil { return nil, err } return post(c, v) }, nil })) eval, err := lang.NewEvaluable(`{"foobar": 50} | foobar + 100`) if err != nil { fmt.Println(err) } value, err := eval(context.Background(), nil) if err != nil { fmt.Println(err) } fmt.Println(value) }
Output: 150
func Arithmetic ¶
func Arithmetic() Language
Arithmetic contains base, plus(+), minus(-), divide(/), power(**), negative(-) and numerical order (<=,<,>,>=)
Arithmetic operators expect float64 operands. Called with unfitting input, they try to convert the input to float64. They can parse strings and convert any type of int or float.
func Base ¶
func Base() Language
Base contains equal (==) and not equal (!=), perentheses and general support for variables, constants and functions It contains true, false, (floating point) number, string ("" or “) and char (”) constants
func Bitmask ¶
func Bitmask() Language
Bitmask contains base, bitwise and(&), bitwise or(|) and bitwise not(^).
Bitmask operators expect float64 operands. Called with unfitting input they try to convert the input to float64. They can parse strings and convert any type of int or float.
func DecimalArithmetic ¶ added in v1.1.3
func DecimalArithmetic() Language
DecimalArithmetic contains base, plus(+), minus(-), divide(/), power(**), negative(-) and numerical order (<=,<,>,>=)
DecimalArithmetic operators expect decimal.Decimal operands (github.com/shopspring/decimal) and are used to calculate money/decimal rather than floating point calculations. Called with unfitting input, they try to convert the input to decimal.Decimal. They can parse strings and convert any type of int or float.
func DefaultExtension ¶ added in v1.1.3
DefaultExtension is a language that runs the given function if no other prefix matches.
func Full ¶
Full is the union of Arithmetic, Bitmask, Text, PropositionalLogic, and Json
Operator in: a in b is true iff value a is an element of array b Operator ??: a ?? b returns a if a is not false or nil, otherwise n Operator ?: a ? b : c returns b if bool a is true, otherwise b
Function Date: Date(a) parses string a. a must match RFC3339, ISO8601, ruby date, or unix date
func Function ¶
Function returns a Language with given function. Function has no conversion for input types.
If the function returns an error it must be the last return parameter.
If the function has (without the error) more then one return parameter, it returns them as []interface{}.
func Ident ¶ added in v1.1.3
func Ident() Language
Ident contains support for variables and functions.
func InfixBoolOperator ¶
InfixBoolOperator for two bool values.
func InfixDecimalOperator ¶ added in v1.1.3
InfixDecimalOperator for two decimal values.
func InfixEvalOperator ¶
InfixEvalOperator operates on the raw operands. Therefore it cannot be combined with operators for other operand types.
func InfixNumberOperator ¶
InfixNumberOperator for two number values.
func InfixOperator ¶
InfixOperator for two arbitrary values.
func InfixShortCircuit ¶
InfixShortCircuit operator is called after the left operand is evaluated.
func InfixTextOperator ¶
InfixTextOperator for two text values.
func Init ¶ added in v1.1.3
Init is a language that does no parsing, but invokes the given function when parsing starts. It is incumbent upon the function to call ParseExpression to continue parsing.
This function can be used to customize the parser settings, such as whitespace or ident behavior.
func JSON ¶
func JSON() Language
JSON contains json objects ({string:expression,...}) and json arrays ([expression, ...])
func NewLanguage ¶
NewLanguage returns the union of given Languages as new Language.
func Parentheses ¶ added in v1.1.3
func Parentheses() Language
Parentheses contains support for parentheses.
func PostfixOperator ¶
func PostfixOperator(name string, ext func(context.Context, *Parser, Evaluable) (Evaluable, error)) Language
PostfixOperator extends a Language.
func Precedence ¶
Precedence of operator. The Operator with higher operatorPrecedence is evaluated first.
func PrefixExtension ¶
PrefixExtension extends a Language
func PrefixMetaPrefix ¶
func PrefixMetaPrefix(r rune, ext func(context.Context, *Parser) (call string, alternative func() (Evaluable, error), err error)) Language
PrefixMetaPrefix chooses a Prefix to be executed
func PrefixOperator ¶
PrefixOperator returns a Language with given prefix
func PropositionalLogic ¶
func PropositionalLogic() Language
PropositionalLogic contains base, not(!), and (&&), or (||) and Base.
Propositional operator expect bool operands. Called with unfitting input they try to convert the input to bool. Numbers other than 0 and the strings "TRUE" and "true" are interpreted as true. 0 and the strings "FALSE" and "false" are interpreted as false.
func Text ¶
func Text() Language
Text contains base, lexical order on strings (<=,<,>,>=), regex match (=~) and regex not match (!~)
func VariableSelector ¶ added in v1.1.3
func VariableSelector(selector func(path Evaluables) Evaluable) Language
VariableSelector returns a Language which uses given variable selector. It must be combined with a Language that uses the vatiable selector. E.g. gval.Base().
Example ¶
package main import ( "context" "fmt" "strings" "github.com/PaesslerAG/gval" ) func main() { value, err := gval.Evaluate(`hello.world`, "!", gval.VariableSelector(func(path gval.Evaluables) gval.Evaluable { return func(c context.Context, v interface{}) (interface{}, error) { keys, err := path.EvalStrings(c, v) if err != nil { return nil, err } return fmt.Sprintf("%s%s", strings.Join(keys, " "), v), nil } }), ) if err != nil { fmt.Println(err) } fmt.Print(value) }
Output: hello world!
type Parser ¶
type Parser struct { Language // contains filtered or unexported fields }
Parser parses expressions in a Language into an Evaluable
func (*Parser) Camouflage ¶
Camouflage rewind the last Scan(). The Parser holds the camouflage error until the next Scan() Do not call Rewind() on a camouflaged Parser
func (*Parser) Next ¶
Next reads and returns the next Unicode character. It returns EOF at the end of the source. Do not call Next() on a camouflaged Parser
func (*Parser) ParseExpression ¶
ParseExpression scans an expression into an Evaluable.
func (*Parser) ParseNextExpression ¶
ParseNextExpression scans the expression ignoring following operators
func (*Parser) ParseSublanguage ¶ added in v1.1.3
ParseSublanguage sets the next language for this parser to parse and calls its initialization function, usually ParseExpression.
Example ¶
package main import ( "context" "fmt" "github.com/PaesslerAG/gval" ) func parseSub(ctx context.Context, p *gval.Parser) (gval.Evaluable, error) { return p.ParseSublanguage(ctx, subLang) } var ( superLang = gval.NewLanguage( gval.PrefixExtension('$', parseSub), ) subLang = gval.NewLanguage( gval.Init(func(ctx context.Context, p *gval.Parser) (gval.Evaluable, error) { return p.Const("hello world"), nil }), ) ) func main() { value, err := superLang.Evaluate("$", nil) if err != nil { fmt.Println(err) } fmt.Println(value) }
Output: hello world
func (*Parser) Peek ¶
Peek returns the next Unicode character in the source without advancing the scanner. It returns EOF if the scanner's position is at the last character of the source. Do not call Peek() on a camouflaged Parser
func (*Parser) Scan ¶
Scan reads the next token or Unicode character from source and returns it. It only recognizes tokens t for which the respective Mode bit (1<<-t) is set. It returns scanner.EOF at the end of the source.
func (*Parser) SetIsIdentRuneFunc ¶ added in v1.1.3
SetIsIdentRuneFunc sets the function that matches ident characters in the underlying scanner.
func (*Parser) SetMode ¶ added in v1.1.3
SetMode sets the tokens that the underlying scanner will match.
func (*Parser) SetWhitespace ¶ added in v1.1.3
SetWhitespace sets the behavior of the whitespace matcher. The given characters must be less than or equal to 0x20 (' ').
func (*Parser) TokenText ¶
TokenText returns the string corresponding to the most recently scanned token. Valid after calling Scan().
type Selector ¶ added in v1.1.3
Selector allows for custom variable selection from structs
Return value is again handled with variable() until end of the given path
Example ¶
package main import ( "context" "fmt" "github.com/PaesslerAG/gval" ) type exampleCustomSelector struct{ hidden string } func (s *exampleCustomSelector) SelectGVal(ctx context.Context, k string) (interface{}, error) { if k == "hidden" { return s.hidden, nil } return nil, nil } func main() { lang := gval.Base() value, err := lang.Evaluate( "myStruct.hidden", map[string]interface{}{"myStruct": &exampleCustomSelector{hidden: "hello world"}}, ) if err != nil { fmt.Println(err) } fmt.Println(value) }
Output: hello world