initial commits
parent
d60a2755b8
commit
0aa3328241
|
@ -0,0 +1,6 @@
|
||||||
|
package brace
|
||||||
|
|
||||||
|
const (
|
||||||
|
leftbrace rune = '{'
|
||||||
|
rightbrace rune = '}'
|
||||||
|
)
|
|
@ -0,0 +1,31 @@
|
||||||
|
package brace
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sourcecode.social/reiver/go-erorr"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
errEmptyString = erorr.Error("empty string")
|
||||||
|
errNilRuneScanner = erorr.Error("brace: nil rune-scanner")
|
||||||
|
errNilWriter = erorr.Error("brace: nil writer")
|
||||||
|
)
|
||||||
|
|
||||||
|
func errFileEndedBeforeBraceStringLiteralClosed(depth int64) error {
|
||||||
|
return erorr.Errorf("file ended before the brace-string literal was closed — expected %d more %q (%U) character(s)", depth, rightbrace, rightbrace)
|
||||||
|
}
|
||||||
|
|
||||||
|
func errInternalError(err error) error {
|
||||||
|
return erorr.Errorf("brace: internal error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func errParserDepthNegative(depth int64) error {
|
||||||
|
return erorr.Errorf("brace: parser depth (%d) is negative", depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
func errProblemReadingCharacterNumber(num uint64, err error) error {
|
||||||
|
return erorr.Errorf("brace: problem reading character №%d of what should have been a brace-string literal: %w", num, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func errProblemUnreadingCharacterNumber(num uint64, err error, r rune) error {
|
||||||
|
return erorr.Errorf("brace: problem unreading character №%d (which is a %q (%U)) of what should have been a brace-string literal: %w", num, r, r, err)
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
package brace
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"sourcecode.social/reiver/go-erorr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parse parses a brace-string literal from a io.RuneScanner.
|
||||||
|
//
|
||||||
|
// Parse will call ‘fn’ for each logical character it receives.
|
||||||
|
// So — if this is the brace-literal it parses:
|
||||||
|
//
|
||||||
|
// `{a b \{ c \} d}`
|
||||||
|
//
|
||||||
|
// Then ‘fn’ would be called 11 times, and given these runes:
|
||||||
|
//
|
||||||
|
// 'a'
|
||||||
|
// ' '
|
||||||
|
// 'b'
|
||||||
|
// ' '
|
||||||
|
// '{'
|
||||||
|
// ' '
|
||||||
|
// 'c'
|
||||||
|
// ' '
|
||||||
|
// '}'
|
||||||
|
// ' '
|
||||||
|
// 'd'
|
||||||
|
//
|
||||||
|
// Note that the beginning '{' and ending '}' of the brace-string literal are not part of this.
|
||||||
|
// Also note that the '\' (black-slash) before the '{' and the '}' was not included either.
|
||||||
|
func Parse(fn func(rune)error, runescanner io.RuneScanner) error {
|
||||||
|
|
||||||
|
if nil == runescanner {
|
||||||
|
return errNilRuneScanner
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextRead uint64 = 1
|
||||||
|
|
||||||
|
{
|
||||||
|
r, _, err := runescanner.ReadRune()
|
||||||
|
if io.EOF == err {
|
||||||
|
return errProblemReadingCharacterNumber(nextRead, errEmptyString)
|
||||||
|
}
|
||||||
|
if nil != err {
|
||||||
|
return errProblemReadingCharacterNumber(nextRead, err)
|
||||||
|
}
|
||||||
|
nextRead++
|
||||||
|
|
||||||
|
{
|
||||||
|
const expected = leftbrace
|
||||||
|
actual := r
|
||||||
|
|
||||||
|
if expected != actual {
|
||||||
|
err := runescanner.UnreadRune()
|
||||||
|
if nil != err {
|
||||||
|
return errProblemUnreadingCharacterNumber(nextRead, errEmptyString, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return erorr.Errorf("brace: expected first character of brace-string literal to be a %q (%U), but actually was %q (%U)", expected, expected, actual, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var depth int64 = 1
|
||||||
|
loop: for {
|
||||||
|
r, _, err := runescanner.ReadRune()
|
||||||
|
if io.EOF == err {
|
||||||
|
return errProblemReadingCharacterNumber(nextRead, errFileEndedBeforeBraceStringLiteralClosed(depth))
|
||||||
|
}
|
||||||
|
if nil != err {
|
||||||
|
return errProblemReadingCharacterNumber(nextRead, err)
|
||||||
|
}
|
||||||
|
nextRead++
|
||||||
|
|
||||||
|
switch r {
|
||||||
|
case leftbrace:
|
||||||
|
depth++
|
||||||
|
case rightbrace:
|
||||||
|
depth--
|
||||||
|
case '\\':
|
||||||
|
r, _, err = runescanner.ReadRune()
|
||||||
|
if io.EOF == err {
|
||||||
|
return errProblemReadingCharacterNumber(nextRead, errFileEndedBeforeBraceStringLiteralClosed(depth))
|
||||||
|
}
|
||||||
|
if nil != err {
|
||||||
|
return erorr.Errorf("brace: problem reading character №%d (which would have followed a black-slash %q (%U) that was just read) of what should have been a brace-string literal: %w", nextRead, r, r, err)
|
||||||
|
}
|
||||||
|
nextRead++
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case 0 == depth:
|
||||||
|
////////////// BREAK
|
||||||
|
break loop
|
||||||
|
case depth < 0:
|
||||||
|
return errInternalError(errParserDepthNegative(depth))
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
err := fn(r)
|
||||||
|
if nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue