...

Source file src/github.com/PaesslerAG/gval/gval.go

Documentation: github.com/PaesslerAG/gval

     1  // Package gval provides a generic expression language.
     2  // All functions, infix and prefix operators can be replaced by composing languages into a new one.
     3  //
     4  // The package contains concrete expression languages for common application in text, arithmetic, decimal arithmetic, propositional logic and so on.
     5  // They can be used as basis for a custom expression language or to evaluate expressions directly.
     6  package gval
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"math"
    12  	"reflect"
    13  	"text/scanner"
    14  	"time"
    15  
    16  	"github.com/shopspring/decimal"
    17  )
    18  
    19  //Evaluate given parameter with given expression in gval full language
    20  func Evaluate(expression string, parameter interface{}, opts ...Language) (interface{}, error) {
    21  	return EvaluateWithContext(context.Background(), expression, parameter, opts...)
    22  }
    23  
    24  //Evaluate given parameter with given expression in gval full language using a context
    25  func EvaluateWithContext(c context.Context, expression string, parameter interface{}, opts ...Language) (interface{}, error) {
    26  	l := full
    27  	if len(opts) > 0 {
    28  		l = NewLanguage(append([]Language{l}, opts...)...)
    29  	}
    30  	return l.EvaluateWithContext(c, expression, parameter)
    31  }
    32  
    33  // Full is the union of Arithmetic, Bitmask, Text, PropositionalLogic, and Json
    34  // 		Operator in: a in b is true iff value a is an element of array b
    35  // 		Operator ??: a ?? b returns a if a is not false or nil, otherwise n
    36  // 		Operator ?: a ? b : c returns b if bool a is true, otherwise b
    37  //
    38  // Function Date: Date(a) parses string a. a must match RFC3339, ISO8601, ruby date, or unix date
    39  func Full(extensions ...Language) Language {
    40  	if len(extensions) == 0 {
    41  		return full
    42  	}
    43  	return NewLanguage(append([]Language{full}, extensions...)...)
    44  }
    45  
    46  // Arithmetic contains base, plus(+), minus(-), divide(/), power(**), negative(-)
    47  // and numerical order (<=,<,>,>=)
    48  //
    49  // Arithmetic operators expect float64 operands.
    50  // Called with unfitting input, they try to convert the input to float64.
    51  // They can parse strings and convert any type of int or float.
    52  func Arithmetic() Language {
    53  	return arithmetic
    54  }
    55  
    56  // DecimalArithmetic contains base, plus(+), minus(-), divide(/), power(**), negative(-)
    57  // and numerical order (<=,<,>,>=)
    58  //
    59  // DecimalArithmetic operators expect decimal.Decimal operands (github.com/shopspring/decimal)
    60  // and are used to calculate money/decimal rather than floating point calculations.
    61  // Called with unfitting input, they try to convert the input to decimal.Decimal.
    62  // They can parse strings and convert any type of int or float.
    63  func DecimalArithmetic() Language {
    64  	return decimalArithmetic
    65  }
    66  
    67  // Bitmask contains base, bitwise and(&), bitwise or(|) and bitwise not(^).
    68  //
    69  // Bitmask operators expect float64 operands.
    70  // Called with unfitting input they try to convert the input to float64.
    71  // They can parse strings and convert any type of int or float.
    72  func Bitmask() Language {
    73  	return bitmask
    74  }
    75  
    76  // Text contains base, lexical order on strings (<=,<,>,>=),
    77  // regex match (=~) and regex not match (!~)
    78  func Text() Language {
    79  	return text
    80  }
    81  
    82  // PropositionalLogic contains base, not(!), and (&&), or (||) and Base.
    83  //
    84  // Propositional operator expect bool operands.
    85  // Called with unfitting input they try to convert the input to bool.
    86  // Numbers other than 0 and the strings "TRUE" and "true" are interpreted as true.
    87  // 0 and the strings "FALSE" and "false" are interpreted as false.
    88  func PropositionalLogic() Language {
    89  	return propositionalLogic
    90  }
    91  
    92  // JSON contains json objects ({string:expression,...})
    93  // and json arrays ([expression, ...])
    94  func JSON() Language {
    95  	return ljson
    96  }
    97  
    98  // Parentheses contains support for parentheses.
    99  func Parentheses() Language {
   100  	return parentheses
   101  }
   102  
   103  // Ident contains support for variables and functions.
   104  func Ident() Language {
   105  	return ident
   106  }
   107  
   108  // Base contains equal (==) and not equal (!=), perentheses and general support for variables, constants and functions
   109  // It contains true, false, (floating point) number, string  ("" or ``) and char ('') constants
   110  func Base() Language {
   111  	return base
   112  }
   113  
   114  var full = NewLanguage(arithmetic, bitmask, text, propositionalLogic, ljson,
   115  
   116  	InfixOperator("in", inArray),
   117  
   118  	InfixShortCircuit("??", func(a interface{}) (interface{}, bool) {
   119  		v := reflect.ValueOf(a)
   120  		return a, a != nil && !v.IsZero()
   121  	}),
   122  	InfixOperator("??", func(a, b interface{}) (interface{}, error) {
   123  		if v := reflect.ValueOf(a); a == nil || v.IsZero() {
   124  			return b, nil
   125  		}
   126  		return a, nil
   127  	}),
   128  
   129  	PostfixOperator("?", parseIf),
   130  
   131  	Function("date", func(arguments ...interface{}) (interface{}, error) {
   132  		if len(arguments) != 1 {
   133  			return nil, fmt.Errorf("date() expects exactly one string argument")
   134  		}
   135  		s, ok := arguments[0].(string)
   136  		if !ok {
   137  			return nil, fmt.Errorf("date() expects exactly one string argument")
   138  		}
   139  		for _, format := range [...]string{
   140  			time.ANSIC,
   141  			time.UnixDate,
   142  			time.RubyDate,
   143  			time.Kitchen,
   144  			time.RFC3339,
   145  			time.RFC3339Nano,
   146  			"2006-01-02",                         // RFC 3339
   147  			"2006-01-02 15:04",                   // RFC 3339 with minutes
   148  			"2006-01-02 15:04:05",                // RFC 3339 with seconds
   149  			"2006-01-02 15:04:05-07:00",          // RFC 3339 with seconds and timezone
   150  			"2006-01-02T15Z0700",                 // ISO8601 with hour
   151  			"2006-01-02T15:04Z0700",              // ISO8601 with minutes
   152  			"2006-01-02T15:04:05Z0700",           // ISO8601 with seconds
   153  			"2006-01-02T15:04:05.999999999Z0700", // ISO8601 with nanoseconds
   154  		} {
   155  			ret, err := time.ParseInLocation(format, s, time.Local)
   156  			if err == nil {
   157  				return ret, nil
   158  			}
   159  		}
   160  		return nil, fmt.Errorf("date() could not parse %s", s)
   161  	}),
   162  )
   163  
   164  var ljson = NewLanguage(
   165  	PrefixExtension('[', parseJSONArray),
   166  	PrefixExtension('{', parseJSONObject),
   167  )
   168  
   169  var arithmetic = NewLanguage(
   170  	InfixNumberOperator("+", func(a, b float64) (interface{}, error) { return a + b, nil }),
   171  	InfixNumberOperator("-", func(a, b float64) (interface{}, error) { return a - b, nil }),
   172  	InfixNumberOperator("*", func(a, b float64) (interface{}, error) { return a * b, nil }),
   173  	InfixNumberOperator("/", func(a, b float64) (interface{}, error) { return a / b, nil }),
   174  	InfixNumberOperator("%", func(a, b float64) (interface{}, error) { return math.Mod(a, b), nil }),
   175  	InfixNumberOperator("**", func(a, b float64) (interface{}, error) { return math.Pow(a, b), nil }),
   176  
   177  	InfixNumberOperator(">", func(a, b float64) (interface{}, error) { return a > b, nil }),
   178  	InfixNumberOperator(">=", func(a, b float64) (interface{}, error) { return a >= b, nil }),
   179  	InfixNumberOperator("<", func(a, b float64) (interface{}, error) { return a < b, nil }),
   180  	InfixNumberOperator("<=", func(a, b float64) (interface{}, error) { return a <= b, nil }),
   181  
   182  	InfixNumberOperator("==", func(a, b float64) (interface{}, error) { return a == b, nil }),
   183  	InfixNumberOperator("!=", func(a, b float64) (interface{}, error) { return a != b, nil }),
   184  
   185  	base,
   186  )
   187  
   188  var decimalArithmetic = NewLanguage(
   189  	InfixDecimalOperator("+", func(a, b decimal.Decimal) (interface{}, error) { return a.Add(b), nil }),
   190  	InfixDecimalOperator("-", func(a, b decimal.Decimal) (interface{}, error) { return a.Sub(b), nil }),
   191  	InfixDecimalOperator("*", func(a, b decimal.Decimal) (interface{}, error) { return a.Mul(b), nil }),
   192  	InfixDecimalOperator("/", func(a, b decimal.Decimal) (interface{}, error) { return a.Div(b), nil }),
   193  	InfixDecimalOperator("%", func(a, b decimal.Decimal) (interface{}, error) { return a.Mod(b), nil }),
   194  	InfixDecimalOperator("**", func(a, b decimal.Decimal) (interface{}, error) { return a.Pow(b), nil }),
   195  
   196  	InfixDecimalOperator(">", func(a, b decimal.Decimal) (interface{}, error) { return a.GreaterThan(b), nil }),
   197  	InfixDecimalOperator(">=", func(a, b decimal.Decimal) (interface{}, error) { return a.GreaterThanOrEqual(b), nil }),
   198  	InfixDecimalOperator("<", func(a, b decimal.Decimal) (interface{}, error) { return a.LessThan(b), nil }),
   199  	InfixDecimalOperator("<=", func(a, b decimal.Decimal) (interface{}, error) { return a.LessThanOrEqual(b), nil }),
   200  
   201  	InfixDecimalOperator("==", func(a, b decimal.Decimal) (interface{}, error) { return a.Equal(b), nil }),
   202  	InfixDecimalOperator("!=", func(a, b decimal.Decimal) (interface{}, error) { return !a.Equal(b), nil }),
   203  	base,
   204  	//Base is before these overrides so that the Base options are overridden
   205  	PrefixExtension(scanner.Int, parseDecimal),
   206  	PrefixExtension(scanner.Float, parseDecimal),
   207  	PrefixOperator("-", func(c context.Context, v interface{}) (interface{}, error) {
   208  		i, ok := convertToFloat(v)
   209  		if !ok {
   210  			return nil, fmt.Errorf("unexpected %v(%T) expected number", v, v)
   211  		}
   212  		return decimal.NewFromFloat(i).Neg(), nil
   213  	}),
   214  )
   215  
   216  var bitmask = NewLanguage(
   217  	InfixNumberOperator("^", func(a, b float64) (interface{}, error) { return float64(int64(a) ^ int64(b)), nil }),
   218  	InfixNumberOperator("&", func(a, b float64) (interface{}, error) { return float64(int64(a) & int64(b)), nil }),
   219  	InfixNumberOperator("|", func(a, b float64) (interface{}, error) { return float64(int64(a) | int64(b)), nil }),
   220  	InfixNumberOperator("<<", func(a, b float64) (interface{}, error) { return float64(int64(a) << uint64(b)), nil }),
   221  	InfixNumberOperator(">>", func(a, b float64) (interface{}, error) { return float64(int64(a) >> uint64(b)), nil }),
   222  
   223  	PrefixOperator("~", func(c context.Context, v interface{}) (interface{}, error) {
   224  		i, ok := convertToFloat(v)
   225  		if !ok {
   226  			return nil, fmt.Errorf("unexpected %T expected number", v)
   227  		}
   228  		return float64(^int64(i)), nil
   229  	}),
   230  )
   231  
   232  var text = NewLanguage(
   233  	InfixTextOperator("+", func(a, b string) (interface{}, error) { return fmt.Sprintf("%v%v", a, b), nil }),
   234  
   235  	InfixTextOperator("<", func(a, b string) (interface{}, error) { return a < b, nil }),
   236  	InfixTextOperator("<=", func(a, b string) (interface{}, error) { return a <= b, nil }),
   237  	InfixTextOperator(">", func(a, b string) (interface{}, error) { return a > b, nil }),
   238  	InfixTextOperator(">=", func(a, b string) (interface{}, error) { return a >= b, nil }),
   239  
   240  	InfixEvalOperator("=~", regEx),
   241  	InfixEvalOperator("!~", notRegEx),
   242  	base,
   243  )
   244  
   245  var propositionalLogic = NewLanguage(
   246  	PrefixOperator("!", func(c context.Context, v interface{}) (interface{}, error) {
   247  		b, ok := convertToBool(v)
   248  		if !ok {
   249  			return nil, fmt.Errorf("unexpected %T expected bool", v)
   250  		}
   251  		return !b, nil
   252  	}),
   253  
   254  	InfixShortCircuit("&&", func(a interface{}) (interface{}, bool) { return false, a == false }),
   255  	InfixBoolOperator("&&", func(a, b bool) (interface{}, error) { return a && b, nil }),
   256  	InfixShortCircuit("||", func(a interface{}) (interface{}, bool) { return true, a == true }),
   257  	InfixBoolOperator("||", func(a, b bool) (interface{}, error) { return a || b, nil }),
   258  
   259  	InfixBoolOperator("==", func(a, b bool) (interface{}, error) { return a == b, nil }),
   260  	InfixBoolOperator("!=", func(a, b bool) (interface{}, error) { return a != b, nil }),
   261  
   262  	base,
   263  )
   264  
   265  var parentheses = NewLanguage(
   266  	PrefixExtension('(', parseParentheses),
   267  )
   268  
   269  var ident = NewLanguage(
   270  	PrefixMetaPrefix(scanner.Ident, parseIdent),
   271  )
   272  
   273  var base = NewLanguage(
   274  	PrefixExtension(scanner.Int, parseNumber),
   275  	PrefixExtension(scanner.Float, parseNumber),
   276  	PrefixOperator("-", func(c context.Context, v interface{}) (interface{}, error) {
   277  		i, ok := convertToFloat(v)
   278  		if !ok {
   279  			return nil, fmt.Errorf("unexpected %v(%T) expected number", v, v)
   280  		}
   281  		return -i, nil
   282  	}),
   283  
   284  	PrefixExtension(scanner.String, parseString),
   285  	PrefixExtension(scanner.Char, parseString),
   286  	PrefixExtension(scanner.RawString, parseString),
   287  
   288  	Constant("true", true),
   289  	Constant("false", false),
   290  
   291  	InfixOperator("==", func(a, b interface{}) (interface{}, error) { return reflect.DeepEqual(a, b), nil }),
   292  	InfixOperator("!=", func(a, b interface{}) (interface{}, error) { return !reflect.DeepEqual(a, b), nil }),
   293  	parentheses,
   294  
   295  	Precedence("??", 0),
   296  
   297  	Precedence("||", 20),
   298  	Precedence("&&", 21),
   299  
   300  	Precedence("==", 40),
   301  	Precedence("!=", 40),
   302  	Precedence(">", 40),
   303  	Precedence(">=", 40),
   304  	Precedence("<", 40),
   305  	Precedence("<=", 40),
   306  	Precedence("=~", 40),
   307  	Precedence("!~", 40),
   308  	Precedence("in", 40),
   309  
   310  	Precedence("^", 60),
   311  	Precedence("&", 60),
   312  	Precedence("|", 60),
   313  
   314  	Precedence("<<", 90),
   315  	Precedence(">>", 90),
   316  
   317  	Precedence("+", 120),
   318  	Precedence("-", 120),
   319  
   320  	Precedence("*", 150),
   321  	Precedence("/", 150),
   322  	Precedence("%", 150),
   323  
   324  	Precedence("**", 200),
   325  
   326  	ident,
   327  )
   328  

View as plain text