Skip to content

Commit e32fe1f

Browse files
committed
feat: add reactive signals
1 parent e72e08f commit e32fe1f

File tree

1 file changed

+170
-0
lines changed

1 file changed

+170
-0
lines changed

compiler/src/jsx.rs

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
use std::collections::HashMap;
2+
3+
#[derive(Debug, Clone)]
4+
pub enum JSXNode {
5+
Element {
6+
tag: String,
7+
attrs: HashMap<String, String>,
8+
children: Vec<JSXNode>,
9+
},
10+
Text(String),
11+
Expression(String),
12+
}
13+
14+
pub fn parse_jsx(input: &str) -> JSXNode {
15+
let mut chars = input.chars().peekable();
16+
parse_element(&mut chars)
17+
}
18+
19+
fn parse_element<I>(chars: &mut std::iter::Peekable<I>) -> JSXNode
20+
where
21+
I: Iterator<Item = char>,
22+
{
23+
consume_whitespace(chars);
24+
assert_eq!(chars.next(), Some('<'));
25+
26+
let tag = parse_identifier(chars);
27+
let attrs = parse_attributes(chars);
28+
29+
let is_self_closing = if let Some('/') = chars.peek() {
30+
chars.next();
31+
assert_eq!(chars.next(), Some('>'));
32+
true
33+
} else {
34+
assert_eq!(chars.next(), Some('>'));
35+
false
36+
};
37+
38+
let mut children = vec![];
39+
40+
if !is_self_closing {
41+
loop {
42+
consume_whitespace(chars);
43+
44+
match chars.peek() {
45+
Some('<') => {
46+
if lookahead(chars, "</") {
47+
consume_until(chars, '>');
48+
chars.next(); // consume '>'
49+
break;
50+
} else {
51+
children.push(parse_element(chars));
52+
}
53+
}
54+
Some('{') => {
55+
chars.next(); // {
56+
let expr = consume_until(chars, '}');
57+
chars.next(); // }
58+
children.push(JSXNode::Expression(expr.trim().to_string()));
59+
}
60+
Some(_) => {
61+
let text = consume_text(chars);
62+
if !text.trim().is_empty() {
63+
children.push(JSXNode::Text(text));
64+
}
65+
}
66+
None => break,
67+
}
68+
}
69+
}
70+
71+
JSXNode::Element {
72+
tag,
73+
attrs,
74+
children,
75+
}
76+
}
77+
78+
fn parse_identifier<I>(chars: &mut std::iter::Peekable<I>) -> String
79+
where
80+
I: Iterator<Item = char>,
81+
{
82+
let mut ident = String::new();
83+
while let Some(&ch) = chars.peek() {
84+
if ch.is_alphanumeric() || ch == '-' {
85+
ident.push(ch);
86+
chars.next();
87+
} else {
88+
break;
89+
}
90+
}
91+
ident
92+
}
93+
94+
fn parse_attributes<I>(chars: &mut std::iter::Peekable<I>) -> HashMap<String, String>
95+
where
96+
I: Iterator<Item = char>,
97+
{
98+
let mut attrs = HashMap::new();
99+
loop {
100+
consume_whitespace(chars);
101+
match chars.peek() {
102+
Some('>') | Some('/') => break,
103+
Some(_) => {
104+
let name = parse_identifier(chars);
105+
consume_whitespace(chars);
106+
assert_eq!(chars.next(), Some('='));
107+
consume_whitespace(chars);
108+
let quote = chars.next().unwrap();
109+
assert!(quote == '"' || quote == '\'');
110+
let value = consume_until(chars, quote);
111+
chars.next(); // closing quote
112+
attrs.insert(name, value);
113+
}
114+
None => break,
115+
}
116+
}
117+
attrs
118+
}
119+
120+
fn consume_whitespace<I>(chars: &mut std::iter::Peekable<I>)
121+
where
122+
I: Iterator<Item = char>,
123+
{
124+
while matches!(chars.peek(), Some(c) if c.is_whitespace()) {
125+
chars.next();
126+
}
127+
}
128+
129+
fn consume_until<I>(chars: &mut std::iter::Peekable<I>, end: char) -> String
130+
where
131+
I: Iterator<Item = char>,
132+
{
133+
let mut result = String::new();
134+
while let Some(&ch) = chars.peek() {
135+
if ch == end {
136+
break;
137+
}
138+
result.push(ch);
139+
chars.next();
140+
}
141+
result
142+
}
143+
144+
fn consume_text<I>(chars: &mut std::iter::Peekable<I>) -> String
145+
where
146+
I: Iterator<Item = char>,
147+
{
148+
let mut result = String::new();
149+
while let Some(&ch) = chars.peek() {
150+
if ch == '<' || ch == '{' {
151+
break;
152+
}
153+
result.push(ch);
154+
chars.next();
155+
}
156+
result
157+
}
158+
159+
fn lookahead<I>(chars: &mut std::iter::Peekable<I>, pat: &str) -> bool
160+
where
161+
I: Iterator<Item = char> + Clone,
162+
{
163+
let mut clone = chars.clone();
164+
for c in pat.chars() {
165+
if clone.next() != Some(c) {
166+
return false;
167+
}
168+
}
169+
true
170+
}

0 commit comments

Comments
 (0)