From 22b372cdb0c09cedecb6ca15864d14ad0ec604f8 Mon Sep 17 00:00:00 2001 From: Alejandro Mery Date: Mon, 28 Aug 2023 23:46:05 +0000 Subject: [PATCH] parser: WIP Signed-off-by: Alejandro Mery --- parser/lexer.go | 58 +++++++++++++++++++++++++++++++++++++++++++ parser/lexer_error.go | 19 ++++++++++++++ parser/lexer_runes.go | 17 +++++++++++++ parser/parser.go | 38 ++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+) create mode 100644 parser/lexer.go create mode 100644 parser/lexer_error.go create mode 100644 parser/lexer_runes.go diff --git a/parser/lexer.go b/parser/lexer.go new file mode 100644 index 0000000..06b5634 --- /dev/null +++ b/parser/lexer.go @@ -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) diff --git a/parser/lexer_error.go b/parser/lexer_error.go new file mode 100644 index 0000000..afe6fab --- /dev/null +++ b/parser/lexer_error.go @@ -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) +} diff --git a/parser/lexer_runes.go b/parser/lexer_runes.go new file mode 100644 index 0000000..4de3b6e --- /dev/null +++ b/parser/lexer_runes.go @@ -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 + } +} diff --git a/parser/parser.go b/parser/parser.go index 5d399fd..739b657 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -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