From 0c3a43e7b2fcbc99279de4a2d0c04b777360e381 Mon Sep 17 00:00:00 2001 From: Rob Pike <r@golang.org> Date: Wed, 6 May 2009 13:42:59 -0700 Subject: [PATCH] error handling had a bug in execute: the error channel was being shared. fix that and clean up state handling a little. R=rsc DELTA=44 (18 added, 8 deleted, 18 changed) OCL=28359 CL=28359 --- src/lib/template/template.go | 56 +++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/src/lib/template/template.go b/src/lib/template/template.go index 8ec0b26598..7519d16f01 100644 --- a/src/lib/template/template.go +++ b/src/lib/template/template.go @@ -142,13 +142,13 @@ type repeatedElement struct { // It is unchanged after parsing. type Template struct { fmap FormatterMap; // formatters for variables - errorchan chan os.Error; // for reporting errors during parse and execute // Used during parsing: ldelim, rdelim []byte; // delimiters; default {} buf []byte; // input text to process p int; // position in buf linenum int; // position in input - // Parsed state: + errors chan os.Error; // for error reporting during parsing (only) + // Parsed results: elems *vector.Vector; } @@ -159,6 +159,11 @@ type state struct { parent *state; // parent in hierarchy data reflect.Value; // the driver data for this section etc. wr io.Write; // 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} } // New creates a new template with the specified formatter map (which @@ -168,21 +173,25 @@ func New(fmap FormatterMap) *Template { t.fmap = fmap; t.ldelim = lbrace; t.rdelim = rbrace; - t.errorchan = make(chan os.Error); + t.errors = make(chan os.Error); t.elems = vector.New(0); return t; } -// Report error and stop parsing. The line number comes from the template state. -func (t *Template) parseError(err string, args ...) { - t.errorchan <- ParseError{fmt.Sprintf("line %d: %s", t.linenum, fmt.Sprintf(err, args))}; +// Generic error handler, called only from execError or parseError. +func error(errors chan os.Error, line int, err string, args ...) { + errors <- ParseError{fmt.Sprintf("line %d: %s", line, fmt.Sprintf(err, args))}; sys.Goexit(); } // Report error and stop executing. The line number must be provided explicitly. -func (t *Template) execError(line int, err string, args ...) { - t.errorchan <- ParseError{fmt.Sprintf("line %d: %s", line, fmt.Sprintf(err, args))}; - sys.Goexit(); +func (t *Template) execError(st *state, line int, err string, args ...) { + error(st.errors, line, err, args); +} + +// Report error and stop parsing. The line number comes from the template state. +func (t *Template) parseError(err string, args ...) { + error(t.errors, t.linenum, err, args) } // -- Lexical analysis @@ -589,7 +598,7 @@ func (t *Template) varValue(v *variableElement, st *state) reflect.Value { field := st.findVar(v.name); if field == nil { if st.parent == nil { - t.execError(t.linenum, "name not found: %s", v.name) + t.execError(st, t.linenum, "name not found: %s", v.name) } return t.varValue(v, st.parent); } @@ -613,7 +622,7 @@ func (t *Template) writeVariable(v *variableElement, st *state) { fn(st.wr, val, v.formatter); return; } - t.execError(v.linenum, "missing formatter %s for variable %s", v.formatter, v.name) + t.execError(st, v.linenum, "missing formatter %s for variable %s", v.formatter, v.name) } // execute{|Element|Section|Repeated} are mutually recursive @@ -640,7 +649,7 @@ func (t *Template) executeElement(i int, st *state) int { return elem.end; } e := t.elems.At(i); - t.execError(0, "internal error: bad directive in execute: %v %T\n", reflect.NewValue(e).Interface(), e); + t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.NewValue(e).Interface(), e); return 0 } @@ -656,9 +665,9 @@ func (t *Template) executeSection(s *sectionElement, st *state) { // Find driver data for this section. It must be in the current struct. field := st.findVar(s.field); if field == nil { - t.execError(s.linenum, ".section: cannot find field %s in %s", s.field, reflect.Indirect(st.data).Type()); + t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, reflect.Indirect(st.data).Type()); } - st = &state{st, field, st.wr}; + st = st.clone(field); start, end := s.start, s.or; if !empty(field, true) { // Execute the normal block. @@ -682,19 +691,19 @@ func (t *Template) executeRepeated(r *repeatedElement, st *state) { // Find driver data for this section. It must be in the current struct. field := st.findVar(r.field); if field == nil { - t.execError(r.linenum, ".repeated: cannot find field %s in %s", r.field, reflect.Indirect(st.data).Type()); + t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, reflect.Indirect(st.data).Type()); } field = reflect.Indirect(field); // Must be an array/slice if field != nil && field.Kind() != reflect.ArrayKind { - t.execError(r.linenum, ".repeated: %s has bad type %s", r.field, field.Type()); + t.execError(st, r.linenum, ".repeated: %s has bad type %s", r.field, field.Type()); } if empty(field, true) { // Execute the .or block, once. If it's missing, do nothing. start, end := r.or, r.end; if start >= 0 { - newst := &state{st, field, st.wr}; + newst := st.clone(field); for i := start; i < end; { i = t.executeElement(i, newst) } @@ -712,7 +721,7 @@ func (t *Template) executeRepeated(r *repeatedElement, st *state) { if field != nil { array := field.(reflect.ArrayValue); for j := 0; j < array.Len(); j++ { - newst := &state{st, array.Elem(j), st.wr}; + newst := st.clone(array.Elem(j)); for i := start; i < end; { i = t.executeElement(i, newst) } @@ -753,9 +762,9 @@ func (t *Template) Parse(s string) os.Error { t.linenum = 0; go func() { t.parse(); - t.errorchan <- nil; // clean return; + t.errors <- nil; // clean return; }(); - return <-t.errorchan; + return <-t.errors; } // Execute applies a parsed template to the specified data object, @@ -763,12 +772,13 @@ func (t *Template) Parse(s string) os.Error { func (t *Template) Execute(data interface{}, wr io.Write) 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}); - t.errorchan <- nil; // clean return; + t.execute(0, t.elems.Len(), &state{nil, val, wr, errors}); + errors <- nil; // clean return; }(); - return <-t.errorchan; + return <-errors; } // SetDelims sets the left and right delimiters for operations in the -- 2.30.9