Disambiguate expressions in rsx by requiring curlies

This commit is contained in:
Jonathan Kelley committed 2024-01-10 19:33:34 -08:00
1 parent 663296ae45
commit d9b84f9f8f
53 files changed
+399 -373

No files matched your search

+115 -66
View File
@@ -6,7 +6,8 @@ use syn::{
braced,
parse::{Parse, ParseStream},
spanned::Spanned,
token, Expr, ExprIf, LitStr, Pat, Result,
token::{self, Brace},
Expr, ExprIf, LitStr, Pat, Result,
};
/*
@@ -15,14 +16,14 @@ Parse
-> Component {}
-> component()
-> "text {with_args}"
-> (0..10).map(|f| rsx!("asd")), // <--- notice the comma - must be a complete expr
-> {(0..10).map(|f| rsx!("asd"))} // <--- notice the curly braces
*/
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub enum BodyNode {
Element(Element),
Component(Component),
ForLoop(ForLoop),
IfChain(ExprIf),
IfChain(IfChain),
Text(IfmtInput),
RawExpr(Expr),
}
@@ -112,7 +113,27 @@ impl Parse for BodyNode {
return Ok(BodyNode::IfChain(stream.parse()?));
}
Ok(BodyNode::RawExpr(stream.parse::<Expr>()?))
// Match statements are special but have no special arm syntax
// we could allow arm syntax if we wanted
//
// ```
// match {
// val => div {}
// other_val => div {}
// }
// ```
if stream.peek(Token![match]) {
return Ok(BodyNode::RawExpr(stream.parse::<Expr>()?));
}
if stream.peek(token::Brace) {
return Ok(BodyNode::RawExpr(stream.parse::<Expr>()?));
}
Err(syn::Error::new(
stream.span(),
"Expected a valid body node.\nExpressions must be wrapped in curly braces.",
))
}
}
@@ -151,71 +172,52 @@ impl ToTokens for BodyNode {
})
}
BodyNode::IfChain(chain) => {
if is_if_chain_terminated(chain) {
tokens.append_all(quote! {
{
let ___nodes = (#chain).into_dyn_node(__cx);
___nodes
}
});
} else {
let ExprIf {
let mut body = TokenStream2::new();
let mut terminated = false;
let mut elif = Some(chain);
while let Some(chain) = elif {
let IfChain {
if_token,
cond,
then_branch,
else_if_branch,
else_branch,
..
} = chain;
let mut body = TokenStream2::new();
let mut renderer: TemplateRenderer = TemplateRenderer {
roots: then_branch,
location: None,
};
body.append_all(quote! {
if #cond {
Some(#then_branch)
}
});
body.append_all(quote! { #if_token #cond { Some({#renderer}) } });
let mut elif = else_branch;
while let Some((_, ref branch)) = elif {
match branch.as_ref() {
Expr::If(ref eelif) => {
let ExprIf {
cond,
then_branch,
else_branch,
..
} = eelif;
body.append_all(quote! {
else if #cond {
Some(#then_branch)
}
});
elif = else_branch;
}
_ => {
body.append_all(quote! {
else {
#branch
}
});
break;
}
}
if let Some(next) = else_if_branch {
body.append_all(quote! { else });
elif = Some(next);
} else if let Some(else_branch) = else_branch {
renderer.roots = else_branch;
body.append_all(quote! { else { Some({#renderer}) } });
terminated = true;
break;
} else {
elif = None;
}
}
if !terminated {
body.append_all(quote! {
else { None }
});
tokens.append_all(quote! {
{
let ___nodes = (#body).into_dyn_node(__cx);
___nodes
}
});
}
tokens.append_all(quote! {
{
let ___nodes = (#body).into_dyn_node(__cx);
___nodes
}
});
}
}
}
@@ -240,26 +242,73 @@ impl Parse for ForLoop {
let in_token: Token![in] = input.parse()?;
let expr: Expr = input.call(Expr::parse_without_eager_brace)?;
let content;
let brace_token = braced!(content in input);
let mut children = vec![];
while !content.is_empty() {
children.push(content.parse()?);
}
let (brace_token, body) = parse_buffer_as_braced_children(input)?;
Ok(Self {
for_token,
pat,
in_token,
body: children,
expr: Box::new(expr),
body,
brace_token,
expr: Box::new(expr),
})
}
}
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct IfChain {
pub if_token: Token![if],
pub cond: Box<Expr>,
pub then_branch: Vec<BodyNode>,
pub else_if_branch: Option<Box<IfChain>>,
pub else_branch: Option<Vec<BodyNode>>,
}
impl Parse for IfChain {
fn parse(input: ParseStream) -> Result<Self> {
let if_token: Token![if] = input.parse()?;
// stolen from ExprIf
let cond = Box::new(input.call(Expr::parse_without_eager_brace)?);
let (_, then_branch) = parse_buffer_as_braced_children(input)?;
let mut else_branch = None;
let mut else_if_branch = None;
// if the next token is `else`, set the else branch as the next if chain
if input.peek(Token![else]) {
input.parse::<Token![else]>()?;
if input.peek(Token![if]) {
else_if_branch = Some(Box::new(input.parse::<IfChain>()?));
} else {
let (_, else_branch_nodes) = parse_buffer_as_braced_children(input)?;
else_branch = Some(else_branch_nodes);
}
}
Ok(Self {
cond,
if_token,
then_branch,
else_if_branch,
else_branch,
})
}
}
fn parse_buffer_as_braced_children(
input: &syn::parse::ParseBuffer<'_>,
) -> Result<(Brace, Vec<BodyNode>)> {
let content;
let brace_token = braced!(content in input);
let mut then_branch = vec![];
while !content.is_empty() {
then_branch.push(content.parse()?);
}
Ok((brace_token, then_branch))
}
pub(crate) fn is_if_chain_terminated(chain: &ExprIf) -> bool {
let mut current = chain;
loop {