]> Cypherpunks repositories - gostls13.git/commitdiff
text/template/parse: make atTerminator more efficient
authorRob Pike <r@golang.org>
Sun, 15 May 2022 03:14:49 +0000 (13:14 +1000)
committerRob Pike <r@golang.org>
Mon, 16 May 2022 21:02:09 +0000 (21:02 +0000)
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í <mvdan@mvdan.cc>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Rob Pike <r@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
src/text/template/parse/lex.go

index 078f714ccf5bfa95186d81b5af87ce55ff5d0dd6..4c32d261f25b5e353e139604e5873408ad093714 100644 (file)
@@ -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
@@ -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++
        }
@@ -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--
+               }
        }
 }
 
@@ -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)
@@ -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