diff --git a/OperatorToken.go b/OperatorToken.go index c9e4365..c96eb8d 100644 --- a/OperatorToken.go +++ b/OperatorToken.go @@ -39,12 +39,16 @@ const ( TERNARY_TRUE TERNARY_FALSE COALESCE + + // as distinct from the TokenKind. + FUNCTION_OPERATOR ) type OperatorPrecedence int const ( VALUE_PRECEDENCE OperatorPrecedence = iota + FUNCTION_PRECEDENCE PREFIX_PRECEDENCE EXPONENTIAL_PRECEDENCE ADDITIVE_PRECEDENCE @@ -113,9 +117,11 @@ func findOperatorPrecedenceForSymbol(symbol OperatorSymbol) OperatorPrecedence { fallthrough case TERNARY_FALSE: return TERNARY_PRECEDENCE + case FUNCTION_OPERATOR: + return FUNCTION_PRECEDENCE } - return -1 + return VALUE_PRECEDENCE } /* diff --git a/evaluationStage.go b/evaluationStage.go index d033ee4..a02012f 100644 --- a/evaluationStage.go +++ b/evaluationStage.go @@ -193,7 +193,17 @@ func makeFunctionStage(function ExpressionFunction) evaluationOperator { return func(left interface{}, right interface{}, parameters Parameters) (interface{}, error) { - return function(right.([]interface{})...) + if(right == nil) { + return function() + } + + switch right.(type) { + case []interface{}: + return function(right.([]interface{})...) + default: + return function(right) + } + } } diff --git a/evaluation_test.go b/evaluation_test.go index 41c0ba4..42e6081 100644 --- a/evaluation_test.go +++ b/evaluation_test.go @@ -12,6 +12,7 @@ import ( type EvaluationTest struct { Name string Input string + Functions map[string]ExpressionFunction Parameters []EvaluationParameter Expected interface{} } @@ -448,6 +449,31 @@ func TestNoParameterEvaluation(test *testing.T) { Input: "1 ?? 2", Expected: 1.0, }, + EvaluationTest{ + + Name: "Single function", + Input: "foo()", + Functions: map[string]ExpressionFunction { + "foo": func(arguments ...interface{}) (interface{}, error) { + return true, nil + }, + }, + + Expected: true, + }, + EvaluationTest{ + + Name: "Function with argument", + Input: "passthrough(1)", + Functions: map[string]ExpressionFunction { + "passthrough": func(arguments ...interface{}) (interface{}, error) { + fmt.Printf("Arg zero: %v\n", arguments) + return arguments[0], nil + }, + }, + + Expected: 1.0, + }, } runEvaluationTests(evaluationTests, test) @@ -898,7 +924,11 @@ func runEvaluationTests(evaluationTests []EvaluationTest, test *testing.T) { // Run the test cases. for _, evaluationTest := range evaluationTests { - expression, err = NewEvaluableExpression(evaluationTest.Input) + if(evaluationTest.Functions != nil) { + expression, err = NewEvaluableExpressionWithFunctions(evaluationTest.Input, evaluationTest.Functions) + } else { + expression, err = NewEvaluableExpression(evaluationTest.Input) + } if err != nil { diff --git a/stagePlanner.go b/stagePlanner.go index 9454100..ce5401c 100644 --- a/stagePlanner.go +++ b/stagePlanner.go @@ -95,6 +95,7 @@ var planShift precedent var planComparator precedent var planLogical precedent var planTernary precedent +var planSeparator precedent func init() { @@ -105,13 +106,13 @@ func init() { validSymbols: PREFIX_SYMBOLS, validKinds: []TokenKind {PREFIX}, typeErrorFormat: TYPEERROR_PREFIX, - nextRight: planValue, + nextRight: planFunction, }) planExponential = makePrecedentFromPlanner(&precedencePlanner{ validSymbols: EXPONENTIAL_SYMBOLS, validKinds: []TokenKind {MODIFIER}, typeErrorFormat: TYPEERROR_MODIFIER, - next: planValue, + next: planFunction, }) planMultiplicative = makePrecedentFromPlanner(&precedencePlanner{ validSymbols: MULTIPLICATIVE_SYMBOLS, @@ -155,6 +156,10 @@ func init() { typeErrorFormat: TYPEERROR_TERNARY, next: planLogical, }) + planSeparator = makePrecedentFromPlanner(&precedencePlanner{ + validKinds: []TokenKind {SEPARATOR}, + next: planTernary, + }) } /* @@ -183,7 +188,7 @@ func planTokens(stream *tokenStream) (*evaluationStage, error) { return nil, nil } - return planTernary(stream) + return planSeparator(stream) } /* @@ -206,6 +211,7 @@ func planPrecedenceLevel( var keyFound bool if leftPrecedent != nil { + leftStage, err = leftPrecedent(stream) if err != nil { return nil, err @@ -215,9 +221,6 @@ func planPrecedenceLevel( for stream.hasNext() { token = stream.next() - if !isString(token.Value) { - break - } if(len(validKinds) > 0) { @@ -233,9 +236,17 @@ func planPrecedenceLevel( break } } - symbol, keyFound = validSymbols[token.Value.(string)] - if !keyFound { - break + + if(validSymbols != nil) { + + if(!isString(token.Value)) { + break; + } + + symbol, keyFound = validSymbols[token.Value.(string)] + if !keyFound { + break + } } if rightPrecedent != nil { @@ -367,6 +378,36 @@ func findTypeChecks(symbol OperatorSymbol) typeChecks { } } +/* + A special case where functions need to be of higher precedence than values, and need a special wrapped execution stage operator. +*/ +func planFunction(stream *tokenStream) (*evaluationStage, error) { + + var token ExpressionToken + var rightStage *evaluationStage + var err error + + token = stream.next() + + if(token.Kind != FUNCTION) { + stream.rewind() + return planValue(stream) + } + + rightStage, err = planValue(stream) + if err != nil { + return nil, err + } + + return &evaluationStage{ + + symbol: FUNCTION_OPERATOR, + rightStage: rightStage, + operator: makeFunctionStage(token.Value.(ExpressionFunction)), + typeErrorFormat: "Unable to run function '%v': %v", + }, nil +} + /* A truly special precedence function, this handles all the "lowest-case" errata of the process, including literals, parmeters, clauses, and prefixes. @@ -392,13 +433,13 @@ func planValue(stream *tokenStream) (*evaluationStage, error) { // advance past the CLAUSE_CLOSE token. We know that it's a CLAUSE_CLOSE, because at parse-time we check for unbalanced parens. stream.next() return ret, nil + + case CLAUSE_CLOSE: + return planTokens(stream) case VARIABLE: operator = makeParameterStage(token.Value.(string)) - case FUNCTION: - operator = makeFunctionStage(token.Value.(ExpressionFunction)) - case NUMERIC: fallthrough case STRING: