diff --git a/EvaluableExpression.go b/EvaluableExpression.go index 3300a03..79a0bbf 100644 --- a/EvaluableExpression.go +++ b/EvaluableExpression.go @@ -12,8 +12,8 @@ const shortCircuitHolder int = -1 var DUMMY_PARAMETERS = MapParameters(map[string]interface{}{}) /* - EvaluableExpression represents a set of ExpressionTokens which, taken together, - are an expression that can be evaluated down into a single value. +EvaluableExpression represents a set of ExpressionTokens which, taken together, +are an expression that can be evaluated down into a single value. */ type EvaluableExpression struct { @@ -39,8 +39,8 @@ type EvaluableExpression struct { } /* - Parses a new EvaluableExpression from the given [expression] string. - Returns an error if the given expression has invalid syntax. +Parses a new EvaluableExpression from the given [expression] string. +Returns an error if the given expression has invalid syntax. */ func NewEvaluableExpression(expression string) (*EvaluableExpression, error) { @@ -49,8 +49,8 @@ func NewEvaluableExpression(expression string) (*EvaluableExpression, error) { } /* - Similar to [NewEvaluableExpression], except that instead of a string, an already-tokenized expression is given. - This is useful in cases where you may be generating an expression automatically, or using some other parser (e.g., to parse from a query language) +Similar to [NewEvaluableExpression], except that instead of a string, an already-tokenized expression is given. +This is useful in cases where you may be generating an expression automatically, or using some other parser (e.g., to parse from a query language) */ func NewEvaluableExpressionFromTokens(tokens []ExpressionToken) (*EvaluableExpression, error) { @@ -85,8 +85,8 @@ func NewEvaluableExpressionFromTokens(tokens []ExpressionToken) (*EvaluableExpre } /* - Similar to [NewEvaluableExpression], except enables the use of user-defined functions. - Functions passed into this will be available to the expression. +Similar to [NewEvaluableExpression], except enables the use of user-defined functions. +Functions passed into this will be available to the expression. */ func NewEvaluableExpressionWithFunctions(expression string, functions map[string]ExpressionFunction) (*EvaluableExpression, error) { @@ -127,15 +127,15 @@ func NewEvaluableExpressionWithFunctions(expression string, functions map[string } /* - Same as `Eval`, but automatically wraps a map of parameters into a `govalute.Parameters` structure. +Same as `Eval`, but automatically wraps a map of parameters into a `govalute.Parameters` structure. */ -func (this EvaluableExpression) Evaluate(parameters map[string]interface{}) (interface{}, error) { +func (e EvaluableExpression) Evaluate(parameters map[string]interface{}) (interface{}, error) { if parameters == nil { - return this.Eval(nil) + return e.Eval(nil) } - return this.Eval(MapParameters(parameters)) + return e.Eval(MapParameters(parameters)) } var sanitizedParamsPool = sync.Pool{ @@ -145,19 +145,19 @@ var sanitizedParamsPool = sync.Pool{ } /* - Runs the entire expression using the given [parameters]. - e.g., If the expression contains a reference to the variable "foo", it will be taken from `parameters.Get("foo")`. +Runs the entire expression using the given [parameters]. +e.g., If the expression contains a reference to the variable "foo", it will be taken from `parameters.Get("foo")`. - This function returns errors if the combination of expression and parameters cannot be run, - such as if a variable in the expression is not present in [parameters]. +This function returns errors if the combination of expression and parameters cannot be run, +such as if a variable in the expression is not present in [parameters]. - In all non-error circumstances, this returns the single value result of the expression and parameters given. - e.g., if the expression is "1 + 1", this will return 2.0. - e.g., if the expression is "foo + 1" and parameters contains "foo" = 2, this will return 3.0 +In all non-error circumstances, this returns the single value result of the expression and parameters given. +e.g., if the expression is "1 + 1", this will return 2.0. +e.g., if the expression is "foo + 1" and parameters contains "foo" = 2, this will return 3.0 */ -func (this EvaluableExpression) Eval(parameters Parameters) (interface{}, error) { +func (e EvaluableExpression) Eval(parameters Parameters) (interface{}, error) { - if this.evaluationStages == nil { + if e.evaluationStages == nil { return nil, nil } @@ -171,20 +171,20 @@ func (this EvaluableExpression) Eval(parameters Parameters) (interface{}, error) parameters = DUMMY_PARAMETERS } - ret, err := this.evaluateStage(this.evaluationStages, parameters) + ret, err := e.evaluateStage(e.evaluationStages, parameters) if free { sanitizedParamsPool.Put(parameters) } return ret, err } -func (this EvaluableExpression) evaluateStage(stage *evaluationStage, parameters Parameters) (interface{}, error) { +func (e EvaluableExpression) evaluateStage(stage *evaluationStage, parameters Parameters) (interface{}, error) { var left, right interface{} var err error if stage.leftStage != nil { - left, err = this.evaluateStage(stage.leftStage, parameters) + left, err = e.evaluateStage(stage.leftStage, parameters) if err != nil { return nil, err } @@ -217,13 +217,13 @@ func (this EvaluableExpression) evaluateStage(stage *evaluationStage, parameters } if right != shortCircuitHolder && stage.rightStage != nil { - right, err = this.evaluateStage(stage.rightStage, parameters) + right, err = e.evaluateStage(stage.rightStage, parameters) if err != nil { return nil, err } } - if this.ChecksTypes { + if e.ChecksTypes { if stage.typeCheck == nil { err = typeCheck(stage.leftTypeCheck, left, stage.symbol, stage.typeErrorFormat) @@ -262,27 +262,27 @@ func typeCheck(check stageTypeCheck, value interface{}, symbol OperatorSymbol, f } /* - Returns an array representing the ExpressionTokens that make up this expression. +Returns an array representing the ExpressionTokens that make up this expression. */ -func (this EvaluableExpression) Tokens() []ExpressionToken { +func (e EvaluableExpression) Tokens() []ExpressionToken { - return this.tokens + return e.tokens } /* - Returns the original expression used to create this EvaluableExpression. +Returns the original expression used to create this EvaluableExpression. */ -func (this EvaluableExpression) String() string { +func (e EvaluableExpression) String() string { - return this.inputExpression + return e.inputExpression } /* - Returns an array representing the variables contained in this EvaluableExpression. +Returns an array representing the variables contained in this EvaluableExpression. */ -func (this EvaluableExpression) Vars() []string { +func (e EvaluableExpression) Vars() []string { var varlist []string - for _, val := range this.Tokens() { + for _, val := range e.Tokens() { if val.Kind == VARIABLE { varlist = append(varlist, val.Value.(string)) } @@ -293,6 +293,6 @@ func (this EvaluableExpression) Vars() []string { /* Removes the tokens from the EvaluableExpression. This will cause the Tokens() and Vars() functions to no longer operate, but will save memory. */ -func (this *EvaluableExpression) CleanupTokens() { - this.tokens = this.tokens[:0] +func (e *EvaluableExpression) CleanupTokens() { + e.tokens = e.tokens[:0] } diff --git a/EvaluableExpression_sql.go b/EvaluableExpression_sql.go index 52409fa..408a483 100644 --- a/EvaluableExpression_sql.go +++ b/EvaluableExpression_sql.go @@ -18,19 +18,19 @@ Boolean values are considered to be "1" for true, "0" for false. Times are formatted according to this.QueryDateFormat. */ -func (this EvaluableExpression) ToSQLQuery() (string, error) { +func (e EvaluableExpression) ToSQLQuery() (string, error) { var stream *tokenStream var transactions *expressionOutputStream var transaction string var err error - stream = newTokenStream(this.tokens) + stream = newTokenStream(e.tokens) transactions = new(expressionOutputStream) for stream.hasNext() { - transaction, err = this.findNextSQLString(stream, transactions) + transaction, err = e.findNextSQLString(stream, transactions) if err != nil { return "", err } @@ -41,7 +41,7 @@ func (this EvaluableExpression) ToSQLQuery() (string, error) { return transactions.createString(" "), nil } -func (this EvaluableExpression) findNextSQLString(stream *tokenStream, transactions *expressionOutputStream) (string, error) { +func (e EvaluableExpression) findNextSQLString(stream *tokenStream, transactions *expressionOutputStream) (string, error) { var token ExpressionToken var ret string @@ -55,7 +55,7 @@ func (this EvaluableExpression) findNextSQLString(stream *tokenStream, transacti case PATTERN: ret = fmt.Sprintf("'%s'", token.Value.(*regexp.Regexp).String()) case TIME: - ret = fmt.Sprintf("'%s'", token.Value.(time.Time).Format(this.QueryDateFormat)) + ret = fmt.Sprintf("'%s'", token.Value.(time.Time).Format(e.QueryDateFormat)) case LOGICALOP: switch logicalSymbols[token.Value.(string)] { @@ -101,7 +101,7 @@ func (this EvaluableExpression) findNextSQLString(stream *tokenStream, transacti case COALESCE: left := transactions.rollback() - right, err := this.findNextSQLString(stream, transactions) + right, err := e.findNextSQLString(stream, transactions) if err != nil { return "", err } @@ -110,7 +110,7 @@ func (this EvaluableExpression) findNextSQLString(stream *tokenStream, transacti case TERNARY_TRUE: fallthrough case TERNARY_FALSE: - return "", errors.New("Ternary operators are unsupported in SQL output") + return "", errors.New("ternary operators are unsupported in sql output") } case PREFIX: switch prefixSymbols[token.Value.(string)] { @@ -119,7 +119,7 @@ func (this EvaluableExpression) findNextSQLString(stream *tokenStream, transacti ret = "NOT" default: - right, err := this.findNextSQLString(stream, transactions) + right, err := e.findNextSQLString(stream, transactions) if err != nil { return "", err } @@ -133,7 +133,7 @@ func (this EvaluableExpression) findNextSQLString(stream *tokenStream, transacti case EXPONENT: left := transactions.rollback() - right, err := this.findNextSQLString(stream, transactions) + right, err := e.findNextSQLString(stream, transactions) if err != nil { return "", err } @@ -142,7 +142,7 @@ func (this EvaluableExpression) findNextSQLString(stream *tokenStream, transacti case MODULUS: left := transactions.rollback() - right, err := this.findNextSQLString(stream, transactions) + right, err := e.findNextSQLString(stream, transactions) if err != nil { return "", err } diff --git a/OperatorSymbol.go b/OperatorSymbol.go index 4b81065..fcebe33 100644 --- a/OperatorSymbol.go +++ b/OperatorSymbol.go @@ -1,8 +1,7 @@ package govaluate /* - Represents the valid symbols for operators. - +Represents the valid symbols for operators. */ type OperatorSymbol int @@ -142,9 +141,9 @@ func findOperatorPrecedenceForSymbol(symbol OperatorSymbol) operatorPrecedence { } /* - Map of all valid comparators, and their string equivalents. - Used during parsing of expressions to determine if a symbol is, in fact, a comparator. - Also used during evaluation to determine exactly which comparator is being used. +Map of all valid comparators, and their string equivalents. +Used during parsing of expressions to determine if a symbol is, in fact, a comparator. +Also used during evaluation to determine exactly which comparator is being used. */ var comparatorSymbols = map[string]OperatorSymbol{ "==": EQ, @@ -221,13 +220,13 @@ var separatorSymbols = map[string]OperatorSymbol{ } /* - Returns true if this operator is contained by the given array of candidate symbols. - False otherwise. +Returns true if this operator is contained by the given array of candidate symbols. +False otherwise. */ -func (this OperatorSymbol) IsModifierType(candidate []OperatorSymbol) bool { +func (o OperatorSymbol) IsModifierType(candidate []OperatorSymbol) bool { for _, symbolType := range candidate { - if this == symbolType { + if o == symbolType { return true } } @@ -236,14 +235,14 @@ func (this OperatorSymbol) IsModifierType(candidate []OperatorSymbol) bool { } /* - Generally used when formatting type check errors. - We could store the stringified symbol somewhere else and not require a duplicated codeblock to translate - OperatorSymbol to string, but that would require more memory, and another field somewhere. - Adding operators is rare enough that we just stringify it here instead. +Generally used when formatting type check errors. +We could store the stringified symbol somewhere else and not require a duplicated codeblock to translate +OperatorSymbol to string, but that would require more memory, and another field somewhere. +Adding operators is rare enough that we just stringify it here instead. */ -func (this OperatorSymbol) String() string { +func (o OperatorSymbol) String() string { - switch this { + switch o { case NOOP: return "NOOP" case VALUE: diff --git a/dummies_test.go b/dummies_test.go index a1df658..7a961e6 100644 --- a/dummies_test.go +++ b/dummies_test.go @@ -18,23 +18,23 @@ type dummyParameter struct { Map map[string]interface{} } -func (this dummyParameter) Func() string { +func (dummyParameter) Func() string { return "funk" } -func (this dummyParameter) Func2() (string, error) { +func (dummyParameter) Func2() (string, error) { return "frink", nil } -func (this *dummyParameter) Func3() string { +func (*dummyParameter) Func3() string { return "fronk" } -func (this dummyParameter) FuncArgStr(arg1 string) string { +func (dummyParameter) FuncArgStr(arg1 string) string { return arg1 } -func (this dummyParameter) TestArgs(str string, ui uint, ui8 uint8, ui16 uint16, ui32 uint32, ui64 uint64, i int, i8 int8, i16 int16, i32 int32, i64 int64, f32 float32, f64 float64, b bool) string { +func (dummyParameter) TestArgs(str string, ui uint, ui8 uint8, ui16 uint16, ui32 uint32, ui64 uint64, i int, i8 int8, i16 int16, i32 int32, i64 int64, f32 float32, f64 float64, b bool) string { var sum float64 @@ -49,7 +49,7 @@ func (this dummyParameter) TestArgs(str string, ui uint, ui8 uint8, ui16 uint16, return fmt.Sprintf("%v: %v", str, sum) } -func (this dummyParameter) AlwaysFail() (interface{}, error) { +func (dummyParameter) AlwaysFail() (interface{}, error) { return nil, errors.New("function should always fail") } @@ -57,7 +57,7 @@ type dummyNestedParameter struct { Funk string } -func (this dummyNestedParameter) Dunk(arg1 string) string { +func (dummyNestedParameter) Dunk(arg1 string) string { return arg1 + "dunk" } diff --git a/evaluationFailure_test.go b/evaluationFailure_test.go index d1b08d6..a0307e8 100644 --- a/evaluationFailure_test.go +++ b/evaluationFailure_test.go @@ -5,7 +5,6 @@ package govaluate */ import ( "errors" - "fmt" "strings" "testing" ) @@ -29,10 +28,10 @@ const ( INVALID_LOGICALOP_TYPES = "cannot be used with the logical operator" INVALID_TERNARY_TYPES = "cannot be used with the ternary operator" ABSENT_PARAMETER = "No parameter" - INVALID_REGEX = "Unable to compile regexp pattern" + INVALID_REGEX = "unable to compile regexp pattern" INVALID_PARAMETER_CALL = "No method or field" - TOO_FEW_ARGS = "Too few arguments to parameter call" - TOO_MANY_ARGS = "Too many arguments to parameter call" + TOO_FEW_ARGS = "too few arguments to parameter call" + TOO_MANY_ARGS = "too many arguments to parameter call" MISMATCHED_PARAMETERS = "Argument type conversion failed" UNEXPORTED_ACCESSOR = "Unable to access unexported" ) @@ -100,86 +99,81 @@ func TestNilParameterUsage(test *testing.T) { func TestModifierTyping(test *testing.T) { evaluationTests := []EvaluationFailureTest{ - EvaluationFailureTest{ + { Name: "PLUS literal number to literal bool", Input: "1 + true", Expected: INVALID_MODIFIER_TYPES, }, - EvaluationFailureTest{ + { Name: "PLUS number to bool", Input: "number + bool", Expected: INVALID_MODIFIER_TYPES, }, - EvaluationFailureTest{ + { Name: "MINUS number to bool", Input: "number - bool", Expected: INVALID_MODIFIER_TYPES, }, - EvaluationFailureTest{ + { Name: "MINUS number to bool", Input: "number - bool", Expected: INVALID_MODIFIER_TYPES, }, - EvaluationFailureTest{ + { Name: "MULTIPLY number to bool", Input: "number * bool", Expected: INVALID_MODIFIER_TYPES, }, - EvaluationFailureTest{ + { Name: "DIVIDE number to bool", Input: "number / bool", Expected: INVALID_MODIFIER_TYPES, }, - EvaluationFailureTest{ + { Name: "EXPONENT number to bool", Input: "number ** bool", Expected: INVALID_MODIFIER_TYPES, }, - EvaluationFailureTest{ + { Name: "MODULUS number to bool", Input: "number % bool", Expected: INVALID_MODIFIER_TYPES, }, - EvaluationFailureTest{ + { Name: "XOR number to bool", Input: "number % bool", Expected: INVALID_MODIFIER_TYPES, }, - EvaluationFailureTest{ - + { Name: "BITWISE_OR number to bool", Input: "number | bool", Expected: INVALID_MODIFIER_TYPES, }, - EvaluationFailureTest{ - + { Name: "BITWISE_AND number to bool", Input: "number & bool", Expected: INVALID_MODIFIER_TYPES, }, - EvaluationFailureTest{ - + { Name: "BITWISE_XOR number to bool", Input: "number ^ bool", Expected: INVALID_MODIFIER_TYPES, }, - EvaluationFailureTest{ - + { Name: "BITWISE_LSHIFT number to bool", Input: "number << bool", Expected: INVALID_MODIFIER_TYPES, }, - EvaluationFailureTest{ - + { Name: "BITWISE_RSHIFT number to bool", Input: "number >> bool", Expected: INVALID_MODIFIER_TYPES, @@ -192,50 +186,42 @@ func TestModifierTyping(test *testing.T) { func TestLogicalOperatorTyping(test *testing.T) { evaluationTests := []EvaluationFailureTest{ - EvaluationFailureTest{ - + { Name: "AND number to number", Input: "number && number", Expected: INVALID_LOGICALOP_TYPES, }, - EvaluationFailureTest{ - + { Name: "OR number to number", Input: "number || number", Expected: INVALID_LOGICALOP_TYPES, }, - EvaluationFailureTest{ - + { Name: "AND string to string", Input: "string && string", Expected: INVALID_LOGICALOP_TYPES, }, - EvaluationFailureTest{ - + { Name: "OR string to string", Input: "string || string", Expected: INVALID_LOGICALOP_TYPES, }, - EvaluationFailureTest{ - + { Name: "AND number to string", Input: "number && string", Expected: INVALID_LOGICALOP_TYPES, }, - EvaluationFailureTest{ - + { Name: "OR number to string", Input: "number || string", Expected: INVALID_LOGICALOP_TYPES, }, - EvaluationFailureTest{ - + { Name: "AND bool to string", Input: "bool && string", Expected: INVALID_LOGICALOP_TYPES, }, - EvaluationFailureTest{ - + { Name: "OR string to bool", Input: "string || bool", Expected: INVALID_LOGICALOP_TYPES, @@ -252,117 +238,97 @@ we also need to make sure that we receive type mismatch errors during evaluation func TestComparatorTyping(test *testing.T) { evaluationTests := []EvaluationFailureTest{ - EvaluationFailureTest{ - + { Name: "GT literal bool to literal bool", Input: "true > true", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "GT bool to bool", Input: "bool > bool", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "GTE bool to bool", Input: "bool >= bool", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "LT bool to bool", Input: "bool < bool", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "LTE bool to bool", Input: "bool <= bool", Expected: INVALID_COMPARATOR_TYPES, }, - - EvaluationFailureTest{ - + { Name: "GT number to string", Input: "number > string", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "GTE number to string", Input: "number >= string", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "LT number to string", Input: "number < string", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "REQ number to string", Input: "number =~ string", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "REQ number to bool", Input: "number =~ bool", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "REQ bool to number", Input: "bool =~ number", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "REQ bool to string", Input: "bool =~ string", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "NREQ number to string", Input: "number !~ string", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "NREQ number to bool", Input: "number !~ bool", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "NREQ bool to number", Input: "bool !~ number", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "NREQ bool to string", Input: "bool !~ string", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "IN non-array numeric", Input: "1 in 2", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "IN non-array string", Input: "1 in 'foo'", Expected: INVALID_COMPARATOR_TYPES, }, - EvaluationFailureTest{ - + { Name: "IN non-array boolean", Input: "1 in true", Expected: INVALID_COMPARATOR_TYPES, @@ -375,14 +341,12 @@ func TestComparatorTyping(test *testing.T) { func TestTernaryTyping(test *testing.T) { evaluationTests := []EvaluationFailureTest{ - EvaluationFailureTest{ - + { Name: "Ternary with number", Input: "10 ? true", Expected: INVALID_TERNARY_TYPES, }, - EvaluationFailureTest{ - + { Name: "Ternary with string", Input: "'foo' ? true", Expected: INVALID_TERNARY_TYPES, @@ -395,8 +359,7 @@ func TestTernaryTyping(test *testing.T) { func TestRegexParameterCompilation(test *testing.T) { evaluationTests := []EvaluationFailureTest{ - EvaluationFailureTest{ - + { Name: "Regex equality runtime parsing", Input: "'foo' =~ foo", Parameters: map[string]interface{}{ @@ -404,8 +367,7 @@ func TestRegexParameterCompilation(test *testing.T) { }, Expected: INVALID_REGEX, }, - EvaluationFailureTest{ - + { Name: "Regex inequality runtime parsing", Input: "'foo' =~ foo", Parameters: map[string]interface{}{ @@ -421,8 +383,7 @@ func TestRegexParameterCompilation(test *testing.T) { func TestFunctionExecution(test *testing.T) { evaluationTests := []EvaluationFailureTest{ - EvaluationFailureTest{ - + { Name: "Function error bubbling", Input: "error()", Functions: map[string]ExpressionFunction{ @@ -440,56 +401,49 @@ func TestFunctionExecution(test *testing.T) { func TestInvalidParameterCalls(test *testing.T) { evaluationTests := []EvaluationFailureTest{ - EvaluationFailureTest{ - + { Name: "Missing parameter field reference", Input: "foo.NotExists", Parameters: fooFailureParameters, Expected: INVALID_PARAMETER_CALL, }, - EvaluationFailureTest{ - + { Name: "Parameter method call on missing function", Input: "foo.NotExist()", Parameters: fooFailureParameters, Expected: INVALID_PARAMETER_CALL, }, - EvaluationFailureTest{ - + { Name: "Nested missing parameter field reference", Input: "foo.Nested.NotExists", Parameters: fooFailureParameters, Expected: INVALID_PARAMETER_CALL, }, - EvaluationFailureTest{ - + { Name: "Parameter method call returns error", Input: "foo.AlwaysFail()", Parameters: fooFailureParameters, Expected: "function should always fail", }, - EvaluationFailureTest{ - + { Name: "Too few arguments to parameter call", Input: "foo.FuncArgStr()", Parameters: fooFailureParameters, Expected: TOO_FEW_ARGS, }, - EvaluationFailureTest{ - + { Name: "Too many arguments to parameter call", Input: "foo.FuncArgStr('foo', 'bar', 15)", Parameters: fooFailureParameters, Expected: TOO_MANY_ARGS, }, - EvaluationFailureTest{ - + { Name: "Mismatched parameters", Input: "foo.FuncArgStr(5)", Parameters: fooFailureParameters, Expected: MISMATCHED_PARAMETERS, }, - EvaluationFailureTest{ + { Name: "Unexported parameter access", Input: "foo.bar", Parameters: map[string]interface{}{ @@ -511,7 +465,7 @@ func runEvaluationFailureTests(evaluationTests []EvaluationFailureTest, test *te var expression *EvaluableExpression var err error - fmt.Printf("Running %d negative parsing test cases...\n", len(evaluationTests)) + // fmt.Printf("Running %d negative parsing test cases...\n", len(evaluationTests)) for _, testCase := range evaluationTests { diff --git a/evaluationStage.go b/evaluationStage.go index 92e813a..cc8ee10 100644 --- a/evaluationStage.go +++ b/evaluationStage.go @@ -48,26 +48,26 @@ var ( _false = interface{}(false) ) -func (this *evaluationStage) swapWith(other *evaluationStage) { +func (e *evaluationStage) swapWith(other *evaluationStage) { temp := *other - other.setToNonStage(*this) - this.setToNonStage(temp) + other.setToNonStage(*e) + e.setToNonStage(temp) } -func (this *evaluationStage) setToNonStage(other evaluationStage) { +func (e *evaluationStage) setToNonStage(other evaluationStage) { - this.symbol = other.symbol - this.operator = other.operator - this.leftTypeCheck = other.leftTypeCheck - this.rightTypeCheck = other.rightTypeCheck - this.typeCheck = other.typeCheck - this.typeErrorFormat = other.typeErrorFormat + e.symbol = other.symbol + e.operator = other.operator + e.leftTypeCheck = other.leftTypeCheck + e.rightTypeCheck = other.rightTypeCheck + e.typeCheck = other.typeCheck + e.typeErrorFormat = other.typeErrorFormat } -func (this *evaluationStage) isShortCircuitable() bool { +func (e *evaluationStage) isShortCircuitable() bool { - switch this.symbol { + switch e.symbol { case AND: fallthrough case OR: @@ -178,7 +178,7 @@ func regexStage(left interface{}, right interface{}, parameters Parameters) (int case string: pattern, err = regexp.Compile(right) if err != nil { - return nil, fmt.Errorf("Unable to compile regexp pattern '%v': %v", right, err) + return nil, fmt.Errorf("unable to compile regexp pattern '%v': %w", right, err) } case *regexp.Regexp: pattern = right @@ -268,9 +268,9 @@ func typeConvertParams(method reflect.Value, params []reflect.Value) ([]reflect. if numIn != numParams { if numIn > numParams { - return nil, fmt.Errorf("Too few arguments to parameter call: got %d arguments, expected %d", len(params), numIn) + return nil, fmt.Errorf("too few arguments to parameter call: got %d arguments, expected %d", len(params), numIn) } - return nil, fmt.Errorf("Too many arguments to parameter call: got %d arguments, expected %d", len(params), numIn) + return nil, fmt.Errorf("too many arguments to parameter call: got %d arguments, expected %d", len(params), numIn) } for i := 0; i < numIn; i++ { diff --git a/evaluation_test.go b/evaluation_test.go index 714c24e..d2a5fad 100644 --- a/evaluation_test.go +++ b/evaluation_test.go @@ -27,534 +27,484 @@ type EvaluationParameter struct { func TestNoParameterEvaluation(test *testing.T) { evaluationTests := []EvaluationTest{ - - EvaluationTest{ + { Name: "Single PLUS", Input: "51 + 49", Expected: 100.0, }, - EvaluationTest{ + { Name: "Single MINUS", Input: "100 - 51", Expected: 49.0, }, - EvaluationTest{ + { Name: "Single BITWISE AND", Input: "100 & 50", Expected: 32.0, }, - EvaluationTest{ + { Name: "Single BITWISE OR", Input: "100 | 50", Expected: 118.0, }, - EvaluationTest{ + { Name: "Single BITWISE XOR", Input: "100 ^ 50", Expected: 86.0, }, - EvaluationTest{ + { Name: "Single shift left", Input: "2 << 1", Expected: 4.0, }, - EvaluationTest{ + { Name: "Single shift right", Input: "2 >> 1", Expected: 1.0, }, - EvaluationTest{ + { Name: "Single BITWISE NOT", Input: "~10", Expected: -11.0, }, - EvaluationTest{ + { Name: "Single MULTIPLY", Input: "5 * 20", Expected: 100.0, }, - EvaluationTest{ + { Name: "Single DIVIDE", Input: "100 / 20", Expected: 5.0, }, - EvaluationTest{ + { Name: "Single even MODULUS", Input: "100 % 2", Expected: 0.0, }, - EvaluationTest{ + { Name: "Single odd MODULUS", Input: "101 % 2", Expected: 1.0, }, - EvaluationTest{ + { Name: "Single EXPONENT", Input: "10 ** 2", Expected: 100.0, }, - EvaluationTest{ + { Name: "Compound PLUS", Input: "20 + 30 + 50", Expected: 100.0, }, - EvaluationTest{ + { Name: "Compound BITWISE AND", Input: "20 & 30 & 50", Expected: 16.0, }, - EvaluationTest{ + { Name: "Mutiple operators", Input: "20 * 5 - 49", Expected: 51.0, }, - EvaluationTest{ + { Name: "Parenthesis usage", Input: "100 - (5 * 10)", Expected: 50.0, }, - EvaluationTest{ + { Name: "Nested parentheses", Input: "50 + (5 * (15 - 5))", Expected: 100.0, }, - EvaluationTest{ + { Name: "Nested parentheses with bitwise", Input: "100 ^ (23 * (2 | 5))", Expected: 197.0, }, - EvaluationTest{ + { Name: "Logical OR operation of two clauses", Input: "(1 == 1) || (true == true)", Expected: true, }, - EvaluationTest{ + { Name: "Logical AND operation of two clauses", Input: "(1 == 1) && (true == true)", Expected: true, }, - EvaluationTest{ + { Name: "Implicit boolean", Input: "2 > 1", Expected: true, }, - EvaluationTest{ + { Name: "Compound boolean", Input: "5 < 10 && 1 < 5", Expected: true, }, - EvaluationTest{ + { Name: "Evaluated true && false operation (for issue #8)", Input: "1 > 10 && 11 > 10", Expected: false, }, - EvaluationTest{ + { Name: "Evaluated true && false operation (for issue #8)", Input: "true == true && false == true", Expected: false, }, - EvaluationTest{ + { Name: "Parenthesis boolean", Input: "10 < 50 && (1 != 2 && 1 > 0)", Expected: true, }, - EvaluationTest{ + { Name: "Comparison of string constants", Input: "'foo' == 'foo'", Expected: true, }, - EvaluationTest{ + { Name: "NEQ comparison of string constants", Input: "'foo' != 'bar'", Expected: true, }, - EvaluationTest{ + { Name: "REQ comparison of string constants", Input: "'foobar' =~ 'oba'", Expected: true, }, - EvaluationTest{ + { Name: "NREQ comparison of string constants", Input: "'foo' !~ 'bar'", Expected: true, }, - EvaluationTest{ + { Name: "Multiplicative/additive order", Input: "5 + 10 * 2", Expected: 25.0, }, - EvaluationTest{ + { Name: "Multiple constant multiplications", Input: "10 * 10 * 10", Expected: 1000.0, }, - EvaluationTest{ + { Name: "Multiple adds/multiplications", Input: "10 * 10 * 10 + 1 * 10 * 10", Expected: 1100.0, }, - EvaluationTest{ + { Name: "Modulus precedence", Input: "1 + 101 % 2 * 5", Expected: 6.0, }, - EvaluationTest{ + { Name: "Exponent precedence", Input: "1 + 5 ** 3 % 2 * 5", Expected: 6.0, }, - EvaluationTest{ + { Name: "Bit shift precedence", Input: "50 << 1 & 90", Expected: 64.0, }, - EvaluationTest{ + { Name: "Bit shift precedence", Input: "90 & 50 << 1", Expected: 64.0, }, - EvaluationTest{ - + { Name: "Bit shift precedence amongst non-bitwise", Input: "90 + 50 << 1 * 5", Expected: 4480.0, }, - EvaluationTest{ + { Name: "Order of non-commutative same-precedence operators (additive)", Input: "1 - 2 - 4 - 8", Expected: -13.0, }, - EvaluationTest{ + { Name: "Order of non-commutative same-precedence operators (multiplicative)", Input: "1 * 4 / 2 * 8", Expected: 16.0, }, - EvaluationTest{ + { Name: "Null coalesce precedence", Input: "true ?? true ? 100 + 200 : 400", Expected: 300.0, }, - EvaluationTest{ - + { Name: "Identical date equivalence", Input: "'2014-01-02 14:12:22' == '2014-01-02 14:12:22'", Expected: true, }, - EvaluationTest{ - + { Name: "Positive date GT", Input: "'2014-01-02 14:12:22' > '2014-01-02 12:12:22'", Expected: true, }, - EvaluationTest{ - + { Name: "Negative date GT", Input: "'2014-01-02 14:12:22' > '2014-01-02 16:12:22'", Expected: false, }, - EvaluationTest{ - + { Name: "Positive date GTE", Input: "'2014-01-02 14:12:22' >= '2014-01-02 12:12:22'", Expected: true, }, - EvaluationTest{ - + { Name: "Negative date GTE", Input: "'2014-01-02 14:12:22' >= '2014-01-02 16:12:22'", Expected: false, }, - EvaluationTest{ - + { Name: "Positive date LT", Input: "'2014-01-02 14:12:22' < '2014-01-02 16:12:22'", Expected: true, }, - EvaluationTest{ - + { Name: "Negative date LT", Input: "'2014-01-02 14:12:22' < '2014-01-02 11:12:22'", Expected: false, }, - EvaluationTest{ - + { Name: "Positive date LTE", Input: "'2014-01-02 09:12:22' <= '2014-01-02 12:12:22'", Expected: true, }, - EvaluationTest{ - + { Name: "Negative date LTE", Input: "'2014-01-02 14:12:22' <= '2014-01-02 11:12:22'", Expected: false, }, - EvaluationTest{ - + { Name: "Sign prefix comparison", Input: "-1 < 0", Expected: true, }, - EvaluationTest{ - + { Name: "Lexicographic LT", Input: "'ab' < 'abc'", Expected: true, }, - EvaluationTest{ - + { Name: "Lexicographic LTE", Input: "'ab' <= 'abc'", Expected: true, }, - EvaluationTest{ - + { Name: "Lexicographic GT", Input: "'aba' > 'abc'", Expected: false, }, - EvaluationTest{ - + { Name: "Lexicographic GTE", Input: "'aba' >= 'abc'", Expected: false, }, - EvaluationTest{ - + { Name: "Boolean sign prefix comparison", Input: "!true == false", Expected: true, }, - EvaluationTest{ - + { Name: "Inversion of clause", Input: "!(10 < 0)", Expected: true, }, - EvaluationTest{ - + { Name: "Negation after modifier", Input: "10 * -10", Expected: -100.0, }, - EvaluationTest{ - + { Name: "Ternary with single boolean", Input: "true ? 10", Expected: 10.0, }, - EvaluationTest{ - + { Name: "Ternary nil with single boolean", Input: "false ? 10", Expected: nil, }, - EvaluationTest{ - + { Name: "Ternary with comparator boolean", Input: "10 > 5 ? 35.50", Expected: 35.50, }, - EvaluationTest{ - + { Name: "Ternary nil with comparator boolean", Input: "1 > 5 ? 35.50", Expected: nil, }, - EvaluationTest{ - + { Name: "Ternary with parentheses", Input: "(5 * (15 - 5)) > 5 ? 35.50", Expected: 35.50, }, - EvaluationTest{ - + { Name: "Ternary precedence", Input: "true ? 35.50 > 10", Expected: true, }, - EvaluationTest{ - + { Name: "Ternary-else", Input: "false ? 35.50 : 50", Expected: 50.0, }, - EvaluationTest{ - + { Name: "Ternary-else inside clause", Input: "(false ? 5 : 35.50) > 10", Expected: true, }, - EvaluationTest{ - + { Name: "Ternary-else (true-case) inside clause", Input: "(true ? 1 : 5) < 10", Expected: true, }, - EvaluationTest{ - + { Name: "Ternary-else before comparator (negative case)", Input: "true ? 1 : 5 > 10", Expected: 1.0, }, - EvaluationTest{ - + { Name: "Nested ternaries (#32)", Input: "(2 == 2) ? 1 : (true ? 2 : 3)", Expected: 1.0, }, - EvaluationTest{ - + { Name: "Nested ternaries, right case (#32)", Input: "false ? 1 : (true ? 2 : 3)", Expected: 2.0, }, - EvaluationTest{ - + { Name: "Doubly-nested ternaries (#32)", Input: "true ? (false ? 1 : (false ? 2 : 3)) : (false ? 4 : 5)", Expected: 3.0, }, - EvaluationTest{ - + { Name: "String to string concat", Input: "'foo' + 'bar' == 'foobar'", Expected: true, }, - EvaluationTest{ - + { Name: "String to float64 concat", Input: "'foo' + 123 == 'foo123'", Expected: true, }, - EvaluationTest{ - + { Name: "Float64 to string concat", Input: "123 + 'bar' == '123bar'", Expected: true, }, - EvaluationTest{ - + { Name: "String to date concat", Input: "'foo' + '02/05/1970' == 'foobar'", Expected: false, }, - EvaluationTest{ - + { Name: "String to bool concat", Input: "'foo' + true == 'footrue'", Expected: true, }, - EvaluationTest{ - + { Name: "Bool to string concat", Input: "true + 'bar' == 'truebar'", Expected: true, }, - EvaluationTest{ - + { Name: "Null coalesce left", Input: "1 ?? 2", Expected: 1.0, }, - EvaluationTest{ - + { Name: "Array membership literals", Input: "1 in (1, 2, 3)", Expected: true, }, - EvaluationTest{ - + { Name: "Array membership literal with inversion", Input: "!(1 in (1, 2, 3))", Expected: false, }, - EvaluationTest{ - + { Name: "Single Element Array membership literal", Input: "1 in (1)", Expected: true, }, - EvaluationTest{ - + { Name: "Logical operator reordering (#30)", Input: "(true && true) || (true && false)", Expected: true, }, - EvaluationTest{ - + { Name: "Logical operator reordering without parens (#30)", Input: "true && true || true && false", Expected: true, }, - EvaluationTest{ - + { Name: "Logical operator reordering with multiple OR (#30)", Input: "false || true && true || false", Expected: true, }, - EvaluationTest{ - + { Name: "Left-side multiple consecutive (should be reordered) operators", Input: "(10 * 10 * 10) > 10", Expected: true, }, - EvaluationTest{ - + { Name: "Three-part non-paren logical op reordering (#44)", Input: "false && true || true", Expected: true, }, - EvaluationTest{ - + { Name: "Three-part non-paren logical op reordering (#44), second one", Input: "true || false && true", Expected: true, }, - EvaluationTest{ - + { Name: "Logical operator reordering without parens (#45)", Input: "true && true || false && false", Expected: true, }, - EvaluationTest{ - + { Name: "Single function", Input: "foo()", Functions: map[string]ExpressionFunction{ @@ -562,11 +512,9 @@ func TestNoParameterEvaluation(test *testing.T) { return true, nil }, }, - Expected: true, }, - EvaluationTest{ - + { Name: "Function with argument", Input: "passthrough(1)", Functions: map[string]ExpressionFunction{ @@ -574,12 +522,9 @@ func TestNoParameterEvaluation(test *testing.T) { return arguments[0], nil }, }, - Expected: 1.0, }, - - EvaluationTest{ - + { Name: "Function with arguments", Input: "passthrough(1, 2)", Functions: map[string]ExpressionFunction{ @@ -587,16 +532,13 @@ func TestNoParameterEvaluation(test *testing.T) { return arguments[0].(float64) + arguments[1].(float64), nil }, }, - Expected: 3.0, }, - EvaluationTest{ - + { Name: "Nested function with precedence", Input: "sum(1, sum(2, 3), 2 + 2, true ? 4 : 5)", Functions: map[string]ExpressionFunction{ "sum": func(arguments ...interface{}) (interface{}, error) { - sum := 0.0 for _, v := range arguments { sum += v.(float64) @@ -604,11 +546,9 @@ func TestNoParameterEvaluation(test *testing.T) { return sum, nil }, }, - Expected: 14.0, }, - EvaluationTest{ - + { Name: "Empty function and modifier, compared", Input: "numeric()-1 > 0", Functions: map[string]ExpressionFunction{ @@ -616,11 +556,9 @@ func TestNoParameterEvaluation(test *testing.T) { return 2.0, nil }, }, - Expected: true, }, - EvaluationTest{ - + { Name: "Empty function comparator", Input: "numeric() > 0", Functions: map[string]ExpressionFunction{ @@ -628,11 +566,9 @@ func TestNoParameterEvaluation(test *testing.T) { return 2.0, nil }, }, - Expected: true, }, - EvaluationTest{ - + { Name: "Empty function logical operator", Input: "success() && !false", Functions: map[string]ExpressionFunction{ @@ -640,11 +576,9 @@ func TestNoParameterEvaluation(test *testing.T) { return true, nil }, }, - Expected: true, }, - EvaluationTest{ - + { Name: "Empty function ternary", Input: "nope() ? 1 : 2.0", Functions: map[string]ExpressionFunction{ @@ -652,11 +586,9 @@ func TestNoParameterEvaluation(test *testing.T) { return false, nil }, }, - Expected: 2.0, }, - EvaluationTest{ - + { Name: "Empty function null coalesce", Input: "null() ?? 2", Functions: map[string]ExpressionFunction{ @@ -664,11 +596,9 @@ func TestNoParameterEvaluation(test *testing.T) { return nil, nil }, }, - Expected: 2.0, }, - EvaluationTest{ - + { Name: "Empty function with prefix", Input: "-ten()", Functions: map[string]ExpressionFunction{ @@ -676,11 +606,9 @@ func TestNoParameterEvaluation(test *testing.T) { return 10.0, nil }, }, - Expected: -10.0, }, - EvaluationTest{ - + { Name: "Empty function as part of chain", Input: "10 - numeric() - 2", Functions: map[string]ExpressionFunction{ @@ -688,11 +616,9 @@ func TestNoParameterEvaluation(test *testing.T) { return 5.0, nil }, }, - Expected: 3.0, }, - EvaluationTest{ - + { Name: "Empty function near separator", Input: "10 in (1, 2, 3, ten(), 8)", Functions: map[string]ExpressionFunction{ @@ -700,11 +626,9 @@ func TestNoParameterEvaluation(test *testing.T) { return 10.0, nil }, }, - Expected: true, }, - EvaluationTest{ - + { Name: "Enclosed empty function with modifier and comparator (#28)", Input: "(ten() - 1) > 3", Functions: map[string]ExpressionFunction{ @@ -712,11 +636,9 @@ func TestNoParameterEvaluation(test *testing.T) { return 10.0, nil }, }, - Expected: true, }, - EvaluationTest{ - + { Name: "Ternary/Java EL ambiguity", Input: "false ? foo:length()", Functions: map[string]ExpressionFunction{ @@ -734,513 +656,469 @@ func TestNoParameterEvaluation(test *testing.T) { func TestParameterizedEvaluation(test *testing.T) { evaluationTests := []EvaluationTest{ - - EvaluationTest{ - + { Name: "Single parameter modified by constant", Input: "foo + 2", Parameters: []EvaluationParameter{ - - EvaluationParameter{ + { Name: "foo", Value: 2.0, }, }, Expected: 4.0, }, - EvaluationTest{ - + { Name: "Single parameter modified by variable", Input: "foo * bar", Parameters: []EvaluationParameter{ - - EvaluationParameter{ + { Name: "foo", Value: 5.0, }, - EvaluationParameter{ + { Name: "bar", Value: 2.0, }, }, Expected: 10.0, }, - EvaluationTest{ - + { Name: "Multiple multiplications of the same parameter", Input: "foo * foo * foo", Parameters: []EvaluationParameter{ - - EvaluationParameter{ + { Name: "foo", Value: 10.0, }, }, Expected: 1000.0, }, - EvaluationTest{ - + { Name: "Multiple additions of the same parameter", Input: "foo + foo + foo", Parameters: []EvaluationParameter{ - - EvaluationParameter{ + { Name: "foo", Value: 10.0, }, }, Expected: 30.0, }, - EvaluationTest{ - + { Name: "Parameter name sensitivity", Input: "foo + FoO + FOO", Parameters: []EvaluationParameter{ - - EvaluationParameter{ + { Name: "foo", Value: 8.0, }, - EvaluationParameter{ + { Name: "FoO", Value: 4.0, }, - EvaluationParameter{ + { Name: "FOO", Value: 2.0, }, }, Expected: 14.0, }, - EvaluationTest{ - + { Name: "Sign prefix comparison against prefixed variable", Input: "-1 < -foo", Parameters: []EvaluationParameter{ - - EvaluationParameter{ + { Name: "foo", Value: -8.0, }, }, Expected: true, }, - EvaluationTest{ - + { Name: "Fixed-point parameter", Input: "foo > 1", Parameters: []EvaluationParameter{ - - EvaluationParameter{ + { Name: "foo", Value: 2, }, }, Expected: true, }, - EvaluationTest{ + { Name: "Modifier after closing clause", Input: "(2 + 2) + 2 == 6", Expected: true, }, - EvaluationTest{ - + { Name: "Comparator after closing clause", Input: "(2 + 2) >= 4", Expected: true, }, - EvaluationTest{ - + { Name: "Two-boolean logical operation (for issue #8)", Input: "(foo == true) || (bar == true)", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: true, }, - EvaluationParameter{ + { Name: "bar", Value: false, }, }, Expected: true, }, - EvaluationTest{ - + { Name: "Two-variable integer logical operation (for issue #8)", Input: "foo > 10 && bar > 10", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: 1, }, - EvaluationParameter{ + { Name: "bar", Value: 11, }, }, Expected: false, }, - EvaluationTest{ - + { Name: "Regex against right-hand parameter", Input: "'foobar' =~ foo", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: "obar", }, }, Expected: true, }, - EvaluationTest{ - + { Name: "Not-regex against right-hand parameter", Input: "'foobar' !~ foo", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: "baz", }, }, Expected: true, }, - EvaluationTest{ - + { Name: "Regex against two parameters", Input: "foo =~ bar", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: "foobar", }, - EvaluationParameter{ + { Name: "bar", Value: "oba", }, }, Expected: true, }, - EvaluationTest{ - + { Name: "Not-regex against two parameters", Input: "foo !~ bar", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: "foobar", }, - EvaluationParameter{ + { Name: "bar", Value: "baz", }, }, Expected: true, }, - EvaluationTest{ - + { Name: "Pre-compiled regex", Input: "foo =~ bar", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: "foobar", }, - EvaluationParameter{ + { Name: "bar", Value: regexp.MustCompile("[fF][oO]+"), }, }, Expected: true, }, - EvaluationTest{ - + { Name: "Pre-compiled not-regex", Input: "foo !~ bar", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: "foobar", }, - EvaluationParameter{ + { Name: "bar", Value: regexp.MustCompile("[fF][oO]+"), }, }, Expected: false, }, - EvaluationTest{ - + { Name: "Single boolean parameter", Input: "commission ? 10", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "commission", Value: true, }, }, Expected: 10.0, }, - EvaluationTest{ - + { Name: "True comparator with a parameter", Input: "partner == 'amazon' ? 10", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "partner", Value: "amazon", }, }, Expected: 10.0, }, - EvaluationTest{ - + { Name: "False comparator with a parameter", Input: "partner == 'amazon' ? 10", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "partner", Value: "ebay", }, }, Expected: nil, }, - EvaluationTest{ - + { Name: "True comparator with multiple parameters", Input: "theft && period == 24 ? 60", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "theft", Value: true, }, - EvaluationParameter{ + { Name: "period", Value: 24, }, }, Expected: 60.0, }, - EvaluationTest{ - + { Name: "False comparator with multiple parameters", Input: "theft && period == 24 ? 60", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "theft", Value: false, }, - EvaluationParameter{ + { Name: "period", Value: 24, }, }, Expected: nil, }, - EvaluationTest{ - + { Name: "String concat with single string parameter", Input: "foo + 'bar'", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: "baz", }, }, Expected: "bazbar", }, - EvaluationTest{ - + { Name: "String concat with multiple string parameter", Input: "foo + bar", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: "baz", }, - EvaluationParameter{ + { Name: "bar", Value: "quux", }, }, Expected: "bazquux", }, - EvaluationTest{ - + { Name: "String concat with float parameter", Input: "foo + bar", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: "baz", }, - EvaluationParameter{ + { Name: "bar", Value: 123.0, }, }, Expected: "baz123", }, - EvaluationTest{ - + { Name: "Mixed multiple string concat", Input: "foo + 123 + 'bar' + true", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: "baz", }, }, Expected: "baz123bartrue", }, - EvaluationTest{ - + { Name: "Integer width spectrum", Input: "uint8 + uint16 + uint32 + uint64 + int8 + int16 + int32 + int64", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "uint8", Value: uint8(0), }, - EvaluationParameter{ + { Name: "uint16", Value: uint16(0), }, - EvaluationParameter{ + { Name: "uint32", Value: uint32(0), }, - EvaluationParameter{ + { Name: "uint64", Value: uint64(0), }, - EvaluationParameter{ + { Name: "int8", Value: int8(0), }, - EvaluationParameter{ + { Name: "int16", Value: int16(0), }, - EvaluationParameter{ + { Name: "int32", Value: int32(0), }, - EvaluationParameter{ + { Name: "int64", Value: int64(0), }, }, Expected: 0.0, }, - EvaluationTest{ - + { Name: "Floats", Input: "float32 + float64", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "float32", Value: float32(0.0), }, - EvaluationParameter{ + { Name: "float64", Value: float64(0.0), }, }, Expected: 0.0, }, - EvaluationTest{ - + { Name: "Null coalesce right", Input: "foo ?? 1.0", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: nil, }, }, Expected: 1.0, }, - EvaluationTest{ - + { Name: "Multiple comparator/logical operators (#30)", Input: "(foo >= 2887057408 && foo <= 2887122943) || (foo >= 168100864 && foo <= 168118271)", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: 2887057409, }, }, Expected: true, }, - EvaluationTest{ - + { Name: "Multiple comparator/logical operators, opposite order (#30)", Input: "(foo >= 168100864 && foo <= 168118271) || (foo >= 2887057408 && foo <= 2887122943)", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: 2887057409, }, }, Expected: true, }, - EvaluationTest{ - + { Name: "Multiple comparator/logical operators, small value (#30)", Input: "(foo >= 2887057408 && foo <= 2887122943) || (foo >= 168100864 && foo <= 168118271)", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: 168100865, }, }, Expected: true, }, - EvaluationTest{ - + { Name: "Multiple comparator/logical operators, small value, opposite order (#30)", Input: "(foo >= 168100864 && foo <= 168118271) || (foo >= 2887057408 && foo <= 2887122943)", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "foo", Value: 168100865, }, }, Expected: true, }, - EvaluationTest{ - + { Name: "Incomparable array equality comparison", Input: "arr == arr", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "arr", Value: []int{0, 0, 0}, }, }, Expected: true, }, - EvaluationTest{ - + { Name: "Incomparable array not-equality comparison", Input: "arr != arr", Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "arr", Value: []int{0, 0, 0}, }, }, Expected: false, }, - EvaluationTest{ - + { Name: "Mixed function and parameters", Input: "sum(1.2, amount) + name", Functions: map[string]ExpressionFunction{ "sum": func(arguments ...interface{}) (interface{}, error) { - sum := 0.0 for _, v := range arguments { sum += v.(float64) @@ -1249,20 +1127,18 @@ func TestParameterizedEvaluation(test *testing.T) { }, }, Parameters: []EvaluationParameter{ - EvaluationParameter{ + { Name: "amount", Value: .8, }, - EvaluationParameter{ + { Name: "name", Value: "awesome", }, }, - Expected: "2awesome", }, - EvaluationTest{ - + { Name: "Short-circuit OR", Input: "true || fail()", Functions: map[string]ExpressionFunction{ @@ -1272,8 +1148,7 @@ func TestParameterizedEvaluation(test *testing.T) { }, Expected: true, }, - EvaluationTest{ - + { Name: "Short-circuit AND", Input: "false && fail()", Functions: map[string]ExpressionFunction{ @@ -1283,8 +1158,7 @@ func TestParameterizedEvaluation(test *testing.T) { }, Expected: false, }, - EvaluationTest{ - + { Name: "Short-circuit ternary", Input: "true ? 1 : fail()", Functions: map[string]ExpressionFunction{ @@ -1294,8 +1168,7 @@ func TestParameterizedEvaluation(test *testing.T) { }, Expected: 1.0, }, - EvaluationTest{ - + { Name: "Short-circuit coalesce", Input: "'foo' ?? fail()", Functions: map[string]ExpressionFunction{ @@ -1305,149 +1178,127 @@ func TestParameterizedEvaluation(test *testing.T) { }, Expected: "foo", }, - EvaluationTest{ - + { Name: "Simple parameter call", Input: "foo.String", Parameters: []EvaluationParameter{fooParameter}, Expected: fooParameter.Value.(dummyParameter).String, }, - EvaluationTest{ - + { Name: "Simple parameter function call", Input: "foo.Func()", Parameters: []EvaluationParameter{fooParameter}, Expected: "funk", }, - EvaluationTest{ - + { Name: "Simple parameter call from pointer", Input: "fooptr.String", Parameters: []EvaluationParameter{fooPtrParameter}, Expected: fooParameter.Value.(dummyParameter).String, }, - EvaluationTest{ - + { Name: "Simple parameter function call from pointer", Input: "fooptr.Func()", Parameters: []EvaluationParameter{fooPtrParameter}, Expected: "funk", }, - EvaluationTest{ - + { Name: "Simple parameter function call from pointer", Input: "fooptr.Func3()", Parameters: []EvaluationParameter{fooPtrParameter}, Expected: "fronk", }, - EvaluationTest{ - + { Name: "Simple parameter call", Input: "foo.String == 'hi'", Parameters: []EvaluationParameter{fooParameter}, Expected: false, }, - EvaluationTest{ - + { Name: "Simple parameter call with modifier", Input: "foo.String + 'hi'", Parameters: []EvaluationParameter{fooParameter}, Expected: fooParameter.Value.(dummyParameter).String + "hi", }, - EvaluationTest{ - + { Name: "Simple parameter function call, two-arg return", Input: "foo.Func2()", Parameters: []EvaluationParameter{fooParameter}, Expected: "frink", }, - EvaluationTest{ - + { Name: "Parameter function call with all argument types", Input: "foo.TestArgs(\"hello\", 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1.0, 2.0, true)", Parameters: []EvaluationParameter{fooParameter}, Expected: "hello: 33", }, - - EvaluationTest{ - + { Name: "Simple parameter function call, one arg", Input: "foo.FuncArgStr('boop')", Parameters: []EvaluationParameter{fooParameter}, Expected: "boop", }, - EvaluationTest{ - + { Name: "Simple parameter function call, one arg", Input: "foo.FuncArgStr('boop') + 'hi'", Parameters: []EvaluationParameter{fooParameter}, Expected: "boophi", }, - EvaluationTest{ - + { Name: "Nested parameter function call", Input: "foo.Nested.Dunk('boop')", Parameters: []EvaluationParameter{fooParameter}, Expected: "boopdunk", }, - EvaluationTest{ - + { Name: "Nested parameter call", Input: "foo.Nested.Funk", Parameters: []EvaluationParameter{fooParameter}, Expected: "funkalicious", }, - EvaluationTest{ - + { Name: "Nested Map string", Input: "foo.Map.String", Parameters: []EvaluationParameter{fooParameter}, Expected: "string!", }, - EvaluationTest{ - + { Name: "Nested Map function", Input: `foo.Map.StringCompare("foo", "bar")`, Parameters: []EvaluationParameter{fooParameter}, Expected: 1.0, }, - EvaluationTest{ - + { Name: "Nested Map IntArray", Input: "3 IN foo.Map.IntArray", Parameters: []EvaluationParameter{fooParameter}, Expected: true, }, - EvaluationTest{ - + { Name: "Nested Map StringArray", Input: `"bar" IN foo.Map.StringArray`, Parameters: []EvaluationParameter{fooParameter}, Expected: true, }, - EvaluationTest{ - + { Name: "Parameter call with + modifier", Input: "1 + foo.Int", Parameters: []EvaluationParameter{fooParameter}, Expected: 102.0, }, - EvaluationTest{ - + { Name: "Parameter string call with + modifier", Input: "'woop' + (foo.String)", Parameters: []EvaluationParameter{fooParameter}, Expected: "woopstring!", }, - EvaluationTest{ - + { Name: "Parameter call with && operator", Input: "true && foo.BoolFalse", Parameters: []EvaluationParameter{fooParameter}, Expected: false, }, - EvaluationTest{ - + { Name: "Null coalesce nested parameter", Input: "foo.Nil ?? false", Parameters: []EvaluationParameter{fooParameter}, diff --git a/expressionOutputStream.go b/expressionOutputStream.go index 88a8416..517bbf1 100644 --- a/expressionOutputStream.go +++ b/expressionOutputStream.go @@ -5,42 +5,42 @@ import ( ) /* - Holds a series of "transactions" which represent each token as it is output by an outputter (such as ToSQLQuery()). - Some outputs (such as SQL) require a function call or non-c-like syntax to represent an expression. - To accomplish this, this struct keeps track of each translated token as it is output, and can return and rollback those transactions. +Holds a series of "transactions" which represent each token as it is output by an outputter (such as ToSQLQuery()). +Some outputs (such as SQL) require a function call or non-c-like syntax to represent an expression. +To accomplish this, this struct keeps track of each translated token as it is output, and can return and rollback those transactions. */ type expressionOutputStream struct { transactions []string } -func (this *expressionOutputStream) add(transaction string) { - this.transactions = append(this.transactions, transaction) +func (e *expressionOutputStream) add(transaction string) { + e.transactions = append(e.transactions, transaction) } -func (this *expressionOutputStream) rollback() string { +func (e *expressionOutputStream) rollback() string { - index := len(this.transactions) - 1 - ret := this.transactions[index] + index := len(e.transactions) - 1 + ret := e.transactions[index] - this.transactions = this.transactions[:index] + e.transactions = e.transactions[:index] return ret } -func (this *expressionOutputStream) createString(delimiter string) string { +func (e *expressionOutputStream) createString(delimiter string) string { var retBuffer bytes.Buffer var transaction string - penultimate := len(this.transactions) - 1 + penultimate := len(e.transactions) - 1 for i := 0; i < penultimate; i++ { - transaction = this.transactions[i] + transaction = e.transactions[i] retBuffer.WriteString(transaction) retBuffer.WriteString(delimiter) } - retBuffer.WriteString(this.transactions[penultimate]) + retBuffer.WriteString(e.transactions[penultimate]) return retBuffer.String() } diff --git a/lexerState.go b/lexerState.go index 6726e90..d879f40 100644 --- a/lexerState.go +++ b/lexerState.go @@ -16,7 +16,7 @@ type lexerState struct { // Constant for all purposes except compiler. var validLexerStates = []lexerState{ - lexerState{ + { kind: UNKNOWN, isEOF: false, isNullable: true, @@ -35,7 +35,7 @@ var validLexerStates = []lexerState{ }, }, - lexerState{ + { kind: CLAUSE, isEOF: false, @@ -56,7 +56,7 @@ var validLexerStates = []lexerState{ }, }, - lexerState{ + { kind: CLAUSE_CLOSE, isEOF: true, @@ -79,7 +79,7 @@ var validLexerStates = []lexerState{ }, }, - lexerState{ + { kind: NUMERIC, isEOF: true, @@ -94,7 +94,7 @@ var validLexerStates = []lexerState{ SEPARATOR, }, }, - lexerState{ + { kind: BOOLEAN, isEOF: true, @@ -109,7 +109,7 @@ var validLexerStates = []lexerState{ SEPARATOR, }, }, - lexerState{ + { kind: STRING, isEOF: true, @@ -124,7 +124,7 @@ var validLexerStates = []lexerState{ SEPARATOR, }, }, - lexerState{ + { kind: TIME, isEOF: true, @@ -138,7 +138,7 @@ var validLexerStates = []lexerState{ SEPARATOR, }, }, - lexerState{ + { kind: PATTERN, isEOF: true, @@ -152,7 +152,7 @@ var validLexerStates = []lexerState{ SEPARATOR, }, }, - lexerState{ + { kind: VARIABLE, isEOF: true, @@ -167,7 +167,7 @@ var validLexerStates = []lexerState{ SEPARATOR, }, }, - lexerState{ + { kind: MODIFIER, isEOF: false, @@ -185,7 +185,7 @@ var validLexerStates = []lexerState{ CLAUSE_CLOSE, }, }, - lexerState{ + { kind: COMPARATOR, isEOF: false, @@ -205,7 +205,7 @@ var validLexerStates = []lexerState{ PATTERN, }, }, - lexerState{ + { kind: LOGICALOP, isEOF: false, @@ -224,7 +224,7 @@ var validLexerStates = []lexerState{ CLAUSE_CLOSE, }, }, - lexerState{ + { kind: PREFIX, isEOF: false, @@ -241,7 +241,7 @@ var validLexerStates = []lexerState{ }, }, - lexerState{ + { kind: TERNARY, isEOF: false, @@ -260,7 +260,7 @@ var validLexerStates = []lexerState{ SEPARATOR, }, }, - lexerState{ + { kind: FUNCTION, isEOF: false, @@ -269,7 +269,7 @@ var validLexerStates = []lexerState{ CLAUSE, }, }, - lexerState{ + { kind: ACCESSOR, isEOF: true, @@ -284,7 +284,7 @@ var validLexerStates = []lexerState{ SEPARATOR, }, }, - lexerState{ + { kind: SEPARATOR, isEOF: false, @@ -304,9 +304,9 @@ var validLexerStates = []lexerState{ }, } -func (this lexerState) canTransitionTo(kind TokenKind) bool { +func (l lexerState) canTransitionTo(kind TokenKind) bool { - for _, validKind := range this.validNextKinds { + for _, validKind := range l.validNextKinds { if validKind == kind { return true @@ -346,7 +346,7 @@ func checkExpressionSyntax(tokens []ExpressionToken) error { if !state.isNullable && token.Value == nil { - errorMsg := fmt.Sprintf("Token kind '%v' cannot have a nil value", token.Kind.String()) + errorMsg := fmt.Sprintf("token kind '%v' cannot have a nil value", token.Kind.String()) return errors.New(errorMsg) } @@ -354,7 +354,7 @@ func checkExpressionSyntax(tokens []ExpressionToken) error { } if !state.isEOF { - return errors.New("Unexpected end of expression") + return errors.New("unexpected end of expression") } return nil } @@ -368,6 +368,6 @@ func getLexerStateForToken(kind TokenKind) (lexerState, error) { } } - errorMsg := fmt.Sprintf("No lexer state found for token kind '%v'\n", kind.String()) + errorMsg := fmt.Sprintf("no lexer state found for token kind '%v'\n", kind.String()) return validLexerStates[0], errors.New(errorMsg) } diff --git a/lexerStream.go b/lexerStream.go index 758682b..7faa52f 100644 --- a/lexerStream.go +++ b/lexerStream.go @@ -34,36 +34,36 @@ func newLexerStream(source string) *lexerStream { return ret } -func (this *lexerStream) readCharacter() rune { - character := this.source[this.position] - this.position += 1 - this.strPosition += utf8.RuneLen(character) +func (stream *lexerStream) readCharacter() rune { + character := stream.source[stream.position] + stream.position += 1 + stream.strPosition += utf8.RuneLen(character) return character } -func (this *lexerStream) rewind(amount int) { +func (stream *lexerStream) rewind(amount int) { if amount < 0 { - this.position -= amount - this.strPosition -= amount + stream.position -= amount + stream.strPosition -= amount } strAmount := 0 for i := 0; i < amount; i++ { - if this.position >= this.length { + if stream.position >= stream.length { strAmount += 1 - this.position -= 1 + stream.position -= 1 continue } - strAmount += utf8.RuneLen(this.source[this.position-1]) - this.position -= 1 + strAmount += utf8.RuneLen(stream.source[stream.position-1]) + stream.position -= 1 } - this.strPosition -= strAmount + stream.strPosition -= strAmount } -func (this lexerStream) canRead() bool { - return this.position < this.length +func (stream lexerStream) canRead() bool { + return stream.position < stream.length } -func (this *lexerStream) close() { - this.source = this.source[:0] - lexerStreamPool.Put(this) +func (stream *lexerStream) close() { + stream.source = stream.source[:0] + lexerStreamPool.Put(stream) } diff --git a/parsing.go b/parsing.go index f351abf..98189c0 100644 --- a/parsing.go +++ b/parsing.go @@ -151,7 +151,7 @@ func readToken(stream *lexerStream, state lexerState, functions map[string]Expre kind = VARIABLE if !completed { - return ExpressionToken{}, errors.New("Unclosed parameter bracket"), false + return ExpressionToken{}, errors.New("unclosed parameter bracket"), false } // above method normally rewinds us to the closing bracket, which we want to skip. @@ -210,7 +210,7 @@ func readToken(stream *lexerStream, state lexerState, functions map[string]Expre tokenValue, completed = readUntilFalse(stream, true, false, true, isNotQuote) if !completed { - return ExpressionToken{}, errors.New("Unclosed string literal"), false + return ExpressionToken{}, errors.New("unclosed string literal"), false } // advance the stream one position, since reading until false assumes the terminator is a real token @@ -442,7 +442,7 @@ func checkBalance(tokens []ExpressionToken) error { stream.close() if parens != 0 { - return errors.New("Unbalanced parenthesis") + return errors.New("unbalanced parenthesis") } return nil } @@ -472,13 +472,13 @@ func isNotQuote(character rune) bool { func isNotAlphanumeric(character rune) bool { - return !(unicode.IsDigit(character) || - unicode.IsLetter(character) || - character == '(' || - character == ')' || - character == '[' || - character == ']' || // starting to feel like there needs to be an `isOperation` func (#59) - !isNotQuote(character)) + return !unicode.IsDigit(character) && + !unicode.IsLetter(character) && + character != '(' && + character != ')' && + character != '[' && + character != ']' && // starting to feel like there needs to be an `isOperation` func (#59) + isNotQuote(character) } func isVariableName(character rune) bool { diff --git a/parsingFailure_test.go b/parsingFailure_test.go index 2411196..6a3dc01 100644 --- a/parsingFailure_test.go +++ b/parsingFailure_test.go @@ -1,19 +1,18 @@ package govaluate import ( - "fmt" "regexp/syntax" "strings" "testing" ) const ( - UNEXPECTED_END = "Unexpected end of expression" + UNEXPECTED_END = "unexpected end of expression" INVALID_TOKEN_TRANSITION = "Cannot transition token types" INVALID_TOKEN_KIND = "Invalid token" - UNCLOSED_QUOTES = "Unclosed string literal" - UNCLOSED_BRACKETS = "Unclosed parameter bracket" - UNBALANCED_PARENTHESIS = "Unbalanced parenthesis" + UNCLOSED_QUOTES = "unclosed string literal" + UNCLOSED_BRACKETS = "unclosed parameter bracket" + UNBALANCED_PARENTHESIS = "unbalanced parenthesis" INVALID_NUMERIC = "Unable to parse numeric value" UNDEFINED_FUNCTION = "Undefined function" HANGING_ACCESSOR = "Hanging accessor on token" @@ -33,166 +32,166 @@ func TestParsingFailure(test *testing.T) { parsingTests := []ParsingFailureTest{ - ParsingFailureTest{ + { Name: "Invalid equality comparator", Input: "1 = 1", Expected: INVALID_TOKEN_KIND, }, - ParsingFailureTest{ + { Name: "Invalid equality comparator", Input: "1 === 1", Expected: INVALID_TOKEN_KIND, }, - ParsingFailureTest{ + { Name: "Too many characters for logical operator", Input: "true &&& false", Expected: INVALID_TOKEN_KIND, }, - ParsingFailureTest{ + { Name: "Too many characters for logical operator", Input: "true ||| false", Expected: INVALID_TOKEN_KIND, }, - ParsingFailureTest{ + { Name: "Premature end to expression, via modifier", Input: "10 > 5 +", Expected: UNEXPECTED_END, }, - ParsingFailureTest{ + { Name: "Premature end to expression, via comparator", Input: "10 + 5 >", Expected: UNEXPECTED_END, }, - ParsingFailureTest{ + { Name: "Premature end to expression, via logical operator", Input: "10 > 5 &&", Expected: UNEXPECTED_END, }, - ParsingFailureTest{ + { Name: "Premature end to expression, via ternary operator", Input: "true ?", Expected: UNEXPECTED_END, }, - ParsingFailureTest{ + { Name: "Hanging REQ", Input: "'wat' =~", Expected: UNEXPECTED_END, }, - ParsingFailureTest{ + { Name: "Invalid operator change to REQ", Input: " / =~", Expected: INVALID_TOKEN_TRANSITION, }, - ParsingFailureTest{ + { Name: "Invalid starting token, comparator", Input: "> 10", Expected: INVALID_TOKEN_TRANSITION, }, - ParsingFailureTest{ + { Name: "Invalid starting token, modifier", Input: "+ 5", Expected: INVALID_TOKEN_TRANSITION, }, - ParsingFailureTest{ + { Name: "Invalid starting token, logical operator", Input: "&& 5 < 10", Expected: INVALID_TOKEN_TRANSITION, }, - ParsingFailureTest{ + { Name: "Invalid NUMERIC transition", Input: "10 10", Expected: INVALID_TOKEN_TRANSITION, }, - ParsingFailureTest{ + { Name: "Invalid STRING transition", Input: "'foo' 'foo'", Expected: INVALID_TOKEN_TRANSITION, }, - ParsingFailureTest{ + { Name: "Invalid operator transition", Input: "10 > < 10", Expected: INVALID_TOKEN_TRANSITION, }, - ParsingFailureTest{ + { Name: "Starting with unbalanced parens", Input: " ) ( arg2", Expected: INVALID_TOKEN_TRANSITION, }, - ParsingFailureTest{ + { Name: "Unclosed bracket", Input: "[foo bar", Expected: UNCLOSED_BRACKETS, }, - ParsingFailureTest{ + { Name: "Unclosed quote", Input: "foo == 'responseTime", Expected: UNCLOSED_QUOTES, }, - ParsingFailureTest{ + { Name: "Constant regex pattern fail to compile", Input: "foo =~ '[abc'", Expected: string(syntax.ErrMissingBracket), }, - ParsingFailureTest{ + { Name: "Unbalanced parenthesis", Input: "10 > (1 + 50", Expected: UNBALANCED_PARENTHESIS, }, - ParsingFailureTest{ + { Name: "Multiple radix", Input: "127.0.0.1", Expected: INVALID_NUMERIC, }, - ParsingFailureTest{ + { Name: "Undefined function", Input: "foobar()", Expected: UNDEFINED_FUNCTION, }, - ParsingFailureTest{ + { Name: "Hanging accessor", Input: "foo.Bar.", Expected: HANGING_ACCESSOR, }, - ParsingFailureTest{ + { Name: "Incomplete Hex", Input: "0x", Expected: INVALID_TOKEN_TRANSITION, }, - ParsingFailureTest{ + { Name: "Invalid Hex literal", Input: "0x > 0", Expected: INVALID_HEX, }, - ParsingFailureTest{ + { Name: "Hex float (Unsupported)", Input: "0x1.1", Expected: INVALID_TOKEN_TRANSITION, }, - ParsingFailureTest{ + { Name: "Hex invalid letter", Input: "0x12g1", Expected: INVALID_TOKEN_TRANSITION, @@ -206,7 +205,7 @@ func runParsingFailureTests(parsingTests []ParsingFailureTest, test *testing.T) var err error - fmt.Printf("Running %d parsing test cases...\n", len(parsingTests)) + // fmt.Printf("Running %d parsing test cases...\n", len(parsingTests)) for _, testCase := range parsingTests { diff --git a/parsing_test.go b/parsing_test.go index d7a2c42..01ebc94 100644 --- a/parsing_test.go +++ b/parsing_test.go @@ -2,7 +2,6 @@ package govaluate import ( "bytes" - "fmt" "reflect" "sync" "testing" @@ -23,429 +22,411 @@ type TokenParsingTest struct { func TestConstantParsing(test *testing.T) { tokenParsingTests := []TokenParsingTest{ - - TokenParsingTest{ - + { Name: "Single numeric", Input: "1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ - + { Name: "Single two-digit numeric", Input: "50", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 50.0, }, }, }, - TokenParsingTest{ - + { Name: "Zero", Input: "0", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 0.0, }, }, }, - TokenParsingTest{ + { Name: "One digit hex", Input: "0x1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ + { Name: "Two digit hex", Input: "0x10", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 16.0, }, }, }, - TokenParsingTest{ + { Name: "Hex with lowercase", Input: "0xabcdef", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 11259375.0, }, }, }, - TokenParsingTest{ + { Name: "Hex with uppercase", Input: "0xABCDEF", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 11259375.0, }, }, }, - TokenParsingTest{ - + { Name: "Single string", Input: "'foo'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "foo", }, }, }, - TokenParsingTest{ - + { Name: "Single time, RFC3339, only date", Input: "'2014-01-02'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: TIME, Value: time.Date(2014, time.January, 2, 0, 0, 0, 0, time.Local), }, }, }, - TokenParsingTest{ - + { Name: "Single time, RFC3339, with hh:mm", Input: "'2014-01-02 14:12'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: TIME, Value: time.Date(2014, time.January, 2, 14, 12, 0, 0, time.Local), }, }, }, - TokenParsingTest{ - + { Name: "Single time, RFC3339, with hh:mm:ss", Input: "'2014-01-02 14:12:22'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: TIME, Value: time.Date(2014, time.January, 2, 14, 12, 22, 0, time.Local), }, }, }, - TokenParsingTest{ - + { Name: "Single boolean", Input: "true", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: BOOLEAN, Value: true, }, }, }, - TokenParsingTest{ - + { Name: "Single large numeric", Input: "1234567890", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1234567890.0, }, }, }, - TokenParsingTest{ - + { Name: "Single floating-point", Input: "0.5", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 0.5, }, }, }, - TokenParsingTest{ - + { Name: "Single large floating point", Input: "3.14567471", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 3.14567471, }, }, }, - TokenParsingTest{ - + { Name: "Single false boolean", Input: "false", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: BOOLEAN, Value: false, }, }, }, - TokenParsingTest{ + { Name: "Single internationalized string", Input: "'ÆŦǽഈᚥஇคٸ'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "ÆŦǽഈᚥஇคٸ", }, }, }, - TokenParsingTest{ + { Name: "Single internationalized parameter", Input: "ÆŦǽഈᚥஇคٸ", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: VARIABLE, Value: "ÆŦǽഈᚥஇคٸ", }, }, }, - TokenParsingTest{ + { Name: "Parameterless function", Input: "foo()", Functions: map[string]ExpressionFunction{"foo": noop}, Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: FUNCTION, Value: noop, }, - ExpressionToken{ + { Kind: CLAUSE, }, - ExpressionToken{ + { Kind: CLAUSE_CLOSE, }, }, }, - TokenParsingTest{ + { Name: "Single parameter function", Input: "foo('bar')", Functions: map[string]ExpressionFunction{"foo": noop}, Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: FUNCTION, Value: noop, }, - ExpressionToken{ + { Kind: CLAUSE, }, - ExpressionToken{ + { Kind: STRING, Value: "bar", }, - ExpressionToken{ + { Kind: CLAUSE_CLOSE, }, }, }, - TokenParsingTest{ + { Name: "Multiple parameter function", Input: "foo('bar', 1.0)", Functions: map[string]ExpressionFunction{"foo": noop}, Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: FUNCTION, Value: noop, }, - ExpressionToken{ + { Kind: CLAUSE, }, - ExpressionToken{ + { Kind: STRING, Value: "bar", }, - ExpressionToken{ + { Kind: SEPARATOR, }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: CLAUSE_CLOSE, }, }, }, - TokenParsingTest{ + { Name: "Nested function", Input: "foo(foo('bar'), 1.0, foo(2.0))", Functions: map[string]ExpressionFunction{"foo": noop}, Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: FUNCTION, Value: noop, }, - ExpressionToken{ + { Kind: CLAUSE, }, - - ExpressionToken{ + { Kind: FUNCTION, Value: noop, }, - ExpressionToken{ + { Kind: CLAUSE, }, - ExpressionToken{ + { Kind: STRING, Value: "bar", }, - ExpressionToken{ + { Kind: CLAUSE_CLOSE, }, - ExpressionToken{ + { Kind: SEPARATOR, }, - - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - - ExpressionToken{ + { Kind: SEPARATOR, }, - - ExpressionToken{ + { Kind: FUNCTION, Value: noop, }, - ExpressionToken{ + { Kind: CLAUSE, }, - ExpressionToken{ + { Kind: NUMERIC, Value: 2.0, }, - ExpressionToken{ + { Kind: CLAUSE_CLOSE, }, - - ExpressionToken{ + { Kind: CLAUSE_CLOSE, }, }, }, - TokenParsingTest{ + { Name: "Function with modifier afterwards (#28)", Input: "foo() + 1", Functions: map[string]ExpressionFunction{"foo": noop}, Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: FUNCTION, Value: noop, }, - ExpressionToken{ + { Kind: CLAUSE, }, - ExpressionToken{ + { Kind: CLAUSE_CLOSE, }, - ExpressionToken{ + { Kind: MODIFIER, Value: "+", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ + { Name: "Function with modifier afterwards and comparator", Input: "(foo()-1) > 3", Functions: map[string]ExpressionFunction{"foo": noop}, Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: CLAUSE, }, - ExpressionToken{ + { Kind: FUNCTION, Value: noop, }, - ExpressionToken{ + { Kind: CLAUSE, }, - ExpressionToken{ + { Kind: CLAUSE_CLOSE, }, - ExpressionToken{ + { Kind: MODIFIER, Value: "-", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: CLAUSE_CLOSE, }, - ExpressionToken{ + { Kind: COMPARATOR, Value: ">", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 3.0, }, }, }, - TokenParsingTest{ + { Name: "Double-quoted string added to square-brackted param (#59)", Input: "\"a\" + [foo]", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "a", }, - ExpressionToken{ + { Kind: MODIFIER, Value: "+", }, - ExpressionToken{ + { Kind: VARIABLE, Value: "foo", }, }, }, - TokenParsingTest{ + { Name: "Accessor variable", Input: "foo.Var", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: ACCESSOR, Value: []string{"foo", "Var"}, }, }, }, - TokenParsingTest{ + { Name: "Accessor function", Input: "foo.Operation()", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: ACCESSOR, Value: []string{"foo", "Operation"}, }, - ExpressionToken{ + { Kind: CLAUSE, }, - ExpressionToken{ + { Kind: CLAUSE_CLOSE, }, }, @@ -459,67 +440,63 @@ func TestConstantParsing(test *testing.T) { func TestLogicalOperatorParsing(test *testing.T) { tokenParsingTests := []TokenParsingTest{ - - TokenParsingTest{ - + { Name: "Boolean AND", Input: "true && false", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: BOOLEAN, Value: true, }, - ExpressionToken{ + { Kind: LOGICALOP, Value: "&&", }, - ExpressionToken{ + { Kind: BOOLEAN, Value: false, }, }, }, - TokenParsingTest{ - + { Name: "Boolean OR", Input: "true || false", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: BOOLEAN, Value: true, }, - ExpressionToken{ + { Kind: LOGICALOP, Value: "||", }, - ExpressionToken{ + { Kind: BOOLEAN, Value: false, }, }, }, - TokenParsingTest{ - + { Name: "Multiple logical operators", Input: "true || false && true", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: BOOLEAN, Value: true, }, - ExpressionToken{ + { Kind: LOGICALOP, Value: "||", }, - ExpressionToken{ + { Kind: BOOLEAN, Value: false, }, - ExpressionToken{ + { Kind: LOGICALOP, Value: "&&", }, - ExpressionToken{ + { Kind: BOOLEAN, Value: true, }, @@ -534,410 +511,400 @@ func TestLogicalOperatorParsing(test *testing.T) { func TestComparatorParsing(test *testing.T) { tokenParsingTests := []TokenParsingTest{ - - TokenParsingTest{ - + { Name: "Numeric EQ", Input: "1 == 2", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "==", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 2.0, }, }, }, - TokenParsingTest{ - + { Name: "Numeric NEQ", Input: "1 != 2", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "!=", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 2.0, }, }, }, - TokenParsingTest{ - + { Name: "Numeric GT", Input: "1 > 0", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: COMPARATOR, Value: ">", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 0.0, }, }, }, - TokenParsingTest{ - + { Name: "Numeric LT", Input: "1 < 2", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "<", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 2.0, }, }, }, - TokenParsingTest{ - + { Name: "Numeric GTE", Input: "1 >= 2", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: COMPARATOR, Value: ">=", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 2.0, }, }, }, - TokenParsingTest{ - + { Name: "Numeric LTE", Input: "1 <= 2", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "<=", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 2.0, }, }, }, - TokenParsingTest{ - + { Name: "String LT", Input: "'ab.cd' < 'abc.def'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "ab.cd", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "<", }, - ExpressionToken{ + { Kind: STRING, Value: "abc.def", }, }, }, - TokenParsingTest{ - + { Name: "String LTE", Input: "'ab.cd' <= 'abc.def'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "ab.cd", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "<=", }, - ExpressionToken{ + { Kind: STRING, Value: "abc.def", }, }, }, - TokenParsingTest{ - + { Name: "String GT", Input: "'ab.cd' > 'abc.def'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "ab.cd", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: ">", }, - ExpressionToken{ + { Kind: STRING, Value: "abc.def", }, }, }, - TokenParsingTest{ + { Name: "String GTE", Input: "'ab.cd' >= 'abc.def'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "ab.cd", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: ">=", }, - ExpressionToken{ + { Kind: STRING, Value: "abc.def", }, }, }, - TokenParsingTest{ + { Name: "String REQ", Input: "'foobar' =~ 'bar'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "foobar", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "=~", }, // it's not particularly clean to test for the contents of a pattern, (since it means modifying the harness below) // so pattern contents are left untested. - ExpressionToken{ + { Kind: PATTERN, }, }, }, - TokenParsingTest{ + { Name: "String NREQ", Input: "'foobar' !~ 'bar'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "foobar", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "!~", }, - ExpressionToken{ + { Kind: PATTERN, }, }, }, - TokenParsingTest{ + { Name: "Comparator against modifier string additive (#22)", Input: "'foo' == '+'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "foo", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "==", }, - ExpressionToken{ + { Kind: STRING, Value: "+", }, }, }, - TokenParsingTest{ + { Name: "Comparator against modifier string multiplicative (#22)", Input: "'foo' == '/'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "foo", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "==", }, - ExpressionToken{ + { Kind: STRING, Value: "/", }, }, }, - TokenParsingTest{ + { Name: "Comparator against modifier string exponential (#22)", Input: "'foo' == '**'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "foo", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "==", }, - ExpressionToken{ + { Kind: STRING, Value: "**", }, }, }, - TokenParsingTest{ + { Name: "Comparator against modifier string bitwise (#22)", Input: "'foo' == '^'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "foo", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "==", }, - ExpressionToken{ + { Kind: STRING, Value: "^", }, }, }, - TokenParsingTest{ + { Name: "Comparator against modifier string shift (#22)", Input: "'foo' == '>>'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "foo", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "==", }, - ExpressionToken{ + { Kind: STRING, Value: ">>", }, }, }, - TokenParsingTest{ + { Name: "Comparator against modifier string ternary (#22)", Input: "'foo' == '?'", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "foo", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "==", }, - ExpressionToken{ + { Kind: STRING, Value: "?", }, }, }, - TokenParsingTest{ + { Name: "Array membership lowercase", Input: "'foo' in ('foo', 'bar')", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "foo", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "in", }, - ExpressionToken{ + { Kind: CLAUSE, }, - ExpressionToken{ + { Kind: STRING, Value: "foo", }, - ExpressionToken{ + { Kind: SEPARATOR, }, - ExpressionToken{ + { Kind: STRING, Value: "bar", }, - ExpressionToken{ + { Kind: CLAUSE_CLOSE, }, }, }, - TokenParsingTest{ + { Name: "Array membership uppercase", Input: "'foo' IN ('foo', 'bar')", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "foo", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "in", }, - ExpressionToken{ + { Kind: CLAUSE, }, - ExpressionToken{ + { Kind: STRING, Value: "foo", }, - ExpressionToken{ + { Kind: SEPARATOR, }, - ExpressionToken{ + { Kind: STRING, Value: "bar", }, - ExpressionToken{ + { Kind: CLAUSE_CLOSE, }, }, @@ -1009,191 +976,191 @@ func TestModifierParsing(test *testing.T) { tokenParsingTests := []TokenParsingTest{ - TokenParsingTest{ + { Name: "Numeric PLUS", Input: "1 + 1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: MODIFIER, Value: "+", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ + { Name: "Numeric MINUS", Input: "1 - 1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: MODIFIER, Value: "-", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ + { Name: "Numeric MULTIPLY", Input: "1 * 1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: MODIFIER, Value: "*", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ + { Name: "Numeric DIVIDE", Input: "1 / 1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: MODIFIER, Value: "/", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ + { Name: "Numeric MODULUS", Input: "1 % 1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: MODIFIER, Value: "%", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ + { Name: "Numeric BITWISE_AND", Input: "1 & 1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: MODIFIER, Value: "&", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ + { Name: "Numeric BITWISE_OR", Input: "1 | 1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: MODIFIER, Value: "|", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ + { Name: "Numeric BITWISE_XOR", Input: "1 ^ 1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: MODIFIER, Value: "^", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ + { Name: "Numeric BITWISE_LSHIFT", Input: "1 << 1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: MODIFIER, Value: "<<", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ + { Name: "Numeric BITWISE_RSHIFT", Input: "1 >> 1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: MODIFIER, Value: ">>", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, @@ -1227,91 +1194,91 @@ func TestPrefixParsing(test *testing.T) { testCases := []TokenParsingTest{ - TokenParsingTest{ + { Name: "Sign prefix", Input: "-1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: PREFIX, Value: "-", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ + { Name: "Sign prefix on variable", Input: "-foo", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: PREFIX, Value: "-", }, - ExpressionToken{ + { Kind: VARIABLE, Value: "foo", }, }, }, - TokenParsingTest{ + { Name: "Boolean prefix", Input: "!true", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: PREFIX, Value: "!", }, - ExpressionToken{ + { Kind: BOOLEAN, Value: true, }, }, }, - TokenParsingTest{ + { Name: "Boolean prefix on variable", Input: "!foo", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: PREFIX, Value: "!", }, - ExpressionToken{ + { Kind: VARIABLE, Value: "foo", }, }, }, - TokenParsingTest{ + { Name: "Bitwise not prefix", Input: "~1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: PREFIX, Value: "~", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ + { Name: "Bitwise not prefix on variable", Input: "~foo", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: PREFIX, Value: "~", }, - ExpressionToken{ + { Kind: VARIABLE, Value: "foo", }, @@ -1327,121 +1294,121 @@ func TestEscapedParameters(test *testing.T) { testCases := []TokenParsingTest{ - TokenParsingTest{ + { Name: "Single escaped parameter", Input: "[foo]", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: VARIABLE, Value: "foo", }, }, }, - TokenParsingTest{ + { Name: "Single escaped parameter with whitespace", Input: "[foo bar]", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: VARIABLE, Value: "foo bar", }, }, }, - TokenParsingTest{ + { Name: "Single escaped parameter with escaped closing bracket", Input: "[foo[bar\\]]", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: VARIABLE, Value: "foo[bar]", }, }, }, - TokenParsingTest{ + { Name: "Escaped parameters and unescaped parameters", Input: "[foo] > bar", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: VARIABLE, Value: "foo", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: ">", }, - ExpressionToken{ + { Kind: VARIABLE, Value: "bar", }, }, }, - TokenParsingTest{ + { Name: "Unescaped parameter with space", Input: "foo\\ bar > bar", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: VARIABLE, Value: "foo bar", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: ">", }, - ExpressionToken{ + { Kind: VARIABLE, Value: "bar", }, }, }, - TokenParsingTest{ + { Name: "Unescaped parameter with space", Input: "response\\-time > bar", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: VARIABLE, Value: "response-time", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: ">", }, - ExpressionToken{ + { Kind: VARIABLE, Value: "bar", }, }, }, - TokenParsingTest{ + { Name: "Parameters with snake_case", Input: "foo_bar > baz_quux", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: VARIABLE, Value: "foo_bar", }, - ExpressionToken{ + { Kind: COMPARATOR, Value: ">", }, - ExpressionToken{ + { Kind: VARIABLE, Value: "baz_quux", }, }, }, - TokenParsingTest{ + { Name: "String literal uses backslash to escape", Input: "\"foo\\'bar\"", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, Value: "foo'bar", }, @@ -1455,66 +1422,66 @@ func TestEscapedParameters(test *testing.T) { func TestTernaryParsing(test *testing.T) { tokenParsingTests := []TokenParsingTest{ - TokenParsingTest{ + { Name: "Ternary after Boolean", Input: "true ? 1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: BOOLEAN, Value: true, }, - ExpressionToken{ + { Kind: TERNARY, Value: "?", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, }, - TokenParsingTest{ + { Name: "Ternary after Comperator", Input: "1 == 0 ? true", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: COMPARATOR, Value: "==", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 0.0, }, - ExpressionToken{ + { Kind: TERNARY, Value: "?", }, - ExpressionToken{ + { Kind: BOOLEAN, Value: true, }, }, }, - TokenParsingTest{ + { Name: "Null coalesce left", Input: "1 ?? 2", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: TERNARY, Value: "??", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 2.0, }, @@ -1646,7 +1613,7 @@ func runTokenParsingTest(tokenParsingTests []TokenParsingTest, test *testing.T) var expectedTokenLength, actualTokenLength int var err error - fmt.Printf("Running %d parsing test cases...\n", len(tokenParsingTests)) + // fmt.Printf("Running %d parsing test cases...\n", len(tokenParsingTests)) // defer func() { // if r := recover(); r != nil { // test.Logf("Panic in test '%s': %v", parsingTest.Name, r) @@ -1750,19 +1717,19 @@ func TestParallelParsing(t *testing.T) { var wg sync.WaitGroup tokenParsingTests := []TokenParsingTest{ - TokenParsingTest{ + { Name: "Simple expression", Input: "1 + 1", Expected: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: MODIFIER, Value: "+", }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, diff --git a/sql_test.go b/sql_test.go index e068c1f..0d3bb5a 100644 --- a/sql_test.go +++ b/sql_test.go @@ -5,7 +5,7 @@ import ( ) /* - Represents a test of correctly creating a SQL query string from an expression. +Represents a test of correctly creating a SQL query string from an expression. */ type QueryTest struct { Name string @@ -17,146 +17,146 @@ func TestSQLSerialization(test *testing.T) { testCases := []QueryTest{ - QueryTest{ + { Name: "Single GT", Input: "1 > 0", Expected: "1 > 0", }, - QueryTest{ + { Name: "Single LT", Input: "0 < 1", Expected: "0 < 1", }, - QueryTest{ + { Name: "Single GTE", Input: "1 >= 0", Expected: "1 >= 0", }, - QueryTest{ + { Name: "Single LTE", Input: "0 <= 1", Expected: "0 <= 1", }, - QueryTest{ + { Name: "Single EQ", Input: "1 == 0", Expected: "1 = 0", }, - QueryTest{ + { Name: "Single NEQ", Input: "1 != 0", Expected: "1 <> 0", }, - QueryTest{ + { Name: "Parameter names", Input: "foo == bar", Expected: "[foo] = [bar]", }, - QueryTest{ + { Name: "Strings", Input: "'foo'", Expected: "'foo'", }, - QueryTest{ + { Name: "Date format", Input: "'2014-07-04T00:00:00Z'", Expected: "'2014-07-04T00:00:00Z'", }, - QueryTest{ + { Name: "Single PLUS", Input: "10 + 10", Expected: "10 + 10", }, - QueryTest{ + { Name: "Single MINUS", Input: "10 - 10", Expected: "10 - 10", }, - QueryTest{ + { Name: "Single MULTIPLY", Input: "10 * 10", Expected: "10 * 10", }, - QueryTest{ + { Name: "Single DIVIDE", Input: "10 / 10", Expected: "10 / 10", }, - QueryTest{ + { Name: "Single true bool", Input: "true", Expected: "1", }, - QueryTest{ + { Name: "Single false bool", Input: "false", Expected: "0", }, - QueryTest{ + { Name: "Single AND", Input: "true && true", Expected: "1 AND 1", }, - QueryTest{ + { Name: "Single OR", Input: "true || true", Expected: "1 OR 1", }, - QueryTest{ + { Name: "Clauses", Input: "10 + (foo + bar)", Expected: "10 + ( [foo] + [bar] )", }, - QueryTest{ + { Name: "Negate prefix", Input: "foo < -1", Expected: "[foo] < -1", }, - QueryTest{ + { Name: "Invert prefix", Input: "!(foo > 1)", Expected: "NOT ( [foo] > 1 )", }, - QueryTest{ + { Name: "Exponent", Input: "1 ** 2", Expected: "POW(1, 2)", }, - QueryTest{ + { Name: "Modulus", Input: "10 % 2", Expected: "MOD(10, 2)", }, - QueryTest{ + { Name: "Membership operator", Input: "foo IN (1, 2, 3)", Expected: "[foo] in ( 1 , 2 , 3 )", }, - QueryTest{ + { Name: "Null coalescence", Input: "foo ?? bar", @@ -188,13 +188,13 @@ func TestSQLSerialization(test *testing.T) { Input: "[foo] ? 1", Expected: "IF([foo] = 0, 1)", },*/ - QueryTest{ + { Name: "Regex equals", Input: "'foo' =~ '[fF][oO]+'", Expected: "'foo' RLIKE '[fF][oO]+'", }, - QueryTest{ + { Name: "Regex not-equals", Input: "'foo' !~ '[fF][oO]+'", diff --git a/tokenExpressionFailure_test.go b/tokenExpressionFailure_test.go index 0a7ebae..924529b 100644 --- a/tokenExpressionFailure_test.go +++ b/tokenExpressionFailure_test.go @@ -11,14 +11,14 @@ const ( ) /* - Contains a single test case for the EvaluableExpression.NewEvaluableExpressionFromTokens() method. +Contains a single test case for the EvaluableExpression.NewEvaluableExpressionFromTokens() method. - These tests, and the ones in `tokenExpressionFailure_test` will be fairly incomplete. - Creating an expression from a string and from tokens _must_ both perform the same syntax checks. - So all the checks in `parsing_test` will follow the same logic as the ones here. +These tests, and the ones in `tokenExpressionFailure_test` will be fairly incomplete. +Creating an expression from a string and from tokens _must_ both perform the same syntax checks. +So all the checks in `parsing_test` will follow the same logic as the ones here. - These tests check some corner cases - such as tokens having nil values when they must have something. - Cases that cannot occur through the normal parser, but may occur in other parsers. +These tests check some corner cases - such as tokens having nil values when they must have something. +Cases that cannot occur through the normal parser, but may occur in other parsers. */ type ExpressionTokenSyntaxTest struct { Name string @@ -29,131 +29,131 @@ type ExpressionTokenSyntaxTest struct { func TestNilValues(test *testing.T) { cases := []ExpressionTokenSyntaxTest{ - ExpressionTokenSyntaxTest{ + { Name: "Nil numeric", Input: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, }, }, Expected: EXPERR_NIL_VALUE, }, - ExpressionTokenSyntaxTest{ + { Name: "Nil string", Input: []ExpressionToken{ - ExpressionToken{ + { Kind: STRING, }, }, Expected: EXPERR_NIL_VALUE, }, - ExpressionTokenSyntaxTest{ + { Name: "Nil bool", Input: []ExpressionToken{ - ExpressionToken{ + { Kind: BOOLEAN, }, }, Expected: EXPERR_NIL_VALUE, }, - ExpressionTokenSyntaxTest{ + { Name: "Nil time", Input: []ExpressionToken{ - ExpressionToken{ + { Kind: TIME, }, }, Expected: EXPERR_NIL_VALUE, }, - ExpressionTokenSyntaxTest{ + { Name: "Nil pattern", Input: []ExpressionToken{ - ExpressionToken{ + { Kind: PATTERN, }, }, Expected: EXPERR_NIL_VALUE, }, - ExpressionTokenSyntaxTest{ + { Name: "Nil variable", Input: []ExpressionToken{ - ExpressionToken{ + { Kind: VARIABLE, }, }, Expected: EXPERR_NIL_VALUE, }, - ExpressionTokenSyntaxTest{ + { Name: "Nil prefix", Input: []ExpressionToken{ - ExpressionToken{ + { Kind: PREFIX, }, }, Expected: EXPERR_NIL_VALUE, }, - ExpressionTokenSyntaxTest{ + { Name: "Nil comparator", Input: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: COMPARATOR, }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, Expected: EXPERR_NIL_VALUE, }, - ExpressionTokenSyntaxTest{ + { Name: "Nil logicalop", Input: []ExpressionToken{ - ExpressionToken{ + { Kind: BOOLEAN, Value: true, }, - ExpressionToken{ + { Kind: LOGICALOP, }, - ExpressionToken{ + { Kind: BOOLEAN, Value: true, }, }, Expected: EXPERR_NIL_VALUE, }, - ExpressionTokenSyntaxTest{ + { Name: "Nil modifer", Input: []ExpressionToken{ - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, - ExpressionToken{ + { Kind: MODIFIER, }, - ExpressionToken{ + { Kind: NUMERIC, Value: 1.0, }, }, Expected: EXPERR_NIL_VALUE, }, - ExpressionTokenSyntaxTest{ + { Name: "Nil ternary", Input: []ExpressionToken{ - ExpressionToken{ + { Kind: BOOLEAN, Value: true, }, - ExpressionToken{ + { Kind: TERNARY, }, - ExpressionToken{ + { Kind: BOOLEAN, Value: true, },