nargo_parser/template/
mod.rs1use crate::{ParseState, TemplateParser};
2use nargo_ir::{AttributeIR, Comment, ElementIR, ExpressionIR, ForIteratorIR, ForNodeIR, IfNodeIR, TemplateNodeIR, Trivia};
3use nargo_types::{is_void_element, Error, Result, Span};
4
5pub struct VueTemplateParser;
6
7pub fn parse(source: &str) -> Result<Vec<TemplateNodeIR>> {
8 let mut state = ParseState::new(source);
9 let parser = VueTemplateParser;
10 parser.parse(&mut state, "html")
11}
12
13impl TemplateParser for VueTemplateParser {
14 fn parse(&self, state: &mut ParseState, _lang: &str) -> Result<Vec<TemplateNodeIR>> {
15 let mut parser = TemplateParserImpl { state };
16 parser.parse()
17 }
18}
19
20struct TemplateParserImpl<'a, 'b> {
21 state: &'a mut ParseState<'b>,
22}
23
24impl<'a, 'b> TemplateParserImpl<'a, 'b> {
25 fn consume_trivia(&mut self) -> Trivia {
26 let leading_whitespace = self.state.cursor.consume_whitespace();
27 let mut leading_comments = Vec::new();
28
29 while self.state.cursor.peek_str("<!--") {
30 let start_pos = self.state.cursor.position();
31 self.state.cursor.consume_n(4); let start = self.state.cursor.pos;
33 while !self.state.cursor.is_eof() && !self.state.cursor.peek_str("-->") {
34 self.state.cursor.consume();
35 }
36 let content = self.state.cursor.current_str(start).to_string();
37 let _ = self.state.cursor.consume_n(3); let end_pos = self.state.cursor.position();
39 leading_comments.push(Comment { content, is_block: true, span: Span { start: start_pos, end: end_pos } });
40 let _extra_ws = self.state.cursor.consume_whitespace();
42 }
45
46 Trivia { leading_whitespace, leading_comments, trailing_comments: Vec::new() }
47 }
48
49 pub fn parse(&mut self) -> Result<Vec<TemplateNodeIR>> {
50 let mut nodes = Vec::new();
51 while !self.state.cursor.is_eof() {
52 if self.state.cursor.peek() == '{' {
53 nodes.push(self.parse_interpolation()?);
54 }
55 else if self.state.cursor.peek() == '<' {
56 if self.state.cursor.peek_str("<!--") {
57 nodes.push(self.parse_comment()?);
58 }
59 else if self.state.cursor.peek_str("</") {
60 break; }
62 else {
63 nodes.push(self.parse_element()?);
64 }
65 }
66 else {
67 nodes.push(self.parse_text()?);
68 }
69 }
70 Ok(nodes)
71 }
72
73 fn parse_element(&mut self) -> Result<TemplateNodeIR> {
74 let start_pos = self.state.cursor.position();
75 self.state.cursor.expect('<')?;
76 let tag = self.state.cursor.consume_while(|c| c.is_alphanumeric() || c == '-');
77 if tag.is_empty() {
78 return Err(Error::parse_error("Expected tag name".to_string(), self.state.cursor.span_at_current()));
79 }
80 println!("Parsing element: <{}>", tag);
81 let mut attributes = Vec::new();
82
83 let mut trivia = self.consume_trivia();
84 while !self.state.cursor.is_eof() && self.state.cursor.peek() != '>' && !self.state.cursor.peek_str("/>") {
85 let attr_start = self.state.cursor.position();
86 let attr_name = self.state.cursor.consume_while(|c| !c.is_whitespace() && c != '=' && c != '>' && c != '/');
87
88 let mut attr_trivia = self.consume_trivia();
89 let attr_value = if self.state.cursor.peek() == '=' {
90 self.state.cursor.consume();
91 attr_trivia.leading_whitespace.push('=');
92 attr_trivia.leading_whitespace.push_str(&self.state.cursor.consume_whitespace());
93
94 let peek = self.state.cursor.peek();
95 if peek == '"' || peek == '\'' {
96 Some(self.state.cursor.consume_string()?)
97 }
98 else if peek == '{' {
99 let interpolation = self.parse_interpolation()?;
101 if let TemplateNodeIR::Interpolation(expr) = interpolation {
102 Some(expr.code)
103 }
104 else {
105 None
106 }
107 }
108 else {
109 Some(self.state.cursor.consume_while(|c| !c.is_whitespace() && c != '>' && c != '/'))
110 }
111 }
112 else {
113 None
114 };
115
116 let is_directive = attr_name.contains(':');
118 let is_dynamic = attr_value.as_ref().map(|v| v.starts_with('{') || is_directive).unwrap_or(false);
119
120 let value_ast = None;
121 let attr_end = self.state.cursor.position();
122 attributes.push(AttributeIR { name: attr_name, value: attr_value, value_ast, argument: None, modifiers: Vec::new(), is_directive, is_dynamic, span: Span { start: attr_start, end: attr_end }, trivia: attr_trivia });
123 trivia = self.consume_trivia();
124 }
125
126 let is_self_closing = if self.state.cursor.peek_str("/>") {
127 self.state.cursor.consume_n(2);
128 true
129 }
130 else {
131 self.state.cursor.expect('>')?;
132 false
133 };
134
135 let mut children = Vec::new();
136 if !is_self_closing && !is_void_element(&tag) {
137 if tag == "script" || tag == "style" {
138 let start = self.state.cursor.pos;
139 let end_tag = format!("</{}>", tag);
140 while !self.state.cursor.is_eof() && !self.state.cursor.peek_str(&end_tag) {
141 self.state.cursor.consume();
142 }
143 let content = self.state.cursor.current_str(start).to_string();
144 if !content.is_empty() {
145 let text_span = self.state.cursor.span_from(start_pos);
146 children.push(TemplateNodeIR::Text(content, text_span, Trivia::default()));
147 }
148 self.state.cursor.expect_str(&end_tag)?;
149 }
150 else if tag == "if" {
151 children = self.parse()?;
153 self.state.cursor.expect_str("</if>")?;
154 let end_pos = self.state.cursor.position();
155
156 let mut condition = ExpressionIR::default();
158 for attr in &attributes {
159 if attr.name == "test" || attr.name == "condition" {
160 if let Some(ref value) = attr.value {
161 condition = ExpressionIR { code: value.clone(), ast: attr.value_ast.clone(), is_static: false, span: attr.span, trivia: attr.trivia.clone() };
162 }
163 break;
164 }
165 }
166
167 return Ok(TemplateNodeIR::If(nargo_ir::IfNodeIR { condition, consequent: children, alternate: None, else_ifs: Vec::new(), span: Span { start: start_pos, end: end_pos } }));
168 }
169 else if tag == "for" {
170 children = self.parse()?;
172 self.state.cursor.expect_str("</for>")?;
173 let end_pos = self.state.cursor.position();
174
175 let mut iterator = nargo_ir::ForIteratorIR::default();
177 for attr in &attributes {
178 if attr.name == "each" {
179 if let Some(ref value) = attr.value {
180 if let Some((left, right)) = value.split_once(" in ") {
182 let left = left.trim();
183 if let Some((item, index)) = left.split_once(',') {
184 iterator.item = item.trim().to_string();
185 iterator.index = Some(index.trim().to_string());
186 }
187 else {
188 iterator.item = left.to_string();
189 }
190 iterator.collection = ExpressionIR { code: right.trim().to_string(), ast: None, is_static: false, span: attr.span, trivia: attr.trivia.clone() };
191 }
192 }
193 break;
194 }
195 }
196
197 return Ok(TemplateNodeIR::For(nargo_ir::ForNodeIR { iterator, body: children, span: Span { start: start_pos, end: end_pos } }));
198 }
199 else {
200 children = self.parse()?;
201 println!("Element <{}> found {} children", tag, children.len());
202 self.state.cursor.expect_str(&format!("</{}>", tag))?;
203 }
204 }
205
206 let end_pos = self.state.cursor.position();
207
208 Ok(TemplateNodeIR::Element(ElementIR { tag, attributes, children, is_static: false, span: Span { start: start_pos, end: end_pos }, trivia }))
209 }
210
211 fn parse_interpolation(&mut self) -> Result<TemplateNodeIR> {
212 let start_pos = self.state.cursor.position();
213 self.state.cursor.expect('{')?;
214 let start = self.state.cursor.pos;
215 let mut depth = 1;
216 while !self.state.cursor.is_eof() && depth > 0 {
217 let c = self.state.cursor.consume();
218 if c == '{' {
219 depth += 1;
220 }
221 else if c == '}' {
222 depth -= 1;
223 }
224 }
225 let content = self.state.cursor.current_str(start);
226 let content = &content[..content.len() - 1]; let content = content.trim().to_string();
228 let content = if content.starts_with('{') && content.ends_with('}') { content[1..content.len() - 1].trim().to_string() } else { content };
230 let end_pos = self.state.cursor.position();
231
232 let ast = None;
233
234 Ok(TemplateNodeIR::Interpolation(ExpressionIR { code: content, ast, is_static: false, span: Span { start: start_pos, end: end_pos }, trivia: Trivia::default() }))
235 }
236
237 fn parse_text(&mut self) -> Result<TemplateNodeIR> {
238 let start_pos = self.state.cursor.position();
239 let start = self.state.cursor.pos;
240 while !self.state.cursor.is_eof() && self.state.cursor.peek() != '<' && self.state.cursor.peek() != '{' {
241 self.state.cursor.consume();
242 }
243 let end_pos = self.state.cursor.position();
244 Ok(TemplateNodeIR::Text(self.state.cursor.current_str(start).to_string(), Span { start: start_pos, end: end_pos }, Trivia::default()))
245 }
246
247 fn parse_comment(&mut self) -> Result<TemplateNodeIR> {
248 let start_pos = self.state.cursor.position();
249 self.state.cursor.expect_str("<!--")?;
250 let start = self.state.cursor.pos;
251 while !self.state.cursor.is_eof() && !self.state.cursor.peek_str("-->") {
252 self.state.cursor.consume();
253 }
254 let content = self.state.cursor.current_str(start).to_string();
255 self.state.cursor.expect_str("-->")?;
256 let end_pos = self.state.cursor.position();
257 Ok(TemplateNodeIR::Comment(content, Span { start: start_pos, end: end_pos }, Trivia::default()))
258 }
259}