]> Cypherpunks repositories - gostls13.git/commitdiff
template: use panic/recover to simplify internal error handling.
authorRob Pike <r@golang.org>
Tue, 13 Apr 2010 20:28:08 +0000 (13:28 -0700)
committerRob Pike <r@golang.org>
Tue, 13 Apr 2010 20:28:08 +0000 (13:28 -0700)
R=rsc
CC=golang-dev
https://golang.org/cl/824049

src/pkg/template/template.go

index 54c22ba8df6e8fa5656d65965d56b25208ac8e12..2bf21610b9fad13386b02056b580bb28678f9f1b 100644 (file)
@@ -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