Skip to content

Pashugan/exprcalc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Status go.dev reference Go Report Card GitHub license GitHub stars

Expression calculator

Overview

A simple embedded expression calculator.

Usage

type Person struct {
	dob     time.Time
	city    string
	married bool
}

func (p *Person) Age() int {
	return int(math.Floor(time.Since(p.dob).Hours() / 24 / 365))
}

// Implements exprcalc.Gettable
func (p *Person) GetByName(name string) (interface{}, error) {
	switch strings.ToLower(name) {
	case "age":
		return p.Age(), nil
	case "city":
		return p.city, nil
	case "married":
		return p.married, nil
	}
	return nil, fmt.Errorf("Getter '%v' not found", name)
}

expr := `(city == "Massachusetts" OR city == "Berkeley") AND age > 23 AND married == true`

unix := &Person{
	dob: time.Unix(0, 0),
	city: "Berkeley",
	married: true,
}
value, err := exprcalc.Eval(expr, unix)

// or preparsed flavour

parsed, err := exprcalc.Parse(expr)
value, err = exprcalc.EvalParsed(parsed, unix)

will return true as the value.

Description

Operands

number (e.g. 1984, 3.14, –273.15, 6.62607004e-34)

string (e.g. "foo", 'bar')

bool (case-insensitive true and false)

identifier, see details below (case-sensitive, C-variable-like names, i.e. starting from an alphabetic symbol or underscore followed by alphabetic symbols, digits or underscore, e.g. foo, BAR, _myvar, __my_var, Moon44)

Operators

Comparison

number == != < > <= >=

string == != < > <= >=

boolean == !=

Logical

AND OR (case-insensitive)

Subexpression

Parentheses, as in maths

( ), e.g. (true OR false) AND true

Operator precedence

( ) (highest)

== != < > <= >=

AND

OR (lowest)

Implicit type casting is not supported, i.e. both operands must be of the same type. Both operands of logical operators must be boolean. Also, see caveats.

Caveat

Short-circuit logical expression evaluation can result in correct evaluation of expressions with incompatible types. This behaviour trades performance against possible bug prone code. For instance, true OR "String" and false AND 123 will not fail because only the first operand is required to evaluate the result of the logical expression.

Identifiers

When you pass a context object implementing exprcalc.Gettable interface, it is possible to use identifiers in expressions, e.g. age > 23. The identifier is just a string that is passed into the GetByName() method of the object, that usually returns object field values or calls object's methods. Obviously, it must return one of the supported data types, i.e. any numeric type, string or boolean. Since using the interface instead of reflection, the overhead of the call is very small.