Fix empty item handling in Split function, rewrite Words function

Test: treehugger
Bug: 172923994
Change-Id: Ic7ee7b1af6e1438df5cf06754b9bec7038b624f2
This commit is contained in:
Sasha Smundak
2020-11-29 12:51:55 -08:00
parent c08f602f31
commit cbc17ee62d
2 changed files with 209 additions and 107 deletions

View File

@@ -15,8 +15,10 @@
package parser
import (
"fmt"
"strings"
"unicode"
"unicode/utf8"
)
// A MakeString is a string that may contain variable substitutions in it.
@@ -130,8 +132,85 @@ func (ms *MakeString) SplitN(sep string, n int) []*MakeString {
})
}
// Words splits MakeString into multiple makeStrings separated by whitespace.
// Thus, " a $(X)b c " will be split into ["a", "$(X)b", "c"].
// Splitting a MakeString consisting solely of whitespace yields empty array.
func (ms *MakeString) Words() []*MakeString {
return ms.splitNFunc(-1, splitWords)
var ch rune // current character
const EOF = -1 // no more characters
const EOS = -2 // at the end of a string chunk
// Next character's chunk and position
iString := 0
iChar := 0
var words []*MakeString
word := SimpleMakeString("", ms.Pos())
nextChar := func() {
if iString >= len(ms.Strings) {
ch = EOF
} else if iChar >= len(ms.Strings[iString]) {
iString++
iChar = 0
ch = EOS
} else {
var w int
ch, w = utf8.DecodeRuneInString(ms.Strings[iString][iChar:])
iChar += w
}
}
appendVariableAndAdvance := func() {
if iString-1 < len(ms.Variables) {
word.appendVariable(ms.Variables[iString-1])
}
nextChar()
}
appendCharAndAdvance := func(c rune) {
if c != EOF {
word.appendString(string(c))
}
nextChar()
}
nextChar()
for ch != EOF {
// Skip whitespace
for ch == ' ' || ch == '\t' {
nextChar()
}
if ch == EOS {
// "... $(X)... " case. The current word should be empty.
if !word.Empty() {
panic(fmt.Errorf("%q: EOS while current word %q is not empty, iString=%d",
ms.Dump(), word.Dump(), iString))
}
appendVariableAndAdvance()
}
// Copy word
for ch != EOF {
if ch == ' ' || ch == '\t' {
words = append(words, word)
word = SimpleMakeString("", ms.Pos())
break
}
if ch == EOS {
// "...a$(X)..." case. Append variable to the current word
appendVariableAndAdvance()
} else {
if ch == '\\' {
appendCharAndAdvance('\\')
}
appendCharAndAdvance(ch)
}
}
}
if !word.Empty() {
words = append(words, word)
}
return words
}
func (ms *MakeString) splitNFunc(n int, splitFunc func(s string, n int) []string) []*MakeString {
@@ -166,9 +245,7 @@ func (ms *MakeString) splitNFunc(n int, splitFunc func(s string, n int) []string
}
}
if !curMs.Empty() {
ret = append(ret, curMs)
}
ret = append(ret, curMs)
return ret
}
@@ -219,44 +296,6 @@ func splitAnyN(s, sep string, n int) []string {
return ret
}
func splitWords(s string, n int) []string {
ret := []string{}
preserve := ""
for n == -1 || n > 1 {
index := strings.IndexAny(s, " \t")
if index == 0 && len(preserve) == 0 {
s = s[1:]
} else if index >= 0 {
escapeCount := 0
for i := index - 1; i >= 0; i-- {
if s[i] != '\\' {
break
}
escapeCount += 1
}
if escapeCount%2 == 1 {
preserve += s[0 : index+1]
s = s[index+1:]
continue
}
ret = append(ret, preserve+s[0:index])
s = s[index+1:]
preserve = ""
if n > 0 {
n--
}
} else {
break
}
}
if preserve != "" || s != "" || len(ret) == 0 {
ret = append(ret, preserve+s)
}
return ret
}
func unescape(s string) string {
ret := ""
for {