in syntax/scan.go [827:908]
func (sc *scanner) scanString(val *tokenValue, quote rune) Token {
start := sc.pos
triple := len(sc.rest) >= 3 && sc.rest[0] == byte(quote) && sc.rest[1] == byte(quote) && sc.rest[2] == byte(quote)
sc.readRune()
// String literals may contain escaped or unescaped newlines,
// causing them to span multiple lines (gulps) of REPL input;
// they are the only such token. Thus we cannot call endToken,
// as it assumes sc.rest is unchanged since startToken.
// Instead, buffer the token here.
// TODO(adonovan): opt: buffer only if we encounter a newline.
raw := new(strings.Builder)
// Copy the prefix, e.g. r' or " (see startToken).
raw.Write(sc.token[:len(sc.token)-len(sc.rest)])
if !triple {
// single-quoted string literal
for {
if sc.eof() {
sc.error(val.pos, "unexpected EOF in string")
}
c := sc.readRune()
raw.WriteRune(c)
if c == quote {
break
}
if c == '\n' {
sc.error(val.pos, "unexpected newline in string")
}
if c == '\\' {
if sc.eof() {
sc.error(val.pos, "unexpected EOF in string")
}
c = sc.readRune()
raw.WriteRune(c)
}
}
} else {
// triple-quoted string literal
sc.readRune()
raw.WriteRune(quote)
sc.readRune()
raw.WriteRune(quote)
quoteCount := 0
for {
if sc.eof() {
sc.error(val.pos, "unexpected EOF in string")
}
c := sc.readRune()
raw.WriteRune(c)
if c == quote {
quoteCount++
if quoteCount == 3 {
break
}
} else {
quoteCount = 0
}
if c == '\\' {
if sc.eof() {
sc.error(val.pos, "unexpected EOF in string")
}
c = sc.readRune()
raw.WriteRune(c)
}
}
}
val.raw = raw.String()
s, _, isByte, err := unquote(val.raw)
if err != nil {
sc.error(start, err.Error())
}
val.string = s
if isByte {
return BYTES
} else {
return STRING
}
}