in starlark/src/syntax/lexer.rs [271:376]
fn string(&mut self, triple: bool, raw: bool, mut stop: impl FnMut(char) -> bool) -> Lexeme {
// We have seen an openning quote, which is either ' or "
// If triple is true, it was a triple quote
// stop lets us know when a string ends.
// Before the first quote character
let string_start = self.lexer.span().start;
// After the first quote character, but before any contents or it tracked stuff
let mut string_end = self.lexer.span().end;
let mut it = CursorBytes::new(self.lexer.remainder());
let it2;
if triple {
it.next();
it.next();
}
let contents_start = it.pos();
// Take the fast path as long as the result is a slice of the original, with no changes.
let mut res;
loop {
match it.next_char() {
None => {
return self.err_span(
LexemeError::UnfinishedStringLiteral,
string_start,
string_end + it.pos(),
);
}
Some(c) => {
if stop(c) {
let contents_end = it.pos() - if triple { 3 } else { 1 };
let contents = &self.lexer.remainder()[contents_start..contents_end];
self.lexer.bump(it.pos());
return Ok((
string_start,
Token::String(contents.to_owned()),
string_end + it.pos(),
));
} else if c == '\\' || c == '\r' || (c == '\n' && !triple) {
res = String::with_capacity(it.pos() + 10);
res.push_str(&self.lexer.remainder()[contents_start..it.pos() - 1]);
it2 = CursorChars::new_offset(self.lexer.remainder(), it.pos() - 1);
break;
}
}
}
}
// We bailed out of the fast path, that means we now accumulate character by character,
// might have an error or be dealing with escape characters.
let mut it = it2;
while let Some(c) = it.next() {
if stop(c) {
self.lexer.bump(it.pos());
if triple {
res.truncate(res.len() - 2);
}
return Ok((string_start, Token::String(res), string_end + it.pos()));
}
match c {
'\n' if !triple => {
// Will raise an error about out of chars.
// But don't include the final \n in the count.
string_end -= 1;
break;
}
'\r' => {
// We just ignore these in all modes
}
'\\' => {
if raw {
match it.next() {
Some(c) => {
if c != '\'' && c != '"' {
res.push('\\');
}
res.push(c);
}
_ => break, // Out of chars
}
} else {
let pos = it.pos();
if Self::escape(&mut it, &mut res).is_err() {
return self.err_span(
LexemeError::InvalidEscapeSequence(
self.lexer.remainder()[pos..it.pos()].to_owned(),
),
string_end + pos - 1,
string_end + it.pos(),
);
}
}
}
c => res.push(c),
}
}
// We ran out of characters
self.err_span(
LexemeError::UnfinishedStringLiteral,
string_start,
string_end + it.pos(),
)
}