Skip to content
Merged
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
43 changes: 42 additions & 1 deletion src/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ pub enum Token {
UnicodeStringLiteral(String),
/// Hexadecimal string literal: i.e.: X'deadbeef'
HexStringLiteral(String),
/// Interpolated text using Mustache-style syntax, e.g. {{FooBar}}.
Mustache(String),
/// Comma
Comma,
/// Whitespace (space, tab, etc)
Expand Down Expand Up @@ -303,6 +305,7 @@ impl fmt::Display for Token {
Token::DoubleQuotedRawStringLiteral(ref s) => write!(f, "R\"{s}\""),
Token::TripleSingleQuotedRawStringLiteral(ref s) => write!(f, "R'''{s}'''"),
Token::TripleDoubleQuotedRawStringLiteral(ref s) => write!(f, "R\"\"\"{s}\"\"\""),
Token::Mustache(ref s) => write!(f, "{{{s}}}"),
Token::Comma => f.write_str(","),
Token::Whitespace(ws) => write!(f, "{ws}"),
Token::DoubleEq => f.write_str("=="),
Expand Down Expand Up @@ -1599,7 +1602,45 @@ impl<'a> Tokenizer<'a> {
_ => Ok(Some(Token::Caret)),
}
}
'{' => self.consume_and_return(chars, Token::LBrace),
'{' => {
chars.next(); // consume the '{'
if let Some('{') = chars.peek() {
chars.next(); // consume the second '{'

let mut s = String::new();
let mut is_terminated = false;
let mut prev: Option<char> = None;

while let Some(&ch) = chars.peek() {
if prev == Some('}') {
if ch == '}' {
chars.next();
is_terminated = true;
break;
} else {
s.push('}');
s.push(ch);
}
} else if ch != '}' {
s.push(ch);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mustache format check is done elsewhere by regular expression, should we add another case for ch == '\n'?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what regular expression you're referring to. I can't think of any particular reason to block multi-line {{ }} other than it doesn't appear to currently work in the product.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, you have more context than I do here. The regular expression I'm referring to pulls out the template keys for replacement. I was thinking this could be a good place to throw an error since it's easy to detect multiline mustaches, but we can likely just as easily detect a missing template key in this case.

}

prev = Some(ch);
chars.next();
}

if chars.peek().is_none() && !is_terminated {
self.tokenizer_error(
chars.location(),
"Unterminated mustache interpolation",
)
} else {
Ok(Some(Token::Mustache(s)))
}
} else {
Ok(Some(Token::LBrace))
}
}
'}' => self.consume_and_return(chars, Token::RBrace),
'#' if dialect_of!(self is SnowflakeDialect | BigQueryDialect | MySqlDialect | HiveDialect) =>
{
Expand Down