package parser import ( "bytes" "io" "strings" "asciigoat.org/core/lexer" ) // TextParser is a generic text parser. type TextParser struct { *lexer.Reader pos lexer.Position } // Init initializes the [TextParser] with a non-nil [io.Reader]. func (p *TextParser) Init(r io.Reader) { switch { case p == nil || r == nil: panic("invalid call") case p.Reader != nil: panic("parser already initialized") default: p.Reader = lexer.NewReader(r) p.pos.Reset() } } // InitBytes initializes the [TextParser] with a byte array func (p *TextParser) InitBytes(b []byte) { p.Init(bytes.NewReader(b)) } // InitString initializes the [TextParser] with a byte array func (p *TextParser) InitString(s string) { p.Init(strings.NewReader(s)) } // Discard shadows [lexer.Reader]'s, and takes in consideration // new lines on the discarded data when moving the position func (p *TextParser) Discard() { s := p.Reader.Emit() l := GetPositionalLength(s) p.pos.Add(l) } // Emit returns the accepted text, its position, and // moves the cursor position accordingly func (p *TextParser) Emit() (lexer.Position, string) { pos := p.pos s := p.Reader.Emit() l := GetPositionalLength(s) p.pos.Add(l) return pos, s } // Step discards what's been accepted and increments the // position assuming they all increment the column counter func (p *TextParser) Step() { s := p.Reader.Emit() p.pos.StepN(len(s)) } // StepLine discards what's been accepted and moves then // position to the beginning of the next line func (p *TextParser) StepLine() { p.Reader.Discard() p.pos.StepLine() } // Position returns the position of the first character // of the accepted text func (p *TextParser) Position() lexer.Position { return p.pos } // AcceptNewLine checks if next is a new line. // It accepts "\n", "\n\r", "\r" and "\r\n". func (p *TextParser) AcceptNewLine() bool { r1, _, err := p.ReadRune() switch { case err != nil: return false case r1 == '\n': p.AcceptRune('\r') return true case r1 == '\r': p.AcceptRune('\n') return true default: p.UnreadRune() return false } } // AcceptRune checks if next is the specified rune func (p *TextParser) AcceptRune(r rune) bool { return p.Accept(func(r2 rune) bool { return r == r2 }) }