From 6f5c80c676ac0132e0f5f6b6d82ad56830a72c4d Mon Sep 17 00:00:00 2001 From: Hidetatz Yaginuma Date: Sat, 9 Sep 2023 10:12:34 +0900 Subject: [PATCH] simplify error --- e2e_test.go | 6 +-- error.go | 112 ++++++++----------------------------------------- parse.go | 2 +- process.go | 107 +++++++++++++++++++--------------------------- result.go | 4 +- shiba.go | 3 +- tokenreader.go | 6 +-- 7 files changed, 71 insertions(+), 169 deletions(-) diff --git a/e2e_test.go b/e2e_test.go index 2a3c1f3..48bc3fe 100644 --- a/e2e_test.go +++ b/e2e_test.go @@ -334,7 +334,7 @@ func TestOutput(t *testing.T) { `), out: d(` 1 - $$filename:5:7 identifier a is undefined + $$filename:5:7 a is undefined `), }, "scope2": { @@ -353,7 +353,7 @@ func TestOutput(t *testing.T) { 2 2 3 - $$filename:5:7 identifier i is undefined + $$filename:5:7 i is undefined `), }, "scope3": { @@ -393,7 +393,7 @@ func TestOutput(t *testing.T) { } `), out: d(` - $$filename:2:8 identifier a is undefined + $$filename:2:8 a is undefined `), }, "scope5": { diff --git a/error.go b/error.go index 225b824..b3986a0 100644 --- a/error.go +++ b/error.go @@ -7,10 +7,7 @@ type shibaErr interface { loc() *loc } -func newsberr(n node, format string, args ...any) shibaErr { - return &sberr{l: n.token().loc, msg: fmt.Sprintf(format, args...)} -} - +// sberr is a primarily used error object. type sberr struct { l *loc msg string @@ -19,60 +16,32 @@ type sberr struct { func (e *sberr) loc() *loc { return e.l } func (e *sberr) Error() string { return e.msg } -type errSimple struct { - l *loc - msg string -} - -func (e *errSimple) loc() *loc { return e.l } -func (e *errSimple) Error() string { return e.msg } - /* - * Parse error + * helpers to create simple sberr */ -type errTokenize struct { - l *loc - msg string -} - -func (e *errTokenize) loc() *loc { return e.l } -func (e *errTokenize) Error() string { return e.msg } - -type errParse struct { - l *loc - msg string -} - -func (e *errParse) loc() *loc { return e.l } -func (e *errParse) Error() string { return e.msg } - -/* - * Evaluation error - */ - -type errTypeMismatch struct { - l *loc - expected string - actual string +func newsberr(n node, format string, args ...any) shibaErr { + return &sberr{l: n.token().loc, msg: fmt.Sprintf(format, args...)} } -func (e *errTypeMismatch) loc() *loc { return e.l } -func (e *errTypeMismatch) Error() string { - return fmt.Sprintf("type %s is expected but got %s", e.expected, e.actual) +func newsberr2(l *loc, format string, args ...any) shibaErr { + return &sberr{l: l, msg: fmt.Sprintf(format, args...)} } -type errInvalidIndex struct { - l *loc - idx int - length int +func newTypeMismatchErr(n node, expected, actual objtyp) shibaErr { + return &sberr{ + l: n.token().loc, + msg: fmt.Sprintf("type %s is expected but got %s", expected, actual), + } } -func (e *errInvalidIndex) loc() *loc { return e.l } -func (e *errInvalidIndex) Error() string { - return fmt.Sprintf("index out of range [%d] with length %d", e.idx, e.length) +func newinterr(n node, format string, args ...any) shibaErr { + return &sberr{l: n.token().loc, msg: "[internal]" + fmt.Sprintf(format, args...)} } +/* + * Define some errors which must be handled. + */ type errUndefinedIdent struct { l *loc ident string @@ -80,42 +49,7 @@ type errUndefinedIdent struct { func (e *errUndefinedIdent) loc() *loc { return e.l } func (e *errUndefinedIdent) Error() string { - return fmt.Sprintf("identifier %s is undefined", e.ident) -} - -type errInvalidAssignOp struct { - l *loc - op string - left string - right string -} - -func (e *errInvalidAssignOp) loc() *loc { return e.l } -func (e *errInvalidAssignOp) Error() string { - return fmt.Sprintf("invalid assignment %s [%s] %s", e.left, e.op, e.right) -} - -type errInvalidBinaryOp struct { - l *loc - op string - left string - right string -} - -func (e *errInvalidBinaryOp) loc() *loc { return e.l } -func (e *errInvalidBinaryOp) Error() string { - return fmt.Sprintf("invalid operation %s [%s] %s", e.left, e.op, e.right) -} - -type errInvalidUnaryOp struct { - l *loc - op string - target string -} - -func (e *errInvalidUnaryOp) loc() *loc { return e.l } -func (e *errInvalidUnaryOp) Error() string { - return fmt.Sprintf("invalid operation [%s]%s", e.op, e.target) + return fmt.Sprintf("%s is undefined", e.ident) } type errDictKeyNotFound struct { @@ -125,15 +59,5 @@ type errDictKeyNotFound struct { func (e *errDictKeyNotFound) loc() *loc { return e.l } func (e *errDictKeyNotFound) Error() string { - return fmt.Sprintf("key %s is not found in dict", e.key) -} - -type errInternal struct { - l *loc - msg string -} - -func (e *errInternal) loc() *loc { return e.l } -func (e *errInternal) Error() string { - return fmt.Sprintf("shiba internal error: %s", e.msg) + return fmt.Sprintf("key %s is not found", e.key) } diff --git a/parse.go b/parse.go index f9a4269..463ead6 100644 --- a/parse.go +++ b/parse.go @@ -73,7 +73,7 @@ func (p *parser) parsestmt() (n node, err shibaErr) { // Returning error will make the parser code not easy to read. defer func() { if r := recover(); r != nil { - err = &errParse{msg: fmt.Sprintf("%v", r), l: p.cur.loc} + err = newsberr2(p.cur.loc, "%v", r) } }() diff --git a/process.go b/process.go index 8aa09a5..0a0d952 100644 --- a/process.go +++ b/process.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "path/filepath" ) @@ -86,18 +85,19 @@ func process(mod *module, nd node) (procResult, shibaErr) { return &prObj{o: &obj{typ: tBool, bval: n.val}}, nil } - return nil, &errInternal{msg: fmt.Sprintf("unhandled nodetype: %s", nd), l: nd.token().loc} + return nil, newinterr(nd, "unhandled nodetype: %s", nd) } func procReturn(mod *module, n *ndReturn) (procResult, shibaErr) { if n.val == nil { - return &prReturn{ret: nil}, nil + return &prReturn{}, nil } o, err := procAsObj(mod, n.val) if err != nil { return nil, err } + return &prReturn{ret: o}, nil } @@ -116,7 +116,7 @@ func procAssign(mod *module, n *ndAssign) (procResult, shibaErr) { // plain assign assigns multiple right values to multiple left operand. func procPlainAssign(mod *module, n *ndAssign) (procResult, shibaErr) { if len(n.left) != len(n.right) { - return nil, &errSimple{msg: "assignment size mismatch", l: n.token().loc} + return nil, newsberr(n, "assignment size mismatch") } for i := range n.left { @@ -187,7 +187,7 @@ func assignTo(mod *module, dst node, o *obj) shibaErr { // The left side size must be the same with right side iterable size. func procUnpackAssign(mod *module, n *ndAssign) (procResult, shibaErr) { if len(n.right) != 1 { - return nil, &errSimple{msg: ":= cannot have multiple operand on right side", l: n.token().loc} + return nil, newsberr(n, ":= cannot have multiple operands on right side") } r, err := procAsObj(mod, n.right[0]) @@ -196,12 +196,12 @@ func procUnpackAssign(mod *module, n *ndAssign) (procResult, shibaErr) { } if !r.cansequence() { - return nil, &errSimple{msg: fmt.Sprintf("cannot unpack %s", r), l: n.token().loc} + return nil, newsberr(n, "cannot unpack %s", r) } seq := r.sequence() if seq.size() != len(n.left) { - return nil, &errSimple{msg: fmt.Sprintf("unpack size mismatch: %s := %s", n.left, r), l: n.token().loc} + return nil, newsberr(n, "size mismatch on unpack: %s := %s", n.left, n.right[0]) } for i := range n.left { @@ -215,11 +215,11 @@ func procUnpackAssign(mod *module, n *ndAssign) (procResult, shibaErr) { func procComputeAssign(mod *module, n *ndAssign) (procResult, shibaErr) { if len(n.left) != 1 { - return nil, &errSimple{msg: fmt.Sprintf("cannot assign to multiple values by %s", n.op), l: n.token().loc} + return nil, newsberr(n, "left must be only one operand on %s", n.op) } if len(n.right) != 1 { - return nil, &errSimple{msg: fmt.Sprintf("cannot assign multiple values with %s", n.op), l: n.token().loc} + return nil, newsberr(n, "right must be only one operand on %s", n.op) } left := n.left[0] @@ -257,12 +257,7 @@ func procComputeAssign(mod *module, n *ndAssign) (procResult, shibaErr) { o, err2 := computeBinaryOp(l, r, bo) if err2 != nil { - return nil, &errInvalidAssignOp{ - left: l.String(), - op: n.op.String(), - right: r.String(), - l: n.token().loc, - } + return nil, newsberr(n, "invalid assignment: %s %s %s", left, bo, right) } l.update(o) @@ -285,11 +280,11 @@ func procMultipleAssign(mod *module, n *ndAssign) (procResult, shibaErr) { } if r.typ != tList { - return nil, &errSimple{msg: fmt.Sprintf("%s must be multiple-values", n.right[0]), l: n.token().loc} + return nil, newsberr(n, "%s must be list", n.right[0]) } if len(n.left) != len(r.list) { - return nil, &errSimple{msg: fmt.Sprintf("assignment mismatch left: %d, right: %d", len(n.left), len(r.list)), l: n.token().loc} + return nil, newsberr(n, "assignment mismatch left: %d, right: %d", len(n.left), len(r.list)) } for i := range n.left { @@ -384,11 +379,11 @@ func procLoop(mod *module, n *ndLoop) (procResult, shibaErr) { defer env.delblockscope(mod) if _, ok := n.cnt.(*ndIdent); !ok { - return nil, &errSimple{msg: fmt.Sprintf("invalid counter %s in loop", n.cnt), l: n.token().loc} + return nil, newsberr(n, "invalid counter %s in loop", n.cnt) } if _, ok := n.elem.(*ndIdent); !ok { - return nil, &errSimple{msg: fmt.Sprintf("invalid element %s in loop", n.cnt), l: n.token().loc} + return nil, newsberr(n, "invalid element %s in loop", n.cnt) } target, err := procAsObj(mod, n.target) @@ -397,7 +392,7 @@ func procLoop(mod *module, n *ndLoop) (procResult, shibaErr) { } if !target.isiterable() { - return nil, &errSimple{msg: "non-iterable loop target", l: n.token().loc} + return nil, newsberr(n, "non-iterable loop target") } iter := target.iterator() @@ -470,7 +465,7 @@ func procCondLoop(mod *module, n *ndCondLoop) (procResult, shibaErr) { func procStructDef(mod *module, n *ndStructDef) (procResult, shibaErr) { if _, ok := n.name.(*ndIdent); !ok { - return nil, &errSimple{msg: fmt.Sprintf("invalid struct name %s", n.name), l: n.token().loc} + return nil, newsberr(n, "invalid struct name %s", n.name) } name := n.name.(*ndIdent).ident @@ -478,7 +473,7 @@ func procStructDef(mod *module, n *ndStructDef) (procResult, shibaErr) { for _, v := range n.vars { if _, ok := v.(*ndIdent); !ok { - return nil, &errSimple{msg: fmt.Sprintf("invalid variable name %s in struct %s", v, name), l: n.token().loc} + return nil, newsberr(n, "invalid variable name %s in struct %s", v, name) } sd.vars = append(sd.vars, v.(*ndIdent).ident) } @@ -489,11 +484,7 @@ func procStructDef(mod *module, n *ndStructDef) (procResult, shibaErr) { for _, p := range nfn.params { i, ok := p.(*ndIdent) if !ok { - return nil, &errSimple{ - msg: fmt.Sprintf("function param %s must be identifier", p), - l: n.token().loc, - } - + return nil, newsberr(n, "function param %s must be identifier", p) } params = append(params, i.ident) } @@ -514,7 +505,7 @@ func procStructDef(mod *module, n *ndStructDef) (procResult, shibaErr) { func procStructInit(mod *module, n *ndStructInit) (procResult, shibaErr) { if _, ok := n.name.(*ndIdent); !ok { - return nil, &errSimple{msg: fmt.Sprintf("invalid struct name %s", n.name), l: n.token().loc} + return nil, newsberr(n, "invalid struct name %s", n.name) } name := n.name.(*ndIdent).ident @@ -522,7 +513,7 @@ func procStructInit(mod *module, n *ndStructInit) (procResult, shibaErr) { sd, ok := env.getstruct(mod, name) if !ok { - return nil, &errSimple{msg: fmt.Sprintf("struct %s is not defined", name), l: n.token().loc} + return nil, newsberr(n, "struct %s is not defined", name) } for _, dsd := range sd.defs { @@ -533,17 +524,17 @@ func procStructInit(mod *module, n *ndStructInit) (procResult, shibaErr) { d, ok := n.values.(*ndDict) if !ok { - return nil, &errInternal{msg: fmt.Sprintf("dict expected in struct init but got %s", n.values), l: n.token().loc} + return nil, newinterr(n, "dict expected in struct init but got %s", n.values) } for i := range d.keys { if _, ok := d.keys[i].(*ndIdent); !ok { - return nil, &errSimple{msg: fmt.Sprintf("invalid field name %s in struct %s", d.keys[i], name), l: n.token().loc} + return nil, newsberr(n, "invalid field name %s in struct %s", d.keys[i], name) } k := d.keys[i].(*ndIdent).ident if !sd.hasfield(k) { - return nil, &errSimple{msg: fmt.Sprintf("struct %s does not have field %s", name, k), l: n.token().loc} + return nil, newsberr(n, "struct %s does not have field %s", name, k) } v, err := procAsObj(mod, d.vals[i]) @@ -562,11 +553,7 @@ func procFunDef(mod *module, n *ndFunDef) (procResult, shibaErr) { for _, p := range n.params { i, ok := p.(*ndIdent) if !ok { - return nil, &errSimple{ - msg: fmt.Sprintf("function param %s must be identifier", p), - l: n.token().loc, - } - + return nil, newsberr(n, "function param %s must be identifier", p) } params = append(params, i.ident) } @@ -598,18 +585,18 @@ func procIndex(mod *module, n *ndIndex) (procResult, shibaErr) { } if idx.typ != tI64 { - return nil, &errTypeMismatch{expected: tI64.String(), actual: idx.typ.String(), l: n.token().loc} + return nil, newTypeMismatchErr(n, tI64, idx.typ) } i := int(idx.ival) if !tgt.cansequence() { - return nil, &errSimple{msg: fmt.Sprintf("%s is not iterable", tgt), l: n.token().loc} + return nil, newsberr(n, "%s is not iterable", tgt) } seq := tgt.sequence() if seq.size() <= i { - return nil, &errInvalidIndex{idx: i, length: seq.size(), l: n.token().loc} + return nil, newsberr(n, "index out of range [%d] with length %d", i, seq.size()) } return &prObj{o: seq.index(i)}, nil @@ -636,7 +623,7 @@ func procSlice(mod *module, n *ndSlice) (procResult, shibaErr) { } if start.typ != tI64 { - return nil, &errTypeMismatch{expected: tI64.String(), actual: start.typ.String(), l: n.token().loc} + return nil, newTypeMismatchErr(n, tI64, start.typ) } end, err := procAsObj(mod, n.end) @@ -645,7 +632,7 @@ func procSlice(mod *module, n *ndSlice) (procResult, shibaErr) { } if end.typ != tI64 { - return nil, &errTypeMismatch{expected: tI64.String(), actual: end.typ.String(), l: n.token().loc} + return nil, newTypeMismatchErr(n, tI64, end.typ) } target, err := procAsObj(mod, n.target) @@ -654,7 +641,7 @@ func procSlice(mod *module, n *ndSlice) (procResult, shibaErr) { } if !target.cansequence() { - return nil, &errSimple{msg: fmt.Sprintf("%s is not iterable", target), l: n.token().loc} + return nil, newsberr(n, "%s is not iterable", target) } seq := target.sequence() @@ -664,7 +651,7 @@ func procSlice(mod *module, n *ndSlice) (procResult, shibaErr) { l := seq.size() if ei < si || si < 0 || l < ei { - return nil, &errSimple{msg: fmt.Sprintf("invalid slice indices [%d:%d]", si, ei), l: n.token().loc} + return nil, newsberr(n, "invalid slice indices [%d:%d]", si, ei) } return &prObj{o: seq.slice(si, ei)}, nil @@ -692,18 +679,18 @@ func procSelector(mod *module, n *ndSelector) (procResult, shibaErr) { if selector.typ == tStruct { field, ok := n.target.(*ndIdent) if !ok { - return nil, &errSimple{msg: fmt.Sprintf("%s must be an identifier", n.target), l: n.token().loc} + return nil, newsberr(n, "%s must be an identifier", n.target) } f, ok := selector.fields[field.ident] if !ok { - return nil, &errSimple{msg: fmt.Sprintf("unknown field name %s in %s", field.ident, selector), l: n.token().loc} + return nil, newsberr(n, "unknown field name %s in %s", field.ident, selector) } return &prObj{o: f}, nil } - return nil, &errSimple{msg: fmt.Sprintf("selector %s is not a module", selector), l: n.token().loc} + return nil, newsberr(n, "selector %s is not a module or struct", selector) } func procFuncall(mod *module, n *ndFuncall) (procResult, shibaErr) { @@ -725,7 +712,7 @@ func procFuncall(mod *module, n *ndFuncall) (procResult, shibaErr) { if fn.typ == tBuiltinFunc { o, err := fn.bfnbody(args...) if err != nil { - return nil, &errSimple{msg: err.Error(), l: n.token().loc} + return nil, newsberr(n, err.Error()) } return &prObj{o: o}, nil @@ -734,7 +721,7 @@ func procFuncall(mod *module, n *ndFuncall) (procResult, shibaErr) { if fn.typ == tGoStdModFunc { o, err := fn.gostdmodfunc(args...) if err != nil { - return nil, &errSimple{msg: err.Error(), l: n.token().loc} + return nil, newsberr(n, err.Error()) } return &prObj{o: o}, nil @@ -742,10 +729,7 @@ func procFuncall(mod *module, n *ndFuncall) (procResult, shibaErr) { if fn.typ == tFunc || fn.typ == tMethod { if len(fn.params) != len(args) { - return nil, &errSimple{ - msg: fmt.Sprintf("argument mismatch on %s()", fn.name), - l: n.token().loc, - } + return nil, newsberr(n, "argument mismatch on %s()", fn.name) } env.createfuncscope(fn.fmod) @@ -771,11 +755,11 @@ func procFuncall(mod *module, n *ndFuncall) (procResult, shibaErr) { } if _, ok := pr.(*prBreak); ok { - return nil, &errSimple{msg: "break in non-loop"} + return nil, newsberr(n, "break in non-loop") } if _, ok := pr.(*prContinue); ok { - return nil, &errSimple{msg: "continue in non-loop"} + return nil, newsberr(n, "continue in non-loop") } } @@ -783,10 +767,7 @@ func procFuncall(mod *module, n *ndFuncall) (procResult, shibaErr) { return nil, nil } - return nil, &errSimple{ - msg: fmt.Sprintf("cannot call %s", n.fn), - l: n.token().loc, - } + return nil, newsberr(n, "cannot call %s", n.fn) } func procImport(mod *module, n *ndImport) (procResult, shibaErr) { @@ -799,11 +780,11 @@ func procImport(mod *module, n *ndImport) (procResult, shibaErr) { // if still err, try to import gostd module objs, ok := gostdmods.objs(n.target) if !ok { - return nil, &errSimple{msg: fmt.Sprintf("cannot import %s: %s", n.target, err), l: n.token().loc} + return nil, newsberr(n, "cannot import %s: %s", n.target, err) } m, err = newgostdmodule(n.target, objs) if err != nil { - return nil, &errSimple{msg: fmt.Sprintf("cannot import %s: %s", n.target, err), l: n.token().loc} + return nil, newsberr(n, "cannot import %s: %s", n.target, err) } } } @@ -830,7 +811,7 @@ func procBinaryOp(mod *module, n *ndBinaryOp) (procResult, shibaErr) { o, err2 := computeBinaryOp(l, r, n.op) if err2 != nil { - return nil, &errSimple{msg: err2.Error(), l: n.token().loc} + return nil, newsberr(n, err2.Error()) } return &prObj{o: o}, nil @@ -868,7 +849,7 @@ func procUnaryOp(mod *module, n *ndUnaryOp) (procResult, shibaErr) { } } - return nil, &errInvalidUnaryOp{op: n.op.String(), target: n.target.String(), l: n.token().loc} + return nil, newsberr(n, "invalid operation [%s]%s", n.op, n.target) } func procList(mod *module, n *ndList) (procResult, shibaErr) { diff --git a/result.go b/result.go index e20e287..a4a134f 100644 --- a/result.go +++ b/result.go @@ -1,7 +1,5 @@ package main -import "fmt" - type procResult interface { typ() string } @@ -42,7 +40,7 @@ func procAsObj(mod *module, n node) (*obj, shibaErr) { o, ok := pr.(*prObj) if !ok { - return nil, &errSimple{msg: fmt.Sprintf("%s is not object", n), l: n.token().loc} + return nil, newsberr(n, "%s is not object", n) } return o.o, nil diff --git a/shiba.go b/shiba.go index 30cdb15..4cd11c5 100644 --- a/shiba.go +++ b/shiba.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "os" ) @@ -60,7 +59,7 @@ func runmod(mod *module) shibaErr { goto finish default: - return &errSimple{msg: fmt.Sprintf("invalid %s in outside function", result.typ()), l: stmt.token().loc} + return newsberr(stmt, "invalid %s in outside function", result.typ()) } } diff --git a/tokenreader.go b/tokenreader.go index a31665a..ebcdfd5 100644 --- a/tokenreader.go +++ b/tokenreader.go @@ -72,7 +72,7 @@ func (t *tokenreader) readtoken() (*token, error) { return tk, nil } - return nil, &errTokenize{msg: "invalid token", l: loc} + return nil, newsberr2(loc, "invalid token") } func (t *tokenreader) readstring() (*token, error) { @@ -83,7 +83,7 @@ func (t *tokenreader) readstring() (*token, error) { // todo: handle intermediate quote for { if !t.hasnext() { - return nil, &errTokenize{msg: "string unterminated", l: loc} + return nil, newsberr2(loc, "string unterminated") } c := t.cur() @@ -118,7 +118,7 @@ func (t *tokenreader) readnum() (*token, error) { dots := strings.Count(s, ".") if dots >= 2 { - return nil, &errTokenize{msg: "invalid decimal expression", l: loc} + return nil, newsberr2(loc, "invalid decimal expression") } return t.newtoken(tkNum, s, loc), nil