Desugar inside string interpolation

This commit is contained in:
Richard Feldman 2023-07-29 18:11:41 -04:00
parent 9e0e756d3b
commit 42fb593471
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B
2 changed files with 64 additions and 4 deletions

View file

@ -2376,14 +2376,13 @@ fn flatten_str_literal<'a>(
}
}
/// Comments and newlines are disallowed inside interpolation
/// Comments, newlines, and nested interpolation are disallowed inside interpolation
pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
match expr {
// These definitely contain neither comments nor newlines, so they are valid
ast::Expr::Var { .. }
| ast::Expr::SingleQuote(_)
| ast::Expr::Str(StrLiteral::PlainLine(_))
| ast::Expr::Str(StrLiteral::Line(_))
| ast::Expr::Float(_)
| ast::Expr::Num(_)
| ast::Expr::NonBase10Int { .. }
@ -2405,6 +2404,16 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
| ast::Expr::Str(StrLiteral::Block(_))
| ast::Expr::SpaceAfter(_, _) => false,
// These can contain subexpressions, so we need to recursively check those
ast::Expr::Str(StrLiteral::Line(segments)) => {
segments.iter().all(|segment| match segment {
ast::StrSegment::EscapedChar(_)
| ast::StrSegment::Unicode(_)
| ast::StrSegment::Plaintext(_) => true,
// Disallow nested interpolation. Alternatively, we could allow it but require
// a comment above it apologizing to the next person who has to read the code.
ast::StrSegment::Interpolated(_) => false,
})
}
ast::Expr::Record(fields) => fields.iter().all(|loc_field| match loc_field.value {
ast::AssignedField::RequiredValue(_label, loc_comments, loc_val)
| ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) => {

View file

@ -7,7 +7,9 @@ use roc_module::called_via::BinOp::Pizza;
use roc_module::called_via::{BinOp, CalledVia};
use roc_module::ident::ModuleName;
use roc_parse::ast::Expr::{self, *};
use roc_parse::ast::{AssignedField, Collection, RecordBuilderField, ValueDef, WhenBranch};
use roc_parse::ast::{
AssignedField, Collection, RecordBuilderField, StrLiteral, StrSegment, ValueDef, WhenBranch,
};
use roc_region::all::{Loc, Region};
// BinOp precedence logic adapted from Gluon by Markus Westerlind
@ -129,7 +131,6 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
Float(..)
| Num(..)
| NonBase10Int { .. }
| Str(_)
| SingleQuote(_)
| AccessorFunction(_)
| Var { .. }
@ -144,6 +145,28 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
| IngestedFile(_, _)
| Crash => loc_expr,
Str(str_literal) => match str_literal {
StrLiteral::PlainLine(_) => loc_expr,
StrLiteral::Line(segments) => {
let region = loc_expr.region;
let value = Str(StrLiteral::Line(desugar_str_segments(arena, segments)));
arena.alloc(Loc { region, value })
}
StrLiteral::Block(lines) => {
let region = loc_expr.region;
let new_lines = Vec::from_iter_in(
lines
.into_iter()
.map(|segments| desugar_str_segments(arena, segments)),
arena,
);
let value = Str(StrLiteral::Block(new_lines.into_bump_slice()));
arena.alloc(Loc { region, value })
}
},
TupleAccess(sub_expr, paths) => {
let region = loc_expr.region;
let loc_sub_expr = Loc {
@ -444,6 +467,34 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
}
}
fn desugar_str_segments<'a>(
arena: &'a Bump,
segments: &'a [StrSegment<'a>],
) -> &'a [StrSegment<'a>] {
Vec::from_iter_in(
segments.into_iter().map(|segment| match segment {
StrSegment::Plaintext(_) | StrSegment::Unicode(_) | StrSegment::EscapedChar(_) => {
*segment
}
StrSegment::Interpolated(loc_expr) => {
let loc_desugared = desugar_expr(
arena,
arena.alloc(Loc {
region: loc_expr.region,
value: *loc_expr.value,
}),
);
StrSegment::Interpolated(Loc {
region: loc_desugared.region,
value: arena.alloc(loc_desugared.value),
})
}
}),
arena,
)
.into_bump_slice()
}
fn desugar_field<'a>(
arena: &'a Bump,
field: &'a AssignedField<'a, Expr<'a>>,