gal

package module
v8.4.0 Latest Latest
Warning

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

Go to latest
Published: May 7, 2024 License: Apache-2.0 Imports: 8 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, sepcial 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 invalid 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

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

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.

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 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 added in v8.1.0

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 (Bool) And added in v8.1.0

func (b Bool) And(other Value) Bool

func (Bool) AsString added in v8.1.0

func (b Bool) AsString() String

func (Bool) Bool added in v8.1.0

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) Or added in v8.1.0

func (b Bool) Or(other Value) Bool

func (Bool) String

func (b Bool) String() string

type Booler

type Booler interface {
	Bool() Bool
}

type Evaler added in v8.4.0

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 UserDefinedFunction

func UserDefinedFunction(name string, userFunctions Functions) FunctionalValue

UserDefinedFunction is a helper function that returns the definition of the provided function name from the supplied userFunctions.

func (FunctionalValue) String

func (fv FunctionalValue) String() string

type Functions

type Functions map[string]FunctionalValue

Functions holds the definition of user-defined functions.

func (Functions) Function

func (f Functions) Function(name string) FunctionalValue

Function returns the function definition of the function of the specified name.

type MultiValue

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

MultiValue is a container of zero or more Value's. TODO: impose a minimum of 1 value? 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. TODO: ... perhaps along the lines of [[v1 v2 ...]] or simply a built-in function such as TODO: ... 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 added in v8.1.0

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

func NewNumberFromFloat

func NewNumberFromFloat(f float64) Number

func NewNumberFromString

func NewNumberFromString(s string) (Number, error)

func ToNumber

func ToNumber(val Value) Number

TODO: we may also want to create ToString() and ToBool()

func (Number) Add

func (n Number) Add(other Value) Value

func (Number) AsString added in v8.1.0

func (n Number) AsString() String

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 added in v8.2.0

func (n Number) Ln(precision int32) Value

func (Number) Log added in v8.2.0

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 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 (String) Add

func (s String) Add(other Value) Value

func (String) AsString added in v8.1.0

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 added in v8.4.0

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) 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(...)) 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 added in v8.1.0

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 added in v8.1.0

func (Undefined) And(other Value) Bool

func (Undefined) AsString added in v8.1.0

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 added in v8.1.0

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 added in v8.4.0

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 added in v8.2.0

func Ln(args ...Value) Value

Ln returns the natural logarithm of d.

func Log added in v8.2.0

func Log(args ...Value) Value

Log returns the logarithm base 10 of d.

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