219 lines
4.4 KiB
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
|
|
}
|