expr

package module
v0.5.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 8, 2022 License: MIT Imports: 6 Imported by: 0

README

expr

GoDoc

Expression evaluator for Go

Features

  • Operators: + - * / % ! < <= == != > >= ?: ??
  • Types: String, Number, Boolean, and Custom types
  • Parenthesized expressions
  • Javascript-like syntax and automatic type conversions

Using

To start using expr, install Go and run go get:

$ go get github.com/tidwall/expr
Basic expressions

For example:

1 + 1
(10 * 5 <= 50) && (50 > 100 || 8 >= 7)
1e+10 > 0 ? "big" : "small"

In Go, you're code may look like the following.

res, _ := expr.Eval(`1 + 1`, nil)
fmt.Println(res)
res, _ := expr.Eval(`(10 * 5 <= 50) && (50 > 100 || 8 >= 7)`, nil)
fmt.Println(res)
res, _ := expr.Eval(`1e+10 > 0 ? "big" : "small"`, nil)
fmt.Println(res)

// Output: 
// 2
// true
// big
Advanced expressions

Using a custom evaluation extender we can extend the eval function to support arithmetic and comparisons on custom types, such as time.Time that is built into Go. We can also provide some extra user data that exposes extra variables to the evaluator.

Example expressions:

timestamp
timestamp - $1h
now + $24h
timestamp < now - $24h ? "old" : "new"
((minX + maxX) / 2) + "," + ((minY + maxY) / 2)

In Go, you would provide a custom Extender to the Eval function.

// Create a user data map that can be referenced by the Eval function.
umap := make(map[string]Value)

// Add a bounding box to the user data map.
umap["minX"] = Number(112.8192)
umap["minY"] = Number(33.4738)
umap["maxX"] = Number(113.9146)
umap["maxY"] = Number(34.3367)

// Add a timestamp value to the user data map.
ts, _ := time.Parse(time.RFC3339, "2022-03-31T09:00:00Z")
umap["timestamp"] = Custom(ts)

// Set up an evaluation extender for referencing the user data and
// using operators on custom types.
ext := NewExtender(
	func(expr string, udata any) (Value, error) {
		switch expr {
		case "now":
			// Get the seconds since Epoch.
			return Custom(time.Now()), nil
		default:
			if len(expr) >= 1 && expr[0] == '$' {
				// Try parsing a time.Duration.
				s := expr[1:]
				d, err := time.ParseDuration(s)
				if err != nil {
					return Undefined, err
				}
				// Valid time.Duration, return as an Int64 value
				return Int64(int64(d)), nil
			}
			// Not a time.Duration, check the umap for the data
			umap, ok := udata.(map[string]Value)
			if !ok {
				return Undefined, ErrUndefined
			}
			return umap[expr], nil
		}
	},
	func(op Op, a, b Value, udata any) (Value, error) {
		// Try to convert a and/or b to time.Time
		at, aok := a.Value().(time.Time)
		bt, bok := b.Value().(time.Time)
		if aok && bok {
			// Both values are time.Time.
			// Perform comparison operation.
			switch op {
			case OpLt:
				return Bool(at.Before(bt)), nil
			case OpLte:
				return Bool(!bt.After(at)), nil
			case OpGt:
				return Bool(at.After(bt)), nil
			case OpGte:
				return Bool(!at.Before(bt)), nil
			}
		} else if aok || bok {
			// Either A or B are time.Time.
			// Perform arithmatic add/sub operation and return a
			// recalcuated time.Time value.
			var x time.Time
			var y int64
			if aok {
				x = at
				y = b.Int64()
			} else {
				x = bt
				y = a.Int64()
			}
			switch op {
			case OpAdd:
				return Custom(x.Add(time.Duration(y))), nil
			case OpSub:
				return Custom(x.Add(-time.Duration(y))), nil
			}
		}
		return Undefined, ErrUndefined
	},
)

// Set up the options
opts := Options{UserData: umap, Extender: ext}

var res Value

// Return the timestamp.
res, _ = Eval(`timestamp`, &opts)
fmt.Println(res)

// Subtract an hour from the timestamp.
res, _ = Eval(`timestamp - $1h`, &opts)
fmt.Println(res)

// Add one day to the current time.
res, _ = Eval(`now + $24h`, &opts)
fmt.Println(res)

// See if timestamp is older than a day
res, _ = Eval(`timestamp < now - $24h ? "old" : "new"`, &opts)
fmt.Println(res)

// Get the center of the bounding box as a concatenated string.
res, _ = Eval(`((minX + maxX) / 2) + "," + ((minY + maxY) / 2)`, &opts)
fmt.Println(res)

// Output:
// 2022-03-31 09:00:00 +0000 UTC
// 2022-03-31 08:00:00 +0000 UTC
// 2022-04-02 06:00:40.834656 -0700 MST m=+86400.000714835
// old
// 113.36689999999999,33.905249999999995

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	Undefined = Value{/* contains filtered or unexported fields */}
	Null      = Value{/* contains filtered or unexported fields */}
)
View Source
var ErrUndefined = errors.New("undefined")

Functions

func CharPosOfErr

func CharPosOfErr(err error) int

CharPosOfErr returns the character position of where the error occured in the Eval function, or -1 if unknown

Types

type Extender

type Extender interface {
	// Eval allows for custom evaluation of an expression.
	Eval(expr string, udata any) (Value, error)
	// Op allows for custom evaluation of an expression.
	Op(op Op, a, b Value, udata any) (Value, error)
}

func NewExtender

func NewExtender(
	eval func(expr string, udata any) (Value, error),
	op func(op Op, a, b Value, udata any) (Value, error),
) Extender

NewExtender is a convenience function for creating a simple extender using the provided eval and op functions.

type Op

type Op int

Op is an operator for Custom values used for the Options.Op function.

const (
	OpAdd  Op // +
	OpSub     // -
	OpMul     // *
	OpDiv     // /
	OpMod     // %
	OpLt      // <
	OpLte     // <=
	OpGt      // >
	OpGte     // >=
	OpEq      // ==
	OpNeq     // !=
	OpAnd     // &&
	OpOr      // ||
	OpCoal    // ??
)

func (Op) String

func (op Op) String() string

type Options

type Options struct {
	UserData any
	Extender Extender
}

Options for Eval

type Value

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

Value represents is the return value of Eval.

func Bool

func Bool(t bool) Value

Bool returns a bool value.

func Custom

func Custom(v interface{}) Value

Custom returns a custom user-defined value.

func Eval

func Eval(expr string, opts *Options) (Value, error)

Eval evaluates an expression and returns the Result.

func Float64

func Float64(x float64) Value

Float64 returns an int64 value.

func Int64

func Int64(x int64) Value

Int64 returns an int64 value.

func Number

func Number(x float64) Value

Number returns a float64 value.

func String

func String(s string) Value

String returns a string value.

func Uint64

func Uint64(x uint64) Value

Uint64 returns a uint64 value.

func (Value) Bool

func (a Value) Bool() bool

Bool returns a boolean representation.

func (Value) Float64

func (a Value) Float64() float64

Float64 returns s float64 representation.

func (Value) Int64

func (a Value) Int64() int64

Int64 returns an int64 representation.

func (Value) IsCustom

func (a Value) IsCustom() bool

func (Value) Number

func (a Value) Number() float64

Number returns s float64 representation.

func (Value) String

func (a Value) String() string

String returns a string representation.

func (Value) Uint64

func (a Value) Uint64() uint64

Uint64 returns a uint64 representation.

func (Value) Value

func (a Value) Value() interface{}

Value returns the native Go representation, which is one of the following:

bool, int64, uint64, float64, string, or nil (if undefined)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL