Skip to content

Commit

Permalink
text/template/parse: make atTerminator more efficient
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
robpike committed May 16, 2022
1 parent a8facc4 commit 053b63c
Showing 1 changed file with 12 additions and 29 deletions.
41 changes: 12 additions & 29 deletions src/text/template/parse/lex.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ type lexer struct {
emitComment bool // emit itemComment tokens.
pos Pos // current position in the input
start Pos // start position of this item
width Pos // width of last rune read from input
atEOF bool // we have hit the end of input and returned eof
items chan item // channel of scanned items
parenDepth int // nesting depth of ( ) exprs
line int // 1+number of newlines seen
Expand All @@ -130,12 +130,11 @@ type lexer struct {
// next returns the next rune in the input.
func (l *lexer) next() rune {
if int(l.pos) >= len(l.input) {
l.width = 0
l.atEOF = true
return eof
}
r, w := utf8.DecodeRuneInString(l.input[l.pos:])
l.width = Pos(w)
l.pos += l.width
l.pos += Pos(w)
if r == '\n' {
l.line++
}
Expand All @@ -149,12 +148,15 @@ func (l *lexer) peek() rune {
return r
}

// backup steps back one rune. Can only be called once per call of next.
// backup steps back one rune.
func (l *lexer) backup() {
l.pos -= l.width
// Correct newline count.
if l.width == 1 && l.input[l.pos] == '\n' {
l.line--
if !l.atEOF && l.pos > 0 {
r, w := utf8.DecodeLastRuneInString(l.input[:l.pos])
l.pos -= Pos(w)
// Correct newline count.
if r == '\n' {
l.line--
}
}
}

Expand Down Expand Up @@ -249,7 +251,6 @@ const (

// lexText scans until an opening action delimiter, "{{".
func lexText(l *lexer) stateFn {
l.width = 0
if x := strings.Index(l.input[l.pos:], l.leftDelim); x >= 0 {
ldn := Pos(len(l.leftDelim))
l.pos += Pos(x)
Expand Down Expand Up @@ -541,25 +542,7 @@ func (l *lexer) atTerminator() bool {
case eof, '.', ',', '|', ':', ')', '(':
return true
}
// Are we at a right delimiter? TODO: This is harder than it should be
// because lookahead is only one rune.
rightDelim := l.rightDelim
defer func(pos Pos, line int) {
l.pos = pos
l.line = line
}(l.pos, l.line)
for len(rightDelim) > 0 {
rNext := l.next()
if rNext == eof {
return false
}
rDelim, size := utf8.DecodeRuneInString(rightDelim)
if rNext != rDelim {
return false
}
rightDelim = rightDelim[size:]
}
return true
return strings.HasPrefix(l.input[l.pos:], l.rightDelim)
}

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

0 comments on commit 053b63c

Please sign in to comment.