gval: github.com/PaesslerAG/gval Index | Examples | Files

package gval

import "github.com/PaesslerAG/gval"

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, propositional logic and so on. They can be used as basis for a custom expression language or to evaluate expressions directly.

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

Examples

Package Files

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

func Evaluate Uses

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

Evaluate given parameter with given expression in gval full language

Code:

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

fmt.Print(value)

Output:

false

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

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

Code:

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

fmt.Print(value)

Output:

-1

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

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

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

Code:

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

fmt.Print(value)

Output:

hello world

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

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

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

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

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

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

type Evaluable Uses

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

Evaluable evaluates given parameter

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 Uses

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

EvalBool evaluates given parameter to a bool

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 Uses

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

EvalFloat64 evaluates given parameter to an int

func (Evaluable) EvalInt Uses

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

EvalInt evaluates given parameter to an int

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 Uses

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

EvalString evaluates given parameter to a string

func (Evaluable) IsConst Uses

func (e Evaluable) IsConst() bool

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

type Evaluables Uses

type Evaluables []Evaluable

Evaluables is a slice of Evaluable.

func (Evaluables) EvalStrings Uses

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

EvalStrings evaluates given parameter to a string slice

type Language Uses

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

Language is an expression language

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 Uses

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 Uses

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 Uses

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 Uses

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

Constant returns a Language with given constant

func Full Uses

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 Uses

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 InfixBoolOperator Uses

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

InfixBoolOperator for two bool values.

func InfixEvalOperator Uses

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 Uses

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

InfixNumberOperator for two number values.

func InfixOperator Uses

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

InfixOperator for two arbitrary values.

func InfixShortCircuit Uses

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

InfixShortCircuit operator is called after the left operand is evaluated.

func InfixTextOperator Uses

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

InfixTextOperator for two text values.

func JSON Uses

func JSON() Language

JSON contains json objects ({string:expression,...}) and json arrays ([expression, ...])

func NewLanguage Uses

func NewLanguage(bases ...Language) Language

NewLanguage returns the union of given Languages as new Language.

func PostfixOperator Uses

func PostfixOperator(name string, ext func(context.Context, *Parser, Evaluable) (Evaluable, error)) Language

PostfixOperator extends a Language.

func Precedence Uses

func Precedence(name string, operatorPrecendence uint8) Language

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

func PrefixExtension Uses

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

PrefixExtension extends a Language

func PrefixMetaPrefix Uses

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 Uses

func PrefixOperator(name string, e Evaluable) Language

PrefixOperator returns a Language with given prefix

func PropositionalLogic Uses

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 Uses

func Text() Language

Text contains base, lexical order on strings (<=,<,>,>=), regex match (=~) and regex not match (!~)

func VariableSelector Uses

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().

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 Uses

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

Evaluate given parameter with given expression

func (Language) NewEvaluable Uses

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

NewEvaluable returns an Evaluable for given expression in the specified language

type Parser Uses

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

Parser parses expressions in a Language into an Evaluable

func (*Parser) Camouflage Uses

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 Uses

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

Const Evaluable represents given constant

func (*Parser) Expected Uses

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

Expected returns an error signaling an unexpected Scan() result

func (*Parser) Next Uses

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 Uses

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

ParseExpression scans an expression into an Evaluable.

func (*Parser) ParseNextExpression Uses

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

ParseNextExpression scans the expression ignoring following operators

func (*Parser) Peek Uses

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 Uses

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) TokenText Uses

func (p *Parser) TokenText() string

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

func (*Parser) Var Uses

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.

Package gval imports 11 packages (graph) and is imported by 5 packages. Updated 2020-01-21. Refresh now. Tools for package owners.