asciigoat's INI parser
https://asciigoat.org/ini
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
103 lines
2.2 KiB
103 lines
2.2 KiB
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 |
|
}) |
|
}
|
|
|