Beego/vendor/github.com/Knetic/govaluate/lexerState.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)
}