diff --git a/go.mod b/go.mod index 534d3cd..afbcf7f 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,10 @@ module asciigoat.org/ini go 1.19 -require github.com/mgechev/revive v1.3.3 +require ( + asciigoat.org/core v0.3.6 + github.com/mgechev/revive v1.3.3 +) require ( github.com/BurntSushi/toml v1.3.2 // indirect diff --git a/go.sum b/go.sum index 495d0a7..7d0556b 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +asciigoat.org/core v0.3.6 h1:b1vL090OxylmSOwLQryjrmC8FhhCtktMyeJSy1e6LwI= +asciigoat.org/core v0.3.6/go.mod h1:tXj+JUutxRbcO40ZQRuUVaZ4rnYz1kAZ0nblisV8u74= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/chavacava/garif v0.0.0-20230608123814-4bd63c2919ab h1:5JxePczlyGAtj6R1MUEFZ/UFud6FfsOejq7xLC2ZIb0= diff --git a/parser/lexer.go b/parser/lexer.go new file mode 100644 index 0000000..e62bfe9 --- /dev/null +++ b/parser/lexer.go @@ -0,0 +1,11 @@ +package parser + +import "asciigoat.org/core/lexer" + +// Run parses the source +func (p *Parser) Run() error { + p.setDefaults() + p.pos.Reset() + + return lexer.Run(nil) +} diff --git a/parser/parser.go b/parser/parser.go new file mode 100644 index 0000000..0edfb1a --- /dev/null +++ b/parser/parser.go @@ -0,0 +1,77 @@ +// Package parser parses dosini-style files +package parser + +import ( + "io" + + "asciigoat.org/core/lexer" +) + +// Parser parses a dosini-style document +type Parser struct { + src *lexer.Reader + + pos lexer.Position + + // OnSection is called after a [section] is parsed. + // Returning an error will abort the process. + OnSection func(pos lexer.Position, name, subname string, hasSubname bool) error + + // OnField is called after a `key = value` entry is parsed + // Returning an error will abort the process. + OnField func(pos lexer.Position, key, value string) error + + // OnComment is called after a comment is parsed + // Returning an error will abort the process. + OnComment func(pos lexer.Position, comment string) error + + // OnError is called after each parsing error, which you are allowed to + // override. + // OnError is called for EOF as well, but this error isn't returned as such by + // Parser.Run(). The caller will receive (nil, nil) instead indicating the + // processes terminated correctly. + OnError func(pos lexer.Position, content string, err error) error +} + +func defaultOnSection(_ lexer.Position, _, _ string, _ bool) error { return nil } +func defaultOnField(_ lexer.Position, _, _ string) error { return nil } +func defaultOnComment(_ lexer.Position, _ string) error { return nil } + +func defaultOnError(pos lexer.Position, content string, err error) error { + return &lexer.Error{ + Line: pos.Line, + Column: pos.Column, + Content: content, + Err: err, + } +} + +func (p *Parser) setDefaults() { + if p.OnSection == nil { + p.OnSection = defaultOnSection + } + + if p.OnField == nil { + p.OnField = defaultOnField + } + + if p.OnComment == nil { + p.OnComment = defaultOnComment + } + + if p.OnError == nil { + p.OnError = defaultOnError + } +} + +// NewParser creates a dosini-style parser using +// an [io.Reader] as source +func NewParser(r io.Reader) *Parser { + if r == nil { + return nil + } + + return &Parser{ + src: lexer.NewReader(r), + } +}