Skip to content

Commit

Permalink
Add unclosed quote error
Browse files Browse the repository at this point in the history
  • Loading branch information
jfecher committed Jun 24, 2024
1 parent 8ecae64 commit 9b59f6b
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 5 deletions.
8 changes: 7 additions & 1 deletion compiler/noirc_frontend/src/lexer/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub enum LexerErrorKind {
InvalidEscape { escaped: char, span: Span },
#[error("Invalid quote delimiter `{delimiter}`, valid delimiters are `{{`, `[`, and `(`")]
InvalidQuoteDelimiter { delimiter: SpannedToken },
#[error("Expected `{end_delim}` to close this {start_delim}")]
UnclosedQuote { start_delim: SpannedToken, end_delim: Token },
}

impl From<LexerErrorKind> for ParserError {
Expand All @@ -50,6 +52,7 @@ impl LexerErrorKind {
LexerErrorKind::UnterminatedStringLiteral { span } => *span,
LexerErrorKind::InvalidEscape { span, .. } => *span,
LexerErrorKind::InvalidQuoteDelimiter { delimiter } => delimiter.to_span(),
LexerErrorKind::UnclosedQuote { start_delim, .. } => start_delim.to_span(),
}
}

Expand Down Expand Up @@ -96,8 +99,11 @@ impl LexerErrorKind {
LexerErrorKind::InvalidEscape { escaped, span } =>
(format!("'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character."), "Invalid escape sequence".to_string(), *span),
LexerErrorKind::InvalidQuoteDelimiter { delimiter } => {
("Invalid quote delimiter `{delimiter}`".to_string(), "Valid delimiters are `{`, `[`, and `(`".to_string(), delimiter.to_span())
(format!("Invalid quote delimiter `{delimiter}`"), "Valid delimiters are `{`, `[`, and `(`".to_string(), delimiter.to_span())
},
LexerErrorKind::UnclosedQuote { start_delim, end_delim } => {
("Unclosed `quote` expression".to_string(), format!("Expected a `{end_delim}` to close this `{start_delim}`"), start_delim.to_span())
}
}
}
}
Expand Down
27 changes: 23 additions & 4 deletions compiler/noirc_frontend/src/lexer/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,15 +536,21 @@ impl<'a> Lexer<'a> {
};

let mut tokens = Vec::new();
let mut nested_delimiters = 1;

while nested_delimiters != 0 {
// Keep track of each nested delimiter we need to close.
let mut nested_delimiters = vec![delimiter];

while !nested_delimiters.is_empty() {
let token = self.next_token()?;

if *token.token() == start_delim {
nested_delimiters += 1;
nested_delimiters.push(token.clone());
} else if *token.token() == end_delim {
nested_delimiters -= 1;
nested_delimiters.pop();
} else if *token.token() == Token::EOF {
let start_delim =
nested_delimiters.pop().expect("If this were empty, we wouldn't be looping");
return Err(LexerErrorKind::UnclosedQuote { start_delim, end_delim });
}

tokens.push(token);
Expand Down Expand Up @@ -1312,4 +1318,17 @@ mod tests {
}
}
}

#[test]
fn test_unclosed_quote() {
let cases = vec!["quote {", "quote { { }", "quote [ []", "quote (((((((())))"];

for source in cases {
// `quote` is not itself a keyword so if the token stream fails to
// parse we don't expect any valid tokens from the quote construct
for token in Lexer::new(source) {
assert!(token.is_err(), "Expected Err, found {token:?}");
}
}
}
}

0 comments on commit 9b59f6b

Please sign in to comment.