From: Rob Pike Date: Sun, 15 May 2022 03:14:49 +0000 (+1000) Subject: text/template/parse: make atTerminator more efficient X-Git-Tag: go1.19beta1~224 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=053b63cfb56f574fd216166020066f3b48d5d878;p=gostls13.git 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í Reviewed-by: Dmitri Shuralyov Run-TryBot: Rob Pike TryBot-Result: Gopher Robot Reviewed-by: Ian Lance Taylor --- diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go index 078f714ccf..4c32d261f2 100644 --- a/src/text/template/parse/lex.go +++ b/src/text/template/parse/lex.go @@ -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