4 Commits

Author SHA1 Message Date
amery 2ef9315ff2 Unmarshal: WIP
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-08-29 16:38:50 +00:00
amery c1943d4285 Decoder: WIP
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-08-29 16:38:50 +00:00
amery efe48ffcb5 basic: WIP
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-08-29 16:38:50 +00:00
amery 22b372cdb0 parser: WIP
Signed-off-by: Alejandro Mery <amery@jpi.io>
2023-08-29 16:38:50 +00:00
8 changed files with 220 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
// Package basic provides a basic representation of dosini-style documents
package basic
+23
View File
@@ -0,0 +1,23 @@
package basic
// Document ...
type Document struct {
Global []Field
Sections []Section
}
// Section ...
type Section struct {
Name string
Key string
HadKey bool
Fields []Field
}
// Field ...
type Field struct {
Key string
Value string
}
+48
View File
@@ -0,0 +1,48 @@
package ini
import (
"bytes"
"io"
"asciigoat.org/core"
"asciigoat.org/ini/parser"
)
// Decoder ...
type Decoder struct {
io.Closer
p *parser.Parser
}
// Decode ...
func (dec *Decoder) Decode() error {
defer dec.Close()
return dec.p.Run()
}
// NewDecoder creates a Decoder over the provided [io.Reader]
func NewDecoder(r io.Reader) *Decoder {
rc := core.NewReadCloser(r)
switch {
case rc == nil:
return nil
default:
dec := &Decoder{
p: parser.NewParser(rc),
Closer: rc,
}
return dec
}
}
// NewDecoderBytes creates a Decoder over a provided bytes array
func NewDecoderBytes(b []byte) *Decoder {
return NewDecoder(bytes.NewBuffer(b))
}
// NewDecoderString creates a Decoder over a provided string of data
func NewDecoderString(s string) *Decoder {
return NewDecoder(bytes.NewBufferString(s))
}
+58
View File
@@ -0,0 +1,58 @@
package parser
import "asciigoat.org/core/lexer"
// Run parses the source
func (p *Parser) Run() error {
p.setDefaults()
p.pos.Reset()
return lexer.Run(p.lexStart)
}
func (p *Parser) lexStart() (lexer.StateFn, error) {
for {
r, _, err := p.src.ReadRune()
switch {
case err != nil:
return p.emitError("", err)
case r == '\n':
// new line
r2, _, err := p.src.ReadRune()
switch {
case err == nil && r2 == '\r':
// MAC new line
case err == nil:
// UNIX new line
p.src.UnreadRune()
}
p.src.Discard()
p.pos.StepLine()
case r == '\r':
// new line
r2, _, err := p.src.ReadRune()
switch {
case err == nil && r2 == '\n':
// DOS new line
case err == nil:
// incomplete CRLN
p.src.UnreadRune()
return p.emitInvalidRune(r2)
}
p.src.Discard()
p.pos.StepLine()
case p.IsSpace(r):
// whitespace
p.src.Discard()
p.pos.Step()
case p.IsCommentStart(r):
// switch to comment lexer
p.src.UnreadRune()
return p.lexComment, nil
}
}
}
func (p *Parser) lexComment() (lexer.StateFn, error)
+19
View File
@@ -0,0 +1,19 @@
package parser
import "asciigoat.org/core/lexer"
func (p *Parser) emitError(content string, err error) (lexer.StateFn, error) {
err2 := p.OnError(p.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)
}
+17
View File
@@ -0,0 +1,17 @@
package parser
import "unicode"
func (*Parser) IsSpace(r rune) bool {
return unicode.IsSpace(r)
}
func (*Parser) IsCommentStart(r rune) bool {
// TODO: use Parser options
switch r {
case ';', '#':
return true
default:
return false
}
}
+38
View File
@@ -10,6 +10,44 @@ import (
// Parser parses a dosini-style document
type Parser struct {
src *lexer.Reader
pos lexer.Position
OnSection func(pos lexer.Position, name, subname string, hasSubname bool) error
OnField func(pos lexer.Position, key, value string) error
OnComment func(pos lexer.Position, comment string) error
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
+15
View File
@@ -0,0 +1,15 @@
package ini
import "io"
// ReadInto ...
func ReadInto(v any, r io.Reader) error {
dec := NewDecoder(r)
return dec.Unmarshal(v)
}
// Unmarshal ...
func (*Decoder) Unmarshal(any) error {
return nil
}