// Package lexer provides basic helpers to implement parsers package lexer import ( "io" "asciigoat.org/core/runes" ) // Lexer adds Accept and AcceptAll support to a // [io.RuneScanner] type Lexer struct { io.RuneScanner } // 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 (p Lexer) Accept(cond func(rune) bool) bool { return Accept(p, cond) } // 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 (p Lexer) AcceptAll(cond func(rune) bool) bool { return AcceptAll(p, cond) } // NewLexer extends a [io.RuneScanner] with Accept()/AcceptAll() // functionality func NewLexer(src io.RuneScanner) *Lexer { if src == nil { return nil } return &Lexer{src} } // NewLexerReader creates a Lexer from an [io.Reader] func NewLexerReader(r io.Reader) *Lexer { if r == nil { return nil } return &Lexer{runes.NewReader(r)} } // NewLexerBytes creates a Lexer from raw bytes func NewLexerBytes(b []byte) *Lexer { return &Lexer{runes.NewReaderBytes(b)} } // NewLexerString creates a Lexer from a data string func NewLexerString(s string) *Lexer { return &Lexer{runes.NewReaderString(s)} } // 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 { var err error for fn != nil && err == nil { fn, err = fn() } return 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 Accept(src io.RuneScanner, cond func(r rune) bool) bool { r, _, err := src.ReadRune() switch { case err != nil: return false case cond(r): return true default: _ = src.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 AcceptAll(src io.RuneScanner, cond func(r rune) bool) bool { var accepted bool for { r, _, err := src.ReadRune() switch { case err != nil: return accepted case cond(r): accepted = true default: _ = src.UnreadRune() return accepted } } }