func lexNumeric(l *Lexer) stateFn {
	r := l.next()
	for isDigit(r) {
		l.accept()
		r = l.next()
	}
	if r != '.' && r != 'E' && r != 'e' && !isDurationSuffix(r) {
		l.backup()
		l.emit(Kind(INTLITERAL))
		return lexProg
	}
	if r == '.' {
		l.accept()
		r = l.next()
		for isDigit(r) {
			l.accept()
			r = l.next()
		}
	}
	if r == 'e' || r == 'E' {
		l.accept()
		r = l.next()
		if r == '+' || r == '-' {
			l.accept()
			r = l.next()
		}
		for isDigit(r) {
			l.accept()
			r = l.next()
		}
	}
	if isDurationSuffix(r) {
		l.accept()
		return lexDuration
	}
	l.backup()
	l.emit(Kind(FLOATLITERAL))
	return lexProg
}
Lex a numerical constant.
package parser
import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"sort"
	"strings"
	"unicode"
	"github.com/golang/glog"
	"github.com/google/mtail/internal/runtime/compiler/position"
)
var keywords = map[string]Kind{
	"after":     AFTER,
	"as":        AS,
	"buckets":   BUCKETS,
	"by":        BY,
	"const":     CONST,
	"counter":   COUNTER,
	"def":       DEF,
	"del":       DEL,
	"else":      ELSE,
	"gauge":     GAUGE,
	"hidden":    HIDDEN,
	"histogram": HISTOGRAM,
	"next":      NEXT,
	"otherwise": OTHERWISE,
	"stop":      STOP,
	"text":      TEXT,
	"timer":     TIMER,
}
var builtins = []string{
	"bool",
	"float",
	"getfilename",
	"int",
	"len",
	"settime",
	"string",
	"strptime",
	"strtol",
	"subst",
	"timestamp",
	"tolower",
}
func Dictionary() (r []string) {
	for k := range keywords {
		r = append(r, k)
	}
	r = append(r, builtins...)
	return
}
type stateFn func(*Lexer) stateFn
type Lexer struct {
	name  string         
	input *bufio.Reader  
	state stateFn        
	rune  rune  
	width int   
	line  int   
	col   int   
	InRegex bool  
	startcol int              
	text     strings.Builder  
	tokens chan Token  
}
func NewLexer(name string, input io.Reader) *Lexer {
	l := &Lexer{
		name:   name,
		input:  bufio.NewReader(input),
		state:  lexProg,
		tokens: make(chan Token, 2),
	}
	return l
}
func (l *Lexer) NextToken() Token {
	for {
		select {
		case tok := <-l.tokens:
			return tok
		default:
			l.state = l.state(l)
		}
	}
}
func (l *Lexer) emit(kind Kind) {
	pos := position.Position{l.name, l.line, l.startcol, l.col - 1}
	glog.V(2).Infof("Emitting %v spelled %q at %v", kind, l.text.String(), pos)
	l.tokens <- Token{kind, l.text.String(), pos}
	l.text.Reset()
	l.startcol = l.col
}
const eof rune = -1
func (l *Lexer) next() rune {
	var err error
	l.rune, l.width, err = l.input.ReadRune()
	if errors.Is(err, io.EOF) {
		l.width = 1
		l.rune = eof
	}
	if l.rune == '␤' {
		l.rune = eof
	}
	return l.rune
}
func (l *Lexer) backup() {
	l.width = 0
	if l.rune == eof {
		return
	}
	if err := l.input.UnreadRune(); err != nil {
		glog.Info(err)
	}
}
func (l *Lexer) stepCursor() {
	if l.rune == '\n' {
		l.line++
		l.col = 0
	} else {
		l.col += l.width
	}
}
func (l *Lexer) accept() {
	l.text.WriteRune(l.rune)
	l.stepCursor()
}
func (l *Lexer) skip() {
	l.stepCursor()
}
func (l *Lexer) ignore() {
	l.stepCursor()
	l.startcol = l.col
}
func (l *Lexer) errorf(format string, args ...interface{}) stateFn {
	pos := position.Position{
		Filename: l.name,
		Line:     l.line,
		Startcol: l.startcol,
		Endcol:   l.col - 1,
	}
	l.tokens <- Token{
		Kind:     INVALID,
		Spelling: fmt.Sprintf(format, args...),
		Pos:      pos,
	}
	l.text.Reset()
	l.startcol = l.col
	return lexProg
}
func lexProg(l *Lexer) stateFn {
	if l.InRegex {
		return lexRegex
	}
	switch r := l.next(); {
	case r == '\n':
		l.accept()
		l.emit(NL)
	case r == '#':
		return lexComment
	case isSpace(r):
		l.ignore()
	case r == '{':
		l.accept()
		l.emit(LCURLY)
	case r == '}':
		l.accept()
		l.emit(RCURLY)
	case r == '(':
		l.accept()
		l.emit(LPAREN)
	case r == ')':
		l.accept()
		l.emit(RPAREN)
	case r == '[':
		l.accept()
		l.emit(LSQUARE)
	case r == ']':
		l.accept()
		l.emit(RSQUARE)
	case r == ',':
		l.accept()
		l.emit(COMMA)
	case r == '-':
		l.accept()
		switch r = l.next(); {
		case r == '-':
			l.accept()
			l.emit(DEC)
		case isDigit(r):
			l.backup()
			return lexNumeric
		default:
			l.backup()
			l.emit(MINUS)
		}
	case r == '+':
		l.accept()
		switch l.next() {
		case '+':
			l.accept()
			l.emit(INC)
		case '=':
			l.accept()
			l.emit(ADD_ASSIGN)
		default:
			l.backup()
			l.emit(PLUS)
		}
	case r == '*':
		l.accept()
		switch l.next() {
		case '*':
			l.accept()
			l.emit(POW)
		default:
			l.backup()
			l.emit(MUL)
		}
	case r == '=':
		l.accept()
		switch l.next() {
		case '=':
			l.accept()
			l.emit(EQ)
		case '~':
			l.accept()
			l.emit(MATCH)
		default:
			l.backup()
			l.emit(ASSIGN)
		}
	case r == '<':
		l.accept()
		switch l.next() {
		case '=':
			l.accept()
			l.emit(LE)
		case '<':
			l.accept()
			l.emit(SHL)
		default:
			l.backup()
			l.emit(LT)
		}
	case r == '>':
		l.accept()
		switch l.next() {
		case '=':
			l.accept()
			l.emit(GE)
		case '>':
			l.accept()
			l.emit(SHR)
		default:
			l.backup()
			l.emit(GT)
		}
	case r == '!':
		l.accept()
		switch l.next() {
		case '=':
			l.accept()
			l.emit(NE)
		case '~':
			l.accept()
			l.emit(NOT_MATCH)
		default:
			l.backup()
			return l.errorf("Unexpected input: %q", r)
		}
	case r == '/':
		l.accept()
		l.emit(DIV)
	case r == '%':
		l.accept()
		l.emit(MOD)
	case r == '&':
		l.accept()
		switch l.next() {
		case '&':
			l.accept()
			l.emit(AND)
		default:
			l.backup()
			l.emit(BITAND)
		}
	case r == '|':
		l.accept()
		switch l.next() {
		case '|':
			l.accept()
			l.emit(OR)
		default:
			l.backup()
			l.emit(BITOR)
		}
	case r == '^':
		l.accept()
		l.emit(XOR)
	case r == '~':
		l.accept()
		l.emit(NOT)
	case r == '"':
		return lexQuotedString
	case r == '$':
		return lexCapref
	case r == '@':
		return lexDecorator
	case isDigit(r):
		l.backup()
		return lexNumeric
	case isAlpha(r):
		return lexIdentifier
	case r == eof:
		l.skip()
		l.emit(EOF)
		return nil
	case r == '.':
		l.backup()
		return lexNumeric
	default:
		l.accept()
		return l.errorf("Unexpected input: %q", r)
	}
	return lexProg
}
func lexComment(l *Lexer) stateFn {
	l.ignore()
Loop:
	for {
		switch l.next() {
		case '\n':
			l.skip()
			fallthrough
		case eof:
			break Loop
		default:
			l.ignore()
		}
	}
	return lexProg
}