From 64b0ade171817db2c56654e80bb639a33fc3fbae Mon Sep 17 00:00:00 2001
From: Julius Volz <julius@soundcloud.com>
Date: Thu, 11 Jul 2013 18:38:44 +0200
Subject: [PATCH] Swap rules lexer for much faster one.

This swaps github.com/kivikakk/golex for github.com/cznic/golex.

The old lexer would have taken 3.5 years to load a set of 5000 test rules
(quadratic time complexity for input length), whereas this one takes only 32ms.
Furthermore, since the new lexer is embedded differently, this gets rid of the
global parser variables and makes the rule loader fully reentrant without a
lock.
---
 rules/Makefile      |    3 +-
 rules/lexer.l       |  120 +--
 rules/lexer.l.go    | 1740 +++++++++++++++++++++++++++++--------------
 rules/load.go       |   90 ++-
 rules/parser.y      |    6 +-
 rules/parser.y.go   |  286 +++----
 rules/rules_test.go |   13 +-
 7 files changed, 1450 insertions(+), 808 deletions(-)

diff --git a/rules/Makefile b/rules/Makefile
index bf8fc3899..489f1e9ec 100644
--- a/rules/Makefile
+++ b/rules/Makefile
@@ -17,7 +17,8 @@ parser.y.go: parser.y
 	go tool yacc -o parser.y.go -v "" parser.y
 
 lexer.l.go: parser.y.go lexer.l
-	golex lexer.l
+	# This is golex from https://github.com/cznic/golex.
+	golex -o="lexer.l.go" lexer.l
 
 clean:
 	rm lexer.l.go parser.y.go
diff --git a/rules/lexer.l b/rules/lexer.l
index 16d9c786f..55b68c42b 100644
--- a/rules/lexer.l
+++ b/rules/lexer.l
@@ -1,25 +1,53 @@
-/* Copyright 2013 Prometheus Team
- * 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. */
+// Copyright 2013 Prometheus Team
+// 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]
@@ -28,44 +56,50 @@ U                       [smhdwy]
 
 %x S_COMMENTS
 
+%yyc c
+%yyn c = lexer.getChar()
+%yyt currentState
+
 %%
-.                        { yypos++; REJECT }
-\n                       { yyline++; yypos = 1; REJECT }
+  lexer.buf = lexer.buf[:0]   // The code before the first rule executed before every scan cycle (rule #0 / state 0 action)
 
-"/*"                     { BEGIN(S_COMMENTS) }
-<S_COMMENTS>"*/"         { BEGIN(0) }
-<S_COMMENTS>.            { /* ignore chars within multi-line comments */ }
+"/*"                     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 */ }
+\/\/[^\r\n]*\n           /* gobble up one-line comments */
 
-ALERT|alert              { return ALERT }
-IF|if                    { return IF }
-FOR|for                  { return FOR }
-WITH|with                { return WITH }
+ALERT|alert              return ALERT
+IF|if                    return IF
+FOR|for                  return FOR
+WITH|with                return WITH
 
-PERMANENT|permanent      { return PERMANENT }
-BY|by                    { return GROUP_OP }
-AVG|SUM|MAX|MIN|COUNT    { yylval.str = yytext; return AGGR_OP }
-avg|sum|max|min|count    { yylval.str = strings.ToUpper(yytext); return AGGR_OP }
-\<|>|AND|OR|and|or       { yylval.str = strings.ToUpper(yytext); return CMP_OP }
-==|!=|>=|<=              { yylval.str = yytext; return CMP_OP }
-[+\-]                    { yylval.str = yytext; return ADDITIVE_OP }
-[*/%]                    { yylval.str = yytext; return MULT_OP }
+PERMANENT|permanent      return PERMANENT
+BY|by                    return GROUP_OP
+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}                  { yylval.str = yytext; return DURATION }
-{L}({L}|{D})*            { yylval.str = yytext; return IDENTIFIER }
+{D}+{U}                  lval.str = lexer.token(); return DURATION
+{L}({L}|{D})*            lval.str = lexer.token(); return IDENTIFIER
 
-\-?{D}+(\.{D}*)?         { num, err := strconv.ParseFloat(yytext, 64);
-                           if (err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax) {
-                             panic("Invalid float")
-                           }
-                           yylval.num = clientmodel.SampleValue(num)
-                           return NUMBER }
+\-?{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
 
-\"(\\.|[^\\"])*\"        { yylval.str = yytext[1:len(yytext) - 1]; return STRING }
-\'(\\.|[^\\'])*\'        { yylval.str = yytext[1:len(yytext) - 1]; return STRING }
+\"(\\.|[^\\"])*\"        lval.str = lexer.token()[1:len(lexer.token()) - 1]; return STRING
+\'(\\.|[^\\'])*\'        lval.str = lexer.token()[1:len(lexer.token()) - 1]; return STRING
 
-[{}\[\]()=,]             { return int(yytext[0]) }
-.                        { /* don't print any remaining chars (whitespace) */ }
-\n                       { /* don't print any remaining chars (whitespace) */ }
+[{}\[\]()=,]             return int(lexer.buf[0])
+[\t\n\r ]                /* gobble up any whitespace */
 %%
+
+  lexer.empty = true
+  return int(c)
+}
diff --git a/rules/lexer.l.go b/rules/lexer.l.go
index b3ca39c3e..d8b435d64 100644
--- a/rules/lexer.l.go
+++ b/rules/lexer.l.go
@@ -1,585 +1,1233 @@
-// Generated by golex
 package rules
 
 import (
-	"bufio"
-	"io"
-	"os"
-	"regexp"
-	"sort"
-)
-import (
-	"strconv"
-	"strings"
+        "fmt"
+        "strconv"
+        "strings"
 
-	clientmodel "github.com/prometheus/client_golang/model"
+        clientmodel "github.com/prometheus/client_golang/model"
 )
 
-var yyin io.Reader = os.Stdin
-var yyout io.Writer = os.Stdout
+func (lexer *RulesLexer) Lex(lval *yySymType) int {
+  // Internal lexer states.
+  const (
+    S_INITIAL = iota
+    S_COMMENTS
+  )
 
-type yyrule struct {
-	regexp     *regexp.Regexp
-	trailing   *regexp.Regexp
-	startConds []yystartcondition
-	sol        bool
-	action     func() yyactionreturn
+  // 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
+  }
+
+
+
+yystate0:
+
+  lexer.buf = lexer.buf[:0]   // The code before the first rule executed before every scan cycle (rule #0 / state 0 action)
+
+switch yyt := currentState; yyt {
+default:
+panic(fmt.Errorf(`invalid start condition %d`, yyt))
+case 0: // start condition: INITIAL
+goto yystart1
+case 1: // start condition: S_COMMENTS
+goto yystart97
 }
 
-type yyactionreturn struct {
-	userReturn int
-	returnType yyactionreturntype
+goto yystate1 // silence unused label error
+yystate1:
+c = lexer.getChar()
+yystart1:
+switch {
+default:
+goto yyabort
+case c == '!':
+goto yystate3
+case c == '"':
+goto yystate5
+case c == '%' || c == '*':
+goto yystate8
+case c == '(' || c == ')' || c == ',' || c == '[' || c == ']' || c == '{' || c == '}':
+goto yystate12
+case c == '+':
+goto yystate13
+case c == '-':
+goto yystate14
+case c == '/':
+goto yystate17
+case c == ':' || c == 'D' || c == 'E' || c == 'G' || c == 'H' || c >= 'J' && c <= 'L' || c == 'N' || c == 'Q' || c == 'R' || c >= 'T' && c <= 'V' || c >= 'X' && c <= 'Z' || c == '_' || c == 'd' || c == 'e' || c == 'g' || c == 'h' || c >= 'j' && c <= 'l' || c == 'n' || c == 'q' || c == 'r' || c >= 't' && c <= 'v' || c >= 'x' && c <= 'z':
+goto yystate23
+case c == '<' || c == '>':
+goto yystate24
+case c == '=':
+goto yystate25
+case c == 'A':
+goto yystate26
+case c == 'B':
+goto yystate35
+case c == 'C':
+goto yystate37
+case c == 'F':
+goto yystate41
+case c == 'I':
+goto yystate44
+case c == 'M':
+goto yystate46
+case c == 'O':
+goto yystate49
+case c == 'P':
+goto yystate50
+case c == 'S':
+goto yystate59
+case c == 'W':
+goto yystate61
+case c == '\'':
+goto yystate9
+case c == '\t' || c == '\n' || c == '\r' || c == ' ':
+goto yystate2
+case c == 'a':
+goto yystate65
+case c == 'b':
+goto yystate72
+case c == 'c':
+goto yystate73
+case c == 'f':
+goto yystate77
+case c == 'i':
+goto yystate79
+case c == 'm':
+goto yystate80
+case c == 'o':
+goto yystate83
+case c == 'p':
+goto yystate84
+case c == 's':
+goto yystate92
+case c == 'w':
+goto yystate94
+case c >= '0' && c <= '9':
+goto yystate21
 }
 
-type yyactionreturntype int
+yystate2:
+c = lexer.getChar()
+goto yyrule23
 
-const (
-	yyRT_FALLTHROUGH yyactionreturntype = iota
-	yyRT_USER_RETURN
-	yyRT_REJECT
-)
-
-var yydata string = ""
-var yyorig string
-var yyorigidx int
-
-var yytext string = ""
-var yytextrepl bool = true
-
-func yymore() {
-	yytextrepl = false
+yystate3:
+c = lexer.getChar()
+switch {
+default:
+goto yyabort
+case c == '=':
+goto yystate4
 }
 
-func yyBEGIN(state yystartcondition) {
-	YY_START = state
+yystate4:
+c = lexer.getChar()
+goto yyrule14
+
+yystate5:
+c = lexer.getChar()
+switch {
+default:
+goto yyabort
+case c == '"':
+goto yystate6
+case c == '\\':
+goto yystate7
+case c >= '\x01' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ':
+goto yystate5
 }
 
-func yyECHO() {
-	yyout.Write([]byte(yytext))
+yystate6:
+c = lexer.getChar()
+goto yyrule20
+
+yystate7:
+c = lexer.getChar()
+switch {
+default:
+goto yyabort
+case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ':
+goto yystate5
 }
 
-func yyREJECT() {
-	panic("yyREJECT")
+yystate8:
+c = lexer.getChar()
+goto yyrule16
+
+yystate9:
+c = lexer.getChar()
+switch {
+default:
+goto yyabort
+case c == '\'':
+goto yystate10
+case c == '\\':
+goto yystate11
+case c >= '\x01' && c <= '&' || c >= '(' && c <= '[' || c >= ']' && c <= 'ÿ':
+goto yystate9
 }
 
-var yylessed int
+yystate10:
+c = lexer.getChar()
+goto yyrule21
 
-func yyless(n int) {
-	yylessed = len(yytext) - n
+yystate11:
+c = lexer.getChar()
+switch {
+default:
+goto yyabort
+case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ':
+goto yystate9
 }
 
-func unput(c uint8) {
-	yyorig = yyorig[:yyorigidx] + string(c) + yyorig[yyorigidx:]
-	yydata = yydata[:len(yytext)-yylessed] + string(c) + yydata[len(yytext)-yylessed:]
+yystate12:
+c = lexer.getChar()
+goto yyrule22
+
+yystate13:
+c = lexer.getChar()
+goto yyrule15
+
+yystate14:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule15
+case c >= '0' && c <= '9':
+goto yystate15
 }
 
-func input() int {
-	if len(yyorig) <= yyorigidx {
-		return EOF
-	}
-	c := yyorig[yyorigidx]
-	yyorig = yyorig[:yyorigidx] + yyorig[yyorigidx+1:]
-	yydata = yydata[:len(yytext)-yylessed] + yydata[len(yytext)-yylessed+1:]
-	return int(c)
+yystate15:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule19
+case c == '.':
+goto yystate16
+case c >= '0' && c <= '9':
+goto yystate15
 }
 
-var EOF int = -1
-
-type yystartcondition int
-
-var INITIAL yystartcondition = 0
-var YY_START yystartcondition = INITIAL
-
-type yylexMatch struct {
-	index     int
-	matchFunc func() yyactionreturn
-	sortLen   int
-	advLen    int
+yystate16:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule19
+case c >= '0' && c <= '9':
+goto yystate16
 }
 
-type yylexMatchList []yylexMatch
-
-func (ml yylexMatchList) Len() int {
-	return len(ml)
+yystate17:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule16
+case c == '*':
+goto yystate18
+case c == '/':
+goto yystate19
 }
 
-func (ml yylexMatchList) Less(i, j int) bool {
-	return ml[i].sortLen > ml[j].sortLen && ml[i].index > ml[j].index
+yystate18:
+c = lexer.getChar()
+goto yyrule1
+
+yystate19:
+c = lexer.getChar()
+switch {
+default:
+goto yyabort
+case c == '\n':
+goto yystate20
+case c >= '\x01' && c <= '\t' || c == '\v' || c == '\f' || c >= '\x0e' && c <= 'ÿ':
+goto yystate19
 }
 
-func (ml yylexMatchList) Swap(i, j int) {
-	ml[i], ml[j] = ml[j], ml[i]
+yystate20:
+c = lexer.getChar()
+goto yyrule4
+
+yystate21:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule19
+case c == '.':
+goto yystate16
+case c == 'd' || c == 'h' || c == 'm' || c == 's' || c == 'w' || c == 'y':
+goto yystate22
+case c >= '0' && c <= '9':
+goto yystate21
 }
 
-func yylex() int {
-	reader := bufio.NewReader(yyin)
+yystate22:
+c = lexer.getChar()
+goto yyrule17
 
-	for {
-		line, err := reader.ReadString('\n')
-		if len(line) == 0 && err == io.EOF {
-			break
-		}
-
-		yydata += line
-	}
-
-	yyorig = yydata
-	yyorigidx = 0
-
-	yyactioninline(yyBEGIN)
-
-	for len(yydata) > 0 {
-		matches := yylexMatchList(make([]yylexMatch, 0, 6))
-		excl := yystartconditionexclmap[YY_START]
-
-		for i, v := range yyrules {
-			sol := yyorigidx == 0 || yyorig[yyorigidx-1] == '\n'
-
-			if v.sol && !sol {
-				continue
-			}
-
-			// Check start conditions.
-			ok := false
-
-			// YY_START or '*' must feature in v.startConds
-			for _, c := range v.startConds {
-				if c == YY_START || c == -1 {
-					ok = true
-					break
-				}
-			}
-
-			if !excl {
-				// If v.startConds is empty, this is also acceptable.
-				if len(v.startConds) == 0 {
-					ok = true
-				}
-			}
-
-			if !ok {
-				continue
-			}
-
-			idxs := v.regexp.FindStringIndex(yydata)
-			if idxs != nil && idxs[0] == 0 {
-				// Check the trailing context, if any.
-				checksOk := true
-				sortLen := idxs[1]
-				advLen := idxs[1]
-
-				if v.trailing != nil {
-					tridxs := v.trailing.FindStringIndex(yydata[idxs[1]:])
-					if tridxs == nil || tridxs[0] != 0 {
-						checksOk = false
-					} else {
-						sortLen += tridxs[1]
-					}
-				}
-
-				if checksOk {
-					matches = append(matches, yylexMatch{i, v.action, sortLen, advLen})
-				}
-			}
-		}
-
-		if yytextrepl {
-			yytext = ""
-		}
-
-		sort.Sort(matches)
-
-	tryMatch:
-		if len(matches) == 0 {
-			yytext += yydata[:1]
-			yydata = yydata[1:]
-			yyorigidx += 1
-
-			yyout.Write([]byte(yytext))
-		} else {
-			m := matches[0]
-			yytext += yydata[:m.advLen]
-			yyorigidx += m.advLen
-
-			yytextrepl, yylessed = true, 0
-			ar := m.matchFunc()
-
-			if ar.returnType != yyRT_REJECT {
-				yydata = yydata[m.advLen-yylessed:]
-				yyorigidx -= yylessed
-			}
-
-			switch ar.returnType {
-			case yyRT_FALLTHROUGH:
-				// Do nothing.
-			case yyRT_USER_RETURN:
-				return ar.userReturn
-			case yyRT_REJECT:
-				matches = matches[1:]
-				yytext = yytext[:len(yytext)-m.advLen]
-				yyorigidx -= m.advLen
-				goto tryMatch
-			}
-		}
-	}
-
-	return 0
+yystate23:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
 }
 
-var S_COMMENTS yystartcondition = 1024
-var yystartconditionexclmap = map[yystartcondition]bool{S_COMMENTS: true}
-var yyrules []yyrule = []yyrule{{regexp.MustCompile("[^\\n]"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		yypos++
-		yyREJECT()
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("\\n"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		yyline++
-		yypos = 1
-		yyREJECT()
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("/\\*"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		yyBEGIN(S_COMMENTS)
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("\\*/"), nil, []yystartcondition{S_COMMENTS}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		yyBEGIN(0)
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("[^\\n]"), nil, []yystartcondition{S_COMMENTS}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("\\/\\/[^\\r\\n]*\\n"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("ALERT|alert"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		return yyactionreturn{ALERT, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("IF|if"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		return yyactionreturn{IF, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("FOR|for"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		return yyactionreturn{FOR, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("WITH|with"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		return yyactionreturn{WITH, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("PERMANENT|permanent"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		return yyactionreturn{PERMANENT, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("BY|by"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		return yyactionreturn{GROUP_OP, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("AVG|SUM|MAX|MIN|COUNT"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		yylval.str = yytext
-		return yyactionreturn{AGGR_OP, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("avg|sum|max|min|count"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		yylval.str = strings.ToUpper(yytext)
-		return yyactionreturn{AGGR_OP, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("\\<|>|AND|OR|and|or"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		yylval.str = strings.ToUpper(yytext)
-		return yyactionreturn{CMP_OP, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("==|!=|>=|<="), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		yylval.str = yytext
-		return yyactionreturn{CMP_OP, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("[+\\-]"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		yylval.str = yytext
-		return yyactionreturn{ADDITIVE_OP, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("[*/%]"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		yylval.str = yytext
-		return yyactionreturn{MULT_OP, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("([0-9])+([smhdwy])"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		yylval.str = yytext
-		return yyactionreturn{DURATION, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("([a-zA-Z_:])(([a-zA-Z_:])|([0-9]))*"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		yylval.str = yytext
-		return yyactionreturn{IDENTIFIER, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("\\-?([0-9])+(\\.([0-9])*)?"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		num, err := strconv.ParseFloat(yytext, 64)
-		if err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax {
-			panic("Invalid float")
-		}
-		yylval.num = clientmodel.SampleValue(num)
-		return yyactionreturn{NUMBER, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("\\\"(\\\\[^\\n]|[^\\\\\"])*\\\""), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		yylval.str = yytext[1 : len(yytext)-1]
-		return yyactionreturn{STRING, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("\\'(\\\\[^\\n]|[^\\\\'])*\\'"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		yylval.str = yytext[1 : len(yytext)-1]
-		return yyactionreturn{STRING, yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("[{}\\[\\]()=,]"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-		return yyactionreturn{int(yytext[0]), yyRT_USER_RETURN}
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("[^\\n]"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}, {regexp.MustCompile("\\n"), nil, []yystartcondition{}, false, func() (yyar yyactionreturn) {
-	defer func() {
-		if r := recover(); r != nil {
-			if r != "yyREJECT" {
-				panic(r)
-			}
-			yyar.returnType = yyRT_REJECT
-		}
-	}()
-	{
-	}
-	return yyactionreturn{0, yyRT_FALLTHROUGH}
-}}}
+yystate24:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule13
+case c == '=':
+goto yystate4
+}
 
-func yyactioninline(BEGIN func(yystartcondition)) {}
+yystate25:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule22
+case c == '=':
+goto yystate4
+}
+
+yystate26:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'L':
+goto yystate27
+case c == 'N':
+goto yystate31
+case c == 'V':
+goto yystate33
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'K' || c == 'M' || c >= 'O' && c <= 'U' || c >= 'W' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate27:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'E':
+goto yystate28
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate28:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'R':
+goto yystate29
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate29:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'T':
+goto yystate30
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate30:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule5
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate31:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'D':
+goto yystate32
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'C' || c >= 'E' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate32:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule13
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate33:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'G':
+goto yystate34
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'F' || c >= 'H' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate34:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule11
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate35:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'Y':
+goto yystate36
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'X' || c == 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate36:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule10
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate37:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'O':
+goto yystate38
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate38:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'U':
+goto yystate39
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate39:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'N':
+goto yystate40
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate40:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'T':
+goto yystate34
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate41:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'O':
+goto yystate42
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'N' || c >= 'P' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate42:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'R':
+goto yystate43
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate43:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule7
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate44:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'F':
+goto yystate45
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'E' || c >= 'G' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate45:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule6
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate46:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'A':
+goto yystate47
+case c == 'I':
+goto yystate48
+case c >= '0' && c <= ':' || c >= 'B' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate47:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'X':
+goto yystate34
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'W' || c == 'Y' || c == 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate48:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'N':
+goto yystate34
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate49:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'R':
+goto yystate32
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate50:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'E':
+goto yystate51
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate51:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'R':
+goto yystate52
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate52:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'M':
+goto yystate53
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'L' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate53:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'A':
+goto yystate54
+case c >= '0' && c <= ':' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate54:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'N':
+goto yystate55
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate55:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'E':
+goto yystate56
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate56:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'N':
+goto yystate57
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'M' || c >= 'O' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate57:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'T':
+goto yystate58
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate58:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule9
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate59:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'U':
+goto yystate60
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate60:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'M':
+goto yystate34
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'L' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate61:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'I':
+goto yystate62
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate62:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'T':
+goto yystate63
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate63:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'H':
+goto yystate64
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'G' || c >= 'I' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate64:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule8
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate65:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'l':
+goto yystate66
+case c == 'n':
+goto yystate69
+case c == 'v':
+goto yystate70
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c == 'm' || c >= 'o' && c <= 'u' || c >= 'w' && c <= 'z':
+goto yystate23
+}
+
+yystate66:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'e':
+goto yystate67
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z':
+goto yystate23
+}
+
+yystate67:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'r':
+goto yystate68
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z':
+goto yystate23
+}
+
+yystate68:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 't':
+goto yystate30
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z':
+goto yystate23
+}
+
+yystate69:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'd':
+goto yystate32
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'c' || c >= 'e' && c <= 'z':
+goto yystate23
+}
+
+yystate70:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'g':
+goto yystate71
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'f' || c >= 'h' && c <= 'z':
+goto yystate23
+}
+
+yystate71:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule12
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z':
+goto yystate23
+}
+
+yystate72:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'y':
+goto yystate36
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'x' || c == 'z':
+goto yystate23
+}
+
+yystate73:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'o':
+goto yystate74
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z':
+goto yystate23
+}
+
+yystate74:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'u':
+goto yystate75
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z':
+goto yystate23
+}
+
+yystate75:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'n':
+goto yystate76
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z':
+goto yystate23
+}
+
+yystate76:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 't':
+goto yystate71
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z':
+goto yystate23
+}
+
+yystate77:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'o':
+goto yystate78
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z':
+goto yystate23
+}
+
+yystate78:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'r':
+goto yystate43
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z':
+goto yystate23
+}
+
+yystate79:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'f':
+goto yystate45
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'e' || c >= 'g' && c <= 'z':
+goto yystate23
+}
+
+yystate80:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'a':
+goto yystate81
+case c == 'i':
+goto yystate82
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'h' || c >= 'j' && c <= 'z':
+goto yystate23
+}
+
+yystate81:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'x':
+goto yystate71
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'w' || c == 'y' || c == 'z':
+goto yystate23
+}
+
+yystate82:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'n':
+goto yystate71
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z':
+goto yystate23
+}
+
+yystate83:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'r':
+goto yystate32
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z':
+goto yystate23
+}
+
+yystate84:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'e':
+goto yystate85
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z':
+goto yystate23
+}
+
+yystate85:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'r':
+goto yystate86
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z':
+goto yystate23
+}
+
+yystate86:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'm':
+goto yystate87
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z':
+goto yystate23
+}
+
+yystate87:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'a':
+goto yystate88
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z':
+goto yystate23
+}
+
+yystate88:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'n':
+goto yystate89
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z':
+goto yystate23
+}
+
+yystate89:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'e':
+goto yystate90
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z':
+goto yystate23
+}
+
+yystate90:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'n':
+goto yystate91
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z':
+goto yystate23
+}
+
+yystate91:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 't':
+goto yystate58
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z':
+goto yystate23
+}
+
+yystate92:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'u':
+goto yystate93
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z':
+goto yystate23
+}
+
+yystate93:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'm':
+goto yystate71
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z':
+goto yystate23
+}
+
+yystate94:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'i':
+goto yystate95
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z':
+goto yystate23
+}
+
+yystate95:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 't':
+goto yystate96
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z':
+goto yystate23
+}
+
+yystate96:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule18
+case c == 'h':
+goto yystate64
+case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'g' || c >= 'i' && c <= 'z':
+goto yystate23
+}
+
+goto yystate97 // silence unused label error
+yystate97:
+c = lexer.getChar()
+yystart97:
+switch {
+default:
+goto yyabort
+case c == '*':
+goto yystate99
+case c >= '\x01' && c <= ')' || c >= '+' && c <= 'ÿ':
+goto yystate98
+}
+
+yystate98:
+c = lexer.getChar()
+goto yyrule3
+
+yystate99:
+c = lexer.getChar()
+switch {
+default:
+goto yyrule3
+case c == '/':
+goto yystate100
+}
+
+yystate100:
+c = lexer.getChar()
+goto yyrule2
+
+yyrule1: // "/*"
+{
+                    currentState = S_COMMENTS
+goto yystate0
+}
+yyrule2: // "*/"
+{
+        currentState = S_INITIAL
+goto yystate0
+}
+yyrule3: // .|\n
+{
+        /* ignore chars within multi-line comments */
+goto yystate0
+}
+yyrule4: // \/\/[^\r\n]*\n
+{
+          /* gobble up one-line comments */
+goto yystate0
+}
+yyrule5: // ALERT|alert
+{
+             return ALERT
+}
+yyrule6: // IF|if
+{
+                   return IF
+}
+yyrule7: // FOR|for
+{
+                 return FOR
+}
+yyrule8: // WITH|with
+{
+               return WITH
+}
+yyrule9: // PERMANENT|permanent
+{
+     return PERMANENT
+}
+yyrule10: // BY|by
+{
+                   return GROUP_OP
+}
+yyrule11: // AVG|SUM|MAX|MIN|COUNT
+{
+   lval.str = lexer.token(); return AGGR_OP
+goto yystate0
+}
+yyrule12: // avg|sum|max|min|count
+{
+   lval.str = strings.ToUpper(lexer.token()); return AGGR_OP
+goto yystate0
+}
+yyrule13: // \<|>|AND|OR|and|or
+{
+      lval.str = strings.ToUpper(lexer.token()); return CMP_OP
+goto yystate0
+}
+yyrule14: // ==|!=|>=|<=
+{
+             lval.str = lexer.token(); return CMP_OP
+goto yystate0
+}
+yyrule15: // [+\-]
+{
+                   lval.str = lexer.token(); return ADDITIVE_OP
+goto yystate0
+}
+yyrule16: // [*/%]
+{
+                   lval.str = lexer.token(); return MULT_OP
+goto yystate0
+}
+yyrule17: // {D}+{U}
+{
+                 lval.str = lexer.token(); return DURATION
+goto yystate0
+}
+yyrule18: // {L}({L}|{D})*
+{
+           lval.str = lexer.token(); return IDENTIFIER
+goto yystate0
+}
+yyrule19: // \-?{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
+}
+yyrule20: // \"(\\.|[^\\"])*\"
+{
+       lval.str = lexer.token()[1:len(lexer.token()) - 1]; return STRING
+goto yystate0
+}
+yyrule21: // \'(\\.|[^\\'])*\'
+{
+       lval.str = lexer.token()[1:len(lexer.token()) - 1]; return STRING
+goto yystate0
+}
+yyrule22: // [{}\[\]()=,]
+{
+            return int(lexer.buf[0])
+}
+yyrule23: // [\t\n\r ]
+{
+               /* gobble up any whitespace */
+goto yystate0
+}
+panic("unreachable")
+
+goto yyabort // silence unused label error
+
+yyabort: // no lexem recognized
+
+  lexer.empty = true
+  return int(c)
+}
diff --git a/rules/load.go b/rules/load.go
index 7bd988e2f..1c211db0c 100644
--- a/rules/load.go
+++ b/rules/load.go
@@ -14,69 +14,87 @@
 package rules
 
 import (
+	"bufio"
 	"errors"
 	"fmt"
-	"github.com/prometheus/prometheus/rules/ast"
 	"io"
+	"log"
 	"os"
 	"strings"
-	"sync"
-)
 
-// GoLex sadly needs these global variables for storing temporary token/parsing information.
-var (
-	yylval     *yySymType // For storing extra token information, like the contents of a string.
-	yyline     int        // Line number within the current file or buffer.
-	yypos      int        // Character position within the current line.
-	parseMutex sync.Mutex // Mutex protecting the parsing-related global state defined above.
+	"github.com/prometheus/prometheus/rules/ast"
 )
 
 type RulesLexer struct {
-	errors      []string // Errors encountered during parsing.
-	startToken  int      // Dummy token to simulate multiple start symbols (see below).
-	parsedRules []Rule   // Parsed full rules.
-	parsedExpr  ast.Node // Parsed single expression.
-}
+	// Errors encountered during parsing.
+	errors []string
+	// Dummy token to simulate multiple start symbols (see below).
+	startToken int
+	// Parsed full rules.
+	parsedRules []Rule
+	// Parsed single expression.
+	parsedExpr ast.Node
 
-func (lexer *RulesLexer) Lex(lval *yySymType) int {
-	yylval = lval
+	// Current character.
+	current byte
+	// Current token buffer.
+	buf []byte
+	// Input text.
+	src *bufio.Reader
+	// Whether we have a current char.
+	empty bool
 
-	// 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
-	}
-
-	tokenType := yylex()
-	return tokenType
+	// Current input line.
+	line int
+	// Current character position within the current input line.
+	pos int
 }
 
 func (lexer *RulesLexer) Error(errorStr string) {
-	err := fmt.Sprintf("Error parsing rules at line %v, char %v: %v", yyline, yypos, errorStr)
+	err := fmt.Sprintf("Error parsing rules at line %v, char %v: %v", lexer.line, lexer.pos, errorStr)
 	lexer.errors = append(lexer.errors, err)
 }
 
-func LoadFromReader(rulesReader io.Reader, singleExpr bool) (interface{}, error) {
-	parseMutex.Lock()
-	defer parseMutex.Unlock()
+func (lexer *RulesLexer) getChar() byte {
+	if lexer.current != 0 {
+		lexer.buf = append(lexer.buf, lexer.current)
+	}
+	lexer.current = 0
+	if b, err := lexer.src.ReadByte(); err == nil {
+		if b == '\n' {
+			lexer.line++
+			lexer.pos = 0
+		} else {
+			lexer.pos++
+		}
+		lexer.current = b
+	} else if err != io.EOF {
+		log.Fatal(err)
+	}
+	return lexer.current
+}
 
-	yyin = rulesReader
-	yypos = 1
-	yyline = 1
-	yydata = ""
-	yytext = ""
+func (lexer *RulesLexer) token() string {
+	return string(lexer.buf)
+}
 
+func newRulesLexer(src io.Reader, singleExpr bool) *RulesLexer {
 	lexer := &RulesLexer{
 		startToken: START_RULES,
+		src:        bufio.NewReader(src),
+		pos:        1,
+		line:       1,
 	}
 
 	if singleExpr {
 		lexer.startToken = START_EXPRESSION
 	}
+	lexer.getChar()
+	return lexer
+}
 
+func LoadFromReader(rulesReader io.Reader, singleExpr bool) (interface{}, error) {
+	lexer := newRulesLexer(rulesReader, singleExpr)
 	ret := yyParse(lexer)
 	if ret != 0 && len(lexer.errors) == 0 {
 		lexer.Error("Unknown parser error")
diff --git a/rules/parser.y b/rules/parser.y
index 30762132b..c579df75a 100644
--- a/rules/parser.y
+++ b/rules/parser.y
@@ -14,10 +14,10 @@
 %{
         package rules
 
-import (
-        clientmodel "github.com/prometheus/client_golang/model"
+        import (
+          clientmodel "github.com/prometheus/client_golang/model"
 
-        import "github.com/prometheus/prometheus/rules/ast"
+          "github.com/prometheus/prometheus/rules/ast"
         )
 %}
 
diff --git a/rules/parser.y.go b/rules/parser.y.go
index 3305933a8..c8dbad4d4 100644
--- a/rules/parser.y.go
+++ b/rules/parser.y.go
@@ -1,22 +1,25 @@
-//line parser.y:15
-package rules
 
+//line parser.y:15
+        package rules
 import __yyfmt__ "fmt"
-
 //line parser.y:15
-import clientmodel "github.com/prometheus/client_golang/model"
-import "github.com/prometheus/prometheus/rules/ast"
+		
+        import (
+          clientmodel "github.com/prometheus/client_golang/model"
 
-//line parser.y:21
+          "github.com/prometheus/prometheus/rules/ast"
+        )
+
+//line parser.y:24
 type yySymType struct {
-	yys            int
-	num            clientmodel.SampleValue
-	str            string
-	ruleNode       ast.Node
-	ruleNodeSlice  []ast.Node
-	boolean        bool
-	labelNameSlice clientmodel.LabelNames
-	labelSet       clientmodel.LabelSet
+	yys int
+        num clientmodel.SampleValue
+        str string
+        ruleNode ast.Node
+        ruleNodeSlice []ast.Node
+        boolean bool
+        labelNameSlice clientmodel.LabelNames
+        labelSet clientmodel.LabelSet
 }
 
 const START_RULES = 57346
@@ -61,7 +64,8 @@ const yyEofCode = 1
 const yyErrCode = 2
 const yyMaxDepth = 200
 
-//line parser.y:188
+//line parser.y:191
+
 
 //line yacctab:1
 var yyExca = []int{
@@ -394,208 +398,134 @@ yydefault:
 	switch yynt {
 
 	case 5:
-		//line parser.y:66
-		{
-			yylex.(*RulesLexer).parsedExpr = yyS[yypt-0].ruleNode
-		}
+		//line parser.y:69
+		{ yylex.(*RulesLexer).parsedExpr = yyS[yypt-0].ruleNode }
 	case 6:
-		//line parser.y:70
+		//line parser.y:73
 		{
-			rule, err := CreateRecordingRule(yyS[yypt-3].str, yyS[yypt-2].labelSet, yyS[yypt-0].ruleNode, yyS[yypt-4].boolean)
-			if err != nil {
-				yylex.Error(err.Error())
-				return 1
-			}
-			yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
-		}
+	                       rule, err := CreateRecordingRule(yyS[yypt-3].str, yyS[yypt-2].labelSet, yyS[yypt-0].ruleNode, yyS[yypt-4].boolean)
+	                       if err != nil { yylex.Error(err.Error()); return 1 }
+	                       yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
+	                     }
 	case 7:
-		//line parser.y:76
+		//line parser.y:79
 		{
-			rule, err := CreateAlertingRule(yyS[yypt-5].str, yyS[yypt-3].ruleNode, yyS[yypt-2].str, yyS[yypt-0].labelSet)
-			if err != nil {
-				yylex.Error(err.Error())
-				return 1
-			}
-			yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
-		}
+	                       rule, err := CreateAlertingRule(yyS[yypt-5].str, yyS[yypt-3].ruleNode, yyS[yypt-2].str, yyS[yypt-0].labelSet)
+	                       if err != nil { yylex.Error(err.Error()); return 1 }
+	                       yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
+	                     }
 	case 8:
-		//line parser.y:84
-		{
-			yyVAL.str = "0s"
-		}
+		//line parser.y:87
+		{ yyVAL.str = "0s" }
 	case 9:
-		//line parser.y:86
-		{
-			yyVAL.str = yyS[yypt-0].str
-		}
+		//line parser.y:89
+		{ yyVAL.str = yyS[yypt-0].str }
 	case 10:
-		//line parser.y:90
-		{
-			yyVAL.boolean = false
-		}
+		//line parser.y:93
+		{ yyVAL.boolean = false }
 	case 11:
-		//line parser.y:92
-		{
-			yyVAL.boolean = true
-		}
+		//line parser.y:95
+		{ yyVAL.boolean = true }
 	case 12:
-		//line parser.y:96
-		{
-			yyVAL.labelSet = clientmodel.LabelSet{}
-		}
+		//line parser.y:99
+		{ yyVAL.labelSet = clientmodel.LabelSet{} }
 	case 13:
-		//line parser.y:98
-		{
-			yyVAL.labelSet = yyS[yypt-1].labelSet
-		}
+		//line parser.y:101
+		{ yyVAL.labelSet = yyS[yypt-1].labelSet  }
 	case 14:
-		//line parser.y:100
-		{
-			yyVAL.labelSet = clientmodel.LabelSet{}
-		}
-	case 15:
 		//line parser.y:103
-		{
-			yyVAL.labelSet = yyS[yypt-0].labelSet
-		}
+		{ yyVAL.labelSet = clientmodel.LabelSet{} }
+	case 15:
+		//line parser.y:106
+		{ yyVAL.labelSet = yyS[yypt-0].labelSet }
 	case 16:
-		//line parser.y:105
-		{
-			for k, v := range yyS[yypt-0].labelSet {
-				yyVAL.labelSet[k] = v
-			}
-		}
+		//line parser.y:108
+		{ for k, v := range yyS[yypt-0].labelSet { yyVAL.labelSet[k] = v } }
 	case 17:
-		//line parser.y:109
-		{
-			yyVAL.labelSet = clientmodel.LabelSet{clientmodel.LabelName(yyS[yypt-2].str): clientmodel.LabelValue(yyS[yypt-0].str)}
-		}
+		//line parser.y:112
+		{ yyVAL.labelSet = clientmodel.LabelSet{ clientmodel.LabelName(yyS[yypt-2].str): clientmodel.LabelValue(yyS[yypt-0].str) } }
 	case 18:
-		//line parser.y:114
-		{
-			yyVAL.ruleNode = yyS[yypt-1].ruleNode
-		}
+		//line parser.y:117
+		{ yyVAL.ruleNode = yyS[yypt-1].ruleNode }
 	case 19:
-		//line parser.y:116
-		{
-			yyS[yypt-0].labelSet[clientmodel.MetricNameLabel] = clientmodel.LabelValue(yyS[yypt-1].str)
-			yyVAL.ruleNode = ast.NewVectorLiteral(yyS[yypt-0].labelSet)
-		}
+		//line parser.y:119
+		{ yyS[yypt-0].labelSet[clientmodel.MetricNameLabel] = clientmodel.LabelValue(yyS[yypt-1].str); yyVAL.ruleNode = ast.NewVectorLiteral(yyS[yypt-0].labelSet) }
 	case 20:
-		//line parser.y:118
+		//line parser.y:121
 		{
-			var err error
-			yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-3].str, yyS[yypt-1].ruleNodeSlice)
-			if err != nil {
-				yylex.Error(err.Error())
-				return 1
-			}
-		}
+	                       var err error
+	                       yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-3].str, yyS[yypt-1].ruleNodeSlice)
+	                       if err != nil { yylex.Error(err.Error()); return 1 }
+	                     }
 	case 21:
-		//line parser.y:124
+		//line parser.y:127
 		{
-			var err error
-			yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-2].str, []ast.Node{})
-			if err != nil {
-				yylex.Error(err.Error())
-				return 1
-			}
-		}
+	                       var err error
+	                       yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-2].str, []ast.Node{})
+	                       if err != nil { yylex.Error(err.Error()); return 1 }
+	                     }
 	case 22:
-		//line parser.y:130
+		//line parser.y:133
 		{
-			var err error
-			yyVAL.ruleNode, err = NewMatrix(yyS[yypt-3].ruleNode, yyS[yypt-1].str)
-			if err != nil {
-				yylex.Error(err.Error())
-				return 1
-			}
-		}
+	                       var err error
+	                       yyVAL.ruleNode, err = NewMatrix(yyS[yypt-3].ruleNode, yyS[yypt-1].str)
+	                       if err != nil { yylex.Error(err.Error()); return 1 }
+	                     }
 	case 23:
-		//line parser.y:136
+		//line parser.y:139
 		{
-			var err error
-			yyVAL.ruleNode, err = NewVectorAggregation(yyS[yypt-4].str, yyS[yypt-2].ruleNode, yyS[yypt-0].labelNameSlice)
-			if err != nil {
-				yylex.Error(err.Error())
-				return 1
-			}
-		}
+	                       var err error
+	                       yyVAL.ruleNode, err = NewVectorAggregation(yyS[yypt-4].str, yyS[yypt-2].ruleNode, yyS[yypt-0].labelNameSlice)
+	                       if err != nil { yylex.Error(err.Error()); return 1 }
+	                     }
 	case 24:
-		//line parser.y:144
+		//line parser.y:147
 		{
-			var err error
-			yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
-			if err != nil {
-				yylex.Error(err.Error())
-				return 1
-			}
-		}
+	                       var err error
+	                       yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
+	                       if err != nil { yylex.Error(err.Error()); return 1 }
+	                     }
 	case 25:
-		//line parser.y:150
+		//line parser.y:153
 		{
-			var err error
-			yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
-			if err != nil {
-				yylex.Error(err.Error())
-				return 1
-			}
-		}
+	                       var err error
+	                       yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
+	                       if err != nil { yylex.Error(err.Error()); return 1 }
+	                     }
 	case 26:
-		//line parser.y:156
+		//line parser.y:159
 		{
-			var err error
-			yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
-			if err != nil {
-				yylex.Error(err.Error())
-				return 1
-			}
-		}
+	                       var err error
+	                       yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
+	                       if err != nil { yylex.Error(err.Error()); return 1 }
+	                     }
 	case 27:
-		//line parser.y:162
-		{
-			yyVAL.ruleNode = ast.NewScalarLiteral(yyS[yypt-0].num)
-		}
+		//line parser.y:165
+		{ yyVAL.ruleNode = ast.NewScalarLiteral(yyS[yypt-0].num)}
 	case 28:
-		//line parser.y:166
-		{
-			yyVAL.labelNameSlice = clientmodel.LabelNames{}
-		}
+		//line parser.y:169
+		{ yyVAL.labelNameSlice = clientmodel.LabelNames{} }
 	case 29:
-		//line parser.y:168
-		{
-			yyVAL.labelNameSlice = yyS[yypt-1].labelNameSlice
-		}
+		//line parser.y:171
+		{ yyVAL.labelNameSlice = yyS[yypt-1].labelNameSlice }
 	case 30:
-		//line parser.y:172
-		{
-			yyVAL.labelNameSlice = clientmodel.LabelNames{clientmodel.LabelName(yyS[yypt-0].str)}
-		}
+		//line parser.y:175
+		{ yyVAL.labelNameSlice = clientmodel.LabelNames{clientmodel.LabelName(yyS[yypt-0].str)} }
 	case 31:
-		//line parser.y:174
-		{
-			yyVAL.labelNameSlice = append(yyVAL.labelNameSlice, clientmodel.LabelName(yyS[yypt-0].str))
-		}
+		//line parser.y:177
+		{ yyVAL.labelNameSlice = append(yyVAL.labelNameSlice, clientmodel.LabelName(yyS[yypt-0].str)) }
 	case 32:
-		//line parser.y:178
-		{
-			yyVAL.ruleNodeSlice = []ast.Node{yyS[yypt-0].ruleNode}
-		}
+		//line parser.y:181
+		{ yyVAL.ruleNodeSlice = []ast.Node{yyS[yypt-0].ruleNode} }
 	case 33:
-		//line parser.y:180
-		{
-			yyVAL.ruleNodeSlice = append(yyVAL.ruleNodeSlice, yyS[yypt-0].ruleNode)
-		}
+		//line parser.y:183
+		{ yyVAL.ruleNodeSlice = append(yyVAL.ruleNodeSlice, yyS[yypt-0].ruleNode) }
 	case 34:
-		//line parser.y:184
-		{
-			yyVAL.ruleNode = yyS[yypt-0].ruleNode
-		}
+		//line parser.y:187
+		{ yyVAL.ruleNode = yyS[yypt-0].ruleNode }
 	case 35:
-		//line parser.y:186
-		{
-			yyVAL.ruleNode = ast.NewStringLiteral(yyS[yypt-0].str)
-		}
+		//line parser.y:189
+		{ yyVAL.ruleNode = ast.NewStringLiteral(yyS[yypt-0].str) }
 	}
 	goto yystack /* stack new state and value */
 }
diff --git a/rules/rules_test.go b/rules/rules_test.go
index 5a63f8f75..502f40d56 100644
--- a/rules/rules_test.go
+++ b/rules/rules_test.go
@@ -123,6 +123,17 @@ func TestExpressions(t *testing.T) {
 			},
 			fullRanges:     0,
 			intervalRanges: 8,
+		}, {
+			expr: `
+				// Test comment.
+				SUM(http_requests) BY /* comments shouldn't
+				have any effect */ (job) // another comment`,
+			output: []string{
+				`http_requests{job="api-server"} => 1000 @[%v]`,
+				`http_requests{job="app-server"} => 2600 @[%v]`,
+			},
+			fullRanges:     0,
+			intervalRanges: 8,
 		}, {
 			expr: `COUNT(http_requests) BY (job)`,
 			output: []string{
@@ -448,7 +459,7 @@ var ruleTests = []struct {
 	{
 		inputFile:   "syntax_error.rules",
 		shouldFail:  true,
-		errContains: "Error parsing rules at line 3",
+		errContains: "Error parsing rules at line 5",
 	},
 	{
 		inputFile:   "non_vector.rules",