Commit effddcad authored by Rob Pike's avatar Rob Pike

template: use panic/recover to simplify internal error handling.

R=rsc
CC=golang-dev
https://golang.org/cl/824049
parent cd242fb4
...@@ -69,7 +69,6 @@ import ( ...@@ -69,7 +69,6 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"reflect" "reflect"
"runtime"
"strings" "strings"
) )
...@@ -153,11 +152,10 @@ type repeatedElement struct { ...@@ -153,11 +152,10 @@ type repeatedElement struct {
type Template struct { type Template struct {
fmap FormatterMap // formatters for variables fmap FormatterMap // formatters for variables
// Used during parsing: // Used during parsing:
ldelim, rdelim []byte // delimiters; default {} ldelim, rdelim []byte // delimiters; default {}
buf []byte // input text to process buf []byte // input text to process
p int // position in buf p int // position in buf
linenum int // position in input linenum int // position in input
error os.Error // error during parsing (only)
// Parsed results: // Parsed results:
elems *vector.Vector elems *vector.Vector
} }
...@@ -169,11 +167,10 @@ type state struct { ...@@ -169,11 +167,10 @@ type state struct {
parent *state // parent in hierarchy parent *state // parent in hierarchy
data reflect.Value // the driver data for this section etc. data reflect.Value // the driver data for this section etc.
wr io.Writer // where to send output wr io.Writer // where to send output
errors chan os.Error // for reporting errors during execute
} }
func (parent *state) clone(data reflect.Value) *state { 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 // New creates a new template with the specified formatter map (which
...@@ -189,14 +186,13 @@ func New(fmap FormatterMap) *Template { ...@@ -189,14 +186,13 @@ func New(fmap FormatterMap) *Template {
// Report error and stop executing. The line number must be provided explicitly. // Report error and stop executing. The line number must be provided explicitly.
func (t *Template) execError(st *state, line int, err string, args ...interface{}) { func (t *Template) execError(st *state, line int, err string, args ...interface{}) {
st.errors <- &Error{line, fmt.Sprintf(err, args)} panic(&Error{line, fmt.Sprintf(err, args)})
runtime.Goexit()
} }
// Report error, save in Template to terminate parsing. // Report error, panic to terminate parsing.
// The line number comes from the template state. // The line number comes from the template state.
func (t *Template) parseError(err string, args ...interface{}) { 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 // -- Lexical analysis
...@@ -427,9 +423,6 @@ func (t *Template) newVariable(name_formatter string) (v *variableElement) { ...@@ -427,9 +423,6 @@ func (t *Template) newVariable(name_formatter string) (v *variableElement) {
// Otherwise return its details. // Otherwise return its details.
func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) { func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) {
tok, w = t.analyze(item) tok, w = t.analyze(item)
if t.error != nil {
return
}
done = true // assume for simplicity done = true // assume for simplicity
switch tok { switch tok {
case tokComment: case tokComment:
...@@ -449,7 +442,6 @@ func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) { ...@@ -449,7 +442,6 @@ func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) {
t.elems.Push(&literalElement{tab}) t.elems.Push(&literalElement{tab})
default: default:
t.parseError("internal error: unknown literal: %s", w[0]) t.parseError("internal error: unknown literal: %s", w[0])
return
} }
return return
case tokVariable: case tokVariable:
...@@ -472,19 +464,13 @@ func (t *Template) parseRepeated(words []string) *repeatedElement { ...@@ -472,19 +464,13 @@ func (t *Template) parseRepeated(words []string) *repeatedElement {
r.altstart = -1 r.altstart = -1
r.altend = -1 r.altend = -1
Loop: Loop:
for t.error == nil { for {
item := t.nextItem() item := t.nextItem()
if t.error != nil {
break
}
if len(item) == 0 { if len(item) == 0 {
t.parseError("missing .end for .repeated section") t.parseError("missing .end for .repeated section")
break break
} }
done, tok, w := t.parseSimple(item) done, tok, w := t.parseSimple(item)
if t.error != nil {
break
}
if done { if done {
continue continue
} }
...@@ -517,9 +503,6 @@ Loop: ...@@ -517,9 +503,6 @@ Loop:
break Loop break Loop
} }
} }
if t.error != nil {
return nil
}
if r.altend < 0 { if r.altend < 0 {
r.altend = t.elems.Len() r.altend = t.elems.Len()
} }
...@@ -536,19 +519,13 @@ func (t *Template) parseSection(words []string) *sectionElement { ...@@ -536,19 +519,13 @@ func (t *Template) parseSection(words []string) *sectionElement {
s.start = t.elems.Len() s.start = t.elems.Len()
s.or = -1 s.or = -1
Loop: Loop:
for t.error == nil { for {
item := t.nextItem() item := t.nextItem()
if t.error != nil {
break
}
if len(item) == 0 { if len(item) == 0 {
t.parseError("missing .end for .section") t.parseError("missing .end for .section")
break break
} }
done, tok, w := t.parseSimple(item) done, tok, w := t.parseSimple(item)
if t.error != nil {
break
}
if done { if done {
continue continue
} }
...@@ -571,19 +548,13 @@ Loop: ...@@ -571,19 +548,13 @@ Loop:
t.parseError("internal error: unknown section item: %s", item) t.parseError("internal error: unknown section item: %s", item)
} }
} }
if t.error != nil {
return nil
}
s.end = t.elems.Len() s.end = t.elems.Len()
return s return s
} }
func (t *Template) parse() { func (t *Template) parse() {
for t.error == nil { for {
item := t.nextItem() item := t.nextItem()
if t.error != nil {
break
}
if len(item) == 0 { if len(item) == 0 {
break break
} }
...@@ -909,37 +880,48 @@ func validDelim(d []byte) bool { ...@@ -909,37 +880,48 @@ func validDelim(d []byte) bool {
return true 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 // -- Public interface
// Parse initializes a Template by parsing its definition. The string // Parse initializes a Template by parsing its definition. The string
// s contains the template text. If any errors occur, Parse returns // s contains the template text. If any errors occur, Parse returns
// the error. // the error.
func (t *Template) Parse(s string) os.Error { func (t *Template) Parse(s string) (err os.Error) {
if t.elems == nil { if t.elems == nil {
return &Error{1, "template not allocated with New"} return &Error{1, "template not allocated with New"}
} }
if !validDelim(t.ldelim) || !validDelim(t.rdelim) { if !validDelim(t.ldelim) || !validDelim(t.rdelim) {
return &Error{1, fmt.Sprintf("bad delimiter strings %q %q", t.ldelim, t.rdelim)} return &Error{1, fmt.Sprintf("bad delimiter strings %q %q", t.ldelim, t.rdelim)}
} }
defer checkError(&err)
t.buf = []byte(s) t.buf = []byte(s)
t.p = 0 t.p = 0
t.linenum = 1 t.linenum = 1
t.parse() t.parse()
return t.error return nil
} }
// Execute applies a parsed template to the specified data object, // Execute applies a parsed template to the specified data object,
// generating output to wr. // 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. // Extract the driver data.
val := reflect.NewValue(data) val := reflect.NewValue(data)
errors := make(chan os.Error) defer checkError(&err)
go func() { t.p = 0
t.p = 0 t.execute(0, t.elems.Len(), &state{nil, val, wr})
t.execute(0, t.elems.Len(), &state{nil, val, wr, errors}) return nil
errors <- nil // clean return;
}()
return <-errors
} }
// SetDelims sets the left and right delimiters for operations in the // SetDelims sets the left and right delimiters for operations in the
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment