// Package parser parses ini-style files package parser import ( "io" "log" "asciigoat.org/core/lexer" ) // Parser parses a ini-style document type Parser struct { p TextParser // OnToken is called for each identified token. if it returns an error // parsing is interrupted. OnToken func(pos lexer.Position, typ TokenType, value string) error // OnError is called in case of a parsing error, and it's allowed // to replace the error returned by [Parser.Run]. // OnError is called for io.EOF, but [Parser.Run] will consider it // normal termination. OnError func(pos lexer.Position, content string, err error) error } func defaultOnToken(pos lexer.Position, typ TokenType, value string) error { log.Printf("%s:%v:%v: %q", typ, pos.Line, pos.Column, value) return nil } func defaultOnError(pos lexer.Position, content string, err error) error { log.Printf("%s:%v:%v: %q: %s", "error", pos.Line, pos.Column, content, err) return lexer.Error{ Line: pos.Line, Column: pos.Column, Content: content, Err: err, } } func (p *Parser) setDefaults() { if p.OnToken == nil { p.OnToken = defaultOnToken } if p.OnError == nil { p.OnError = defaultOnError } } func (p *Parser) emitString(typ TokenType) error { pos, s := p.p.Emit() return p.OnToken(pos, typ, s) } func (p *Parser) emitError(content string, err error) (lexer.StateFn, error) { pos := p.p.Position() err2 := p.OnError(pos, content, err) switch { case err2 != nil: // return wrapped error return nil, err2 default: // return original error return nil, err } } func (p *Parser) emitInvalidRune(r rune) (lexer.StateFn, error) { return p.emitError(string([]rune{r}), lexer.ErrUnacceptableRune) } // stepLine discards the data and moves the position // to the next line. func (p *Parser) stepLine() { p.p.StepLine() } // stepString discards the data and moves the position // forward on the same line the length of the discarded // content. func (p *Parser) stepString() { p.p.Step() } // NewParser creates a ini-style parser using // an [io.Reader] as source func NewParser(r io.Reader) *Parser { var p *Parser if r != nil { p = new(Parser) p.p.Init(r) } return p }