| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917 |
- // Copyright 2015 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 promql
- import (
- "fmt"
- "strings"
- "unicode"
- "unicode/utf8"
- )
- // item represents a token or text string returned from the scanner.
- type item struct {
- typ ItemType // The type of this item.
- pos Pos // The starting position, in bytes, of this item in the input string.
- val string // The value of this item.
- }
- // String returns a descriptive string for the item.
- func (i item) String() string {
- switch {
- case i.typ == ItemEOF:
- return "EOF"
- case i.typ == ItemError:
- return i.val
- case i.typ == ItemIdentifier || i.typ == ItemMetricIdentifier:
- return fmt.Sprintf("%q", i.val)
- case i.typ.isKeyword():
- return fmt.Sprintf("<%s>", i.val)
- case i.typ.isOperator():
- return fmt.Sprintf("<op:%s>", i.val)
- case i.typ.isAggregator():
- return fmt.Sprintf("<aggr:%s>", i.val)
- case len(i.val) > 10:
- return fmt.Sprintf("%.10q...", i.val)
- }
- return fmt.Sprintf("%q", i.val)
- }
- // isOperator returns true if the item corresponds to a arithmetic or set operator.
- // Returns false otherwise.
- func (i ItemType) isOperator() bool { return i > operatorsStart && i < operatorsEnd }
- // isAggregator returns true if the item belongs to the aggregator functions.
- // Returns false otherwise
- func (i ItemType) isAggregator() bool { return i > aggregatorsStart && i < aggregatorsEnd }
- // isAggregator returns true if the item is an aggregator that takes a parameter.
- // Returns false otherwise
- func (i ItemType) isAggregatorWithParam() bool {
- return i == ItemTopK || i == ItemBottomK || i == ItemCountValues || i == ItemQuantile
- }
- // isKeyword returns true if the item corresponds to a keyword.
- // Returns false otherwise.
- func (i ItemType) isKeyword() bool { return i > keywordsStart && i < keywordsEnd }
- // isCompairsonOperator returns true if the item corresponds to a comparison operator.
- // Returns false otherwise.
- func (i ItemType) isComparisonOperator() bool {
- switch i {
- case ItemEQL, ItemNEQ, ItemLTE, ItemLSS, ItemGTE, ItemGTR:
- return true
- default:
- return false
- }
- }
- // isSetOperator returns whether the item corresponds to a set operator.
- func (i ItemType) isSetOperator() bool {
- switch i {
- case ItemLAND, ItemLOR, ItemLUnless:
- return true
- }
- return false
- }
- // LowestPrec is a constant for operator precedence in expressions.
- const LowestPrec = 0 // Non-operators.
- // Precedence returns the operator precedence of the binary
- // operator op. If op is not a binary operator, the result
- // is LowestPrec.
- func (i ItemType) precedence() int {
- switch i {
- case ItemLOR:
- return 1
- case ItemLAND, ItemLUnless:
- return 2
- case ItemEQL, ItemNEQ, ItemLTE, ItemLSS, ItemGTE, ItemGTR:
- return 3
- case ItemADD, ItemSUB:
- return 4
- case ItemMUL, ItemDIV, ItemMOD:
- return 5
- case ItemPOW:
- return 6
- default:
- return LowestPrec
- }
- }
- func (i ItemType) isRightAssociative() bool {
- switch i {
- case ItemPOW:
- return true
- default:
- return false
- }
- }
- type ItemType int
- const (
- ItemError ItemType = iota // Error occurred, value is error message
- ItemEOF
- ItemComment
- ItemIdentifier
- ItemMetricIdentifier
- ItemLeftParen
- ItemRightParen
- ItemLeftBrace
- ItemRightBrace
- ItemLeftBracket
- ItemRightBracket
- ItemComma
- ItemAssign
- ItemColon
- ItemSemicolon
- ItemString
- ItemNumber
- ItemDuration
- ItemBlank
- ItemTimes
- ItemSpace
- operatorsStart
- // Operators.
- ItemSUB
- ItemADD
- ItemMUL
- ItemMOD
- ItemDIV
- ItemLAND
- ItemLOR
- ItemLUnless
- ItemEQL
- ItemNEQ
- ItemLTE
- ItemLSS
- ItemGTE
- ItemGTR
- ItemEQLRegex
- ItemNEQRegex
- ItemPOW
- operatorsEnd
- aggregatorsStart
- // Aggregators.
- ItemAvg
- ItemCount
- ItemSum
- ItemMin
- ItemMax
- ItemStddev
- ItemStdvar
- ItemTopK
- ItemBottomK
- ItemCountValues
- ItemQuantile
- aggregatorsEnd
- keywordsStart
- // Keywords.
- ItemOffset
- ItemBy
- ItemWithout
- ItemOn
- ItemIgnoring
- ItemGroupLeft
- ItemGroupRight
- ItemBool
- keywordsEnd
- )
- var key = map[string]ItemType{
- // Operators.
- "and": ItemLAND,
- "or": ItemLOR,
- "unless": ItemLUnless,
- // Aggregators.
- "sum": ItemSum,
- "avg": ItemAvg,
- "count": ItemCount,
- "min": ItemMin,
- "max": ItemMax,
- "stddev": ItemStddev,
- "stdvar": ItemStdvar,
- "topk": ItemTopK,
- "bottomk": ItemBottomK,
- "count_values": ItemCountValues,
- "quantile": ItemQuantile,
- // Keywords.
- "offset": ItemOffset,
- "by": ItemBy,
- "without": ItemWithout,
- "on": ItemOn,
- "ignoring": ItemIgnoring,
- "group_left": ItemGroupLeft,
- "group_right": ItemGroupRight,
- "bool": ItemBool,
- }
- // These are the default string representations for common items. It does not
- // imply that those are the only character sequences that can be lexed to such an item.
- var itemTypeStr = map[ItemType]string{
- ItemLeftParen: "(",
- ItemRightParen: ")",
- ItemLeftBrace: "{",
- ItemRightBrace: "}",
- ItemLeftBracket: "[",
- ItemRightBracket: "]",
- ItemComma: ",",
- ItemAssign: "=",
- ItemColon: ":",
- ItemSemicolon: ";",
- ItemBlank: "_",
- ItemTimes: "x",
- ItemSpace: "<space>",
- ItemSUB: "-",
- ItemADD: "+",
- ItemMUL: "*",
- ItemMOD: "%",
- ItemDIV: "/",
- ItemEQL: "==",
- ItemNEQ: "!=",
- ItemLTE: "<=",
- ItemLSS: "<",
- ItemGTE: ">=",
- ItemGTR: ">",
- ItemEQLRegex: "=~",
- ItemNEQRegex: "!~",
- ItemPOW: "^",
- }
- func init() {
- // Add keywords to item type strings.
- for s, ty := range key {
- itemTypeStr[ty] = s
- }
- // Special numbers.
- key["inf"] = ItemNumber
- key["nan"] = ItemNumber
- }
- func (i ItemType) String() string {
- if s, ok := itemTypeStr[i]; ok {
- return s
- }
- return fmt.Sprintf("<item %d>", i)
- }
- func (i item) desc() string {
- if _, ok := itemTypeStr[i.typ]; ok {
- return i.String()
- }
- if i.typ == ItemEOF {
- return i.typ.desc()
- }
- return fmt.Sprintf("%s %s", i.typ.desc(), i)
- }
- func (i ItemType) desc() string {
- switch i {
- case ItemError:
- return "error"
- case ItemEOF:
- return "end of input"
- case ItemComment:
- return "comment"
- case ItemIdentifier:
- return "identifier"
- case ItemMetricIdentifier:
- return "metric identifier"
- case ItemString:
- return "string"
- case ItemNumber:
- return "number"
- case ItemDuration:
- return "duration"
- }
- return fmt.Sprintf("%q", i)
- }
- const eof = -1
- // stateFn represents the state of the scanner as a function that returns the next state.
- type stateFn func(*lexer) stateFn
- // Pos is the position in a string.
- type Pos int
- // lexer holds the state of the scanner.
- type lexer struct {
- input string // The string being scanned.
- state stateFn // The next lexing function to enter.
- pos Pos // Current position in the input.
- start Pos // Start position of this item.
- width Pos // Width of last rune read from input.
- lastPos Pos // Position of most recent item returned by nextItem.
- items chan item // Channel of scanned items.
- parenDepth int // Nesting depth of ( ) exprs.
- braceOpen bool // Whether a { is opened.
- bracketOpen bool // Whether a [ is opened.
- gotColon bool // Whether we got a ':' after [ was opened.
- stringOpen rune // Quote rune of the string currently being read.
- // seriesDesc is set when a series description for the testing
- // language is lexed.
- seriesDesc bool
- }
- // next returns the next rune in the input.
- func (l *lexer) next() rune {
- if int(l.pos) >= len(l.input) {
- l.width = 0
- return eof
- }
- r, w := utf8.DecodeRuneInString(l.input[l.pos:])
- l.width = Pos(w)
- l.pos += l.width
- return r
- }
- // peek returns but does not consume the next rune in the input.
- func (l *lexer) peek() rune {
- r := l.next()
- l.backup()
- return r
- }
- // backup steps back one rune. Can only be called once per call of next.
- func (l *lexer) backup() {
- l.pos -= l.width
- }
- // emit passes an item back to the client.
- func (l *lexer) emit(t ItemType) {
- l.items <- item{t, l.start, l.input[l.start:l.pos]}
- l.start = l.pos
- }
- // ignore skips over the pending input before this point.
- func (l *lexer) ignore() {
- l.start = l.pos
- }
- // accept consumes the next rune if it's from the valid set.
- func (l *lexer) accept(valid string) bool {
- if strings.ContainsRune(valid, l.next()) {
- return true
- }
- l.backup()
- return false
- }
- // acceptRun consumes a run of runes from the valid set.
- func (l *lexer) acceptRun(valid string) {
- for strings.ContainsRune(valid, l.next()) {
- // consume
- }
- l.backup()
- }
- // lineNumber reports which line we're on, based on the position of
- // the previous item returned by nextItem. Doing it this way
- // means we don't have to worry about peek double counting.
- func (l *lexer) lineNumber() int {
- return 1 + strings.Count(l.input[:l.lastPos], "\n")
- }
- // linePosition reports at which character in the current line
- // we are on.
- func (l *lexer) linePosition() int {
- lb := strings.LastIndex(l.input[:l.lastPos], "\n")
- if lb == -1 {
- return 1 + int(l.lastPos)
- }
- return 1 + int(l.lastPos) - lb
- }
- // errorf returns an error token and terminates the scan by passing
- // back a nil pointer that will be the next state, terminating l.nextItem.
- func (l *lexer) errorf(format string, args ...interface{}) stateFn {
- l.items <- item{ItemError, l.start, fmt.Sprintf(format, args...)}
- return nil
- }
- // nextItem returns the next item from the input.
- func (l *lexer) nextItem() item {
- item := <-l.items
- l.lastPos = item.pos
- return item
- }
- // lex creates a new scanner for the input string.
- func lex(input string) *lexer {
- l := &lexer{
- input: input,
- items: make(chan item),
- }
- go l.run()
- return l
- }
- // run runs the state machine for the lexer.
- func (l *lexer) run() {
- for l.state = lexStatements; l.state != nil; {
- l.state = l.state(l)
- }
- close(l.items)
- }
- // Release resources used by lexer.
- func (l *lexer) close() {
- for range l.items {
- // Consume.
- }
- }
- // lineComment is the character that starts a line comment.
- const lineComment = "#"
- // lexStatements is the top-level state for lexing.
- func lexStatements(l *lexer) stateFn {
- if l.braceOpen {
- return lexInsideBraces
- }
- if strings.HasPrefix(l.input[l.pos:], lineComment) {
- return lexLineComment
- }
- switch r := l.next(); {
- case r == eof:
- if l.parenDepth != 0 {
- return l.errorf("unclosed left parenthesis")
- } else if l.bracketOpen {
- return l.errorf("unclosed left bracket")
- }
- l.emit(ItemEOF)
- return nil
- case r == ',':
- l.emit(ItemComma)
- case isSpace(r):
- return lexSpace
- case r == '*':
- l.emit(ItemMUL)
- case r == '/':
- l.emit(ItemDIV)
- case r == '%':
- l.emit(ItemMOD)
- case r == '+':
- l.emit(ItemADD)
- case r == '-':
- l.emit(ItemSUB)
- case r == '^':
- l.emit(ItemPOW)
- case r == '=':
- if t := l.peek(); t == '=' {
- l.next()
- l.emit(ItemEQL)
- } else if t == '~' {
- return l.errorf("unexpected character after '=': %q", t)
- } else {
- l.emit(ItemAssign)
- }
- case r == '!':
- if t := l.next(); t == '=' {
- l.emit(ItemNEQ)
- } else {
- return l.errorf("unexpected character after '!': %q", t)
- }
- case r == '<':
- if t := l.peek(); t == '=' {
- l.next()
- l.emit(ItemLTE)
- } else {
- l.emit(ItemLSS)
- }
- case r == '>':
- if t := l.peek(); t == '=' {
- l.next()
- l.emit(ItemGTE)
- } else {
- l.emit(ItemGTR)
- }
- case isDigit(r) || (r == '.' && isDigit(l.peek())):
- l.backup()
- return lexNumberOrDuration
- case r == '"' || r == '\'':
- l.stringOpen = r
- return lexString
- case r == '`':
- l.stringOpen = r
- return lexRawString
- case isAlpha(r) || r == ':':
- if !l.bracketOpen {
- l.backup()
- return lexKeywordOrIdentifier
- }
- if l.gotColon {
- return l.errorf("unexpected colon %q", r)
- }
- l.emit(ItemColon)
- l.gotColon = true
- case r == '(':
- l.emit(ItemLeftParen)
- l.parenDepth++
- return lexStatements
- case r == ')':
- l.emit(ItemRightParen)
- l.parenDepth--
- if l.parenDepth < 0 {
- return l.errorf("unexpected right parenthesis %q", r)
- }
- return lexStatements
- case r == '{':
- l.emit(ItemLeftBrace)
- l.braceOpen = true
- return lexInsideBraces(l)
- case r == '[':
- if l.bracketOpen {
- return l.errorf("unexpected left bracket %q", r)
- }
- l.gotColon = false
- l.emit(ItemLeftBracket)
- l.bracketOpen = true
- return lexDuration
- case r == ']':
- if !l.bracketOpen {
- return l.errorf("unexpected right bracket %q", r)
- }
- l.emit(ItemRightBracket)
- l.bracketOpen = false
- default:
- return l.errorf("unexpected character: %q", r)
- }
- return lexStatements
- }
- // lexInsideBraces scans the inside of a vector selector. Keywords are ignored and
- // scanned as identifiers.
- func lexInsideBraces(l *lexer) stateFn {
- if strings.HasPrefix(l.input[l.pos:], lineComment) {
- return lexLineComment
- }
- switch r := l.next(); {
- case r == eof:
- return l.errorf("unexpected end of input inside braces")
- case isSpace(r):
- return lexSpace
- case isAlpha(r):
- l.backup()
- return lexIdentifier
- case r == ',':
- l.emit(ItemComma)
- case r == '"' || r == '\'':
- l.stringOpen = r
- return lexString
- case r == '`':
- l.stringOpen = r
- return lexRawString
- case r == '=':
- if l.next() == '~' {
- l.emit(ItemEQLRegex)
- break
- }
- l.backup()
- l.emit(ItemEQL)
- case r == '!':
- switch nr := l.next(); {
- case nr == '~':
- l.emit(ItemNEQRegex)
- case nr == '=':
- l.emit(ItemNEQ)
- default:
- return l.errorf("unexpected character after '!' inside braces: %q", nr)
- }
- case r == '{':
- return l.errorf("unexpected left brace %q", r)
- case r == '}':
- l.emit(ItemRightBrace)
- l.braceOpen = false
- if l.seriesDesc {
- return lexValueSequence
- }
- return lexStatements
- default:
- return l.errorf("unexpected character inside braces: %q", r)
- }
- return lexInsideBraces
- }
- // lexValueSequence scans a value sequence of a series description.
- func lexValueSequence(l *lexer) stateFn {
- switch r := l.next(); {
- case r == eof:
- return lexStatements
- case isSpace(r):
- l.emit(ItemSpace)
- lexSpace(l)
- case r == '+':
- l.emit(ItemADD)
- case r == '-':
- l.emit(ItemSUB)
- case r == 'x':
- l.emit(ItemTimes)
- case r == '_':
- l.emit(ItemBlank)
- case isDigit(r) || (r == '.' && isDigit(l.peek())):
- l.backup()
- lexNumber(l)
- case isAlpha(r):
- l.backup()
- // We might lex invalid items here but this will be caught by the parser.
- return lexKeywordOrIdentifier
- default:
- return l.errorf("unexpected character in series sequence: %q", r)
- }
- return lexValueSequence
- }
- // lexEscape scans a string escape sequence. The initial escaping character (\)
- // has already been seen.
- //
- // NOTE: This function as well as the helper function digitVal() and associated
- // tests have been adapted from the corresponding functions in the "go/scanner"
- // package of the Go standard library to work for Prometheus-style strings.
- // None of the actual escaping/quoting logic was changed in this function - it
- // was only modified to integrate with our lexer.
- func lexEscape(l *lexer) {
- var n int
- var base, max uint32
- ch := l.next()
- switch ch {
- case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', l.stringOpen:
- return
- case '0', '1', '2', '3', '4', '5', '6', '7':
- n, base, max = 3, 8, 255
- case 'x':
- ch = l.next()
- n, base, max = 2, 16, 255
- case 'u':
- ch = l.next()
- n, base, max = 4, 16, unicode.MaxRune
- case 'U':
- ch = l.next()
- n, base, max = 8, 16, unicode.MaxRune
- case eof:
- l.errorf("escape sequence not terminated")
- default:
- l.errorf("unknown escape sequence %#U", ch)
- }
- var x uint32
- for n > 0 {
- d := uint32(digitVal(ch))
- if d >= base {
- if ch == eof {
- l.errorf("escape sequence not terminated")
- }
- l.errorf("illegal character %#U in escape sequence", ch)
- }
- x = x*base + d
- ch = l.next()
- n--
- }
- if x > max || 0xD800 <= x && x < 0xE000 {
- l.errorf("escape sequence is an invalid Unicode code point")
- }
- }
- // digitVal returns the digit value of a rune or 16 in case the rune does not
- // represent a valid digit.
- func digitVal(ch rune) int {
- switch {
- case '0' <= ch && ch <= '9':
- return int(ch - '0')
- case 'a' <= ch && ch <= 'f':
- return int(ch - 'a' + 10)
- case 'A' <= ch && ch <= 'F':
- return int(ch - 'A' + 10)
- }
- return 16 // Larger than any legal digit val.
- }
- // lexString scans a quoted string. The initial quote has already been seen.
- func lexString(l *lexer) stateFn {
- Loop:
- for {
- switch l.next() {
- case '\\':
- lexEscape(l)
- case utf8.RuneError:
- return l.errorf("invalid UTF-8 rune")
- case eof, '\n':
- return l.errorf("unterminated quoted string")
- case l.stringOpen:
- break Loop
- }
- }
- l.emit(ItemString)
- return lexStatements
- }
- // lexRawString scans a raw quoted string. The initial quote has already been seen.
- func lexRawString(l *lexer) stateFn {
- Loop:
- for {
- switch l.next() {
- case utf8.RuneError:
- return l.errorf("invalid UTF-8 rune")
- case eof:
- return l.errorf("unterminated raw string")
- case l.stringOpen:
- break Loop
- }
- }
- l.emit(ItemString)
- return lexStatements
- }
- // lexSpace scans a run of space characters. One space has already been seen.
- func lexSpace(l *lexer) stateFn {
- for isSpace(l.peek()) {
- l.next()
- }
- l.ignore()
- return lexStatements
- }
- // lexLineComment scans a line comment. Left comment marker is known to be present.
- func lexLineComment(l *lexer) stateFn {
- l.pos += Pos(len(lineComment))
- for r := l.next(); !isEndOfLine(r) && r != eof; {
- r = l.next()
- }
- l.backup()
- l.emit(ItemComment)
- return lexStatements
- }
- func lexDuration(l *lexer) stateFn {
- if l.scanNumber() {
- return l.errorf("missing unit character in duration")
- }
- // Next two chars must be a valid unit and a non-alphanumeric.
- if l.accept("smhdwy") {
- if isAlphaNumeric(l.next()) {
- return l.errorf("bad duration syntax: %q", l.input[l.start:l.pos])
- }
- l.backup()
- l.emit(ItemDuration)
- return lexStatements
- }
- return l.errorf("bad duration syntax: %q", l.input[l.start:l.pos])
- }
- // lexNumber scans a number: decimal, hex, oct or float.
- func lexNumber(l *lexer) stateFn {
- if !l.scanNumber() {
- return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
- }
- l.emit(ItemNumber)
- return lexStatements
- }
- // lexNumberOrDuration scans a number or a duration item.
- func lexNumberOrDuration(l *lexer) stateFn {
- if l.scanNumber() {
- l.emit(ItemNumber)
- return lexStatements
- }
- // Next two chars must be a valid unit and a non-alphanumeric.
- if l.accept("smhdwy") {
- if isAlphaNumeric(l.next()) {
- return l.errorf("bad number or duration syntax: %q", l.input[l.start:l.pos])
- }
- l.backup()
- l.emit(ItemDuration)
- return lexStatements
- }
- return l.errorf("bad number or duration syntax: %q", l.input[l.start:l.pos])
- }
- // scanNumber scans numbers of different formats. The scanned item is
- // not necessarily a valid number. This case is caught by the parser.
- func (l *lexer) scanNumber() bool {
- digits := "0123456789"
- // Disallow hexadecimal in series descriptions as the syntax is ambiguous.
- if !l.seriesDesc && l.accept("0") && l.accept("xX") {
- digits = "0123456789abcdefABCDEF"
- }
- l.acceptRun(digits)
- if l.accept(".") {
- l.acceptRun(digits)
- }
- if l.accept("eE") {
- l.accept("+-")
- l.acceptRun("0123456789")
- }
- // Next thing must not be alphanumeric unless it's the times token
- // for series repetitions.
- if r := l.peek(); (l.seriesDesc && r == 'x') || !isAlphaNumeric(r) {
- return true
- }
- return false
- }
- // lexIdentifier scans an alphanumeric identifier. The next character
- // is known to be a letter.
- func lexIdentifier(l *lexer) stateFn {
- for isAlphaNumeric(l.next()) {
- // absorb
- }
- l.backup()
- l.emit(ItemIdentifier)
- return lexStatements
- }
- // lexKeywordOrIdentifier scans an alphanumeric identifier which may contain
- // a colon rune. If the identifier is a keyword the respective keyword item
- // is scanned.
- func lexKeywordOrIdentifier(l *lexer) stateFn {
- Loop:
- for {
- switch r := l.next(); {
- case isAlphaNumeric(r) || r == ':':
- // absorb.
- default:
- l.backup()
- word := l.input[l.start:l.pos]
- if kw, ok := key[strings.ToLower(word)]; ok {
- l.emit(kw)
- } else if !strings.Contains(word, ":") {
- l.emit(ItemIdentifier)
- } else {
- l.emit(ItemMetricIdentifier)
- }
- break Loop
- }
- }
- if l.seriesDesc && l.peek() != '{' {
- return lexValueSequence
- }
- return lexStatements
- }
- func isSpace(r rune) bool {
- return r == ' ' || r == '\t' || r == '\n' || r == '\r'
- }
- // isEndOfLine reports whether r is an end-of-line character.
- func isEndOfLine(r rune) bool {
- return r == '\r' || r == '\n'
- }
- // isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
- func isAlphaNumeric(r rune) bool {
- return isAlpha(r) || isDigit(r)
- }
- // isDigit reports whether r is a digit. Note: we cannot use unicode.IsDigit()
- // instead because that also classifies non-Latin digits as digits. See
- // https://github.com/prometheus/prometheus/issues/939.
- func isDigit(r rune) bool {
- return '0' <= r && r <= '9'
- }
- // isAlpha reports whether r is an alphabetic or underscore.
- func isAlpha(r rune) bool {
- return r == '_' || ('a' <= r && r <= 'z') || ('A' <= r && r <= 'Z')
- }
- // isLabel reports whether the string can be used as label.
- func isLabel(s string) bool {
- if len(s) == 0 || !isAlpha(rune(s[0])) {
- return false
- }
- for _, c := range s[1:] {
- if !isAlphaNumeric(c) {
- return false
- }
- }
- return true
- }
|