351 lines
5.2 KiB
Go
351 lines
5.2 KiB
Go
package govaluate
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
type lexerState struct {
|
|
isEOF bool
|
|
isNullable bool
|
|
kind TokenKind
|
|
validNextKinds []TokenKind
|
|
}
|
|
|
|
// lexer states.
|
|
// Constant for all purposes except compiler.
|
|
var validLexerStates = []lexerState{
|
|
|
|
lexerState{
|
|
kind: UNKNOWN,
|
|
isEOF: false,
|
|
isNullable: true,
|
|
validNextKinds: []TokenKind{
|
|
|
|
PREFIX,
|
|
NUMERIC,
|
|
BOOLEAN,
|
|
VARIABLE,
|
|
PATTERN,
|
|
FUNCTION,
|
|
STRING,
|
|
TIME,
|
|
CLAUSE,
|
|
},
|
|
},
|
|
|
|
lexerState{
|
|
|
|
kind: CLAUSE,
|
|
isEOF: false,
|
|
isNullable: true,
|
|
validNextKinds: []TokenKind{
|
|
|
|
PREFIX,
|
|
NUMERIC,
|
|
BOOLEAN,
|
|
VARIABLE,
|
|
PATTERN,
|
|
FUNCTION,
|
|
STRING,
|
|
TIME,
|
|
CLAUSE,
|
|
CLAUSE_CLOSE,
|
|
},
|
|
},
|
|
|
|
lexerState{
|
|
|
|
kind: CLAUSE_CLOSE,
|
|
isEOF: true,
|
|
isNullable: true,
|
|
validNextKinds: []TokenKind{
|
|
|
|
COMPARATOR,
|
|
MODIFIER,
|
|
NUMERIC,
|
|
BOOLEAN,
|
|
VARIABLE,
|
|
STRING,
|
|
PATTERN,
|
|
TIME,
|
|
CLAUSE,
|
|
CLAUSE_CLOSE,
|
|
LOGICALOP,
|
|
TERNARY,
|
|
SEPARATOR,
|
|
},
|
|
},
|
|
|
|
lexerState{
|
|
|
|
kind: NUMERIC,
|
|
isEOF: true,
|
|
isNullable: false,
|
|
validNextKinds: []TokenKind{
|
|
|
|
MODIFIER,
|
|
COMPARATOR,
|
|
LOGICALOP,
|
|
CLAUSE_CLOSE,
|
|
TERNARY,
|
|
SEPARATOR,
|
|
},
|
|
},
|
|
lexerState{
|
|
|
|
kind: BOOLEAN,
|
|
isEOF: true,
|
|
isNullable: false,
|
|
validNextKinds: []TokenKind{
|
|
|
|
MODIFIER,
|
|
COMPARATOR,
|
|
LOGICALOP,
|
|
CLAUSE_CLOSE,
|
|
TERNARY,
|
|
SEPARATOR,
|
|
},
|
|
},
|
|
lexerState{
|
|
|
|
kind: STRING,
|
|
isEOF: true,
|
|
isNullable: false,
|
|
validNextKinds: []TokenKind{
|
|
|
|
MODIFIER,
|
|
COMPARATOR,
|
|
LOGICALOP,
|
|
CLAUSE_CLOSE,
|
|
TERNARY,
|
|
SEPARATOR,
|
|
},
|
|
},
|
|
lexerState{
|
|
|
|
kind: TIME,
|
|
isEOF: true,
|
|
isNullable: false,
|
|
validNextKinds: []TokenKind{
|
|
|
|
MODIFIER,
|
|
COMPARATOR,
|
|
LOGICALOP,
|
|
CLAUSE_CLOSE,
|
|
SEPARATOR,
|
|
},
|
|
},
|
|
lexerState{
|
|
|
|
kind: PATTERN,
|
|
isEOF: true,
|
|
isNullable: false,
|
|
validNextKinds: []TokenKind{
|
|
|
|
MODIFIER,
|
|
COMPARATOR,
|
|
LOGICALOP,
|
|
CLAUSE_CLOSE,
|
|
SEPARATOR,
|
|
},
|
|
},
|
|
lexerState{
|
|
|
|
kind: VARIABLE,
|
|
isEOF: true,
|
|
isNullable: false,
|
|
validNextKinds: []TokenKind{
|
|
|
|
MODIFIER,
|
|
COMPARATOR,
|
|
LOGICALOP,
|
|
CLAUSE_CLOSE,
|
|
TERNARY,
|
|
SEPARATOR,
|
|
},
|
|
},
|
|
lexerState{
|
|
|
|
kind: MODIFIER,
|
|
isEOF: false,
|
|
isNullable: false,
|
|
validNextKinds: []TokenKind{
|
|
|
|
PREFIX,
|
|
NUMERIC,
|
|
VARIABLE,
|
|
FUNCTION,
|
|
STRING,
|
|
BOOLEAN,
|
|
CLAUSE,
|
|
CLAUSE_CLOSE,
|
|
},
|
|
},
|
|
lexerState{
|
|
|
|
kind: COMPARATOR,
|
|
isEOF: false,
|
|
isNullable: false,
|
|
validNextKinds: []TokenKind{
|
|
|
|
PREFIX,
|
|
NUMERIC,
|
|
BOOLEAN,
|
|
VARIABLE,
|
|
FUNCTION,
|
|
STRING,
|
|
TIME,
|
|
CLAUSE,
|
|
CLAUSE_CLOSE,
|
|
PATTERN,
|
|
},
|
|
},
|
|
lexerState{
|
|
|
|
kind: LOGICALOP,
|
|
isEOF: false,
|
|
isNullable: false,
|
|
validNextKinds: []TokenKind{
|
|
|
|
PREFIX,
|
|
NUMERIC,
|
|
BOOLEAN,
|
|
VARIABLE,
|
|
FUNCTION,
|
|
STRING,
|
|
TIME,
|
|
CLAUSE,
|
|
CLAUSE_CLOSE,
|
|
},
|
|
},
|
|
lexerState{
|
|
|
|
kind: PREFIX,
|
|
isEOF: false,
|
|
isNullable: false,
|
|
validNextKinds: []TokenKind{
|
|
|
|
NUMERIC,
|
|
BOOLEAN,
|
|
VARIABLE,
|
|
FUNCTION,
|
|
CLAUSE,
|
|
CLAUSE_CLOSE,
|
|
},
|
|
},
|
|
|
|
lexerState{
|
|
|
|
kind: TERNARY,
|
|
isEOF: false,
|
|
isNullable: false,
|
|
validNextKinds: []TokenKind{
|
|
|
|
PREFIX,
|
|
NUMERIC,
|
|
BOOLEAN,
|
|
STRING,
|
|
TIME,
|
|
VARIABLE,
|
|
FUNCTION,
|
|
CLAUSE,
|
|
SEPARATOR,
|
|
},
|
|
},
|
|
lexerState{
|
|
|
|
kind: FUNCTION,
|
|
isEOF: false,
|
|
isNullable: false,
|
|
validNextKinds: []TokenKind{
|
|
CLAUSE,
|
|
},
|
|
},
|
|
lexerState{
|
|
|
|
kind: SEPARATOR,
|
|
isEOF: false,
|
|
isNullable: true,
|
|
validNextKinds: []TokenKind{
|
|
|
|
PREFIX,
|
|
NUMERIC,
|
|
BOOLEAN,
|
|
STRING,
|
|
TIME,
|
|
VARIABLE,
|
|
FUNCTION,
|
|
CLAUSE,
|
|
},
|
|
},
|
|
}
|
|
|
|
func (this lexerState) canTransitionTo(kind TokenKind) bool {
|
|
|
|
for _, validKind := range this.validNextKinds {
|
|
|
|
if validKind == kind {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func checkExpressionSyntax(tokens []ExpressionToken) error {
|
|
|
|
var state lexerState
|
|
var lastToken ExpressionToken
|
|
var err error
|
|
|
|
state = validLexerStates[0]
|
|
|
|
for _, token := range tokens {
|
|
|
|
if !state.canTransitionTo(token.Kind) {
|
|
|
|
// call out a specific error for tokens looking like they want to be functions.
|
|
if lastToken.Kind == VARIABLE && token.Kind == CLAUSE {
|
|
return errors.New("Undefined function " + lastToken.Value.(string))
|
|
}
|
|
|
|
firstStateName := fmt.Sprintf("%s [%v]", state.kind.String(), lastToken.Value)
|
|
nextStateName := fmt.Sprintf("%s [%v]", token.Kind.String(), token.Value)
|
|
|
|
return errors.New("Cannot transition token types from " + firstStateName + " to " + nextStateName)
|
|
}
|
|
|
|
state, err = getLexerStateForToken(token.Kind)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !state.isNullable && token.Value == nil {
|
|
|
|
errorMsg := fmt.Sprintf("Token kind '%v' cannot have a nil value", token.Kind.String())
|
|
return errors.New(errorMsg)
|
|
}
|
|
|
|
lastToken = token
|
|
}
|
|
|
|
if !state.isEOF {
|
|
return errors.New("Unexpected end of expression")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getLexerStateForToken(kind TokenKind) (lexerState, error) {
|
|
|
|
for _, possibleState := range validLexerStates {
|
|
|
|
if possibleState.kind == kind {
|
|
return possibleState, nil
|
|
}
|
|
}
|
|
|
|
errorMsg := fmt.Sprintf("No lexer state found for token kind '%v'\n", kind.String())
|
|
return validLexerStates[0], errors.New(errorMsg)
|
|
}
|