230 lines
3.4 KiB
Go
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)
|
|
}
|