Skip to content

Commit 053b63c

Browse files
committed
text/template/parse: make atTerminator more efficient
The change https://go.dev/cl/398475 was too complicated and expensive. Since the whole string is always available, all that's needed is a call to strings.HasPrefix. While we're here, change the way lexer.backup works so it can be called repeatedly to back up more than one rune, in case that becomes necessary. This change also requires less state to maintain, as lexer.width was only there for backup, and prevented multiple steps. Fixes #52191 Change-Id: I43b64fc66edeb8ba73ba5aa72f3b727c377dc067 Reviewed-on: https://go-review.googlesource.com/c/go/+/406476 Reviewed-by: Daniel Martí <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Run-TryBot: Rob Pike <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent a8facc4 commit 053b63c

File tree

1 file changed

+12
-29
lines changed

1 file changed

+12
-29
lines changed

src/text/template/parse/lex.go

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ type lexer struct {
118118
emitComment bool // emit itemComment tokens.
119119
pos Pos // current position in the input
120120
start Pos // start position of this item
121-
width Pos // width of last rune read from input
121+
atEOF bool // we have hit the end of input and returned eof
122122
items chan item // channel of scanned items
123123
parenDepth int // nesting depth of ( ) exprs
124124
line int // 1+number of newlines seen
@@ -130,12 +130,11 @@ type lexer struct {
130130
// next returns the next rune in the input.
131131
func (l *lexer) next() rune {
132132
if int(l.pos) >= len(l.input) {
133-
l.width = 0
133+
l.atEOF = true
134134
return eof
135135
}
136136
r, w := utf8.DecodeRuneInString(l.input[l.pos:])
137-
l.width = Pos(w)
138-
l.pos += l.width
137+
l.pos += Pos(w)
139138
if r == '\n' {
140139
l.line++
141140
}
@@ -149,12 +148,15 @@ func (l *lexer) peek() rune {
149148
return r
150149
}
151150

152-
// backup steps back one rune. Can only be called once per call of next.
151+
// backup steps back one rune.
153152
func (l *lexer) backup() {
154-
l.pos -= l.width
155-
// Correct newline count.
156-
if l.width == 1 && l.input[l.pos] == '\n' {
157-
l.line--
153+
if !l.atEOF && l.pos > 0 {
154+
r, w := utf8.DecodeLastRuneInString(l.input[:l.pos])
155+
l.pos -= Pos(w)
156+
// Correct newline count.
157+
if r == '\n' {
158+
l.line--
159+
}
158160
}
159161
}
160162

@@ -249,7 +251,6 @@ const (
249251

250252
// lexText scans until an opening action delimiter, "{{".
251253
func lexText(l *lexer) stateFn {
252-
l.width = 0
253254
if x := strings.Index(l.input[l.pos:], l.leftDelim); x >= 0 {
254255
ldn := Pos(len(l.leftDelim))
255256
l.pos += Pos(x)
@@ -541,25 +542,7 @@ func (l *lexer) atTerminator() bool {
541542
case eof, '.', ',', '|', ':', ')', '(':
542543
return true
543544
}
544-
// Are we at a right delimiter? TODO: This is harder than it should be
545-
// because lookahead is only one rune.
546-
rightDelim := l.rightDelim
547-
defer func(pos Pos, line int) {
548-
l.pos = pos
549-
l.line = line
550-
}(l.pos, l.line)
551-
for len(rightDelim) > 0 {
552-
rNext := l.next()
553-
if rNext == eof {
554-
return false
555-
}
556-
rDelim, size := utf8.DecodeRuneInString(rightDelim)
557-
if rNext != rDelim {
558-
return false
559-
}
560-
rightDelim = rightDelim[size:]
561-
}
562-
return true
545+
return strings.HasPrefix(l.input[l.pos:], l.rightDelim)
563546
}
564547

565548
// lexChar scans a character constant. The initial quote is already

0 commit comments

Comments
 (0)