/* Copyright 2013 The Prometheus Authors
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. */

%{
package rules

import (
        "fmt"
        "strconv"
        "strings"

        clientmodel "github.com/prometheus/client_golang/model"
)

// Lex is called by the parser generated by "go tool yacc" to obtain each
// token. The method is opened before the matching rules block and closed at
// the end of the file.
func (lexer *RulesLexer) Lex(lval *yySymType) int {
  // Internal lexer states.
  const (
    S_INITIAL = iota
    S_COMMENTS
  )

  // We simulate multiple start symbols for closely-related grammars via dummy tokens. See
  // http://www.gnu.org/software/bison/manual/html_node/Multiple-start_002dsymbols.html
  // Reason: we want to be able to parse lists of named rules as well as single expressions.
  if lexer.startToken != 0 {
    startToken := lexer.startToken
    lexer.startToken = 0
    return startToken
  }

  c := lexer.current
  currentState := 0

  if lexer.empty {
    c, lexer.empty = lexer.getChar(), false
  }

%}

D                       [0-9]
L                       [a-zA-Z_]
M                       [a-zA-Z_:]
U                       [smhdwy]

%x S_COMMENTS

%yyc c
%yyn c = lexer.getChar()
%yyt currentState

%%
  lexer.buf = lexer.buf[:0]   // The code before the first rule executed before every scan cycle (rule #0 / state 0 action)

"/*"                     currentState = S_COMMENTS
<S_COMMENTS>"*/"         currentState = S_INITIAL
<S_COMMENTS>.|\n         /* ignore chars within multi-line comments */

\/\/[^\r\n]*\n           /* gobble up one-line comments */

ALERT|alert              return ALERT
IF|if                    return IF
FOR|for                  return FOR
WITH|with                return WITH
SUMMARY|summary          return SUMMARY
DESCRIPTION|description  return DESCRIPTION

PERMANENT|permanent      return PERMANENT
BY|by                    return GROUP_OP
KEEPING_EXTRA|keeping_extra return KEEPING_EXTRA
OFFSET|offset            return OFFSET
AVG|SUM|MAX|MIN|COUNT    lval.str = lexer.token(); return AGGR_OP
avg|sum|max|min|count    lval.str = strings.ToUpper(lexer.token()); return AGGR_OP
\<|>|AND|OR|and|or       lval.str = strings.ToUpper(lexer.token()); return CMP_OP
==|!=|>=|<=|=~|!~        lval.str = lexer.token(); return CMP_OP
[+\-]                    lval.str = lexer.token(); return ADDITIVE_OP
[*/%]                    lval.str = lexer.token(); return MULT_OP

{D}+{U}                  lval.str = lexer.token(); return DURATION
{L}({L}|{D})*            lval.str = lexer.token(); return IDENTIFIER
{M}({M}|{D})*            lval.str = lexer.token(); return METRICNAME

\-?{D}+(\.{D}*)?         num, err := strconv.ParseFloat(lexer.token(), 64);
                         if (err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax) {
                           panic("Invalid float")
                         }
                         lval.num = clientmodel.SampleValue(num)
                         return NUMBER

\"(\\.|[^\\"])*\"        lval.str = lexer.token()[1:len(lexer.token()) - 1]; return STRING
\'(\\.|[^\\'])*\'        lval.str = lexer.token()[1:len(lexer.token()) - 1]; return STRING

[{}\[\]()=,]             return int(lexer.buf[0])
[\t\n\r ]                /* gobble up any whitespace */
%%

  lexer.empty = true
  return int(c)
}
