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 }