1
0
Эх сурвалжийг харах

wip: more upgades to html parser

Jonathan Kelley 3 жил өмнө
parent
commit
22f894e6b9

+ 22 - 69
packages/core-macro/src/rsx/body.rs

@@ -16,41 +16,12 @@ pub struct RsxBody<const AS: HTML_OR_RSX> {
 /// The custom rusty variant of parsing rsx!
 impl Parse for RsxBody<AS_RSX> {
     fn parse(input: ParseStream) -> Result<Self> {
-        // if input.peek(LitStr) {
-        //     return input.parse::<LitStr>()?.parse::<RsxRender>();
-        // }
-
-        // 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::<Token![in]>()?;
-                let name = input.parse::<Ident>()?;
-                if is_valid_tag(&name.to_string()) {
-                    return Err(Error::new(
-                        input.span(),
-                        "Custom context cannot be an html element name",
-                    ));
-                } else {
-                    input.parse::<Token![,]>().unwrap();
-                    Some(name)
-                }
-            } else {
-                None
-            };
-
-        let mut body = Vec::new();
-        let mut children = Vec::new();
-        let mut manual_props = None;
-        let cfg: BodyParseConfig<AS_RSX> = 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::<AS_RSX>::new_as_body().parse_component_body(&input)?;
         Ok(Self {
-            roots: children,
             custom_context,
+            roots,
         })
     }
 }
@@ -58,46 +29,28 @@ impl Parse for RsxBody<AS_RSX> {
 /// The HTML variant of parsing rsx!
 impl Parse for RsxBody<AS_HTML> {
     fn parse(input: ParseStream) -> Result<Self> {
-        // if input.peek(LitStr) {
-        //     return input.parse::<LitStr>()?.parse::<RsxRender>();
-        // }
-
-        // 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::<Token![in]>()?;
-                let name = input.parse::<Ident>()?;
-                if is_valid_tag(&name.to_string()) {
-                    return Err(Error::new(
-                        input.span(),
-                        "Custom context cannot be an html element name",
-                    ));
-                } else {
-                    input.parse::<Token![,]>().unwrap();
-                    Some(name)
-                }
-            } else {
-                None
-            };
-
-        let mut body = Vec::new();
-        let mut children = Vec::new();
-        let mut manual_props = None;
-
-        let cfg: BodyParseConfig<AS_HTML> = 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::<AS_HTML>::new_as_body().parse_component_body(&input)?;
         Ok(Self {
-            roots: children,
             custom_context,
+            roots,
         })
     }
 }
 
+fn try_parse_custom_context(input: ParseStream) -> Result<Option<Ident>> {
+    let res = if input.peek(Token![in]) && input.peek2(Ident) && input.peek3(Token![,]) {
+        let _ = input.parse::<Token![in]>()?;
+        let name = input.parse::<Ident>()?;
+        input.parse::<Token![,]>()?;
+        Some(name)
+    } else {
+        None
+    };
+    Ok(res)
+}
+
 /// Serialize the same way, regardless of flavor
 impl<const A: HTML_OR_RSX> ToTokens for RsxBody<A> {
     fn to_tokens(&self, out_tokens: &mut TokenStream2) {
@@ -113,7 +66,7 @@ impl<const A: HTML_OR_RSX> ToTokens for RsxBody<A> {
             // 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<const A: HTML_OR_RSX> ToTokens for RsxBody<A> {
             // 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
                  })

+ 36 - 22
packages/core-macro/src/rsx/component.rs

@@ -41,17 +41,13 @@ impl Parse for Component<AS_RSX> {
         let content: ParseBuffer;
         syn::braced!(content in stream);
 
-        let mut body: Vec<ComponentField<AS_RSX>> = Vec::new();
-        let mut children: Vec<BodyNode<AS_RSX>> = Vec::new();
-        let mut manual_props = None;
-
         let cfg: BodyParseConfig<AS_RSX> = 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<AS_HTML> {
         let content: ParseBuffer;
         syn::braced!(content in stream);
 
-        let mut body: Vec<ComponentField<AS_HTML>> = Vec::new();
-        let mut children: Vec<BodyNode<AS_HTML>> = Vec::new();
-        let mut manual_props = None;
-
         let cfg: BodyParseConfig<AS_HTML> = 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<const AS: HTML_OR_RSX> {
     pub allow_children: bool,
     pub allow_manual_props: bool,
 }
+
+impl<const AS: HTML_OR_RSX> BodyParseConfig<AS> {
+    /// 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<AS_RSX> {
     // 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<ComponentField<AS_RSX>>,
-        children: &mut Vec<BodyNode<AS_RSX>>,
-        manual_props: &mut Option<Expr>,
-    ) -> Result<()> {
+    ) -> Result<(
+        Vec<ComponentField<AS_RSX>>,
+        Vec<BodyNode<AS_RSX>>,
+        Option<Expr>,
+    )> {
+        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<AS_RSX> {
                     ));
                 }
                 content.parse::<Token![..]>()?;
-                *manual_props = Some(content.parse::<Expr>()?);
+                manual_props = Some(content.parse::<Expr>()?);
             } 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<AS_RSX> {
                 let _ = content.parse::<Token![,]>();
             }
         }
-        Ok(())
+        Ok((body, children, manual_props))
     }
 }
 impl BodyParseConfig<AS_HTML> {
@@ -153,10 +162,15 @@ impl BodyParseConfig<AS_HTML> {
     pub fn parse_component_body(
         &self,
         content: &ParseBuffer,
-        body: &mut Vec<ComponentField<AS_HTML>>,
-        children: &mut Vec<BodyNode<AS_HTML>>,
-        manual_props: &mut Option<Expr>,
-    ) -> Result<()> {
+    ) -> Result<(
+        Vec<ComponentField<AS_HTML>>,
+        Vec<BodyNode<AS_HTML>>,
+        Option<Expr>,
+    )> {
+        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<AS_HTML> {
                     ));
                 }
                 content.parse::<Token![..]>()?;
-                *manual_props = Some(content.parse::<Expr>()?);
+                manual_props = Some(content.parse::<Expr>()?);
             } 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<AS_HTML> {
                 let _ = content.parse::<Token![,]>();
             }
         }
-        Ok(())
+        Ok((body, children, manual_props))
     }
 }
 

+ 107 - 37
packages/core-macro/src/rsx/element.rs

@@ -14,8 +14,8 @@ use syn::{
 pub struct Element<const AS: HTML_OR_RSX> {
     name: Ident,
     key: Option<AttrType>,
-    attributes: Vec<ElementAttr>,
-    listeners: Vec<ElementAttr>,
+    attributes: Vec<ElementAttr<AS>>,
+    listeners: Vec<ElementAttr<AS>>,
     children: Vec<BodyNode<AS>>,
     is_static: bool,
 }
@@ -28,8 +28,8 @@ impl Parse for Element<AS_RSX> {
         let content: ParseBuffer;
         syn::braced!(content in stream);
 
-        let mut attributes: Vec<ElementAttr> = vec![];
-        let mut listeners: Vec<ElementAttr> = vec![];
+        let mut attributes: Vec<ElementAttr<AS_RSX>> = vec![];
+        let mut listeners: Vec<ElementAttr<AS_RSX>> = vec![];
         let mut children: Vec<BodyNode<AS_RSX>> = vec![];
         let mut key = None;
 
@@ -40,7 +40,7 @@ impl Parse for Element<AS_RSX> {
             }
 
             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<AS_RSX> {
 
 impl Parse for Element<AS_HTML> {
     fn parse(stream: ParseStream) -> Result<Self> {
-        let name = Ident::parse(stream)?;
+        let l_tok = stream.parse::<Token![<]>()?;
+        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<ElementAttr> = vec![];
-        let mut listeners: Vec<ElementAttr> = vec![];
+        let mut attributes: Vec<ElementAttr<AS_HTML>> = vec![];
+        let mut listeners: Vec<ElementAttr<AS_HTML>> = vec![];
         let mut children: Vec<BodyNode<AS_HTML>> = 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::<Token![/]>()?;
+                stream.parse::<Token![>]>()?;
+
+                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::<Token![=]>()?;
+            if name_str.starts_with("on") {
+                todo!()
             } else {
-                children.push(content.parse::<BodyNode<AS_HTML>>()?);
-            }
+                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::<LitStr>().unwrap();
+                            AttrType::BumpText(rawtext)
+                        } else {
+                            let toks = stream.parse::<Expr>()?;
+                            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::<Token![,]>();
-            }
+            // } else {
+            // }
+            // if name_str.starts_with("on") {}
+
+            // attributes.push(stream.parse()?);
+        }
+        stream.parse::<Token![>]>()?;
+
+        // closing element
+        stream.parse::<Token![<]>()?;
+        stream.parse::<Token![/]>()?;
+        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::<Token![>]>()?;
+        // '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::<BodyNode<AS_HTML>>()?);
+        //     }
+        // }
 
         Ok(Self {
             key,
-            name,
+            name: el_name,
             attributes,
             children,
             listeners,
@@ -140,7 +209,7 @@ impl<const AS: HTML_OR_RSX> ToTokens for Element<AS> {
 /// =======================================
 /// Parse a VElement's Attributes
 /// =======================================
-struct ElementAttr {
+struct ElementAttr<const AS: HTML_OR_RSX> {
     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<ElementAttr>,
-    listeners: &mut Vec<ElementAttr>,
+    attrs: &mut Vec<ElementAttr<AS_RSX>>,
+    listeners: &mut Vec<ElementAttr<AS_RSX>>,
     key: &mut Option<AttrType>,
     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::<Token![:]>()?;
 
@@ -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::<Token![:]>().unwrap();
             let inner;
             syn::braced!(inner in stream);
 
@@ -267,7 +337,7 @@ fn parse_element_body(
     Ok(())
 }
 
-impl ToTokens for ElementAttr {
+impl<const AS: HTML_OR_RSX> ToTokens for ElementAttr<AS> {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         let el_name = &self.element_name;
         let name_str = self.name.to_string();