gal

package module
v9.0.2 Latest Latest
Warning

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

Go to latest
Published: May 11, 2024 License: Apache-2.0 Imports: 11 Imported by: 0

README

Go Eval

gal gal

A simple but powerful expression parser and evaluator in Go.

This project started as a personal research.

Examples

Check the tests for ideas of usage and capability.

Simple:

func main() {
    expr := `trunc(tan(10 + sin(cos(3*4.4))) 6)`
    gal.Parse(expr).Eval() // returns 3.556049
}

Advanced example, with user-defined functions and variables redefined once.
In this case, the expression is parsed once but evaluate twice:

// see TestWithVariablesAndFunctions() in gal_test.go for full code
func main() {
    // first of all, parse the expression (once only)
    expr := `double(:val1:) + triple(:val2:)`
    parsedExpr := gal.Parse(expr)

    // step 1: define funcs and vars and Eval the expression
    funcs := gal.Functions{
        "double": func(args ...gal.Value) gal.Value {
            value := args[0].(gal.Numberer)
            return value.Number().Multiply(gal.NewNumber(2))
        },
        "triple": func(args ...gal.Value) gal.Value {
            value := args[0].(gal.Numberer)
            return value.Number().Multiply(gal.NewNumber(3))
        },
    }

    vars := gal.Variables{
        ":val1:": gal.NewNumber(4),
        ":val2:": gal.NewNumber(5),
    }

    // returns 4 * 2 + 5 * 3 == 23
    parsedExpr.Eval(
        gal.WithVariables(vars),
        gal.WithFunctions(funcs),
    )

    // step 2: re-define funcs and vars and Eval the expression again
    // note that we do not need to parse the expression again, only just evaluate it
    funcs = gal.Functions{
        "double": func(args ...gal.Value) gal.Value {
            value := args[0].(gal.Numberer)
            return value.Number().Divide(gal.NewNumber(2))
        },
        "triple": func(args ...gal.Value) gal.Value {
            value := args[0].(gal.Numberer)
            return value.Number().Divide(gal.NewNumber(3))
        },
    }

    vars = gal.Variables{
        ":val1:": gal.NewNumber(2),
        ":val2:": gal.NewNumber(6),
    }

    // returns 2 / 2 + 6 / 3 == 3 this time
    parsedExpr.Eval(
        gal.WithVariables(vars),
        gal.WithFunctions(funcs),
    )
}

Type interfaces

gal comes with pre-defined type interfaces: Numberer, Booler, Stringer (and maybe more in the future).

They allow the general use of types. For instance, the String "123" can be converted to the Number 123. With Numberer, a user-defined function can transparently use String and Number when both hold a number representation.

A user-defined function can do this:

n := args[0].(gal.Numberer).Number()

or, for additional type safety:

value, ok := args[0].(gal.Numberer)
if !ok {
    return gal.NewUndefinedWithReasonf("NaN '%s'", args[0])
}
n := value.Number()
/* ... */

Both examples will happily accept a Value of type String or Number and process it as if it were a Number.

Numbers

Numbers implement arbitrary precision fixed-point decimal arithmetic with shopspring/decimal.

Strings

Strings must be enclosed in double-quotes (") e.g. valid: "this is a string", invalid: this is a syntax error (missing double-quotes).

Escapes are supported:

  • "this is \"also\" a valid string"
  • "this is fine too\\" (escapes cancel each other out)

Bools

In addition to boolean expressions, special contants True and False may be used.

Do not double-quote them, or they will become plain strings!

MultiValue

This is container Value. It can contain zero or any number of Value's. Currently, this is only truly useful with functions, mostly because it is yet undecided how to define what operations would mean on a MultiValue.

Supported operations

  • Operators: + - * / % ** << >> < <= == != > >= And && Or ||
    • Precedence, highest to lowest:
      • **
      • * / %
      • + -
      • << >>
      • < <= == != > >=
      • And && Or ||
    • Notes:
      • Go classifies bit shift operators with the higher *.
      • && is synonymous of And.
      • || is synonymous of Or.
      • Worded operators such as And and Or are case-sensitive and must be followed by a blank character. True Or (False) is a Bool expression with the Or operator but True Or(False) is an expression attempting to call a user-defined function called Or().
  • Types: String, Number, Bool, MultiValue
  • Associativity with parentheses: ( and )
  • Functions:
    • Built-in: pi, cos, floor, sin, sqrt, trunc, eval, and more (see function.go: Eval())
    • User-defined, injected via WithFunctions()
  • Variables, defined as :variable_name: and injected via WithVariables()

Functions

(See also Objects)

A function is defined as a Go type: type FunctionalValue func(...Value) Value

Function names are case-insensitive.

A function can optionally accept one or more space-separated arguments, but it must return a single Value.

It should be noted that a MultiValue type is available that can hold multiple Value elements. A function can use MultiValue as its return type to effectively return multiple Value's. Of course, as MultiValue is a Value type, functions can also accept it as part of their argument(s). Refer to the test TestMultiValueFunctions, for an example.

User function definitions are passed as a map[string]FunctionalValue using WithFunctions when calling Eval from Tree.

This allows parsing the expression once with Parse and run Tree.Eval multiple times with different user function definitions.

Variables

(See also Objects)

Variable names are case-sensitive.

Values are passed as a map[string]Value using WithVariables when calling Eval from Tree.

This allows parsing the expression once with Parse and run Tree.Eval multiple times with different variable values.

Objects

Objects are Go struct's which properties act as gal variables and methods as gal functions.

Object definitions are passed as a map[string]Object using WithObjects when calling Eval from Tree.

This allows parsing the expression once with Parse and run Tree.Eval multiple times with different instances of an object.

Object methods generally follow the same rules as gal Functions:

  • methods can optionally accept one or more arguments
  • methods must return a single value (which can be a MultiValue to emulate multiple return values)
  • arguments and return value are preferred to be of gal.Value type. However, gal will attempt to convert Go types to gal.Value types on best endeavour:
    • A method signature of MyMethod(arg1 int64) bool will translate the supplied gal.Value's and attempt to map them to int64 and bool using gal.Numberer and gal.Booler.
    • Type conversion may lead to a panic when the type cannot be interpreted.

Example:

type Car struct has several properties and methods - one of which is func (c *Car) CurrentSpeed() gal.Value.

	expr := `aCar.MaxSpeed - aCar.CurrentSpeed()`
	parsedExpr := gal.Parse(expr)

	got := parsedExpr.Eval(
		gal.WithObjects(map[string]gal.Object{
			"aCar": Car{
				Make:     "Lotus Esprit",
				Mileage:  gal.NewNumberFromInt(2000),
				Speed:    100,
				MaxSpeed: 250,
			},
		}),
	)
    // result: 150 == 250 - 100

High level design

Expressions are parsed in two stages:

  • Transformation into a Tree of Values and Operators.
  • Evaluation of the Tree for calculation.

Notes:

  • a Tree may contain one or more sub-Trees (recursively or not) to hold functions or to express associativity.
  • Calculation is performed in successive rounds of decreased operator precedence. This is to enforce natural associativity.

Code structure

The main entry point is Parse in gal.go.

Parse instantiates a TreeBuilder. It subsequently calls TreeBuilder's FromExpr method to create a parsed Tree representation of the expression to be evaluated.

Finally, Tree's Eval method performs the evaluation of the Tree and returns the resultant Value to gal.go's Eval function.

To do

A number of TODO's exist throughout the code.

The next priorities are:

  • review TODO's

Documentation

Index

Constants

View Source
const Pi51199 = "" /* 51199-byte string literal not displayed */

Pi51197 returns Pi with 51197 decimal digits.

Variables

View Source
var (
	False = NewBool(false)
	True  = NewBool(true)
)

Functions

func WithFunctions

func WithFunctions(funcs Functions) treeOption

WithFunctions is a functional parameter for Tree evaluation. It provides user-defined functions.

func WithObjects

func WithObjects(objects Objects) treeOption

WithObjects is a functional parameter for Tree evaluation. It provides user-defined Objects. These objects can carry both properties and methods that can be accessed by gal in place of variables and functions.

func WithVariables

func WithVariables(vars Variables) treeOption

WithVariables is a functional parameter for Tree evaluation. It provides user-defined variables.

Types

type Bool

type Bool struct {
	Undefined
	// contains filtered or unexported fields
}

func NewBool

func NewBool(b bool) Bool

func NewBoolFromString

func NewBoolFromString(s string) (Bool, error)

TODO: another option would be to return a Value and hence allow Undefined when neither True nor False is provided.

func ToBool

func ToBool(val Value) Bool

func (Bool) And

func (b Bool) And(other Value) Bool

func (Bool) AsString

func (b Bool) AsString() String

func (Bool) Bool

func (b Bool) Bool() Bool

func (Bool) Equal

func (b Bool) Equal(other Bool) bool

Equal satisfies the external Equaler interface such as in testify assertions and the cmp package

func (Bool) EqualTo

func (b Bool) EqualTo(other Value) Bool

func (Bool) Not

func (b Bool) Not() Bool

func (Bool) NotEqualTo

func (b Bool) NotEqualTo(other Value) Bool

func (Bool) Number

func (b Bool) Number() Number

func (Bool) Or

func (b Bool) Or(other Value) Bool

func (Bool) String

func (b Bool) String() string

type Booler

type Booler interface {
	Bool() Bool
}

type Evaler

type Evaler interface {
	Eval() Value
}

type Function

type Function struct {
	Name   string
	BodyFn FunctionalValue
	Args   []Tree
}

func NewFunction

func NewFunction(name string, bodyFn FunctionalValue, args ...Tree) Function

func (Function) Equal

func (f Function) Equal(other Function) bool

Equal satisfies the external Equaler interface such as in testify assertions and the cmp package

func (Function) Eval

func (f Function) Eval(opts ...treeOption) Value

type FunctionalValue

type FunctionalValue func(...Value) Value

func BuiltInFunction

func BuiltInFunction(name string) FunctionalValue

BuiltInFunction returns a built-in function body if known. It returns `nil` when no built-in function exists by the specified name. This signals the Evaluator to attempt to find a user defined function.

func ObjectGetMethod

func ObjectGetMethod(obj Object, name string) (FunctionalValue, bool)

func (FunctionalValue) String

func (fv FunctionalValue) String() string

type Functions

type Functions map[string]FunctionalValue

Functions holds the definition of user-defined functions.

type MultiValue

type MultiValue struct {
	Undefined
	// contains filtered or unexported fields
}

MultiValue is a container of zero or more Value's. For the time being, it is only usable and useful with functions. Functions can accept a MultiValue, and also return a MultiValue. This allows a function to effectively return multiple values as a MultiValue. TODO: we could add a syntax to instantiate a MultiValue within an expression. ... perhaps along the lines of [[v1 v2 ...]] or simply a built-in function such as ... MultiValue(...) - nothing stops the user from creating their own for now :-)

TODO: implement other methods such as Add, LessThan, etc (if meaningful)

func NewMultiValue

func NewMultiValue(values ...Value) MultiValue

func (MultiValue) AsString

func (m MultiValue) AsString() String

func (MultiValue) Equal

func (m MultiValue) Equal(other MultiValue) bool

Equal satisfies the external Equaler interface such as in testify assertions and the cmp package Note that the current implementation defines equality as values matching and in order they appear.

func (MultiValue) Get

func (m MultiValue) Get(i int) Value

func (MultiValue) Size

func (m MultiValue) Size() int

func (MultiValue) String

func (m MultiValue) String() string

type Number

type Number struct {
	Undefined
	// contains filtered or unexported fields
}

func NewNumber

func NewNumber(i int64, exp int32) Number

func NewNumberFromFloat

func NewNumberFromFloat(f float64) Number

func NewNumberFromInt

func NewNumberFromInt(i int64) Number

func NewNumberFromString

func NewNumberFromString(s string) (Number, error)

func ToNumber

func ToNumber(val Value) Number

func (Number) Add

func (n Number) Add(other Value) Value

func (Number) AsString

func (n Number) AsString() String

func (Number) Bool

func (n Number) Bool() Bool

func (Number) Cos

func (n Number) Cos() Number

func (Number) Divide

func (n Number) Divide(other Value) Value

func (Number) Equal

func (n Number) Equal(other Number) bool

Equal satisfies the external Equaler interface such as in testify assertions and the cmp package

func (Number) EqualTo

func (n Number) EqualTo(other Value) Bool

func (Number) Factorial

func (n Number) Factorial() Value

func (Number) Float64

func (n Number) Float64() float64

func (Number) Floor

func (n Number) Floor() Number

func (Number) GreaterThan

func (n Number) GreaterThan(other Value) Bool

func (Number) GreaterThanOrEqual

func (n Number) GreaterThanOrEqual(other Value) Bool

func (Number) Int64

func (n Number) Int64() int64

func (Number) IntPart

func (n Number) IntPart() Value

func (Number) LShift

func (n Number) LShift(other Value) Value

func (Number) LessThan

func (n Number) LessThan(other Value) Bool

func (Number) LessThanOrEqual

func (n Number) LessThanOrEqual(other Value) Bool

func (Number) Ln

func (n Number) Ln(precision int32) Value

func (Number) Log

func (n Number) Log(precision int32) Value

func (Number) Mod

func (n Number) Mod(other Value) Value

func (Number) Multiply

func (n Number) Multiply(other Value) Value

func (Number) Neg

func (n Number) Neg() Number

func (Number) NotEqualTo

func (n Number) NotEqualTo(other Value) Bool

func (Number) Number

func (n Number) Number() Number

func (Number) PowerOf

func (n Number) PowerOf(other Value) Value

func (Number) RShift

func (n Number) RShift(other Value) Value

func (Number) Sin

func (n Number) Sin() Number

func (Number) Sqrt

func (n Number) Sqrt() Value

func (Number) String

func (n Number) String() string

func (Number) Sub

func (n Number) Sub(other Value) Value

func (Number) Tan

func (n Number) Tan() Number

func (Number) Trunc

func (n Number) Trunc(precision int32) Number

type Numberer

type Numberer interface {
	Number() Number
}

type Object

type Object any

Object holds user-defined objects that can carry properties and functions that may be referenced within a gal expression during evaluation.

type Objects

type Objects map[string]Object

Objects is a collection of Object's in the form of a map which keys are the name of the object and values are the actual Object's.

type Operator

type Operator string
const (
	Plus               Operator = "+"
	Minus              Operator = "-"
	Multiply           Operator = "*"
	Divide             Operator = "/"
	Modulus            Operator = "%"
	Power              Operator = "**"
	LShift             Operator = "<<"
	RShift             Operator = ">>"
	LessThan           Operator = "<"
	LessThanOrEqual    Operator = "<="
	EqualTo            Operator = "=="
	NotEqualTo         Operator = "!="
	GreaterThan        Operator = ">"
	GreaterThanOrEqual Operator = ">="
	And                Operator = "And" // TODO: case sentive for now
	And2               Operator = "&&"
	Or                 Operator = "Or" // TODO: case sentive for now
	Or2                Operator = "||"
)

func (Operator) String

func (o Operator) String() string

type String

type String struct {
	Undefined
	// contains filtered or unexported fields
}

func NewString

func NewString(s string) String

func ToString

func ToString(val Value) String

func (String) Add

func (s String) Add(other Value) Value

func (String) AsString

func (s String) AsString() String

func (String) Equal

func (s String) Equal(other String) bool

Equal satisfies the external Equaler interface such as in testify assertions and the cmp package

func (String) EqualTo

func (s String) EqualTo(other Value) Bool

func (String) Eval

func (s String) Eval() Value

func (String) GreaterThan

func (s String) GreaterThan(other Value) Bool

func (String) GreaterThanOrEqual

func (s String) GreaterThanOrEqual(other Value) Bool

func (String) LShift

func (s String) LShift(Value) Value

func (String) LessThan

func (s String) LessThan(other Value) Bool

func (String) LessThanOrEqual

func (s String) LessThanOrEqual(other Value) Bool

func (String) Multiply

func (s String) Multiply(other Value) Value

func (String) NotEqualTo

func (s String) NotEqualTo(other Value) Bool

func (String) Number

func (s String) Number() Number

func (String) RShift

func (s String) RShift(Value) Value

func (String) RawString

func (s String) RawString() string

func (String) String

func (s String) String() string

type Stringer

type Stringer interface {
	AsString() String // name is not String so to not clash with fmt.Stringer interface
}

type Tree

type Tree []entry

func Parse

func Parse(expr string) Tree

Example: Parse("blah").Eval(WithVariables(...), WithFunctions(...), WithObjects(...)) This allows to parse an expression and then use the resulting Tree for multiple evaluations with different variables provided.

func (Tree) Calc

func (tree Tree) Calc(isOperatorInPrecedenceGroup func(Operator) bool, cfg *treeConfig) Tree

Calc is a reduction operation that calculates the Value of sub-expressions contained in this Tree, based on operator precedence. When isOperatorInPrecedenceGroup returns true, the operator is calculated and the resultant Value is inserted in _replacement_ of the terms (elements) of this Tree that where calculated. For instance, a tree representing the expression '2 + 5 * 4 / 2' with an operator precedence of 'multiplicativeOperators' would read the Tree left to right and return a new Tree that represents: '2 + 10' where 10 was calculated (and reduced) from 5 * 4 = 20 / 2 = 10.

nolint: gocognit,gocyclo,cyclop

func (Tree) CleanUp

func (tree Tree) CleanUp() Tree

CleanUp performs simplification operations before calculating this tree.

func (Tree) Eval

func (tree Tree) Eval(opts ...treeOption) Value

Eval evaluates this tree and returns its value. It accepts optional functional parameters to supply user-defined entities such as functions and variables.

func (Tree) FullLen

func (tree Tree) FullLen() int

FullLen returns the total number of non 'Tree-type' elements in the tree.

func (Tree) Split

func (tree Tree) Split() []Tree

Split divides a Tree trunk at points where two consecutive entries are present without an operator in between.

func (Tree) String

func (tree Tree) String(indents ...string) string

func (Tree) TrunkLen

func (tree Tree) TrunkLen() int

type TreeBuilder

type TreeBuilder struct{}

func NewTreeBuilder

func NewTreeBuilder() *TreeBuilder

func (TreeBuilder) FromExpr

func (tb TreeBuilder) FromExpr(expr string) (Tree, error)

nolint: gocognit,gocyclo,cyclop

type Undefined

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

func NewUndefined

func NewUndefined() Undefined

func NewUndefinedWithReasonf

func NewUndefinedWithReasonf(format string, a ...interface{}) Undefined

func (Undefined) Add

func (Undefined) Add(Value) Value

func (Undefined) And

func (Undefined) And(other Value) Bool

func (Undefined) AsString

func (u Undefined) AsString() String

func (Undefined) Divide

func (Undefined) Divide(Value) Value

func (Undefined) Equal

func (u Undefined) Equal(other Undefined) bool

Equal satisfies the external Equaler interface such as in testify assertions and the cmp package

func (Undefined) EqualTo

func (u Undefined) EqualTo(other Value) Bool

func (Undefined) GreaterThan

func (u Undefined) GreaterThan(other Value) Bool

func (Undefined) GreaterThanOrEqual

func (u Undefined) GreaterThanOrEqual(other Value) Bool

func (Undefined) LShift

func (Undefined) LShift(Value) Value

func (Undefined) LessThan

func (u Undefined) LessThan(other Value) Bool

func (Undefined) LessThanOrEqual

func (u Undefined) LessThanOrEqual(other Value) Bool

func (Undefined) Mod

func (Undefined) Mod(Value) Value

func (Undefined) Multiply

func (Undefined) Multiply(Value) Value

func (Undefined) NotEqualTo

func (u Undefined) NotEqualTo(other Value) Bool

func (Undefined) Or

func (Undefined) Or(other Value) Bool

func (Undefined) PowerOf

func (Undefined) PowerOf(Value) Value

func (Undefined) RShift

func (Undefined) RShift(Value) Value

func (Undefined) String

func (u Undefined) String() string

func (Undefined) Sub

func (Undefined) Sub(Value) Value

type Value

type Value interface {
	// Calculation
	Add(Value) Value
	Sub(Value) Value
	Multiply(Value) Value
	Divide(Value) Value
	PowerOf(Value) Value
	Mod(Value) Value
	LShift(Value) Value
	RShift(Value) Value
	// Logical
	LessThan(Value) Bool
	LessThanOrEqual(Value) Bool
	EqualTo(Value) Bool
	NotEqualTo(Value) Bool
	GreaterThan(Value) Bool
	GreaterThanOrEqual(Value) Bool
	And(Value) Bool
	Or(Value) Bool
	// Helpers
	Stringer
	fmt.Stringer
	// contains filtered or unexported methods
}

func Cos

func Cos(args ...Value) Value

Cos returns the cosine.

func Eval

func Eval(args ...Value) Value

func Factorial

func Factorial(args ...Value) Value

Factorial returns the factorial of the provided argument.

func Floor

func Floor(args ...Value) Value

return the floor.

func Ln

func Ln(args ...Value) Value

Ln returns the natural logarithm of d.

func Log

func Log(args ...Value) Value

Log returns the logarithm base 10 of d.

func ObjectGetProperty

func ObjectGetProperty(obj Object, name string) (Value, bool)

TODO: implement support for nested structs?

func Pi

func Pi(args ...Value) Value

Pi returns the Value of math.Pi.

func PiLong

func PiLong(args ...Value) Value

PiLong returns a value of Pi with many more digits than Pi.

func Sin

func Sin(args ...Value) Value

Sin returns the sine.

func Sqrt

func Sqrt(args ...Value) Value

Sqrt returns the square root.

func Tan

func Tan(args ...Value) Value

Tan returns the tangent.

func ToValue

func ToValue(value any) Value

func Trunc

func Trunc(args ...Value) Value

type Variable

type Variable struct {
	Name string
}

func NewVariable

func NewVariable(name string) Variable

func (Variable) String

func (v Variable) String() string

type Variables

type Variables map[string]Value

Variables holds the value of user-defined variables.

Jump to

Keyboard shortcuts

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