diff --git a/EvaluableExpression.go b/EvaluableExpression.go index a5fe50d..09ae0fd 100644 --- a/EvaluableExpression.go +++ b/EvaluableExpression.go @@ -255,11 +255,30 @@ func (this EvaluableExpression) Tokens() []ExpressionToken { } /* - Returns the original expression used to create this EvaluableExpression. + Returns a string representation of this expression. */ func (this EvaluableExpression) String() string { + if this.inputExpression != "" { + return this.inputExpression + } + + var expressionText string + for _, val := range this.Tokens() { + switch val.Kind { + case VARIABLE: + expressionText += fmt.Sprintf("[%+v]", val.Meta) + case STRING, TIME: + expressionText += fmt.Sprintf("'%+v'", val.Meta) + case COMPARATOR, LOGICALOP, MODIFIER, TERNARY: + expressionText += fmt.Sprintf(" %+v ", val.Meta) + case SEPARATOR: + expressionText += fmt.Sprintf("%+v ", val.Meta) + default: + expressionText += fmt.Sprintf("%+v", val.Meta) + } + } - return this.inputExpression + return expressionText } /* diff --git a/ExpressionToken.go b/ExpressionToken.go index f849f38..e559133 100644 --- a/ExpressionToken.go +++ b/ExpressionToken.go @@ -6,4 +6,5 @@ package govaluate type ExpressionToken struct { Kind TokenKind Value interface{} + Meta interface{} } diff --git a/evaluationFailure_test.go b/evaluationFailure_test.go index e04f24f..7f4f76a 100644 --- a/evaluationFailure_test.go +++ b/evaluationFailure_test.go @@ -45,6 +45,47 @@ var EVALUATION_FAILURE_PARAMETERS = map[string]interface{}{ "bool": true, } +func TestFullCycle(test *testing.T) { + currentExpressionString := "2 > 1 && " + + "'something' != 'nothing' || (1,2,3) != (3,2,1) || " + + "func1(1337,'leet',sawce) < 'Wed Jul 8 23:07:35 MDT 2015' && " + + "[escapedVariable name with spaces] <= unescaped\\-variableName && " + + "modifierTest + 0xa3a2b3 / 2 > (80 * 100 % 2) && true ? true : false" + extraFuncs := map[string]ExpressionFunction{ + "func1": func(args ...interface{}) (interface{}, error) { + return "2014-01-20", nil + }, + } + + var initialExpression, cycledExpression, finalExpression *EvaluableExpression + initialExpression, err := NewEvaluableExpressionWithFunctions(currentExpressionString, extraFuncs) + if err != nil { + test.Errorf("Failed to build expression from string... ERROR: %+v", err) + } + cycledExpression, err = NewEvaluableExpressionFromTokens(initialExpression.Tokens()) + if err != nil { + test.Errorf("Failed to build expression from tokens, from string... ERROR: %+v", err) + } + temp2, err := NewEvaluableExpressionWithFunctions(cycledExpression.String(), extraFuncs) + if err != nil { + test.Errorf("Failed to build expression from string, from tokens, from string... ERROR: %+v", err) + } + finalExpression, err = NewEvaluableExpressionFromTokens(temp2.Tokens()) + if err != nil { + test.Errorf("Failed to build expression from tokens, from string, from tokens, from string... ERROR: %+v", err) + } + + if finalExpression.String() != cycledExpression.String() { + test.Errorf("The final result string was not synonymous with the initial's... FAILURE:\n\"%+v\"\n !=\n\"%+v\"", finalExpression.String(), initialExpression.String()) + } + if len(finalExpression.Vars()) != len(initialExpression.Vars()) { + test.Errorf("The final result variables were not synonymous with the initial's... FAILURE:\n\"%+v\"\n !=\n\"%+v\"", finalExpression.Vars(), initialExpression.Vars()) + } + if len(finalExpression.Tokens()) != len(initialExpression.Tokens()) { + test.Errorf("The final result tokens were not synonymous with the initial's... FAILURE:\n\"%+v\"\n !=\n\"%+v\"", finalExpression.Tokens(), initialExpression.Tokens()) + } +} + func TestComplexParameter(test *testing.T) { var expression *EvaluableExpression diff --git a/parsing.go b/parsing.go index 40c7ed2..bf080d3 100644 --- a/parsing.go +++ b/parsing.go @@ -64,6 +64,7 @@ func readToken(stream *lexerStream, state lexerState, functions map[string]Expre var found bool var completed bool var err error + var meta interface{} // numeric is 0-9, or . or 0x followed by digits // string starts with ' @@ -79,6 +80,7 @@ func readToken(stream *lexerStream, state lexerState, functions map[string]Expre continue } + meta = character kind = UNKNOWN // numeric constant @@ -96,6 +98,7 @@ func readToken(stream *lexerStream, state lexerState, functions map[string]Expre return ExpressionToken{}, errors.New(errorMsg), false } + meta = "0x" + tokenString kind = NUMERIC tokenValue = float64(tokenValueInt) break @@ -111,6 +114,8 @@ func readToken(stream *lexerStream, state lexerState, functions map[string]Expre errorMsg := fmt.Sprintf("Unable to parse numeric value '%v' to float64\n", tokenString) return ExpressionToken{}, errors.New(errorMsg), false } + + meta = tokenString kind = NUMERIC break } @@ -119,6 +124,7 @@ func readToken(stream *lexerStream, state lexerState, functions map[string]Expre if character == ',' { tokenValue = "," + meta = tokenValue kind = SEPARATOR break } @@ -127,6 +133,7 @@ func readToken(stream *lexerStream, state lexerState, functions map[string]Expre if character == '[' { tokenValue, completed = readUntilFalse(stream, true, false, true, isNotClosingBracket) + meta = tokenValue kind = VARIABLE if !completed { @@ -142,22 +149,15 @@ func readToken(stream *lexerStream, state lexerState, functions map[string]Expre if unicode.IsLetter(character) { tokenString = readTokenUntilFalse(stream, isVariableName) - + meta = tokenString tokenValue = tokenString kind = VARIABLE // boolean? - if tokenValue == "true" { + if tokenValue == "true" || tokenValue == "false" { kind = BOOLEAN - tokenValue = true - } else { - - if tokenValue == "false" { - - kind = BOOLEAN - tokenValue = false - } + tokenValue = tokenValue == "true" } // textual operator? @@ -205,6 +205,7 @@ func readToken(stream *lexerStream, state lexerState, functions map[string]Expre if !isNotQuote(character) { tokenValue, completed = readUntilFalse(stream, true, false, true, isNotQuote) + meta = tokenValue if !completed { return ExpressionToken{}, errors.New("Unclosed string literal"), false @@ -225,12 +226,16 @@ func readToken(stream *lexerStream, state lexerState, functions map[string]Expre } if character == '(' { + + meta = "(" tokenValue = character kind = CLAUSE break } if character == ')' { + + meta = ")" tokenValue = character kind = CLAUSE_CLOSE break @@ -238,6 +243,7 @@ func readToken(stream *lexerStream, state lexerState, functions map[string]Expre // must be a known symbol tokenString = readTokenUntilFalse(stream, isNotAlphanumeric) + meta = tokenString tokenValue = tokenString // quick hack for the case where "-" can mean "prefixed negation" or "minus", which are used @@ -284,6 +290,7 @@ func readToken(stream *lexerStream, state lexerState, functions map[string]Expre ret.Kind = kind ret.Value = tokenValue + ret.Meta = meta return ret, nil, (kind != UNKNOWN) }