go-toml/parsekit/matchers.go

219 lines
4.4 KiB
Go

package parsekit
import "unicode/utf8"
// Not in need of it myself, but nice to have I guess:
// - NotFollowedBy
// - Discard
// - Separated
type MatchDialog struct {
p *P
runes []rune
widths []int
offset int
curRune rune
curWidth int
forked bool
}
func (m *MatchDialog) Fork() *MatchDialog {
fork := &MatchDialog{
p: m.p,
offset: m.offset,
forked: true,
}
return fork
}
func (m *MatchDialog) Join(fork *MatchDialog) bool {
if !fork.forked {
panic("Cannot join a non-forked MatchDialog")
}
m.runes = append(m.runes, fork.runes...)
m.widths = append(m.widths, fork.widths...)
m.offset = fork.offset
fork.runes = []rune{}
fork.widths = []int{}
return true
}
func (m *MatchDialog) NextRune() (rune, bool) {
if m.curRune == utf8.RuneError {
panic("Matcher must not call NextRune() after it returned false")
}
r, w := utf8.DecodeRuneInString(m.p.input[m.p.pos+m.offset:])
m.offset += w
m.curRune = r
m.curWidth = w
m.runes = append(m.runes, r)
m.widths = append(m.widths, w)
return r, r != EOF && r != INVALID
}
// Matcher is the interface that can be implemented to provide
// a matching stategy for the match() function.
// A MatchDialog is provided as input. This implements a
// specific set of methods that a Matcher needs to retrieve data
// from the parser and to report back results.
type Matcher interface {
Match(*MatchDialog) bool
}
type MatcherConstructors struct {
Any func() MatchAny
Rune func(rune rune) MatchRune
RuneRange func(start rune, end rune) MatchRuneRange
Runes func(runes ...rune) MatchAnyOf
AnyOf func(matchers ...Matcher) MatchAnyOf
Repeat func(count int, matcher Matcher) MatchRepeat
Sequence func(matchers ...Matcher) MatchSequence
ZeroOrMore func(matcher Matcher) MatchZeroOrMore
OneOrMore func(matcher Matcher) MatchOneOrMore
Optional func(matcher Matcher) MatchOptional
}
var C = MatcherConstructors{
Any: func() MatchAny {
return MatchAny{}
},
Rune: func(rune rune) MatchRune {
return MatchRune{rune}
},
RuneRange: func(start rune, end rune) MatchRuneRange {
return MatchRuneRange{start, end}
},
Runes: func(runes ...rune) MatchAnyOf {
m := make([]Matcher, len(runes))
for i, r := range runes {
m[i] = MatchRune{r}
}
return MatchAnyOf{m}
},
AnyOf: func(matchers ...Matcher) MatchAnyOf {
return MatchAnyOf{matchers}
},
Repeat: func(count int, matcher Matcher) MatchRepeat {
return MatchRepeat{count, matcher}
},
Sequence: func(matchers ...Matcher) MatchSequence {
return MatchSequence{matchers}
},
OneOrMore: func(matcher Matcher) MatchOneOrMore {
return MatchOneOrMore{matcher}
},
ZeroOrMore: func(matcher Matcher) MatchZeroOrMore {
return MatchZeroOrMore{matcher}
},
Optional: func(matcher Matcher) MatchOptional {
return MatchOptional{matcher}
},
}
type MatchAny struct{}
func (c MatchAny) Match(m *MatchDialog) bool {
_, ok := m.NextRune()
return ok
}
type MatchRune struct {
match rune
}
func (c MatchRune) Match(m *MatchDialog) bool {
r, ok := m.NextRune()
return ok && r == c.match
}
type MatchRuneRange struct {
start rune
end rune
}
func (c MatchRuneRange) Match(m *MatchDialog) bool {
r, ok := m.NextRune()
return ok && r >= c.start && r <= c.end
}
type MatchAnyOf struct {
matcher []Matcher
}
func (c MatchAnyOf) Match(m *MatchDialog) bool {
for _, matcher := range c.matcher {
mc := m.Fork()
if matcher.Match(mc) {
return m.Join(mc)
}
}
return false
}
type MatchRepeat struct {
count int
matcher Matcher
}
func (c MatchRepeat) Match(m *MatchDialog) bool {
mc := m.Fork()
for i := 0; i < c.count; i++ {
if !c.matcher.Match(mc) {
return false
}
}
m.Join(mc)
return true
}
type MatchSequence struct {
matchers []Matcher
}
func (c MatchSequence) Match(m *MatchDialog) bool {
mPart := m.Fork()
for _, matcher := range c.matchers {
if !matcher.Match(mPart) {
return false
}
}
m.Join(mPart)
return true
}
type MatchOneOrMore struct {
matcher Matcher
}
func (c MatchOneOrMore) Match(m *MatchDialog) bool {
mc := m.Fork()
for c.matcher.Match(mc) {
m.Join(mc)
}
return len(m.runes) > 0
}
type MatchZeroOrMore struct {
matcher Matcher
}
func (c MatchZeroOrMore) Match(m *MatchDialog) bool {
mc := m.Fork()
for c.matcher.Match(mc) {
m.Join(mc)
}
return true
}
type MatchOptional struct {
matcher Matcher
}
func (c MatchOptional) Match(m *MatchDialog) bool {
mc := m.Fork()
if c.matcher.Match(mc) {
m.Join(mc)
}
return true
}