Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement string escape sequences #2803

Merged
merged 1 commit into from
Sep 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions compiler/noirc_frontend/src/lexer/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ pub enum LexerErrorKind {
LogicalAnd { span: Span },
#[error("Unterminated block comment")]
UnterminatedBlockComment { span: Span },
#[error("Unterminated string literal")]
UnterminatedStringLiteral { span: Span },
#[error(
"'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character."
)]
InvalidEscape { escaped: char, span: Span },
}

impl LexerErrorKind {
Expand All @@ -33,6 +39,8 @@ impl LexerErrorKind {
LexerErrorKind::TooManyBits { span, .. } => *span,
LexerErrorKind::LogicalAnd { span } => *span,
LexerErrorKind::UnterminatedBlockComment { span } => *span,
LexerErrorKind::UnterminatedStringLiteral { span } => *span,
LexerErrorKind::InvalidEscape { span, .. } => *span,
}
}

Expand All @@ -46,30 +54,30 @@ impl LexerErrorKind {
let found: String = found.map(Into::into).unwrap_or_else(|| "<eof>".into());

(
"an unexpected character was found".to_string(),
format!(" expected {expected} , but got {found}"),
"An unexpected character was found".to_string(),
format!("Expected {expected}, but found {found}"),
*span,
)
},
LexerErrorKind::NotADoubleChar { span, found } => (
format!("tried to parse {found} as double char"),
format!("Tried to parse {found} as double char"),
format!(
" {found:?} is not a double char, this is an internal error"
),
*span,
),
LexerErrorKind::InvalidIntegerLiteral { span, found } => (
"invalid integer literal".to_string(),
"Invalid integer literal".to_string(),
format!(" {found} is not an integer"),
*span,
),
LexerErrorKind::MalformedFuncAttribute { span, found } => (
"malformed function attribute".to_string(),
"Malformed function attribute".to_string(),
format!(" {found} is not a valid attribute"),
*span,
),
LexerErrorKind::TooManyBits { span, max, got } => (
"integer literal too large".to_string(),
"Integer literal too large".to_string(),
format!(
"The maximum number of bits needed to represent a field is {max}, This integer type needs {got} bits"
),
Expand All @@ -80,7 +88,11 @@ impl LexerErrorKind {
"Try `&` instead, or use `if` only if you require short-circuiting".to_string(),
*span,
),
LexerErrorKind::UnterminatedBlockComment { span } => ("unterminated block comment".to_string(), "Unterminated block comment".to_string(), *span),
LexerErrorKind::UnterminatedBlockComment { span } => ("Unterminated block comment".to_string(), "Unterminated block comment".to_string(), *span),
LexerErrorKind::UnterminatedStringLiteral { span } =>
("Unterminated string literal".to_string(), "Unterminated string literal".to_string(), *span),
LexerErrorKind::InvalidEscape { escaped, span } =>
(format!("'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character."), "Invalid escape sequence".to_string(), *span),
}
}
}
Expand Down
30 changes: 26 additions & 4 deletions compiler/noirc_frontend/src/lexer/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,12 +320,34 @@ impl<'a> Lexer<'a> {

fn eat_string_literal(&mut self) -> SpannedTokenResult {
let start = self.position;
let mut string = String::new();

while let Some(next) = self.next_char() {
let char = match next {
'"' => break,
'\\' => match self.next_char() {
Some('r') => '\r',
Some('n') => '\n',
Some('t') => '\t',
Some('0') => '\0',
Some('"') => '"',
Some('\\') => '\\',
Some(escaped) => {
let span = Span::inclusive(start, self.position);
return Err(LexerErrorKind::InvalidEscape { escaped, span });
}
None => {
let span = Span::inclusive(start, self.position);
return Err(LexerErrorKind::UnterminatedStringLiteral { span });
}
},
other => other,
};

let str_literal = self.eat_while(None, |ch| ch != '"');

let str_literal_token = Token::Str(str_literal);
string.push(char);
}

self.next_char(); // Advance past the closing quote
let str_literal_token = Token::Str(string);

let end = self.position;
Ok(str_literal_token.into_span(start, end))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn main(message : pub str<11>, y : Field, hex_as_string : str<4>, hex_as_field :
assert(y == 5); // Change to y != 5 to see how the later print statements are not called
std::println(array);

bad_message = "helld world";
bad_message = "hell\0\"world";
std::println(bad_message);
assert(message != bad_message);

Expand Down
Loading