Skip to content

Commit da7d25b

Browse files
committed
feat(parser)!: fragments (#8)
1 parent 5f8e7f6 commit da7d25b

File tree

5 files changed

+64
-3
lines changed

5 files changed

+64
-3
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,12 @@ assert_eq!(nodes[0].children[0].value_as_string().unwrap(), "hi");
5858
<div key=some::value() />
5959
```
6060

61-
- **Doctypes and Comments**
61+
- **Doctypes, Comments and Fragments**
6262

6363
```html
6464
<!DOCTYPE html>
6565
<!-- "comment" -->
66+
<></>
6667
```
6768

6869
- **Braced blocks are parsed as arbitrary Rust code**

benches/bench.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use quote::quote;
44
fn criterion_benchmark(c: &mut Criterion) {
55
let tokens = quote! {
66
<!DOCTYPE html>
7-
<div>
7+
<>
88
<!-- "comment" -->
99
<hello world />
1010
<div>"String literal"</div>
@@ -16,7 +16,7 @@ fn criterion_benchmark(c: &mut Criterion) {
1616
<div>{ let block = "in node position"; }</div>
1717
<div { let block = "in attribute position"; } />
1818
<div key={ let block = "in attribute value position"; } />
19-
</div>
19+
</>
2020
};
2121

2222
c.bench_function("syn_rsx::parse2", |b| {

src/node.rs

+4
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ pub enum NodeType {
102102
/// node value in this case
103103
Doctype,
104104

105+
/// Fragment: `<></>`
106+
Fragment,
107+
105108
/// Arbitrary rust code in braced `{}` blocks
106109
Block,
107110
}
@@ -117,6 +120,7 @@ impl fmt::Display for NodeType {
117120
Self::Text => "NodeType::Text",
118121
Self::Comment => "NodeType::Comment",
119122
Self::Doctype => "NodeType::Doctype",
123+
Self::Fragment => "NodeType::Fragment",
120124
Self::Block => "NodeType::Block",
121125
}
122126
)

src/parser.rs

+43
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ impl Parser {
137137
} else {
138138
self.comment(input)
139139
}
140+
} else if input.peek2(Token![>]) {
141+
self.fragment(input)
140142
} else {
141143
self.element(input)
142144
}
@@ -446,6 +448,47 @@ impl Parser {
446448
})
447449
}
448450

451+
fn fragment(&self, input: ParseStream) -> Result<Node> {
452+
self.fragment_open(input)?;
453+
454+
let mut children = vec![];
455+
loop {
456+
if input.is_empty() {
457+
return Err(input.error("unexpected end of input"));
458+
}
459+
460+
if let Ok(_) = self.fragment_close(&input.fork()) {
461+
self.fragment_close(input)?;
462+
break;
463+
}
464+
465+
children.append(&mut self.node(input)?);
466+
}
467+
468+
Ok(Node {
469+
name: None,
470+
value: None,
471+
node_type: NodeType::Fragment,
472+
attributes: vec![],
473+
children,
474+
})
475+
}
476+
477+
fn fragment_open(&self, input: ParseStream) -> Result<()> {
478+
input.parse::<Token![<]>()?;
479+
input.parse::<Token![>]>()?;
480+
481+
Ok(())
482+
}
483+
484+
fn fragment_close(&self, input: ParseStream) -> Result<()> {
485+
input.parse::<Token![<]>()?;
486+
input.parse::<Token![/]>()?;
487+
input.parse::<Token![>]>()?;
488+
489+
Ok(())
490+
}
491+
449492
fn node_name(&self, input: ParseStream) -> Result<NodeName> {
450493
if input.peek2(Colon2) {
451494
self.node_name_punctuated_ident::<Colon2, fn(_) -> Colon2, PathSegment>(input, Colon2)

tests/test.rs

+13
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,16 @@ fn test_comment() {
227227
Some("comment2".to_owned())
228228
);
229229
}
230+
231+
#[test]
232+
fn test_fragment() {
233+
let tokens = quote! {
234+
<>
235+
<div />
236+
</>
237+
};
238+
239+
let nodes = parse2(tokens);
240+
241+
assert!(nodes.is_ok());
242+
}

0 commit comments

Comments
 (0)