...

Package gval

import "github.com/PaesslerAG/gval"
Overview
Index
Examples

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

Code:

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
    func (evs Evaluables) EvalStrings(c context.Context, parameter interface{}) ([]string, error)
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, ext func(context.Context, *Parser) (call string, alternative func() (Evaluable, error), err error)) Language
    func PrefixOperator(name string, e Evaluable) Language
    func PropositionalLogic() Language
    func Text() Language
    func VariableSelector(selector func(path Evaluables) Evaluable) Language
    func (l Language) Evaluate(expression string, parameter interface{}) (interface{}, error)
    func (l Language) EvaluateWithContext(c context.Context, expression string, parameter interface{}) (interface{}, error)
    func (l Language) NewEvaluable(expression string) (Evaluable, error)
    func (l Language) NewEvaluableWithContext(c context.Context, expression string) (Evaluable, error)
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

Package files

evaluable.go functions.go gval.go language.go operator.go parse.go parser.go

func Evaluate

func Evaluate(expression string, parameter interface{}, opts ...Language) (interface{}, error)

Evaluate given parameter with given expression in gval full language

Example

Code:

value, err := gval.Evaluate("foo > 0", map[string]interface{}{
    "foo": -1.,
})
if err != nil {
    fmt.Println(err)
}

fmt.Print(value)

Output:

false

Example (Accessor)

Code:

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)

Code:

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)

Code:

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)

Code:

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)

Code:

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)

Code:

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)

Code:

value, err := gval.Evaluate(`Hello + World()`,
    exampleType{Hello: "hello "},
)
if err != nil {
    fmt.Println(err)
}

fmt.Print(value)

Output:

hello world

Example (Float64)

Code:

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)

Code:

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)

Code:

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)

Code:

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)

Code:

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)

Code:

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

func EvaluateWithContext

func EvaluateWithContext(c context.Context, expression string, parameter interface{}, opts ...Language) (interface{}, error)

Evaluate given parameter with given expression in gval full language using a context

type Evaluable

Evaluable evaluates given parameter

type Evaluable func(c context.Context, parameter interface{}) (interface{}, error)

Example

Code:

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

func (e Evaluable) EvalBool(c context.Context, parameter interface{}) (bool, error)

EvalBool evaluates given parameter to a bool

Example

Code:

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

func (e Evaluable) EvalFloat64(c context.Context, parameter interface{}) (float64, error)

EvalFloat64 evaluates given parameter to a float64

func (Evaluable) EvalInt

func (e Evaluable) EvalInt(c context.Context, parameter interface{}) (int, error)

EvalInt evaluates given parameter to an int

Example

Code:

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

func (e Evaluable) EvalString(c context.Context, parameter interface{}) (string, error)

EvalString evaluates given parameter to a string

func (Evaluable) IsConst

func (e Evaluable) IsConst() bool

IsConst returns if the Evaluable is a Parser.Const() value

type Evaluables

Evaluables is a slice of Evaluable.

type Evaluables []Evaluable

func (Evaluables) EvalStrings

func (evs Evaluables) EvalStrings(c context.Context, parameter interface{}) ([]string, error)

EvalStrings evaluates given parameter to a string slice

type Language

Language is an expression language

type Language struct {
    // contains filtered or unexported fields
}

Example

Code:

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 Constant

func Constant(name string, value interface{}) Language

Constant returns a Language with given constant

func DecimalArithmetic

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

func DefaultExtension(ext func(context.Context, *Parser) (Evaluable, error)) Language

DefaultExtension is a language that runs the given function if no other prefix matches.

func Full

func Full(extensions ...Language) Language

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

func Function(name string, function interface{}) Language

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

func Ident() Language

Ident contains support for variables and functions.

func InfixBoolOperator

func InfixBoolOperator(name string, f func(a, b bool) (interface{}, error)) Language

InfixBoolOperator for two bool values.

func InfixDecimalOperator

func InfixDecimalOperator(name string, f func(a, b decimal.Decimal) (interface{}, error)) Language

InfixDecimalOperator for two decimal values.

func InfixEvalOperator

func InfixEvalOperator(name string, f func(a, b Evaluable) (Evaluable, error)) Language

InfixEvalOperator operates on the raw operands. Therefore it cannot be combined with operators for other operand types.

func InfixNumberOperator

func InfixNumberOperator(name string, f func(a, b float64) (interface{}, error)) Language

InfixNumberOperator for two number values.

func InfixOperator

func InfixOperator(name string, f func(a, b interface{}) (interface{}, error)) Language

InfixOperator for two arbitrary values.

func InfixShortCircuit

func InfixShortCircuit(name string, f func(a interface{}) (interface{}, bool)) Language

InfixShortCircuit operator is called after the left operand is evaluated.

func InfixTextOperator

func InfixTextOperator(name string, f func(a, b string) (interface{}, error)) Language

InfixTextOperator for two text values.

func Init

func Init(ext func(context.Context, *Parser) (Evaluable, error)) Language

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

func NewLanguage(bases ...Language) Language

NewLanguage returns the union of given Languages as new Language.

func Parentheses

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

func Precedence(name string, operatorPrecendence uint8) Language

Precedence of operator. The Operator with higher operatorPrecedence is evaluated first.

func PrefixExtension

func PrefixExtension(r rune, ext func(context.Context, *Parser) (Evaluable, error)) Language

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

func PrefixOperator(name string, e Evaluable) Language

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

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

Code:

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!

func (Language) Evaluate

func (l Language) Evaluate(expression string, parameter interface{}) (interface{}, error)

Evaluate given parameter with given expression

func (Language) EvaluateWithContext

func (l Language) EvaluateWithContext(c context.Context, expression string, parameter interface{}) (interface{}, error)

Evaluate given parameter with given expression using context

func (Language) NewEvaluable

func (l Language) NewEvaluable(expression string) (Evaluable, error)

NewEvaluable returns an Evaluable for given expression in the specified language

func (Language) NewEvaluableWithContext

func (l Language) NewEvaluableWithContext(c context.Context, expression string) (Evaluable, error)

NewEvaluableWithContext returns an Evaluable for given expression in the specified language using context

type Parser

Parser parses expressions in a Language into an Evaluable

type Parser struct {
    Language
    // contains filtered or unexported fields
}

func (*Parser) Camouflage

func (p *Parser) Camouflage(unit string, expected ...rune)

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) Const

func (*Parser) Const(value interface{}) Evaluable

Const Evaluable represents given constant

func (*Parser) Expected

func (p *Parser) Expected(unit string, expected ...rune) error

Expected returns an error signaling an unexpected Scan() result

func (*Parser) Next

func (p *Parser) Next() rune

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

func (p *Parser) ParseExpression(c context.Context) (eval Evaluable, err error)

ParseExpression scans an expression into an Evaluable.

func (*Parser) ParseNextExpression

func (p *Parser) ParseNextExpression(c context.Context) (eval Evaluable, err error)

ParseNextExpression scans the expression ignoring following operators

func (*Parser) ParseSublanguage

func (p *Parser) ParseSublanguage(c context.Context, l Language) (Evaluable, error)

ParseSublanguage sets the next language for this parser to parse and calls its initialization function, usually ParseExpression.

Example

Code:

value, err := superLang.Evaluate("$", nil)

if err != nil {
    fmt.Println(err)
}

fmt.Println(value)

Output:

hello world

func (*Parser) Peek

func (p *Parser) Peek() rune

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

func (p *Parser) Scan() rune

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

func (p *Parser) SetIsIdentRuneFunc(fn func(ch rune, i int) bool)

SetIsIdentRuneFunc sets the function that matches ident characters in the underlying scanner.

func (*Parser) SetMode

func (p *Parser) SetMode(mode uint)

SetMode sets the tokens that the underlying scanner will match.

func (*Parser) SetWhitespace

func (p *Parser) SetWhitespace(chars ...rune)

SetWhitespace sets the behavior of the whitespace matcher. The given characters must be less than or equal to 0x20 (' ').

func (*Parser) TokenText

func (p *Parser) TokenText() string

TokenText returns the string corresponding to the most recently scanned token. Valid after calling Scan().

func (*Parser) Var

func (p *Parser) Var(path ...Evaluable) Evaluable

Var Evaluable represents value at given path. It supports with default language VariableSelector:

	map[interface{}]interface{},
	map[string]interface{} and
	[]interface{} and via reflect
	struct fields,
	struct methods,
	slices and
 map with int or string key.

type Selector

Selector allows for custom variable selection from structs

Return value is again handled with variable() until end of the given path

type Selector interface {
    SelectGVal(c context.Context, key string) (interface{}, error)
}

Example

Code:

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