diff --git a/charm/charmParse.go b/charm/charmParse.go index da137b9..b38b44c 100644 --- a/charm/charmParse.go +++ b/charm/charmParse.go @@ -26,16 +26,17 @@ func innerParse(first State, in io.RuneReader) (ret State, err error) { for i := 0; ; i++ { if r, _, e := in.ReadRune(); e != nil { if e != io.EOF { - err = errors.Join(e, EndpointError{errContext(r, in), i, try, e.Error()}) + err = errors.Join(e, EndpointError{r, in, i, try}) } break } else { if next := try.NewRune(r); next == nil { // no states left to parse remaining input - err = EndpointError{errContext(r, in), i, try, "unhandled rune"} + e := errors.New("unhandled rune") + err = errors.Join(e, EndpointError{r, in, i, try}) break } else if es, ok := next.(Terminal); ok { - err = EndpointError{errContext(r, in), i, try, es.err.Error()} + err = errors.Join(es.err, EndpointError{r, in, i, try}) break } else { try = next @@ -72,11 +73,8 @@ func ParseEof(str string, first State) (err error) { err = e } else if last != nil { if fini := last.NewRune(Eof); fini != nil { - if es, ok := fini.(Terminal); ok && es.err != nil { - err = fmt.Errorf("%s handling eof for %q", es.err, str) - } else { - // and if we are passing eof, shouldnt the states check for it and return nil? - // err = EndpointError{str, len(str), fini} + if es, ok := fini.(Terminal); !ok || !es.Finished() { + err = fmt.Errorf("%s at eof for %q", es.err, str) } } } @@ -85,10 +83,10 @@ func ParseEof(str string, first State) (err error) { // ended before the whole input was parsed. type EndpointError struct { - str string - end int - last State - reason string + r rune + in io.RuneReader + end int + last State } // index of the failure point in the input @@ -97,6 +95,6 @@ func (e EndpointError) End() int { } func (e EndpointError) Error() (ret string) { - return fmt.Sprintf("%s %q (%q ended at index %d)", - e.reason, e.str, StateName(e.last), e.end) + sink := errContext(e.r, e.in) + return fmt.Sprintf("%q (%q ended at index %d)", sink, StateName(e.last), e.end) } diff --git a/charm/charmRequires_test.go b/charm/charmRequires_test.go index bfc810b..b980b6e 100644 --- a/charm/charmRequires_test.go +++ b/charm/charmRequires_test.go @@ -12,11 +12,12 @@ func TestRequires(t *testing.T) { // index of the fail point, or -1 if success is expected count := func(failPoint int, str string, style State) (err error) { + var ep EndpointError if e := ParseEof(str, style); e == nil && failPoint != -1 { err = errors.New("unexpected success") - } else if n, ok := e.(EndpointError); !ok { + } else if !errors.As(e, &ep) { err = e - } else if at := n.End(); at != failPoint { + } else if at := ep.End(); at != failPoint { // 0 means okay, -1 incomplete, >0 the one-index of the failure point. err = fmt.Errorf("%s len: %d", str, at) } diff --git a/charmed/charmedHeader.go b/charmed/charmedHeader.go index af85376..127544e 100644 --- a/charmed/charmedHeader.go +++ b/charmed/charmedHeader.go @@ -58,7 +58,7 @@ func (h *headCount) update(q rune, report headerNotifier) (err error) { } else if prev, width := h.token, h.width; t == prev { h.width++ } else if prev == headerRedirect && width != 3 { - err = customTagError + err = errCustomTag } else { h.token, h.width = t, 1 if prev != headerSpaces { @@ -70,7 +70,7 @@ func (h *headCount) update(q rune, report headerNotifier) (err error) { return } -var customTagError = errors.New("custom closing tags require exactly three redirect markers ('<<<')") +var errCustomTag = errors.New("custom closing tags require exactly three redirect markers ('<<<')") // determine which header type, if any, the passed rune belongs to // ( false if its some classifiable rune ) diff --git a/charmed/charmedNum.go b/charmed/charmedNum.go index 065a98d..b66aa75 100644 --- a/charmed/charmedNum.go +++ b/charmed/charmedNum.go @@ -29,7 +29,6 @@ func (*NumParser) String() string { return "Numbers" } - // fix: this is currently less of a number parser, and more a number validator. func (p *NumParser) accept(q rune, s charm.State) charm.State { p.runes.WriteRune(q) @@ -46,7 +45,7 @@ func (p *NumParser) GetNumber() (ret any, err error) { case modeFloat: ret = fromFloat(s) default: - err = fmt.Errorf("unknown number: '%v' is %v.", s, p.mode) + err = fmt.Errorf("unknown number: '%v' is %v", s, p.mode) } return } @@ -61,7 +60,7 @@ func (p *NumParser) GetFloat() (ret float64, err error) { case modeFloat: ret = fromFloat(s) default: - err = fmt.Errorf("unknown number: '%v' is %v.", s, p.mode) + err = fmt.Errorf("unknown number: '%v' is %v", s, p.mode) } return } diff --git a/charmed/charmedNum_test.go b/charmed/charmedNum_test.go index 10933ad..7faf90f 100644 --- a/charmed/charmedNum_test.go +++ b/charmed/charmedNum_test.go @@ -1,6 +1,7 @@ package charmed import ( + "errors" "math" "testing" @@ -85,10 +86,11 @@ func TestNum(t *testing.T) { t.Fatal("expected success", e) break } else if test.endpoint > 0 { - if c, ok := e.(charm.EndpointError); !ok { + var ep charm.EndpointError + if !errors.As(e, &ep) { t.Fatal("unexpected error", e) break - } else if c.End() != test.endpoint-1 { + } else if ep.End() != test.endpoint-1 { t.Fatal("mismatched endpoint at", e) break } diff --git a/decode/decodeArray.go b/decode/decodeArray.go index 296fb0d..ea5a412 100644 --- a/decode/decodeArray.go +++ b/decode/decodeArray.go @@ -24,7 +24,7 @@ func (d *Decoder) waitForSep(at token.Pos, tokenType token.Type, val any) (err e d.state = d.waitForEl } default: - err = errors.New("expected an array separator, or array close.") + err = errors.New("expected an array separator or array close") } } return diff --git a/encode/encodeMap.go b/encode/encodeMap.go index 3e589c1..db4b8b3 100644 --- a/encode/encodeMap.go +++ b/encode/encodeMap.go @@ -42,7 +42,7 @@ func keyTransform(v r.Value) (ret string) { return } -func (m *MapTransform) makeMapping(src r.Value) (retIt Iterator, err error) { +func (m *MapTransform) makeMapping(src r.Value) (Iterator, error) { keyLess := m.keyLess if keyLess == nil { keyLess = func(a, b string) bool { return a < b } @@ -62,16 +62,10 @@ func (m *MapTransform) makeMapping(src r.Value) (retIt Iterator, err error) { mk = mapKeys{str: str, val: keys, keyLess: keyLess} sort.Sort(&mk) } - if err == nil { - retIt = &mapIter{src: src, mapKeys: mk} - } - return + // + return &mapIter{src: src, mapKeys: mk}, nil } -// not quite sure how to turn string into an interface without something like this. ugh. -var anyBlank = [1]any{""} -var blank = r.ValueOf(anyBlank).Index(0) - type mapIter struct { src r.Value // the native map mapKeys mapKeys diff --git a/token/tokens.go b/token/tokens.go index 8a4238b..e076f36 100644 --- a/token/tokens.go +++ b/token/tokens.go @@ -151,7 +151,7 @@ func (n *tokenizer) wordDecoder() charm.State { } else if terminal(sign) { // sign is mostly superset of bool; (except for the eof/eol cases) // if it dies and boolean didnt just succeed; they're both dead. - ret = charm.Error(wordyError) + ret = charm.Error(errWordy) } return }) @@ -169,7 +169,7 @@ func (n *tokenizer) decodeSignature() charm.State { if sign = sign.NewRune(q); sign == nil { ret = n.notifyRune(q, Key, sig.String()) } else if terminal(sign) { - ret = charm.Error(wordyError) + ret = charm.Error(errWordy) } return }) @@ -248,7 +248,7 @@ const ( boolTrue // true ) -var wordyError = errors.New("couldn't read words. strings should be quoted, booleans should be 'true' or 'false', and map keys should start with a letter and end with a colon.") +var errWordy = errors.New("couldn't read words. strings should be quoted, booleans should be 'true' or 'false', and map keys should start with a letter and end with a colon") // is the next state an error? func terminal(next charm.State) (okay bool) {