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)
 | 
						|
}
 |