diff options
author | Rob Pike <r@golang.org> | 2010-04-13 13:28:08 -0700 |
---|---|---|
committer | Rob Pike <r@golang.org> | 2010-04-13 13:28:08 -0700 |
commit | 7d564e8188bc713659585dd60f7ac9c6602c8aea (patch) | |
tree | 826d74a3826b3e63652a6ad46ceee48588909b0d /src | |
parent | 9888dd4238147da282d07349b553f4a09b28fe7b (diff) | |
download | go-7d564e8188bc713659585dd60f7ac9c6602c8aea.tar.gz |
template: use panic/recover to simplify internal error handling.
R=rsc
CC=golang-dev
http://codereview.appspot.com/824049
Diffstat (limited to 'src')
-rw-r--r-- | src/pkg/template/template.go | 82 |
1 files changed, 32 insertions, 50 deletions
diff --git a/src/pkg/template/template.go b/src/pkg/template/template.go index 54c22ba8d..2bf21610b 100644 --- a/src/pkg/template/template.go +++ b/src/pkg/template/template.go @@ -69,7 +69,6 @@ import ( "io/ioutil" "os" "reflect" - "runtime" "strings" ) @@ -153,11 +152,10 @@ type repeatedElement struct { type Template struct { fmap FormatterMap // formatters for variables // Used during parsing: - ldelim, rdelim []byte // delimiters; default {} - buf []byte // input text to process - p int // position in buf - linenum int // position in input - error os.Error // error during parsing (only) + ldelim, rdelim []byte // delimiters; default {} + buf []byte // input text to process + p int // position in buf + linenum int // position in input // Parsed results: elems *vector.Vector } @@ -169,11 +167,10 @@ type state struct { parent *state // parent in hierarchy data reflect.Value // the driver data for this section etc. wr io.Writer // where to send output - errors chan os.Error // for reporting errors during execute } func (parent *state) clone(data reflect.Value) *state { - return &state{parent, data, parent.wr, parent.errors} + return &state{parent, data, parent.wr} } // New creates a new template with the specified formatter map (which @@ -189,14 +186,13 @@ func New(fmap FormatterMap) *Template { // Report error and stop executing. The line number must be provided explicitly. func (t *Template) execError(st *state, line int, err string, args ...interface{}) { - st.errors <- &Error{line, fmt.Sprintf(err, args)} - runtime.Goexit() + panic(&Error{line, fmt.Sprintf(err, args)}) } -// Report error, save in Template to terminate parsing. +// Report error, panic to terminate parsing. // The line number comes from the template state. func (t *Template) parseError(err string, args ...interface{}) { - t.error = &Error{t.linenum, fmt.Sprintf(err, args)} + panic(&Error{t.linenum, fmt.Sprintf(err, args)}) } // -- Lexical analysis @@ -427,9 +423,6 @@ func (t *Template) newVariable(name_formatter string) (v *variableElement) { // Otherwise return its details. func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) { tok, w = t.analyze(item) - if t.error != nil { - return - } done = true // assume for simplicity switch tok { case tokComment: @@ -449,7 +442,6 @@ func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) { t.elems.Push(&literalElement{tab}) default: t.parseError("internal error: unknown literal: %s", w[0]) - return } return case tokVariable: @@ -472,19 +464,13 @@ func (t *Template) parseRepeated(words []string) *repeatedElement { r.altstart = -1 r.altend = -1 Loop: - for t.error == nil { + for { item := t.nextItem() - if t.error != nil { - break - } if len(item) == 0 { t.parseError("missing .end for .repeated section") break } done, tok, w := t.parseSimple(item) - if t.error != nil { - break - } if done { continue } @@ -517,9 +503,6 @@ Loop: break Loop } } - if t.error != nil { - return nil - } if r.altend < 0 { r.altend = t.elems.Len() } @@ -536,19 +519,13 @@ func (t *Template) parseSection(words []string) *sectionElement { s.start = t.elems.Len() s.or = -1 Loop: - for t.error == nil { + for { item := t.nextItem() - if t.error != nil { - break - } if len(item) == 0 { t.parseError("missing .end for .section") break } done, tok, w := t.parseSimple(item) - if t.error != nil { - break - } if done { continue } @@ -571,19 +548,13 @@ Loop: t.parseError("internal error: unknown section item: %s", item) } } - if t.error != nil { - return nil - } s.end = t.elems.Len() return s } func (t *Template) parse() { - for t.error == nil { + for { item := t.nextItem() - if t.error != nil { - break - } if len(item) == 0 { break } @@ -909,37 +880,48 @@ func validDelim(d []byte) bool { return true } +// checkError is a deferred function to turn a panic with type *Error into a plain error return. +// Other panics are unexpected and so are re-enabled. +func checkError(error *os.Error) { + if v := recover(); v != nil { + if e, ok := v.(*Error); ok { + *error = e + } else { + // runtime errors should crash + panic(v) + } + } +} + // -- Public interface // Parse initializes a Template by parsing its definition. The string // s contains the template text. If any errors occur, Parse returns // the error. -func (t *Template) Parse(s string) os.Error { +func (t *Template) Parse(s string) (err os.Error) { if t.elems == nil { return &Error{1, "template not allocated with New"} } if !validDelim(t.ldelim) || !validDelim(t.rdelim) { return &Error{1, fmt.Sprintf("bad delimiter strings %q %q", t.ldelim, t.rdelim)} } + defer checkError(&err) t.buf = []byte(s) t.p = 0 t.linenum = 1 t.parse() - return t.error + return nil } // Execute applies a parsed template to the specified data object, // generating output to wr. -func (t *Template) Execute(data interface{}, wr io.Writer) os.Error { +func (t *Template) Execute(data interface{}, wr io.Writer) (err os.Error) { // Extract the driver data. val := reflect.NewValue(data) - errors := make(chan os.Error) - go func() { - t.p = 0 - t.execute(0, t.elems.Len(), &state{nil, val, wr, errors}) - errors <- nil // clean return; - }() - return <-errors + defer checkError(&err) + t.p = 0 + t.execute(0, t.elems.Len(), &state{nil, val, wr}) + return nil } // SetDelims sets the left and right delimiters for operations in the |