291 lines
6.7 KiB
Go
291 lines
6.7 KiB
Go
package parsekit
|
|
|
|
import (
|
|
"unicode"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// Not in need of it myself, but nice to have I guess:
|
|
// - NotFollowedBy
|
|
// - Separated
|
|
|
|
// MatchDialog is used by Matcher implementations as a means
|
|
// to retrieve data to match against and to report back
|
|
// successful matches.
|
|
type MatchDialog struct {
|
|
p *P
|
|
runes []rune
|
|
widths []int
|
|
offset int
|
|
curRune rune
|
|
curWidth int
|
|
parent *MatchDialog
|
|
}
|
|
|
|
// Fork splits off a child MatchDialog, containing the same
|
|
// offset as the parent MatchDialog, but with all other data
|
|
// in a new state.
|
|
// By forking, a Matcher implementation can freely work with
|
|
// a MatchDialog, without affecting the parent MatchDialog.
|
|
// When the Matcher decides that a match was found, it can
|
|
// use the Merge() method on the child to merge the child's
|
|
// matching data into the parent MatchDialog.
|
|
func (m *MatchDialog) Fork() *MatchDialog {
|
|
child := &MatchDialog{
|
|
p: m.p,
|
|
offset: m.offset,
|
|
parent: m,
|
|
}
|
|
return child
|
|
}
|
|
|
|
// Merge merges the data for a a forked child MatchDialog back
|
|
// into its parent:
|
|
// * the runes that are accumulated in the child are added
|
|
// to the parent's runes
|
|
// * the parent's offset is set to the child's offset
|
|
// After a Merge, the child MatchDialog is reset so it can
|
|
// immediately be reused for performing another match.
|
|
func (m *MatchDialog) Merge() bool {
|
|
if m.parent == nil {
|
|
panic("Cannot call Merge a a non-forked MatchDialog")
|
|
}
|
|
m.parent.runes = append(m.parent.runes, m.runes...)
|
|
m.parent.widths = append(m.parent.widths, m.widths...)
|
|
m.parent.offset = m.offset
|
|
m.Clear()
|
|
return true
|
|
}
|
|
|
|
// NextRune can be called by a Matcher on a MatchDialog in order
|
|
// to receive the next rune from the input.
|
|
// The rune is automatically added to the MatchDialog's runes.
|
|
// Returns the rune and a boolean. The boolean will be false in
|
|
// case an invalid UTF8 rune of the end of the file was encountered.
|
|
func (m *MatchDialog) NextRune() (rune, bool) {
|
|
if m.curRune == utf8.RuneError {
|
|
panic("Matcher must not call NextRune() after it returned false")
|
|
}
|
|
r, w, ok := m.p.peek(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, ok
|
|
}
|
|
|
|
// Clear empties out the accumulated runes that are stored
|
|
// in the MatchDialog.
|
|
func (m *MatchDialog) Clear() {
|
|
m.runes = []rune{}
|
|
m.widths = []int{}
|
|
}
|
|
|
|
// Matcher is the interface that must 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) MatchRune
|
|
RuneRange func(rune, rune) MatchRuneRange
|
|
Runes func(...rune) MatchAnyOf
|
|
String func(string) MatchSequence
|
|
StringNoCase func(string) MatchSequence
|
|
AnyOf func(...Matcher) MatchAnyOf
|
|
Repeat func(int, Matcher) MatchRepeat
|
|
Sequence func(...Matcher) MatchSequence
|
|
ZeroOrMore func(Matcher) MatchZeroOrMore
|
|
OneOrMore func(Matcher) MatchOneOrMore
|
|
Optional func(Matcher) MatchOptional
|
|
Drop func(Matcher) MatchDrop
|
|
}
|
|
|
|
// C provides access to a wide range of parser/combinator
|
|
// constructors that can be used to build matching expressions.
|
|
// When using C in your own parser, then it is advised to create
|
|
// an alias in your own package for easy reference:
|
|
// var c = parsekit.C
|
|
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}
|
|
},
|
|
String: func(s string) MatchSequence {
|
|
m := make([]Matcher, len(s))
|
|
for i, r := range s {
|
|
m[i] = MatchRune{r}
|
|
}
|
|
return MatchSequence{m}
|
|
},
|
|
StringNoCase: func(s string) MatchSequence {
|
|
m := make([]Matcher, len(s))
|
|
for i, r := range s {
|
|
u := MatchRune{unicode.ToUpper(r)}
|
|
l := MatchRune{unicode.ToLower(r)}
|
|
m[i] = MatchAnyOf{[]Matcher{u, l}}
|
|
}
|
|
return MatchSequence{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}
|
|
},
|
|
Drop: func(matcher Matcher) MatchDrop {
|
|
return MatchDrop{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 {
|
|
child := m.Fork()
|
|
if matcher.Match(child) {
|
|
return child.Merge()
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
type MatchRepeat struct {
|
|
count int
|
|
matcher Matcher
|
|
}
|
|
|
|
func (c MatchRepeat) Match(m *MatchDialog) bool {
|
|
child := m.Fork()
|
|
for i := 0; i < c.count; i++ {
|
|
if !c.matcher.Match(child) {
|
|
return false
|
|
}
|
|
}
|
|
child.Merge()
|
|
return true
|
|
}
|
|
|
|
type MatchSequence struct {
|
|
matchers []Matcher
|
|
}
|
|
|
|
func (c MatchSequence) Match(m *MatchDialog) bool {
|
|
child := m.Fork()
|
|
for _, matcher := range c.matchers {
|
|
if !matcher.Match(child) {
|
|
return false
|
|
}
|
|
}
|
|
child.Merge()
|
|
return true
|
|
}
|
|
|
|
type MatchOneOrMore struct {
|
|
matcher Matcher
|
|
}
|
|
|
|
func (c MatchOneOrMore) Match(m *MatchDialog) bool {
|
|
child := m.Fork()
|
|
for c.matcher.Match(child) {
|
|
child.Merge()
|
|
}
|
|
return len(m.runes) > 0
|
|
}
|
|
|
|
type MatchZeroOrMore struct {
|
|
matcher Matcher
|
|
}
|
|
|
|
func (c MatchZeroOrMore) Match(m *MatchDialog) bool {
|
|
child := m.Fork()
|
|
for c.matcher.Match(child) {
|
|
child.Merge()
|
|
}
|
|
return true
|
|
}
|
|
|
|
type MatchOptional struct {
|
|
matcher Matcher
|
|
}
|
|
|
|
func (c MatchOptional) Match(m *MatchDialog) bool {
|
|
child := m.Fork()
|
|
if c.matcher.Match(child) {
|
|
child.Merge()
|
|
}
|
|
return true
|
|
}
|
|
|
|
type MatchDrop struct {
|
|
matcher Matcher
|
|
}
|
|
|
|
func (c MatchDrop) Match(m *MatchDialog) bool {
|
|
child := m.Fork()
|
|
if c.matcher.Match(child) {
|
|
child.Clear()
|
|
child.Merge()
|
|
return true
|
|
}
|
|
return false
|
|
}
|