Merge pull request #7497 from smores56/new-interpolation-syntax

Move to new interpolation syntax
This commit is contained in:
Sam Mohr 2025-01-10 15:25:12 -08:00 committed by GitHub
commit 528d1d2b69
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
66 changed files with 630 additions and 596 deletions

View file

@ -425,16 +425,18 @@ pub fn parse_str_like_literal<'a>() -> impl Parser<'a, StrLikeLiteral<'a>, EStri
}
}
}
b'(' if preceded_by_dollar && !is_single_quote => {
b'(' | b'{' if preceded_by_dollar && !is_single_quote => {
let old_style_interpolation_block = one_byte == b'(';
// We're about to begin string interpolation!
//
// End the previous segment so we can begin a new one.
// Retroactively end it right before the `$` char we parsed.
// (We can't use end_segment! here because it ends it right after
// the just-parsed character, which here would be '(' rather than '$')
// the just-parsed character, which here would be '{' rather than '$')
// Don't push anything if the string would be empty.
if segment_parsed_bytes > 2 {
// exclude the 2 chars we just parsed, namely '$' and '('
// exclude the 2 chars we just parsed, namely '$' and '{'
let string_bytes = &state.bytes()[0..(segment_parsed_bytes - 2)];
match std::str::from_utf8(string_bytes) {
@ -452,19 +454,27 @@ pub fn parse_str_like_literal<'a>() -> impl Parser<'a, StrLikeLiteral<'a>, EStri
}
}
// Advance past the `$(`
// Advance past the `${`
state.advance_mut(2);
let original_byte_count = state.bytes().len();
// Parse an arbitrary expression, followed by ')'
// Parse an arbitrary expression, followed by '}' or ')'
let terminating_char = if old_style_interpolation_block {
b')'
} else {
b'}'
};
let (_progress, (mut loc_expr, sp), new_state) = and(
specialize_err_ref(
EString::Format,
loc(allocated(reset_min_indent(expr::expr_help())))
.trace("str_interpolation"),
),
skip_second(space0_e(EString::FormatEnd), byte(b')', EString::FormatEnd)),
skip_second(
space0_e(EString::FormatEnd),
byte(terminating_char, EString::FormatEnd),
),
)
.parse(arena, state, min_indent)?;
@ -488,8 +498,8 @@ pub fn parse_str_like_literal<'a>() -> impl Parser<'a, StrLikeLiteral<'a>, EStri
}
}
// iff the '$' is followed by '(', this is string interpolation.
// We'll check for the '(' on the next iteration of the loop.
// iff the '$' is followed by '{', this is string interpolation.
// We'll check for the '{' on the next iteration of the loop.
preceded_by_dollar = one_byte == b'$';
}

View file

@ -160,17 +160,17 @@ mod test_parse {
#[test]
fn escaped_interpolation() {
assert_segments(r#""Hi, \$(name)!""#, |arena| {
assert_segments(r#""Hi, \${name}!""#, |arena| {
bumpalo::vec![in arena;
Plaintext("Hi, "),
EscapedChar(EscapedChar::Dollar),
Plaintext("(name)!"),
Plaintext("{name}!"),
]
});
}
#[test]
fn string_with_interpolation_in_middle() {
fn string_with_old_interpolation_still_works_for_now() {
assert_segments(r#""Hi, $(name)!""#, |arena| {
let expr = arena.alloc(Var {
module_name: "",
@ -185,9 +185,31 @@ mod test_parse {
});
}
#[test]
fn string_with_mixed_new_and_old_interpolation_braces_fails() {
assert_parsing_fails(r#""${foo)""#, SyntaxError::Unexpected(Region::zero()));
assert_parsing_fails(r#""$(foo}""#, SyntaxError::Unexpected(Region::zero()));
}
#[test]
fn string_with_interpolation_in_middle() {
assert_segments(r#""Hi, ${name}!""#, |arena| {
let expr = arena.alloc(Var {
module_name: "",
ident: "name",
});
bumpalo::vec![in arena;
Plaintext("Hi, "),
Interpolated(Loc::new(7, 11, expr)),
Plaintext("!")
]
});
}
#[test]
fn string_with_interpolation_in_front() {
assert_segments(r#""$(name), hi!""#, |arena| {
assert_segments(r#""${name}, hi!""#, |arena| {
let expr = arena.alloc(Var {
module_name: "",
ident: "name",
@ -232,7 +254,7 @@ mod test_parse {
#[test]
fn string_with_interpolation_in_back() {
assert_segments(r#""Hello $(name)""#, |arena| {
assert_segments(r#""Hello ${name}""#, |arena| {
let expr = arena.alloc(Var {
module_name: "",
ident: "name",
@ -247,7 +269,7 @@ mod test_parse {
#[test]
fn string_with_multiple_interpolations() {
assert_segments(r#""Hi, $(name)! How is $(project) going?""#, |arena| {
assert_segments(r#""Hi, ${name}! How is ${project} going?""#, |arena| {
let expr1 = arena.alloc(Var {
module_name: "",
ident: "name",
@ -271,7 +293,7 @@ mod test_parse {
#[test]
fn string_with_non_interpolation_dollar_signs() {
assert_segments(
r#""$a Hi, $(name)! $b How is $(project) going? $c""#,
r#""$a Hi, ${name}! $b How is ${project} going? $c""#,
|arena| {
let expr1 = arena.alloc(Var {
module_name: "",