Skip to content

Commit

Permalink
working evaluation of expression functions, untested with separators
Browse files Browse the repository at this point in the history
  • Loading branch information
Knetic committed Aug 11, 2016
1 parent be63b73 commit a6558f5
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 15 deletions.
8 changes: 7 additions & 1 deletion OperatorToken.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}

/*
Expand Down
12 changes: 11 additions & 1 deletion evaluationStage.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

}
}

Expand Down
32 changes: 31 additions & 1 deletion evaluation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
type EvaluationTest struct {
Name string
Input string
Functions map[string]ExpressionFunction
Parameters []EvaluationParameter
Expected interface{}
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {

Expand Down
65 changes: 53 additions & 12 deletions stagePlanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ var planShift precedent
var planComparator precedent
var planLogical precedent
var planTernary precedent
var planSeparator precedent

func init() {

Expand All @@ -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,
Expand Down Expand Up @@ -155,6 +156,10 @@ func init() {
typeErrorFormat: TYPEERROR_TERNARY,
next: planLogical,
})
planSeparator = makePrecedentFromPlanner(&precedencePlanner{
validKinds: []TokenKind {SEPARATOR},
next: planTernary,
})
}

/*
Expand Down Expand Up @@ -183,7 +188,7 @@ func planTokens(stream *tokenStream) (*evaluationStage, error) {
return nil, nil
}

return planTernary(stream)
return planSeparator(stream)
}

/*
Expand All @@ -206,6 +211,7 @@ func planPrecedenceLevel(
var keyFound bool

if leftPrecedent != nil {

leftStage, err = leftPrecedent(stream)
if err != nil {
return nil, err
Expand All @@ -215,9 +221,6 @@ func planPrecedenceLevel(
for stream.hasNext() {

token = stream.next()
if !isString(token.Value) {
break
}

if(len(validKinds) > 0) {

Expand All @@ -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 {
Expand Down Expand Up @@ -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.
Expand All @@ -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:
Expand Down

0 comments on commit a6558f5

Please sign in to comment.