Commit b83350a2 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

Revert "text/template: efficient reporting of line numbers"

This reverts commit 794fb71d.

Reason for revert: submitted without TryBots and it broke all three race builders.

Change-Id: I80a1e566616f0ee8fa3529d4eeee04268f8a713b
Reviewed-on: https://go-review.googlesource.com/33232Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 2442b49c
...@@ -1152,7 +1152,7 @@ func TestUnterminatedStringError(t *testing.T) { ...@@ -1152,7 +1152,7 @@ func TestUnterminatedStringError(t *testing.T) {
t.Fatal("expected error") t.Fatal("expected error")
} }
str := err.Error() str := err.Error()
if !strings.Contains(str, "X:3: unexpected unterminated raw quoted string") { if !strings.Contains(str, "X:3: unexpected unterminated raw quoted strin") {
t.Fatalf("unexpected error: %s", str) t.Fatalf("unexpected error: %s", str)
} }
} }
......
...@@ -16,7 +16,6 @@ type item struct { ...@@ -16,7 +16,6 @@ type item struct {
typ itemType // The type of this item. typ itemType // The type of this item.
pos Pos // The starting position, in bytes, of this item in the input string. pos Pos // The starting position, in bytes, of this item in the input string.
val string // The value of this item. val string // The value of this item.
line int // The line number at the start of this item.
} }
func (i item) String() string { func (i item) String() string {
...@@ -117,7 +116,6 @@ type lexer struct { ...@@ -117,7 +116,6 @@ type lexer struct {
lastPos Pos // position of most recent item returned by nextItem lastPos Pos // position of most recent item returned by nextItem
items chan item // channel of scanned items items chan item // channel of scanned items
parenDepth int // nesting depth of ( ) exprs parenDepth int // nesting depth of ( ) exprs
line int // 1+number of newlines seen
} }
// next returns the next rune in the input. // next returns the next rune in the input.
...@@ -129,9 +127,6 @@ func (l *lexer) next() rune { ...@@ -129,9 +127,6 @@ func (l *lexer) next() rune {
r, w := utf8.DecodeRuneInString(l.input[l.pos:]) r, w := utf8.DecodeRuneInString(l.input[l.pos:])
l.width = Pos(w) l.width = Pos(w)
l.pos += l.width l.pos += l.width
if r == '\n' {
l.line++
}
return r return r
} }
...@@ -145,20 +140,11 @@ func (l *lexer) peek() rune { ...@@ -145,20 +140,11 @@ func (l *lexer) peek() rune {
// backup steps back one rune. Can only be called once per call of next. // backup steps back one rune. Can only be called once per call of next.
func (l *lexer) backup() { func (l *lexer) backup() {
l.pos -= l.width l.pos -= l.width
// Correct newline count.
if l.width == 1 && l.input[l.pos] == '\n' {
l.line--
}
} }
// emit passes an item back to the client. // emit passes an item back to the client.
func (l *lexer) emit(t itemType) { func (l *lexer) emit(t itemType) {
l.items <- item{t, l.start, l.input[l.start:l.pos], l.line} l.items <- item{t, l.start, l.input[l.start:l.pos]}
// Some items contain text internally. If so, count their newlines.
switch t {
case itemText, itemRawString, itemLeftDelim, itemRightDelim:
l.line += strings.Count(l.input[l.start:l.pos], "\n")
}
l.start = l.pos l.start = l.pos
} }
...@@ -183,10 +169,17 @@ func (l *lexer) acceptRun(valid string) { ...@@ -183,10 +169,17 @@ func (l *lexer) acceptRun(valid string) {
l.backup() l.backup()
} }
// lineNumber reports which line we're on, based on the position of
// the previous item returned by nextItem. Doing it this way
// means we don't have to worry about peek double counting.
func (l *lexer) lineNumber() int {
return 1 + strings.Count(l.input[:l.lastPos], "\n")
}
// errorf returns an error token and terminates the scan by passing // errorf returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.nextItem. // back a nil pointer that will be the next state, terminating l.nextItem.
func (l *lexer) errorf(format string, args ...interface{}) stateFn { func (l *lexer) errorf(format string, args ...interface{}) stateFn {
l.items <- item{itemError, l.start, fmt.Sprintf(format, args...), l.line} l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
return nil return nil
} }
...@@ -219,7 +212,6 @@ func lex(name, input, left, right string) *lexer { ...@@ -219,7 +212,6 @@ func lex(name, input, left, right string) *lexer {
leftDelim: left, leftDelim: left,
rightDelim: right, rightDelim: right,
items: make(chan item), items: make(chan item),
line: 1,
} }
go l.run() go l.run()
return l return l
...@@ -610,14 +602,10 @@ Loop: ...@@ -610,14 +602,10 @@ Loop:
// lexRawQuote scans a raw quoted string. // lexRawQuote scans a raw quoted string.
func lexRawQuote(l *lexer) stateFn { func lexRawQuote(l *lexer) stateFn {
startLine := l.line
Loop: Loop:
for { for {
switch l.next() { switch l.next() {
case eof: case eof:
// Restore line number to location of opening quote.
// We will error out so it's ok just to overwrite the field.
l.line = startLine
return l.errorf("unterminated raw quoted string") return l.errorf("unterminated raw quoted string")
case '`': case '`':
break Loop break Loop
......
This diff is collapsed.
...@@ -157,7 +157,7 @@ func (t *Tree) ErrorContext(n Node) (location, context string) { ...@@ -157,7 +157,7 @@ func (t *Tree) ErrorContext(n Node) (location, context string) {
// errorf formats the error and terminates processing. // errorf formats the error and terminates processing.
func (t *Tree) errorf(format string, args ...interface{}) { func (t *Tree) errorf(format string, args ...interface{}) {
t.Root = nil t.Root = nil
format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.line, format) format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.lineNumber(), format)
panic(fmt.Errorf(format, args...)) panic(fmt.Errorf(format, args...))
} }
...@@ -376,17 +376,15 @@ func (t *Tree) action() (n Node) { ...@@ -376,17 +376,15 @@ func (t *Tree) action() (n Node) {
return t.withControl() return t.withControl()
} }
t.backup() t.backup()
token := t.peek()
// Do not pop variables; they persist until "end". // Do not pop variables; they persist until "end".
return t.newAction(token.pos, token.line, t.pipeline("command")) return t.newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command"))
} }
// Pipeline: // Pipeline:
// declarations? command ('|' command)* // declarations? command ('|' command)*
func (t *Tree) pipeline(context string) (pipe *PipeNode) { func (t *Tree) pipeline(context string) (pipe *PipeNode) {
var decl []*VariableNode var decl []*VariableNode
token := t.peekNonSpace() pos := t.peekNonSpace().pos
pos := token.pos
// Are there declarations? // Are there declarations?
for { for {
if v := t.peekNonSpace(); v.typ == itemVariable { if v := t.peekNonSpace(); v.typ == itemVariable {
...@@ -415,7 +413,7 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) { ...@@ -415,7 +413,7 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) {
} }
break break
} }
pipe = t.newPipeline(pos, token.line, decl) pipe = t.newPipeline(pos, t.lex.lineNumber(), decl)
for { for {
switch token := t.nextNonSpace(); token.typ { switch token := t.nextNonSpace(); token.typ {
case itemRightDelim, itemRightParen: case itemRightDelim, itemRightParen:
...@@ -452,6 +450,7 @@ func (t *Tree) checkPipeline(pipe *PipeNode, context string) { ...@@ -452,6 +450,7 @@ func (t *Tree) checkPipeline(pipe *PipeNode, context string) {
func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) { func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
defer t.popVars(len(t.vars)) defer t.popVars(len(t.vars))
line = t.lex.lineNumber()
pipe = t.pipeline(context) pipe = t.pipeline(context)
var next Node var next Node
list, next = t.itemList() list, next = t.itemList()
...@@ -480,7 +479,7 @@ func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int ...@@ -480,7 +479,7 @@ func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int
t.errorf("expected end; found %s", next) t.errorf("expected end; found %s", next)
} }
} }
return pipe.Position(), pipe.Line, pipe, list, elseList return pipe.Position(), line, pipe, list, elseList
} }
// If: // If:
...@@ -522,10 +521,9 @@ func (t *Tree) elseControl() Node { ...@@ -522,10 +521,9 @@ func (t *Tree) elseControl() Node {
peek := t.peekNonSpace() peek := t.peekNonSpace()
if peek.typ == itemIf { if peek.typ == itemIf {
// We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ". // We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ".
return t.newElse(peek.pos, peek.line) return t.newElse(peek.pos, t.lex.lineNumber())
} }
token := t.expect(itemRightDelim, "else") return t.newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber())
return t.newElse(token.pos, token.line)
} }
// Block: // Block:
...@@ -552,7 +550,7 @@ func (t *Tree) blockControl() Node { ...@@ -552,7 +550,7 @@ func (t *Tree) blockControl() Node {
block.add() block.add()
block.stopParse() block.stopParse()
return t.newTemplate(token.pos, token.line, name, pipe) return t.newTemplate(token.pos, t.lex.lineNumber(), name, pipe)
} }
// Template: // Template:
...@@ -569,7 +567,7 @@ func (t *Tree) templateControl() Node { ...@@ -569,7 +567,7 @@ func (t *Tree) templateControl() Node {
// Do not pop variables; they persist until "end". // Do not pop variables; they persist until "end".
pipe = t.pipeline(context) pipe = t.pipeline(context)
} }
return t.newTemplate(token.pos, token.line, name, pipe) return t.newTemplate(token.pos, t.lex.lineNumber(), name, pipe)
} }
func (t *Tree) parseTemplateName(token item, context string) (name string) { func (t *Tree) parseTemplateName(token item, context string) (name string) {
......
...@@ -484,37 +484,3 @@ func TestBlock(t *testing.T) { ...@@ -484,37 +484,3 @@ func TestBlock(t *testing.T) {
t.Errorf("inner template = %q, want %q", g, w) t.Errorf("inner template = %q, want %q", g, w)
} }
} }
func TestLineNum(t *testing.T) {
const count = 100
text := strings.Repeat("{{printf 1234}}\n", count)
tree, err := New("bench").Parse(text, "", "", make(map[string]*Tree), builtins)
if err != nil {
t.Fatal(err)
}
// Check the line numbers. Each line is an action containing a template, followed by text.
// That's two nodes per line.
nodes := tree.Root.Nodes
for i := 0; i < len(nodes); i += 2 {
line := 1 + i/2
// Action first.
action := nodes[i].(*ActionNode)
if action.Line != line {
t.Fatalf("line %d: action is line %d", line, action.Line)
}
pipe := action.Pipe
if pipe.Line != line {
t.Fatalf("line %d: pipe is line %d", line, pipe.Line)
}
}
}
func BenchmarkParseLarge(b *testing.B) {
text := strings.Repeat("{{1234}}\n", 10000)
for i := 0; i < b.N; i++ {
_, err := New("bench").Parse(text, "", "", make(map[string]*Tree), builtins)
if err != nil {
b.Fatal(err)
}
}
}
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