From 22f894e6b98073bffa39f08b890071ffc00b8d49 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 22 Jul 2021 02:06:49 -0400 Subject: [PATCH] wip: more upgades to html parser --- packages/core-macro/src/rsx/body.rs | 91 ++++---------- packages/core-macro/src/rsx/component.rs | 58 +++++---- packages/core-macro/src/rsx/element.rs | 144 +++++++++++++++++------ 3 files changed, 165 insertions(+), 128 deletions(-) diff --git a/packages/core-macro/src/rsx/body.rs b/packages/core-macro/src/rsx/body.rs index cfbc405d2..9ea37585d 100644 --- a/packages/core-macro/src/rsx/body.rs +++ b/packages/core-macro/src/rsx/body.rs @@ -16,41 +16,12 @@ pub struct RsxBody { /// The custom rusty variant of parsing rsx! impl Parse for RsxBody { fn parse(input: ParseStream) -> Result { - // if input.peek(LitStr) { - // return input.parse::()?.parse::(); - // } - - // try to parse the first ident and comma - let custom_context = - if input.peek(Token![in]) && input.peek2(Ident) && input.peek3(Token![,]) { - let _ = input.parse::()?; - let name = input.parse::()?; - if is_valid_tag(&name.to_string()) { - return Err(Error::new( - input.span(), - "Custom context cannot be an html element name", - )); - } else { - input.parse::().unwrap(); - Some(name) - } - } else { - None - }; - - let mut body = Vec::new(); - let mut children = Vec::new(); - let mut manual_props = None; - let cfg: BodyParseConfig = BodyParseConfig { - allow_children: true, - allow_fields: false, - allow_manual_props: false, - }; - cfg.parse_component_body(input, &mut body, &mut children, &mut manual_props)?; - + let custom_context = try_parse_custom_context(input)?; + let (_, roots, _) = + BodyParseConfig::::new_as_body().parse_component_body(&input)?; Ok(Self { - roots: children, custom_context, + roots, }) } } @@ -58,46 +29,28 @@ impl Parse for RsxBody { /// The HTML variant of parsing rsx! impl Parse for RsxBody { fn parse(input: ParseStream) -> Result { - // if input.peek(LitStr) { - // return input.parse::()?.parse::(); - // } - - // try to parse the first ident and comma - let custom_context = - if input.peek(Token![in]) && input.peek2(Ident) && input.peek3(Token![,]) { - let _ = input.parse::()?; - let name = input.parse::()?; - if is_valid_tag(&name.to_string()) { - return Err(Error::new( - input.span(), - "Custom context cannot be an html element name", - )); - } else { - input.parse::().unwrap(); - Some(name) - } - } else { - None - }; - - let mut body = Vec::new(); - let mut children = Vec::new(); - let mut manual_props = None; - - let cfg: BodyParseConfig = BodyParseConfig { - allow_children: true, - allow_fields: false, - allow_manual_props: false, - }; - cfg.parse_component_body(input, &mut body, &mut children, &mut manual_props)?; - + let custom_context = try_parse_custom_context(input)?; + let (_, roots, _) = + BodyParseConfig::::new_as_body().parse_component_body(&input)?; Ok(Self { - roots: children, custom_context, + roots, }) } } +fn try_parse_custom_context(input: ParseStream) -> Result> { + let res = if input.peek(Token![in]) && input.peek2(Ident) && input.peek3(Token![,]) { + let _ = input.parse::()?; + let name = input.parse::()?; + input.parse::()?; + Some(name) + } else { + None + }; + Ok(res) +} + /// Serialize the same way, regardless of flavor impl ToTokens for RsxBody { fn to_tokens(&self, out_tokens: &mut TokenStream2) { @@ -113,7 +66,7 @@ impl ToTokens for RsxBody { // The `in cx` pattern allows directly rendering Some(ident) => out_tokens.append_all(quote! { #ident.render(dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{ - use dioxus_elements::GlobalAttributes; + use dioxus_elements::{GlobalAttributes, SvgAttributes}; #inner })) @@ -121,7 +74,7 @@ impl ToTokens for RsxBody { // Otherwise we just build the LazyNode wrapper None => out_tokens.append_all(quote! { dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{ - use dioxus_elements::GlobalAttributes; + use dioxus_elements::{GlobalAttributes, SvgAttributes}; #inner }) diff --git a/packages/core-macro/src/rsx/component.rs b/packages/core-macro/src/rsx/component.rs index 79a4d26c4..f493cbe3b 100644 --- a/packages/core-macro/src/rsx/component.rs +++ b/packages/core-macro/src/rsx/component.rs @@ -41,17 +41,13 @@ impl Parse for Component { let content: ParseBuffer; syn::braced!(content in stream); - let mut body: Vec> = Vec::new(); - let mut children: Vec> = Vec::new(); - let mut manual_props = None; - let cfg: BodyParseConfig = BodyParseConfig { allow_children: true, allow_fields: true, allow_manual_props: true, }; - cfg.parse_component_body(&content, &mut body, &mut children, &mut manual_props)?; + let (body, children, manual_props) = cfg.parse_component_body(&content)?; Ok(Self { name, @@ -69,17 +65,13 @@ impl Parse for Component { let content: ParseBuffer; syn::braced!(content in stream); - let mut body: Vec> = Vec::new(); - let mut children: Vec> = Vec::new(); - let mut manual_props = None; - let cfg: BodyParseConfig = BodyParseConfig { allow_children: true, allow_fields: true, allow_manual_props: true, }; - cfg.parse_component_body(&content, &mut body, &mut children, &mut manual_props)?; + let (body, children, manual_props) = cfg.parse_component_body(&content)?; Ok(Self { name, @@ -95,16 +87,33 @@ pub struct BodyParseConfig { pub allow_children: bool, pub allow_manual_props: bool, } + +impl BodyParseConfig { + /// The configuration to parse the root + pub fn new_as_body() -> Self { + Self { + allow_children: true, + allow_fields: false, + allow_manual_props: false, + } + } +} + impl BodyParseConfig { // todo: unify this body parsing for both elements and components // both are style rather ad-hoc, though components are currently more configured pub fn parse_component_body( &self, content: &ParseBuffer, - body: &mut Vec>, - children: &mut Vec>, - manual_props: &mut Option, - ) -> Result<()> { + ) -> Result<( + Vec>, + Vec>, + Option, + )> { + let mut body = Vec::new(); + let mut children = Vec::new(); + let mut manual_props = None; + 'parsing: loop { // [1] Break if empty if content.is_empty() { @@ -119,7 +128,7 @@ impl BodyParseConfig { )); } content.parse::()?; - *manual_props = Some(content.parse::()?); + manual_props = Some(content.parse::()?); } else if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) { if !self.allow_fields { return Err(Error::new( @@ -144,7 +153,7 @@ impl BodyParseConfig { let _ = content.parse::(); } } - Ok(()) + Ok((body, children, manual_props)) } } impl BodyParseConfig { @@ -153,10 +162,15 @@ impl BodyParseConfig { pub fn parse_component_body( &self, content: &ParseBuffer, - body: &mut Vec>, - children: &mut Vec>, - manual_props: &mut Option, - ) -> Result<()> { + ) -> Result<( + Vec>, + Vec>, + Option, + )> { + let mut body = Vec::new(); + let mut children = Vec::new(); + let mut manual_props = None; + 'parsing: loop { // [1] Break if empty if content.is_empty() { @@ -171,7 +185,7 @@ impl BodyParseConfig { )); } content.parse::()?; - *manual_props = Some(content.parse::()?); + manual_props = Some(content.parse::()?); } else if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) { if !self.allow_fields { return Err(Error::new( @@ -196,7 +210,7 @@ impl BodyParseConfig { let _ = content.parse::(); } } - Ok(()) + Ok((body, children, manual_props)) } } diff --git a/packages/core-macro/src/rsx/element.rs b/packages/core-macro/src/rsx/element.rs index af0e6daa3..e5973d263 100644 --- a/packages/core-macro/src/rsx/element.rs +++ b/packages/core-macro/src/rsx/element.rs @@ -14,8 +14,8 @@ use syn::{ pub struct Element { name: Ident, key: Option, - attributes: Vec, - listeners: Vec, + attributes: Vec>, + listeners: Vec>, children: Vec>, is_static: bool, } @@ -28,8 +28,8 @@ impl Parse for Element { let content: ParseBuffer; syn::braced!(content in stream); - let mut attributes: Vec = vec![]; - let mut listeners: Vec = vec![]; + let mut attributes: Vec> = vec![]; + let mut listeners: Vec> = vec![]; let mut children: Vec> = vec![]; let mut key = None; @@ -40,7 +40,7 @@ impl Parse for Element { } if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) { - parse_element_body( + parse_rsx_element_field( &content, &mut attributes, &mut listeners, @@ -71,45 +71,114 @@ impl Parse for Element { impl Parse for Element { fn parse(stream: ParseStream) -> Result { - let name = Ident::parse(stream)?; + let l_tok = stream.parse::()?; + let el_name = Ident::parse(stream)?; // parse the guts - let content: ParseBuffer; - syn::braced!(content in stream); + // let content: ParseBuffer; + // syn::braced!(content in stream); - let mut attributes: Vec = vec![]; - let mut listeners: Vec = vec![]; + let mut attributes: Vec> = vec![]; + let mut listeners: Vec> = vec![]; let mut children: Vec> = vec![]; let mut key = None; - 'parsing: loop { - // [1] Break if empty - if content.is_empty() { - break 'parsing; + // loop { + // if stream.peek(Token![>]) { + // break; + // } else { + // } + // } + while !stream.peek(Token![>]) { + // self-closing + if stream.peek(Token![/]) { + stream.parse::()?; + stream.parse::]>()?; + + return Ok(Self { + name: el_name, + key: None, + attributes, + is_static: false, + listeners, + children, + }); } - if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) { - parse_element_body( - &content, - &mut attributes, - &mut listeners, - &mut key, - name.clone(), - )?; + let name = Ident::parse_any(stream)?; + let name_str = name.to_string(); + stream.parse::()?; + if name_str.starts_with("on") { + todo!() } else { - children.push(content.parse::>()?); - } + match name_str.as_str() { + "style" => todo!(), + "key" => todo!(), + "classes" => todo!(), + "namespace" => todo!(), + "ref" => todo!(), + _ => { + let ty = if stream.peek(LitStr) { + let rawtext = stream.parse::().unwrap(); + AttrType::BumpText(rawtext) + } else { + let toks = stream.parse::()?; + AttrType::FieldTokens(toks) + }; + attributes.push(ElementAttr { + element_name: el_name.clone(), + name, + value: ty, + namespace: None, + }) + } + } + }; + // if stream.peek(LitStr) { - // consume comma if it exists - // we don't actually care if there *are* commas after elements/text - if content.peek(Token![,]) { - let _ = content.parse::(); - } + // } else { + // } + // if name_str.starts_with("on") {} + + // attributes.push(stream.parse()?); } + stream.parse::]>()?; + + // closing element + stream.parse::()?; + stream.parse::()?; + let close = Ident::parse_any(stream)?; + if close.to_string() != el_name.to_string() { + return Err(Error::new_spanned( + close, + "closing element does not match opening", + )); + } + stream.parse::]>()?; + // 'parsing: loop { + // // if stream.peek(Token![>]) {} + + // // // [1] Break if empty + // // if content.is_empty() { + // // break 'parsing; + // // } + + // if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) { + // parse_element_body( + // &content, + // &mut attributes, + // &mut listeners, + // &mut key, + // name.clone(), + // )?; + // } else { + // children.push(stream.parse::>()?); + // } + // } Ok(Self { key, - name, + name: el_name, attributes, children, listeners, @@ -140,7 +209,7 @@ impl ToTokens for Element { /// ======================================= /// Parse a VElement's Attributes /// ======================================= -struct ElementAttr { +struct ElementAttr { element_name: Ident, name: Ident, value: AttrType, @@ -157,14 +226,14 @@ enum AttrType { // We parse attributes and dump them into the attribute vec // This is because some tags might be namespaced (IE style) // These dedicated tags produce multiple name-spaced attributes -fn parse_element_body( +fn parse_rsx_element_field( stream: ParseStream, - attrs: &mut Vec, - listeners: &mut Vec, + attrs: &mut Vec>, + listeners: &mut Vec>, key: &mut Option, element_name: Ident, ) -> Result<()> { - let mut name = Ident::parse_any(stream)?; + let name = Ident::parse_any(stream)?; let name_str = name.to_string(); stream.parse::()?; @@ -199,7 +268,8 @@ fn parse_element_body( let ty: AttrType = match name_str.as_str() { // short circuit early if style is using the special syntax - "style" if stream.peek(token::Brace) => { + "style" if stream.peek(Token![:]) => { + stream.parse::().unwrap(); let inner; syn::braced!(inner in stream); @@ -267,7 +337,7 @@ fn parse_element_body( Ok(()) } -impl ToTokens for ElementAttr { +impl ToTokens for ElementAttr { fn to_tokens(&self, tokens: &mut TokenStream2) { let el_name = &self.element_name; let name_str = self.name.to_string();