Commit 236a9de9 authored by Rob Pike's avatar Rob Pike

remove goroutines from template parsing.

fix up one usage to take advantage.

R=rsc
CC=go-dev
http://go/go-review/1018023
parent f32cde88
...@@ -12,7 +12,6 @@ package rpc ...@@ -12,7 +12,6 @@ package rpc
import ( import (
"fmt"; "fmt";
"http"; "http";
"os";
"sort"; "sort";
"template"; "template";
) )
...@@ -37,7 +36,7 @@ const debugText = `<html> ...@@ -37,7 +36,7 @@ const debugText = `<html>
</body> </body>
</html>` </html>`
var debug *template.Template var debug = template.MustParse(debugText, nil)
type debugMethod struct { type debugMethod struct {
m *methodType; m *methodType;
...@@ -76,14 +75,6 @@ func (m methodArray) Swap(i, j int) { ...@@ -76,14 +75,6 @@ func (m methodArray) Swap(i, j int) {
// Runs at /debug/rpc // Runs at /debug/rpc
func debugHTTP(c *http.Conn, req *http.Request) { func debugHTTP(c *http.Conn, req *http.Request) {
var err os.Error;
if debug == nil {
debug, err = template.Parse(debugText, nil);
if err != nil {
fmt.Fprintln(c, "rpc can't create debug HTML template:", err.String());
return;
}
}
// Build a sorted version of the data. // Build a sorted version of the data.
var services = make(serviceArray, len(server.serviceMap)); var services = make(serviceArray, len(server.serviceMap));
i := 0; i := 0;
...@@ -100,7 +91,7 @@ func debugHTTP(c *http.Conn, req *http.Request) { ...@@ -100,7 +91,7 @@ func debugHTTP(c *http.Conn, req *http.Request) {
} }
server.Unlock(); server.Unlock();
sort.Sort(services); sort.Sort(services);
err = debug.Execute(services, c); err := debug.Execute(services, c);
if err != nil { if err != nil {
fmt.Fprintln(c, "rpc: error executing template:", err.String()); fmt.Fprintln(c, "rpc: error executing template:", err.String());
} }
......
...@@ -151,7 +151,7 @@ type Template struct { ...@@ -151,7 +151,7 @@ type Template struct {
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
errors chan os.Error; // for error reporting during parsing (only) error os.Error; // error during parsing (only)
// Parsed results: // Parsed results:
elems *vector.Vector; elems *vector.Vector;
} }
...@@ -177,25 +177,20 @@ func New(fmap FormatterMap) *Template { ...@@ -177,25 +177,20 @@ func New(fmap FormatterMap) *Template {
t.fmap = fmap; t.fmap = fmap;
t.ldelim = lbrace; t.ldelim = lbrace;
t.rdelim = rbrace; t.rdelim = rbrace;
t.errors = make(chan os.Error);
t.elems = vector.New(0); t.elems = vector.New(0);
return t; return t;
} }
// Generic error handler, called only from execError or parseError. // Report error and stop executing. The line number must be provided explicitly.
func error(errors chan os.Error, line int, err string, args ...) {
errors <- &Error{line, fmt.Sprintf(err, args)};
runtime.Goexit();
}
// Report error and stop executing. The line number must be provided explicitly.
func (t *Template) execError(st *state, line int, err string, args ...) { func (t *Template) execError(st *state, line int, err string, args ...) {
error(st.errors, line, err, args); st.errors <- &Error{line, fmt.Sprintf(err, args)};
runtime.Goexit();
} }
// Report error and stop parsing. The line number comes from the template state. // Report error, save in Template to terminate parsing.
// The line number comes from the template state.
func (t *Template) parseError(err string, args ...) { func (t *Template) parseError(err string, args ...) {
error(t.errors, t.linenum, err, args) t.error = &Error{t.linenum, fmt.Sprintf(err, args)};
} }
// -- Lexical analysis // -- Lexical analysis
...@@ -261,7 +256,8 @@ Loop: ...@@ -261,7 +256,8 @@ Loop:
i = j - 1; i = j - 1;
case equal(t.buf, i, t.rdelim): case equal(t.buf, i, t.rdelim):
if !sawLeft { if !sawLeft {
t.parseError("unmatched closing delimiter") t.parseError("unmatched closing delimiter");
return nil;
} }
sawLeft = false; sawLeft = false;
i += len(t.rdelim); i += len(t.rdelim);
...@@ -271,7 +267,8 @@ Loop: ...@@ -271,7 +267,8 @@ Loop:
} }
} }
if sawLeft { if sawLeft {
t.parseError("unmatched opening delimiter") t.parseError("unmatched opening delimiter");
return nil;
} }
item := t.buf[start:i]; item := t.buf[start:i];
if special && trim_white { if special && trim_white {
...@@ -322,13 +319,15 @@ func (t *Template) analyze(item []byte) (tok int, w []string) { ...@@ -322,13 +319,15 @@ func (t *Template) analyze(item []byte) (tok int, w []string) {
// item is known to be non-empty // item is known to be non-empty
if !equal(item, 0, t.ldelim) { // doesn't start with left delimiter if !equal(item, 0, t.ldelim) { // doesn't start with left delimiter
tok = tokText; tok = tokText;
return return;
} }
if !equal(item, len(item)-len(t.rdelim), t.rdelim) { // doesn't end with right delimiter if !equal(item, len(item)-len(t.rdelim), t.rdelim) { // doesn't end with right delimiter
t.parseError("internal error: unmatched opening delimiter") // lexing should prevent this t.parseError("internal error: unmatched opening delimiter"); // lexing should prevent this
return;
} }
if len(item) <= len(t.ldelim)+len(t.rdelim) { // no contents if len(item) <= len(t.ldelim)+len(t.rdelim) { // no contents
t.parseError("empty directive") t.parseError("empty directive");
return;
} }
// Comment // Comment
if item[len(t.ldelim)] == '#' { if item[len(t.ldelim)] == '#' {
...@@ -338,7 +337,8 @@ func (t *Template) analyze(item []byte) (tok int, w []string) { ...@@ -338,7 +337,8 @@ func (t *Template) analyze(item []byte) (tok int, w []string) {
// Split into words // Split into words
w = words(item[len(t.ldelim): len(item)-len(t.rdelim)]); // drop final delimiter w = words(item[len(t.ldelim): len(item)-len(t.rdelim)]); // drop final delimiter
if len(w) == 0 { if len(w) == 0 {
t.parseError("empty directive") t.parseError("empty directive");
return;
} }
if len(w) == 1 && w[0][0] != '.' { if len(w) == 1 && w[0][0] != '.' {
tok = tokVariable; tok = tokVariable;
...@@ -356,19 +356,22 @@ func (t *Template) analyze(item []byte) (tok int, w []string) { ...@@ -356,19 +356,22 @@ func (t *Template) analyze(item []byte) (tok int, w []string) {
return; return;
case ".section": case ".section":
if len(w) != 2 { if len(w) != 2 {
t.parseError("incorrect fields for .section: %s", item) t.parseError("incorrect fields for .section: %s", item);
return;
} }
tok = tokSection; tok = tokSection;
return; return;
case ".repeated": case ".repeated":
if len(w) != 3 || w[1] != "section" { if len(w) != 3 || w[1] != "section" {
t.parseError("incorrect fields for .repeated: %s", item) t.parseError("incorrect fields for .repeated: %s", item);
return;
} }
tok = tokRepeated; tok = tokRepeated;
return; return;
case ".alternates": case ".alternates":
if len(w) != 2 || w[1] != "with" { if len(w) != 2 || w[1] != "with" {
t.parseError("incorrect fields for .alternates: %s", item) t.parseError("incorrect fields for .alternates: %s", item);
return;
} }
tok = tokAlternates; tok = tokAlternates;
return; return;
...@@ -413,6 +416,9 @@ func (t *Template) newVariable(name_formatter string) (v *variableElement) { ...@@ -413,6 +416,9 @@ 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:
...@@ -432,6 +438,7 @@ func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) { ...@@ -432,6 +438,7 @@ 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:
...@@ -454,12 +461,19 @@ func (t *Template) parseRepeated(words []string) *repeatedElement { ...@@ -454,12 +461,19 @@ func (t *Template) parseRepeated(words []string) *repeatedElement {
r.altstart = -1; r.altstart = -1;
r.altend = -1; r.altend = -1;
Loop: Loop:
for { for t.error == nil {
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;
} }
done, tok, w := t.parseSimple(item); done, tok, w := t.parseSimple(item);
if t.error != nil {
break;
}
if done { if done {
continue continue
} }
...@@ -469,6 +483,7 @@ Loop: ...@@ -469,6 +483,7 @@ Loop:
case tokOr: case tokOr:
if r.or >= 0 { if r.or >= 0 {
t.parseError("extra .or in .repeated section"); t.parseError("extra .or in .repeated section");
break Loop;
} }
r.altend = t.elems.Len(); r.altend = t.elems.Len();
r.or = t.elems.Len(); r.or = t.elems.Len();
...@@ -479,15 +494,21 @@ Loop: ...@@ -479,15 +494,21 @@ Loop:
case tokAlternates: case tokAlternates:
if r.altstart >= 0 { if r.altstart >= 0 {
t.parseError("extra .alternates in .repeated section"); t.parseError("extra .alternates in .repeated section");
break Loop;
} }
if r.or >= 0 { if r.or >= 0 {
t.parseError(".alternates inside .or block in .repeated section"); t.parseError(".alternates inside .or block in .repeated section");
break Loop;
} }
r.altstart = t.elems.Len(); r.altstart = t.elems.Len();
default: default:
t.parseError("internal error: unknown repeated section item: %s", item); t.parseError("internal error: unknown repeated section item: %s", item);
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()
} }
...@@ -504,12 +525,19 @@ func (t *Template) parseSection(words []string) *sectionElement { ...@@ -504,12 +525,19 @@ 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 { for t.error == nil {
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;
} }
done, tok, w := t.parseSimple(item); done, tok, w := t.parseSimple(item);
if t.error != nil {
break;
}
if done { if done {
continue continue
} }
...@@ -519,6 +547,7 @@ Loop: ...@@ -519,6 +547,7 @@ Loop:
case tokOr: case tokOr:
if s.or >= 0 { if s.or >= 0 {
t.parseError("extra .or in .section"); t.parseError("extra .or in .section");
break Loop;
} }
s.or = t.elems.Len(); s.or = t.elems.Len();
case tokSection: case tokSection:
...@@ -531,13 +560,19 @@ Loop: ...@@ -531,13 +560,19 @@ 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 { for t.error == nil {
item := t.nextItem(); item := t.nextItem();
if t.error != nil {
break
}
if len(item) == 0 { if len(item) == 0 {
break break
} }
...@@ -816,11 +851,8 @@ func (t *Template) Parse(s string) os.Error { ...@@ -816,11 +851,8 @@ func (t *Template) Parse(s string) os.Error {
t.buf = strings.Bytes(s); t.buf = strings.Bytes(s);
t.p = 0; t.p = 0;
t.linenum = 0; t.linenum = 0;
go func() { t.parse();
t.parse(); return t.error;
t.errors <- nil; // clean return;
}();
return <-t.errors;
} }
// Execute applies a parsed template to the specified data object, // Execute applies a parsed template to the specified data object,
...@@ -855,5 +887,17 @@ func (t *Template) SetDelims(left, right string) { ...@@ -855,5 +887,17 @@ func (t *Template) SetDelims(left, right string) {
func Parse(s string, fmap FormatterMap) (t *Template, err os.Error) { func Parse(s string, fmap FormatterMap) (t *Template, err os.Error) {
t = New(fmap); t = New(fmap);
err = t.Parse(s); err = t.Parse(s);
if err != nil {
t = nil
}
return return
} }
// MustParse is like Parse but panics if the template cannot be parsed.
func MustParse(s string, fmap FormatterMap) *Template {
t , err := Parse(s, fmap);
if err != nil {
panic("template parse error: ", err);
}
return t
}
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