}
// nextItem returns the next item from the input.
+// Called by the parser, not in the lexing goroutine.
func (l *lexer) nextItem() item {
item := <-l.items
l.lastPos = item.pos
return item
}
+// drain drains the output so the lexing goroutine will exit.
+// Called by the parser, not in the lexing goroutine.
+func (l *lexer) drain() {
+ if l == nil {
+ return
+ }
+ for range l.items {
+ }
+}
+
// lex creates a new scanner for the input string.
func lex(name, input, left, right string) *lexer {
if left == "" {
for l.state = lexText; l.state != nil; {
l.state = l.state(l)
}
+ close(l.items)
}
// state functions
}
}
}
+
+// Test that an error shuts down the lexing goroutine.
+func TestShutdown(t *testing.T) {
+ // We need to duplicate template.Parse here to hold on to the lexer.
+ const text = "erroneous{{define}}{{else}}1234"
+ lexer := lex("foo", text, "{{", "}}")
+ _, err := New("root").parseLexer(lexer, text)
+ if err == nil {
+ t.Fatalf("expected error")
+ }
+ // The error should have drained the input. Therefore, the lexer should be shut down.
+ token, ok := <-lexer.items
+ if ok {
+ t.Fatalf("input was not drained; got %v", token)
+ }
+}
+
+// parseLexer is a local version of parse that lets us pass in the lexer instead of building it.
+// We expect an error, so the tree set and funcs list are explicitly nil.
+func (t *Tree) parseLexer(lex *lexer, text string) (tree *Tree, err error) {
+ defer t.recover(&err)
+ t.ParseName = t.Name
+ t.startParse(nil, lex)
+ t.parse(nil)
+ t.add(nil)
+ t.stopParse()
+ return t, nil
+}