exprcalc

package module
v0.0.0-...-3009198 Latest Latest
Warning

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

Go to latest
Published: Sep 12, 2020 License: MIT Imports: 5 Imported by: 0

README

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.

Documentation

Overview

Copyright 2020 Pavel Knoblokh. All rights reserved. Use of this source code is governed by MIT License that can be found in the LICENSE file. nolint: govet

Index

Constants

This section is empty.

Variables

View Source
var Debug = false
View Source
var (
	Parser = participle.MustBuild(
		&Expression{},
		participle.Lexer(myLexer),
		participle.Unquote("String"),
		participle.CaseInsensitive("LogicOp", "Boolean"),
	)
)

Functions

func Eval

func Eval(expr string, obj Gettable) (interface{}, error)

Returns a float64, string or bool

func EvalParsed

func EvalParsed(expr *Expression, obj Gettable) (interface{}, error)

Types

type Boolean

type Boolean bool

func (*Boolean) Capture

func (b *Boolean) Capture(values []string) error

type Compare

type Compare struct {
	Pos lexer.Position

	Operator string `@( "<=" | ">=" | "==" | "<" | ">" | "!=" )`
	Term     *Term  `( @@ )`
}

func (*Compare) Eval

func (c *Compare) Eval(ctx *Context, lhs interface{}) (interface{}, error)

type ConditionOperand

type ConditionOperand struct {
	Pos lexer.Position

	Term    *Term    `@@`
	Compare *Compare `[ @@ ]`
}

func (*ConditionOperand) Eval

func (c *ConditionOperand) Eval(ctx *Context) (interface{}, error)

type Context

type Context struct {
	Object Gettable
}

type Evaluable

type Evaluable interface {
	Eval(ctx *Context) (interface{}, error)
}

type Expression

type Expression struct {
	Pos lexer.Position

	Or []*OrCondition `@@ { "OR" @@ }`
}

func Parse

func Parse(expr string) (*Expression, error)

func (*Expression) Eval

func (e *Expression) Eval(ctx *Context) (interface{}, error)

type Gettable

type Gettable interface {
	// Currently must return a number, string or bool
	GetByName(string) (interface{}, error)
}

type OrCondition

type OrCondition struct {
	Pos lexer.Position

	And []*ConditionOperand `@@ { "AND" @@ }`
}

func (*OrCondition) Eval

func (o *OrCondition) Eval(ctx *Context) (interface{}, error)

type Term

type Term struct {
	Pos lexer.Position

	Value         *Value      `@@`
	Identifier    *string     `| @Ident`
	SubExpression *Expression `| "(" @@ ")"`
}

func (*Term) Eval

func (t *Term) Eval(ctx *Context) (interface{}, error)

type Value

type Value struct {
	Pos lexer.Position

	Number  *float64 `(  @Number`
	String  *string  ` | @String`
	Boolean *Boolean ` | @Boolean )`
}

func (*Value) Eval

func (v *Value) Eval(ctx *Context) (interface{}, error)

Jump to

Keyboard shortcuts

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