diff --git a/lexer/lexer.go b/lexer/lexer.go index 33b17b6..991372c 100644 --- a/lexer/lexer.go +++ b/lexer/lexer.go @@ -1,2 +1,31 @@ // Package lexer provides basic helpers to implement parsers package lexer + +import ( + "errors" + "io" +) + +// StateFn is a State Function of the parser +type StateFn func() (StateFn, error) + +// Run runs a state machine until the state function either +// returns nil or an error +func Run(fn StateFn) error { + for fn != nil { + var err error + + fn, err = fn() + switch { + case errors.Is(err, io.EOF): + // EOF + return nil + case err != nil: + // failed + return err + } + } + + // ended + return nil +} diff --git a/lexer/reader.go b/lexer/reader.go index 7c0aaa3..6ab8c6b 100644 --- a/lexer/reader.go +++ b/lexer/reader.go @@ -198,6 +198,41 @@ func (b *Reader) PeekRune() (rune, int, error) { return r, l, err } +// Accept consumes a rune from the source if it meets the condition. +// it returns true if the condition was met and false if it wasn't. +func (b *Reader) Accept(cond func(r rune) bool) bool { + r, _, err := b.ReadRune() + switch { + case err != nil: + return false + case cond(r): + return true + default: + _ = b.UnreadRune() + return false + } +} + +// AcceptAll consumes runes from the source as long as they meet the +// condition. it returns true if the condition was met for at least one rune, +// and false if it wasn't. +func (b *Reader) AcceptAll(cond func(r rune) bool) bool { + var accepted bool + + for { + r, _, err := b.ReadRune() + switch { + case err != nil: + return accepted + case cond(r): + accepted = true + default: + _ = b.UnreadRune() + return accepted + } + } +} + // NewReader creates a new runes [Reader] using the given [io.Reader] func NewReader(r io.Reader) *Reader { if r == nil {