crushkv/crushmap/text_lexer.go
Vasiliy Tolstov 24f641df0d initial rewrite
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2019-04-18 23:13:04 +03:00

230 lines
3.4 KiB
Go

package crushmap
import (
"errors"
"strings"
"unicode/utf8"
)
type stateFn func(*lex) stateFn
type tokType int
const (
EOFRune rune = -1
)
const (
itemError tokType = iota
itemEOF
itemComment
itemTunableBeg
itemTunableKey
itemTunableVal
itemTunableEnd
itemDeviceBeg
itemDeviceID
itemDeviceName
itemDeviceClass
itemDeviceEnd
itemTypeBeg
itemTypeID
itemTypeName
itemTypeEnd
itemBucketBeg
itemBucketName
itemBucketID
itemBucketIDClass
itemBucketAlg
itemBucketHash
itemBucketItemBeg
itemBucketItemName
itemBucketItemWeight
itemBucketItemPos
itemBucketItemEnd
itemBucketEnd
itemRuleBeg
itemRuleName
itemRuleID
itemRuleRuleset
itemRuleType
itemRuleMinSize
itemRuleMaxSize
itemRuleStepBeg
itemRuleStepSetChooseleafTries
itemRuleStepSetChooseTries
itemRuleStepTake
itemRuleStepTakeType
itemRuleStepChoose
itemRuleStepTakeClass
itemRuleStepChooseFirstN
itemRuleStepChooseIndep
itemRuleStepChooseType
itemRuleStepEmit
itemRuleStepEnd
itemRuleEnd
)
type item struct {
itype tokType
ivalue string
iline int
}
type lex struct {
source string
start int
position int
line int
startState stateFn
err error
items chan item
errHandler func(string)
rewind runeStack
stack []stateFn
}
func lexNew(src string, start stateFn) *lex {
buffSize := len(src) / 2
if buffSize <= 0 {
buffSize = 1
}
return &lex{
source: src,
startState: start,
line: 1,
rewind: newRuneStack(),
items: make(chan item, buffSize),
stack: make([]stateFn, 0, 10),
}
}
func (l *lex) lexStart() {
go l.lexRun()
}
func (l *lex) lexStartSync() {
l.lexRun()
}
func lexIsDigit(r rune) bool {
return r >= '0' && r <= '9'
}
func (l *lex) lexCurrent() string {
return l.source[l.start:l.position]
}
func (l *lex) lexEmit(t tokType) {
itm := item{
itype: t,
ivalue: l.lexCurrent(),
}
l.items <- itm
l.start = l.position
l.rewind.clear()
}
func (l *lex) lexEmitTrim(t tokType) {
itm := item{
itype: t,
ivalue: strings.TrimSpace(l.lexCurrent()),
}
l.items <- itm
l.start = l.position
l.rewind.clear()
}
func (l *lex) lexIgnore() {
l.rewind.clear()
l.start = l.position
}
func (l *lex) lexPeek() rune {
r := l.lexNext()
l.lexRewind()
return r
}
func (l *lex) lexRewind() {
r := l.rewind.pop()
if r > EOFRune {
size := utf8.RuneLen(r)
l.position -= size
if l.position < l.start {
l.position = l.start
}
}
}
func (l *lex) lexNext() rune {
var (
r rune
s int
)
str := l.source[l.position:]
if len(str) == 0 {
r, s = EOFRune, 0
} else {
r, s = utf8.DecodeRuneInString(str)
}
l.position += s
l.rewind.push(r)
return r
}
func (l *lex) lexPush(state stateFn) {
l.stack = append(l.stack, state)
}
func (l *lex) lexPop() stateFn {
if len(l.stack) == 0 {
l.lexErr("BUG in lexer: no states to pop")
}
last := l.stack[len(l.stack)-1]
l.stack = l.stack[0 : len(l.stack)-1]
return last
}
func (l *lex) lexTake(chars string) {
r := l.lexNext()
for strings.ContainsRune(chars, r) {
r = l.lexNext()
}
l.lexRewind() // last next wasn't a match
}
func (l *lex) lexNextToken() (*item, bool) {
if itm, ok := <-l.items; ok {
return &itm, false
} else {
return nil, true
}
}
func (l *lex) lexErr(e string) {
if l.errHandler != nil {
l.err = errors.New(e)
l.errHandler(e)
} else {
panic(e)
}
}
func (l *lex) lexRun() {
state := l.startState
for state != nil {
state = state(l)
}
close(l.items)
}