1use crate::{
11 ast::*,
12 error::{SyntaxError, SyntaxErrorKind},
13 helpers,
14};
15use std::{cmp::Ordering, iter::Peekable, ops::ControlFlow, str::CharIndices};
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum Language {
20 Html,
21 Vue,
22 Svelte,
23 Astro,
24 Angular,
25 Jinja,
26 Vento,
27 Mustache,
28 Xml,
29}
30
31pub struct Parser<'s> {
32 source: &'s str,
33 language: Language,
34 chars: Peekable<CharIndices<'s>>,
35 state: ParserState,
36}
37
38#[derive(Default)]
39struct ParserState {
40 has_front_matter: bool,
41}
42
43impl<'s> Parser<'s> {
44 pub fn new(source: &'s str, language: Language) -> Self {
45 Self {
46 source,
47 language,
48 chars: source.char_indices().peekable(),
49 state: Default::default(),
50 }
51 }
52
53 fn try_parse<F, R>(&mut self, f: F) -> PResult<R>
54 where
55 F: FnOnce(&mut Self) -> PResult<R>,
56 {
57 let chars = self.chars.clone();
58 let result = f(self);
59 if result.is_err() {
60 self.chars = chars;
61 }
62 result
63 }
64
65 fn emit_error(&mut self, kind: SyntaxErrorKind) -> SyntaxError {
66 let pos = self
67 .chars
68 .peek()
69 .map(|(pos, _)| *pos)
70 .unwrap_or(self.source.len());
71 self.emit_error_with_pos(kind, pos)
72 }
73
74 fn emit_error_with_pos(&self, kind: SyntaxErrorKind, pos: usize) -> SyntaxError {
75 let (line, column) = self.pos_to_line_col(pos);
76 SyntaxError {
77 kind,
78 pos,
79 line,
80 column,
81 }
82 }
83 fn pos_to_line_col(&self, pos: usize) -> (usize, usize) {
84 let search = memchr::memchr_iter(b'\n', self.source.as_bytes()).try_fold(
85 (1, 0),
86 |(line, prev_offset), offset| match pos.cmp(&offset) {
87 Ordering::Less => ControlFlow::Break((line, prev_offset)),
88 Ordering::Equal => ControlFlow::Break((line, prev_offset)),
89 Ordering::Greater => ControlFlow::Continue((line + 1, offset)),
90 },
91 );
92 match search {
93 ControlFlow::Break((line, offset)) => (line, pos - offset + 1),
94 ControlFlow::Continue((line, _)) => (line, 0),
95 }
96 }
97
98 fn skip_ws(&mut self) {
99 while self
100 .chars
101 .next_if(|(_, c)| c.is_ascii_whitespace())
102 .is_some()
103 {}
104 }
105
106 fn with_taken<T, F>(&mut self, parser: F) -> PResult<(T, &'s str)>
107 where
108 F: FnOnce(&mut Self) -> PResult<T>,
109 {
110 let start = self
111 .chars
112 .peek()
113 .map(|(i, _)| *i)
114 .unwrap_or(self.source.len());
115 let parsed = parser(self)?;
116 let end = self
117 .chars
118 .peek()
119 .map(|(i, _)| *i)
120 .unwrap_or(self.source.len());
121 Ok((parsed, unsafe { self.source.get_unchecked(start..end) }))
122 }
123
124 fn parse_angular_control_flow_children(&mut self) -> PResult<Vec<Node<'s>>> {
125 if self.chars.next_if(|(_, c)| *c == '{').is_none() {
126 return Err(self.emit_error(SyntaxErrorKind::ExpectChar('{')));
127 }
128
129 let mut children = vec![];
130 while let Some((_, c)) = self.chars.peek() {
131 if *c == '}' {
132 self.chars.next();
133 break;
134 } else {
135 children.push(self.parse_node()?);
136 }
137 }
138 Ok(children)
139 }
140
141 fn parse_angular_defer(&mut self) -> PResult<Vec<AngularGenericBlock<'s>>> {
142 if self
143 .chars
144 .next_if(|(_, c)| *c == '@')
145 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'd'))
146 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'e'))
147 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'f'))
148 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'e'))
149 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'r'))
150 .is_none()
151 {
152 return Err(self.emit_error(SyntaxErrorKind::ExpectAngularBlock("defer")));
153 }
154 self.skip_ws();
155 let mut blocks = vec![self.parse_angular_generic_block("defer")?];
156
157 loop {
158 let chars = self.chars.clone();
159 self.skip_ws();
160 if self.chars.next_if(|(_, c)| *c == '@').is_some()
161 && let Ok(name) = self.parse_identifier()
162 && matches!(name, "loading" | "error" | "placeholder")
163 {
164 self.skip_ws();
165 blocks.push(self.parse_angular_generic_block(name)?);
166 } else {
167 self.chars = chars;
168 return Ok(blocks);
169 }
170 }
171 }
172
173 fn parse_angular_for(&mut self) -> PResult<AngularFor<'s>> {
174 if self
175 .chars
176 .next_if(|(_, c)| *c == '@')
177 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'f'))
178 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'o'))
179 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'r'))
180 .is_none()
181 {
182 return Err(self.emit_error(SyntaxErrorKind::ExpectAngularBlock("for")));
183 }
184 self.skip_ws();
185
186 let Some((start, _)) = self.chars.next_if(|(_, c)| *c == '(') else {
187 return Err(self.emit_error(SyntaxErrorKind::ExpectChar('(')));
188 };
189
190 let (header, header_start) = self.parse_angular_inline_script(start + 1)?;
191 let Some((binding, expr)) = header.split_once(" of ").map(|(binding, expr)| {
192 (
193 (binding.trim_end(), header_start),
194 (expr.trim_start(), header_start + 4),
195 )
196 }) else {
197 return Err(self.emit_error(SyntaxErrorKind::ExpectKeyword("of")));
198 };
199
200 let mut track = None;
201 if self.chars.next_if(|(_, c)| *c == ';').is_some() {
202 self.skip_ws();
203 if self
204 .chars
205 .next_if(|(_, c)| *c == 't')
206 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'r'))
207 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'a'))
208 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'c'))
209 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'k'))
210 .is_some()
211 {
212 self.skip_ws();
213 if let Some((start, _)) = self.chars.peek() {
214 let start = *start;
215 track = Some(self.parse_angular_inline_script(start)?);
216 }
217 }
218 }
219
220 let mut aliases = vec![];
221 while self.chars.next_if(|(_, c)| *c == ';').is_some() {
222 self.skip_ws();
223 let mut chars = self.chars.clone();
224 if chars
225 .next_if(|(_, c)| *c == 'l')
226 .and_then(|_| chars.next_if(|(_, c)| *c == 'e'))
227 .and_then(|_| chars.next_if(|(_, c)| *c == 't'))
228 .is_some()
229 && let Some((start, _)) = self.chars.peek()
230 {
231 let start = *start;
232 aliases.push(self.parse_angular_inline_script(start)?);
233 }
234 }
235
236 self.chars.next_if(|(_, c)| *c == ';');
237 self.skip_ws();
238 if self.chars.next_if(|(_, c)| *c == ')').is_none() {
239 return Err(self.emit_error(SyntaxErrorKind::ExpectChar(')')));
240 };
241 self.skip_ws();
242 let children = self.parse_angular_control_flow_children()?;
243
244 let mut empty = None;
245 let mut chars = self.chars.clone();
246 while chars.next_if(|(_, c)| c.is_ascii_whitespace()).is_some() {}
247 if chars
248 .next_if(|(_, c)| *c == '@')
249 .and_then(|_| chars.next_if(|(_, c)| *c == 'e'))
250 .and_then(|_| chars.next_if(|(_, c)| *c == 'm'))
251 .and_then(|_| chars.next_if(|(_, c)| *c == 'p'))
252 .and_then(|_| chars.next_if(|(_, c)| *c == 't'))
253 .and_then(|_| chars.next_if(|(_, c)| *c == 'y'))
254 .is_some()
255 {
256 self.chars = chars;
257 self.skip_ws();
258 empty = Some(self.parse_angular_control_flow_children()?);
259 }
260
261 Ok(AngularFor {
262 binding,
263 expr,
264 track,
265 aliases,
266 children,
267 empty,
268 })
269 }
270
271 fn parse_angular_generic_block(
272 &mut self,
273 keyword: &'s str,
274 ) -> PResult<AngularGenericBlock<'s>> {
275 let header = if let Some((start, _)) = self.chars.next_if(|(_, c)| *c == '(') {
276 let mut paren_stack = 0u8;
277 loop {
278 match self.chars.next() {
279 Some((_, '(')) => paren_stack += 1,
280 Some((i, ')')) => {
281 if paren_stack == 0 {
282 break Some(unsafe { self.source.get_unchecked(start..i + 1) });
283 } else {
284 paren_stack -= 1;
285 }
286 }
287 Some(..) => {}
288 None => return Err(self.emit_error(SyntaxErrorKind::ExpectChar(')'))),
289 }
290 }
291 } else {
292 None
293 };
294 self.skip_ws();
295 Ok(AngularGenericBlock {
296 keyword,
297 header,
298 children: self.parse_angular_control_flow_children()?,
299 })
300 }
301
302 fn parse_angular_if(&mut self) -> PResult<AngularIf<'s>> {
303 if self
304 .chars
305 .next_if(|(_, c)| *c == '@')
306 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'i'))
307 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'f'))
308 .is_none()
309 {
310 return Err(self.emit_error(SyntaxErrorKind::ExpectAngularBlock("if")));
311 }
312 self.skip_ws();
313
314 let (expr, reference) = self.parse_angular_if_cond()?;
315 self.skip_ws();
316 let children = self.parse_angular_control_flow_children()?;
317
318 let mut else_if_blocks = vec![];
319 let mut else_children = None;
320 'alter: loop {
321 let mut chars = self.chars.clone();
322 'peek: loop {
323 match chars.next() {
324 Some((_, c)) if c.is_ascii_whitespace() => continue 'peek,
325 Some((_, '@')) => {
326 if chars
327 .next_if(|(_, c)| *c == 'e')
328 .and_then(|_| chars.next_if(|(_, c)| *c == 'l'))
329 .and_then(|_| chars.next_if(|(_, c)| *c == 's'))
330 .and_then(|_| chars.next_if(|(_, c)| *c == 'e'))
331 .is_some()
332 {
333 self.chars = chars;
334 break 'peek;
335 } else {
336 break 'alter;
337 }
338 }
339 _ => break 'alter,
340 }
341 }
342 self.skip_ws();
343
344 if self
345 .chars
346 .next_if(|(_, c)| *c == 'i')
347 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'f'))
348 .is_some()
349 {
350 self.skip_ws();
351 let (expr, reference) = self.parse_angular_if_cond()?;
352 self.skip_ws();
353 let children = self.parse_angular_control_flow_children()?;
354 else_if_blocks.push(AngularElseIf {
355 expr,
356 reference,
357 children,
358 });
359 } else {
360 else_children = Some(self.parse_angular_control_flow_children()?);
361 break;
362 }
363 }
364
365 Ok(AngularIf {
366 expr,
367 reference,
368 children,
369 else_if_blocks,
370 else_children,
371 })
372 }
373
374 fn parse_angular_if_cond(&mut self) -> PResult<AngularIfCond<'s>> {
375 let Some((start, _)) = self.chars.next_if(|(_, c)| *c == '(') else {
376 return Err(self.emit_error(SyntaxErrorKind::ExpectChar('(')));
377 };
378
379 let expr = self.parse_angular_inline_script(start + 1)?;
380
381 let mut reference = None;
382 if self.chars.next_if(|(_, c)| *c == ';').is_some() {
383 self.skip_ws();
384 if self
385 .chars
386 .next_if(|(_, c)| *c == 'a')
387 .and_then(|_| self.chars.next_if(|(_, c)| *c == 's'))
388 .is_none()
389 {
390 return Err(self.emit_error(SyntaxErrorKind::ExpectKeyword("as")));
391 }
392 self.skip_ws();
393 if let Some((start, _)) = self.chars.peek() {
394 let start = *start;
395 reference = Some(self.parse_angular_inline_script(start)?);
396 }
397 }
398
399 if self.chars.next_if(|(_, c)| *c == ')').is_none() {
400 return Err(self.emit_error(SyntaxErrorKind::ExpectChar(')')));
401 }
402
403 Ok((expr, reference))
404 }
405
406 fn parse_angular_inline_script(&mut self, start: usize) -> PResult<(&'s str, usize)> {
407 let end;
408 let mut chars_stack = vec![];
409 loop {
410 match self.chars.peek() {
411 Some((_, c @ '\'' | c @ '"' | c @ '`')) => {
412 if chars_stack.last().is_some_and(|last| last == c) {
413 chars_stack.pop();
414 } else {
415 chars_stack.push(*c);
416 }
417 self.chars.next();
418 }
419 Some((_, '(')) => {
420 chars_stack.push('(');
421 self.chars.next();
422 }
423 Some((i, ')')) => {
424 if chars_stack.is_empty() {
425 end = *i;
426 break;
427 } else if chars_stack.last().is_some_and(|last| *last == '(') {
428 chars_stack.pop();
429 self.chars.next();
430 }
431 }
432 Some((i, ';')) if chars_stack.is_empty() => {
433 end = *i;
434 break;
435 }
436 Some(..) => {
437 self.chars.next();
438 }
439 None => {
440 end = start;
441 break;
442 }
443 }
444 }
445 Ok((unsafe { self.source.get_unchecked(start..end) }, start))
446 }
447
448 fn parse_angular_let(&mut self) -> PResult<AngularLet<'s>> {
449 if self
450 .chars
451 .next_if(|(_, c)| *c == '@')
452 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'l'))
453 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'e'))
454 .and_then(|_| self.chars.next_if(|(_, c)| *c == 't'))
455 .is_none()
456 {
457 return Err(self.emit_error(SyntaxErrorKind::ExpectAngularLet));
458 }
459 self.skip_ws();
460
461 let name = self.parse_identifier()?;
462 self.skip_ws();
463 if self.chars.next_if(|(_, c)| *c == '=').is_none() {
464 return Err(self.emit_error(SyntaxErrorKind::ExpectChar('=')));
465 }
466 self.skip_ws();
467 let start = self
468 .chars
469 .peek()
470 .map(|(i, _)| *i)
471 .unwrap_or(self.source.len());
472 let expr = self.parse_angular_inline_script(start)?;
473 if self.chars.next_if(|(_, c)| *c == ';').is_none() {
474 return Err(self.emit_error(SyntaxErrorKind::ExpectChar(';')));
475 }
476
477 Ok(AngularLet { name, expr })
478 }
479
480 fn parse_angular_switch(&mut self) -> PResult<AngularSwitch<'s>> {
481 if self
482 .chars
483 .next_if(|(_, c)| *c == '@')
484 .and_then(|_| self.chars.next_if(|(_, c)| *c == 's'))
485 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'w'))
486 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'i'))
487 .and_then(|_| self.chars.next_if(|(_, c)| *c == 't'))
488 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'c'))
489 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'h'))
490 .is_none()
491 {
492 return Err(self.emit_error(SyntaxErrorKind::ExpectAngularSwitch));
493 }
494 self.skip_ws();
495
496 let Some((start, _)) = self.chars.next_if(|(_, c)| *c == '(') else {
497 return Err(self.emit_error(SyntaxErrorKind::ExpectChar('(')));
498 };
499 let expr = self.parse_angular_inline_script(start + 1)?;
500 if self.chars.next_if(|(_, c)| *c == ')').is_none() {
501 return Err(self.emit_error(SyntaxErrorKind::ExpectChar(')')));
502 }
503
504 self.skip_ws();
505 if self.chars.next_if(|(_, c)| *c == '{').is_none() {
506 return Err(self.emit_error(SyntaxErrorKind::ExpectChar('{')));
507 }
508 self.skip_ws();
509
510 let mut arms = Vec::with_capacity(2);
511 while let Some((_, '@')) = self.chars.peek() {
512 self.chars.next();
513 match self.chars.peek() {
514 Some((_, 'c')) => {
515 if self
516 .chars
517 .next_if(|(_, c)| *c == 'c')
518 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'a'))
519 .and_then(|_| self.chars.next_if(|(_, c)| *c == 's'))
520 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'e'))
521 .is_none()
522 {
523 return Err(self.emit_error(SyntaxErrorKind::ExpectKeyword("case")));
524 }
525 self.skip_ws();
526 let Some((start, _)) = self.chars.next_if(|(_, c)| *c == '(') else {
527 return Err(self.emit_error(SyntaxErrorKind::ExpectChar('(')));
528 };
529 let expr = self.parse_angular_inline_script(start + 1)?;
530 if self.chars.next_if(|(_, c)| *c == ')').is_none() {
531 return Err(self.emit_error(SyntaxErrorKind::ExpectChar(')')));
532 }
533 self.skip_ws();
534 let children = self.parse_angular_control_flow_children()?;
535 arms.push(AngularSwitchArm {
536 keyword: "case",
537 expr: Some(expr),
538 children,
539 });
540 self.skip_ws();
541 }
542 Some((_, 'd')) => {
543 if self
544 .chars
545 .next_if(|(_, c)| *c == 'd')
546 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'e'))
547 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'f'))
548 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'a'))
549 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'u'))
550 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'l'))
551 .and_then(|_| self.chars.next_if(|(_, c)| *c == 't'))
552 .is_none()
553 {
554 return Err(self.emit_error(SyntaxErrorKind::ExpectKeyword("default")));
555 }
556 self.skip_ws();
557 arms.push(AngularSwitchArm {
558 keyword: "default",
559 expr: None,
560 children: self.parse_angular_control_flow_children()?,
561 });
562 self.skip_ws();
563 }
564 _ => return Err(self.emit_error(SyntaxErrorKind::ExpectKeyword("case"))),
565 }
566 }
567
568 self.skip_ws();
569 if self.chars.next_if(|(_, c)| *c == '}').is_none() {
570 return Err(self.emit_error(SyntaxErrorKind::ExpectChar('}')));
571 }
572
573 Ok(AngularSwitch { expr, arms })
574 }
575
576 fn parse_astro_attr(&mut self) -> PResult<AstroAttribute<'s>> {
577 let (name, start, first_char) =
578 if let Some((start, first_char)) = self.chars.next_if(|(_, c)| *c == '{') {
579 (None, start, first_char)
580 } else {
581 let name = self.parse_attr_name()?;
582 self.skip_ws();
583 if let Some((start, first_char)) = self
584 .chars
585 .next_if(|(_, c)| *c == '=')
586 .map(|_| self.skip_ws())
587 .and_then(|_| self.chars.next_if(|(_, c)| matches!(c, '{' | '`')))
588 {
589 (Some(name), start, first_char)
590 } else {
591 return Err(self.emit_error(SyntaxErrorKind::ExpectAstroAttr));
592 }
593 };
594
595 if first_char == '`' {
596 while self.chars.next_if(|(_, c)| *c != '`').is_some() {}
597 if let Some((end, _)) = self.chars.next_if(|(_, c)| *c == '`') {
598 Ok(AstroAttribute {
599 name,
600 expr: unsafe { (self.source.get_unchecked(start..end + 1), start) },
601 })
602 } else {
603 Err(self.emit_error(SyntaxErrorKind::ExpectAstroAttr))
604 }
605 } else {
606 self.parse_svelte_or_astro_expr()
607 .map(|expr| AstroAttribute { name, expr })
608 }
609 }
610
611 fn parse_astro_expr(&mut self) -> PResult<AstroExpr<'s>> {
612 let Some((start, _)) = self.chars.next_if(|(_, c)| *c == '{') else {
613 return Err(self.emit_error(SyntaxErrorKind::ExpectAstroExpr));
614 };
615
616 let mut children = Vec::with_capacity(1);
617 let mut has_line_comment = false;
618 let mut pair_stack = vec![];
619 let mut pos = self
620 .chars
621 .peek()
622 .map(|(i, _)| *i)
623 .unwrap_or(self.source.len());
624 while let Some((i, c)) = self.chars.peek() {
625 match c {
626 '{' => {
627 pair_stack.push('{');
628 self.chars.next();
629 }
630 '}' => {
631 let i = *i;
632 self.chars.next();
633 if pair_stack.is_empty() {
634 debug_assert!(matches!(
635 children.last(),
636 Some(AstroExprChild::Template(..)) | None
637 ));
638 children.push(AstroExprChild::Script(unsafe {
639 self.source.get_unchecked(pos..i)
640 }));
641 break;
642 }
643 pair_stack.pop();
644 }
645 '<' if !matches!(pair_stack.last(), Some('/' | '*' | '\'' | '"' | '`')) => {
646 let i = *i;
647 let mut chars = self.chars.clone();
648 chars.next();
649 if chars
650 .next_if(|(_, c)| is_html_tag_name_char(*c) || *c == '!' || *c == '>')
651 .is_some()
652 {
653 let prev = unsafe { self.source.get_unchecked(pos..i) };
654 if prev.is_empty() {
655 } else if prev.chars().all(|c| c.is_ascii_whitespace()) {
657 if let Some(AstroExprChild::Template(nodes)) = children.last_mut() {
658 nodes.push(Node {
659 kind: NodeKind::Text(TextNode {
660 raw: prev,
661 line_breaks: prev.chars().filter(|c| *c == '\n').count(),
662 start: pos,
663 }),
664 raw: prev,
665 });
666 }
667 } else {
668 children.push(AstroExprChild::Script(prev));
669 }
670
671 let node = self.parse_node()?;
672 if let Some(AstroExprChild::Template(nodes)) = children.last_mut() {
673 nodes.push(node);
674 } else {
675 debug_assert!(matches!(
676 children.last(),
677 Some(AstroExprChild::Script(..)) | None
678 ));
679 children.push(AstroExprChild::Template(vec![node]));
680 }
681 pos = self
682 .chars
683 .peek()
684 .map(|(i, _)| *i)
685 .unwrap_or(self.source.len());
686 } else {
687 self.chars.next();
688 }
689 }
690 '\'' | '"' | '`' => {
691 let last = pair_stack.last();
692 if last.is_some_and(|last| last == c) {
693 pair_stack.pop();
694 } else if matches!(last, Some('$' | '{') | None) {
695 pair_stack.push(*c);
696 }
697 self.chars.next();
698 }
699 '$' if matches!(pair_stack.last(), Some('`')) => {
700 self.chars.next();
701 if self.chars.next_if(|(_, c)| *c == '{').is_some() {
702 pair_stack.push('$');
703 }
704 }
705 '/' if !matches!(pair_stack.last(), Some('\'' | '"' | '`' | '/' | '*')) => {
706 self.chars.next();
707 match self.chars.peek() {
708 Some((_, '/')) => {
709 pair_stack.push('/');
710 has_line_comment = true;
711 self.chars.next();
712 }
713 Some((_, '*')) => {
714 pair_stack.push('*');
715 self.chars.next();
716 }
717 _ => {}
718 }
719 }
720 '\n' => {
721 self.chars.next();
722 if let Some('/') = pair_stack.last() {
723 pair_stack.pop();
724 }
725 }
726 '*' => {
727 self.chars.next();
728 if self
729 .chars
730 .next_if(|(_, c)| *c == '/' && matches!(pair_stack.last(), Some('*')))
731 .is_some()
732 {
733 pair_stack.pop();
734 }
735 }
736 '\\' if matches!(pair_stack.last(), Some('\'' | '"' | '`')) => {
737 self.chars.next();
738 }
739 _ => {
740 self.chars.next();
741 }
742 }
743 }
744
745 Ok(AstroExpr {
746 children,
747 has_line_comment,
748 start: start + 1,
749 })
750 }
751
752 fn parse_attr(&mut self) -> PResult<Attribute<'s>> {
753 match self.language {
754 Language::Html | Language::Angular | Language::Mustache | Language::Xml => {
755 self.parse_native_attr().map(Attribute::Native)
756 }
757 Language::Vue => self
758 .try_parse(Parser::parse_vue_directive)
759 .map(Attribute::VueDirective)
760 .or_else(|_| self.parse_native_attr().map(Attribute::Native)),
761 Language::Svelte => self
762 .try_parse(Parser::parse_svelte_attachment)
763 .map(Attribute::SvelteAttachment)
764 .or_else(|_| {
765 self.try_parse(Parser::parse_svelte_attr)
766 .map(Attribute::Svelte)
767 })
768 .or_else(|_| self.parse_native_attr().map(Attribute::Native)),
769 Language::Astro => self
770 .try_parse(Parser::parse_astro_attr)
771 .map(Attribute::Astro)
772 .or_else(|_| self.parse_native_attr().map(Attribute::Native)),
773 Language::Jinja => {
774 self.skip_ws();
775 let result = if matches!(self.chars.peek(), Some((_, '{'))) {
776 let mut chars = self.chars.clone();
777 chars.next();
778 match chars.next() {
779 Some((_, '{')) => self.parse_native_attr().map(Attribute::Native),
780 Some((_, '#')) => self.parse_jinja_comment().map(Attribute::JinjaComment),
781 _ => self.parse_jinja_tag_or_block(None, &mut Parser::parse_attr),
782 }
783 } else {
784 self.parse_native_attr().map(Attribute::Native)
785 };
786 if result.is_ok() {
787 self.skip_ws();
788 }
789 result
790 }
791 Language::Vento => self
792 .try_parse(|parser| parser.parse_vento_tag_or_block(None))
793 .map(Attribute::VentoTagOrBlock)
794 .or_else(|_| self.parse_native_attr().map(Attribute::Native)),
795 }
796 }
797
798 fn parse_attr_name(&mut self) -> PResult<&'s str> {
799 if matches!(
800 self.language,
801 Language::Jinja | Language::Vento | Language::Mustache
802 ) {
803 let Some((start, mut end)) = (match self.chars.peek() {
804 Some((i, '{')) => {
805 let start = *i;
806 let mut chars = self.chars.clone();
807 chars.next();
808 if let Some((_, '{')) = chars.next() {
809 let end =
810 start + self.parse_mustache_interpolation()?.0.len() + "{{}}".len();
811 Some((start, end))
812 } else {
813 None
814 }
815 }
816 Some((_, c)) if is_attr_name_char(*c) => self
817 .chars
818 .next()
819 .map(|(start, c)| (start, start + c.len_utf8())),
820 _ => None,
821 }) else {
822 return Err(self.emit_error(SyntaxErrorKind::ExpectAttrName));
823 };
824
825 while let Some((_, c)) = self.chars.peek() {
826 if is_attr_name_char(*c) && *c != '{' {
827 end += c.len_utf8();
828 self.chars.next();
829 } else if *c == '{' {
830 let mut chars = self.chars.clone();
831 chars.next();
832 match chars.next() {
833 Some((_, '%')) => {
834 break;
835 }
836 Some((_, '{')) => {
837 end += self.parse_mustache_interpolation()?.0.len() + "{{}}".len();
838 }
839 Some((_, c)) => {
840 end += c.len_utf8();
841 self.chars.next();
842 }
843 None => break,
844 }
845 } else {
846 break;
847 }
848 }
849
850 unsafe { Ok(self.source.get_unchecked(start..end)) }
851 } else {
852 let Some((start, start_char)) = self.chars.next_if(|(_, c)| is_attr_name_char(*c))
853 else {
854 return Err(self.emit_error(SyntaxErrorKind::ExpectAttrName));
855 };
856 let mut end = start + start_char.len_utf8();
857
858 while let Some((_, c)) = self.chars.next_if(|(_, c)| is_attr_name_char(*c)) {
859 end += c.len_utf8();
860 }
861
862 unsafe { Ok(self.source.get_unchecked(start..end)) }
863 }
864 }
865
866 fn parse_attr_value(&mut self) -> PResult<(&'s str, usize)> {
867 let quote = self.chars.next_if(|(_, c)| *c == '"' || *c == '\'');
868
869 if let Some((start, quote)) = quote {
870 let can_interpolate = matches!(
871 self.language,
872 Language::Svelte | Language::Jinja | Language::Vento | Language::Mustache
873 );
874 let start = start + 1;
875 let mut end = start;
876 let mut chars_stack = vec![];
877 loop {
878 match self.chars.next() {
879 Some((i, c)) if c == quote => {
880 if chars_stack.is_empty() || !can_interpolate {
881 end = i;
882 break;
883 } else if chars_stack.last().is_some_and(|last| *last == c) {
884 chars_stack.pop();
885 } else {
886 chars_stack.push(c);
887 }
888 }
889 Some((_, '{')) if can_interpolate => {
890 chars_stack.push('{');
891 }
892 Some((_, '}'))
893 if can_interpolate
894 && chars_stack.last().is_some_and(|last| *last == '{') =>
895 {
896 chars_stack.pop();
897 }
898 Some(..) => continue,
899 None => break,
900 }
901 }
902 Ok((unsafe { self.source.get_unchecked(start..end) }, start))
903 } else {
904 fn is_unquoted_attr_value_char(c: char) -> bool {
905 !c.is_ascii_whitespace() && !matches!(c, '"' | '\'' | '=' | '<' | '>' | '`')
906 }
907
908 let start = match self.chars.peek() {
909 Some((i, c)) if is_unquoted_attr_value_char(*c) => *i,
910 _ => return Err(self.emit_error(SyntaxErrorKind::ExpectAttrValue)),
911 };
912
913 let mut end = start;
914 loop {
915 match self.chars.peek() {
916 Some((i, '{'))
917 if matches!(
918 self.language,
919 Language::Jinja | Language::Vento | Language::Mustache
920 ) =>
921 {
922 end = *i;
923 let mut chars = self.chars.clone();
924 chars.next();
925 match chars.peek() {
926 Some((_, '%')) => {
927 if self
928 .parse_jinja_tag_or_block(None, &mut Parser::parse_node)
929 .is_ok()
930 {
931 end =
932 self.chars.peek().map(|(i, _)| i - 1).ok_or_else(|| {
933 self.emit_error(SyntaxErrorKind::ExpectAttrValue)
934 })?;
935 } else {
936 self.chars.next();
937 }
938 }
939 Some((_, '{')) => {
940 chars.next();
941 let (interpolation, _) = self.parse_mustache_interpolation()?;
944 end += interpolation.len() + "{{}}".len() - 1;
945 }
946 _ => {
947 self.chars.next();
948 }
949 }
950 }
951 Some((i, c)) if is_unquoted_attr_value_char(*c) => {
952 end = *i;
953 self.chars.next();
954 }
955 _ => break,
956 }
957 }
958
959 Ok((unsafe { self.source.get_unchecked(start..=end) }, start))
960 }
961 }
962
963 fn parse_cdata(&mut self) -> PResult<Cdata<'s>> {
964 let Some((start, _)) = self
965 .chars
966 .next_if(|(_, c)| *c == '<')
967 .and_then(|_| self.chars.next_if(|(_, c)| *c == '!'))
968 .and_then(|_| self.chars.next_if(|(_, c)| *c == '['))
969 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'C'))
970 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'D'))
971 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'A'))
972 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'T'))
973 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'A'))
974 .and_then(|_| self.chars.next_if(|(_, c)| *c == '['))
975 else {
976 return Err(self.emit_error(SyntaxErrorKind::ExpectCdata));
977 };
978 let start = start + 1;
979
980 let mut end = start;
981 loop {
982 match self.chars.next() {
983 Some((i, ']')) => {
984 let mut chars = self.chars.clone();
985 if chars
986 .next_if(|(_, c)| *c == ']')
987 .and_then(|_| chars.next_if(|(_, c)| *c == '>'))
988 .is_some()
989 {
990 end = i;
991 self.chars = chars;
992 break;
993 }
994 }
995 Some(..) => continue,
996 None => break,
997 }
998 }
999
1000 Ok(Cdata {
1001 raw: unsafe { self.source.get_unchecked(start..end) },
1002 })
1003 }
1004
1005 fn parse_comment(&mut self) -> PResult<Comment<'s>> {
1006 let Some((start, _)) = self
1007 .chars
1008 .next_if(|(_, c)| *c == '<')
1009 .and_then(|_| self.chars.next_if(|(_, c)| *c == '!'))
1010 .and_then(|_| self.chars.next_if(|(_, c)| *c == '-'))
1011 .and_then(|_| self.chars.next_if(|(_, c)| *c == '-'))
1012 else {
1013 return Err(self.emit_error(SyntaxErrorKind::ExpectComment));
1014 };
1015 let start = start + 1;
1016
1017 let mut end = start;
1018 loop {
1019 match self.chars.next() {
1020 Some((i, '-')) => {
1021 let mut chars = self.chars.clone();
1022 if chars
1023 .next_if(|(_, c)| *c == '-')
1024 .and_then(|_| chars.next_if(|(_, c)| *c == '>'))
1025 .is_some()
1026 {
1027 end = i;
1028 self.chars = chars;
1029 break;
1030 }
1031 }
1032 Some(..) => continue,
1033 None => break,
1034 }
1035 }
1036
1037 Ok(Comment {
1038 raw: unsafe { self.source.get_unchecked(start..end) },
1039 })
1040 }
1041
1042 fn parse_doctype(&mut self) -> PResult<Doctype<'s>> {
1043 let keyword_start = if let Some((start, _)) = self
1044 .chars
1045 .next_if(|(_, c)| *c == '<')
1046 .and_then(|_| self.chars.next_if(|(_, c)| *c == '!'))
1047 {
1048 start + 1
1049 } else {
1050 return Err(self.emit_error(SyntaxErrorKind::ExpectDoctype));
1051 };
1052 let keyword = if let Some((end, _)) = self
1053 .chars
1054 .next_if(|(_, c)| c.eq_ignore_ascii_case(&'d'))
1055 .and_then(|_| self.chars.next_if(|(_, c)| c.eq_ignore_ascii_case(&'o')))
1056 .and_then(|_| self.chars.next_if(|(_, c)| c.eq_ignore_ascii_case(&'c')))
1057 .and_then(|_| self.chars.next_if(|(_, c)| c.eq_ignore_ascii_case(&'t')))
1058 .and_then(|_| self.chars.next_if(|(_, c)| c.eq_ignore_ascii_case(&'y')))
1059 .and_then(|_| self.chars.next_if(|(_, c)| c.eq_ignore_ascii_case(&'p')))
1060 .and_then(|_| self.chars.next_if(|(_, c)| c.eq_ignore_ascii_case(&'e')))
1061 {
1062 unsafe { self.source.get_unchecked(keyword_start..end + 1) }
1063 } else {
1064 return Err(self.emit_error(SyntaxErrorKind::ExpectDoctype));
1065 };
1066 self.skip_ws();
1067
1068 let value_start = if let Some((start, _)) = self.chars.peek() {
1069 *start
1070 } else {
1071 return Err(self.emit_error(SyntaxErrorKind::ExpectDoctype));
1072 };
1073 while self.chars.next_if(|(_, c)| *c != '>').is_some() {}
1074
1075 if let Some((value_end, _)) = self.chars.next_if(|(_, c)| *c == '>') {
1076 Ok(Doctype {
1077 keyword,
1078 value: unsafe { self.source.get_unchecked(value_start..value_end) }.trim_end(),
1079 })
1080 } else {
1081 Err(self.emit_error(SyntaxErrorKind::ExpectDoctype))
1082 }
1083 }
1084
1085 fn parse_element(&mut self) -> PResult<Element<'s>> {
1086 let Some((element_start, _)) = self.chars.next_if(|(_, c)| *c == '<') else {
1087 return Err(self.emit_error(SyntaxErrorKind::ExpectElement));
1088 };
1089 let tag_name = self.parse_tag_name()?;
1090 let void_element = helpers::is_void_element(tag_name, self.language);
1091
1092 let mut attrs = vec![];
1093 let mut first_attr_same_line = true;
1094 loop {
1095 match self.chars.peek() {
1096 Some((_, '/')) => {
1097 self.chars.next();
1098 if self.chars.next_if(|(_, c)| *c == '>').is_some() {
1099 return Ok(Element {
1100 tag_name,
1101 attrs,
1102 first_attr_same_line,
1103 children: vec![],
1104 self_closing: true,
1105 void_element,
1106 });
1107 }
1108 return Err(self.emit_error(SyntaxErrorKind::ExpectSelfCloseTag));
1109 }
1110 Some((_, '>')) => {
1111 self.chars.next();
1112 if void_element {
1113 return Ok(Element {
1114 tag_name,
1115 attrs,
1116 first_attr_same_line,
1117 children: vec![],
1118 self_closing: false,
1119 void_element,
1120 });
1121 }
1122 break;
1123 }
1124 Some((_, '\n')) => {
1125 if attrs.is_empty() {
1126 first_attr_same_line = false;
1127 }
1128 self.chars.next();
1129 }
1130 Some((_, c)) if c.is_ascii_whitespace() => {
1131 self.chars.next();
1132 }
1133 _ => {
1134 attrs.push(self.parse_attr()?);
1135 }
1136 }
1137 }
1138
1139 let mut children = vec![];
1140 let should_parse_raw = self.language != Language::Xml
1141 && (tag_name.eq_ignore_ascii_case("script")
1142 || tag_name.eq_ignore_ascii_case("style")
1143 || tag_name.eq_ignore_ascii_case("pre")
1144 || tag_name.eq_ignore_ascii_case("textarea"));
1145 if should_parse_raw {
1146 let text_node = self.parse_raw_text_node(tag_name)?;
1147 let raw = text_node.raw;
1148 if !raw.is_empty() {
1149 children.push(Node {
1150 kind: NodeKind::Text(text_node),
1151 raw,
1152 });
1153 }
1154 }
1155
1156 loop {
1157 match self.chars.peek() {
1158 Some((_, '<')) => {
1159 let mut chars = self.chars.clone();
1160 chars.next();
1161 if let Some((pos, _)) = chars.next_if(|(_, c)| *c == '/') {
1162 self.chars = chars;
1163 let close_tag_name = self.parse_tag_name()?;
1164 if !close_tag_name.eq_ignore_ascii_case(tag_name) {
1165 let (line, column) = self.pos_to_line_col(element_start);
1166 return Err(self.emit_error_with_pos(
1167 SyntaxErrorKind::ExpectCloseTag {
1168 tag_name: tag_name.into(),
1169 line,
1170 column,
1171 },
1172 pos,
1173 ));
1174 }
1175 self.skip_ws();
1176 if self.chars.next_if(|(_, c)| *c == '>').is_some() {
1177 break;
1178 }
1179 let (line, column) = self.pos_to_line_col(element_start);
1180 return Err(self.emit_error(SyntaxErrorKind::ExpectCloseTag {
1181 tag_name: tag_name.into(),
1182 line,
1183 column,
1184 }));
1185 }
1186 children.push(self.parse_node()?);
1187 }
1188 Some(..) => {
1189 if should_parse_raw {
1190 let text_node = self.parse_raw_text_node(tag_name)?;
1191 let raw = text_node.raw;
1192 if !raw.is_empty() {
1193 children.push(Node {
1194 kind: NodeKind::Text(text_node),
1195 raw,
1196 });
1197 }
1198 } else {
1199 children.push(self.parse_node()?);
1200 }
1201 }
1202 None => {
1203 let (line, column) = self.pos_to_line_col(element_start);
1204 return Err(self.emit_error(SyntaxErrorKind::ExpectCloseTag {
1205 tag_name: tag_name.into(),
1206 line,
1207 column,
1208 }));
1209 }
1210 }
1211 }
1212
1213 Ok(Element {
1214 tag_name,
1215 attrs,
1216 first_attr_same_line,
1217 children,
1218 self_closing: false,
1219 void_element,
1220 })
1221 }
1222
1223 fn parse_front_matter(&mut self) -> PResult<FrontMatter<'s>> {
1224 let Some((start, _)) = self
1225 .chars
1226 .next_if(|(_, c)| *c == '-')
1227 .and_then(|_| self.chars.next_if(|(_, c)| *c == '-'))
1228 .and_then(|_| self.chars.next_if(|(_, c)| *c == '-'))
1229 else {
1230 return Err(self.emit_error(SyntaxErrorKind::ExpectFrontMatter));
1231 };
1232 let start = start + 1;
1233
1234 let mut pair_stack = vec![];
1235 let mut end = start;
1236 loop {
1237 match self.chars.next() {
1238 Some((i, '-')) if pair_stack.is_empty() => {
1239 let mut chars = self.chars.clone();
1240 if chars
1241 .next_if(|(_, c)| *c == '-')
1242 .and_then(|_| chars.next_if(|(_, c)| *c == '-'))
1243 .is_some()
1244 {
1245 end = i;
1246 self.chars = chars;
1247 break;
1248 }
1249 }
1250 Some((_, c @ '\'' | c @ '"' | c @ '`')) => {
1251 let last = pair_stack.last();
1252 if last.is_some_and(|last| *last == c) {
1253 pair_stack.pop();
1254 } else if matches!(last, Some('$' | '{') | None) {
1255 pair_stack.push(c);
1256 }
1257 }
1258 Some((_, '$')) if matches!(pair_stack.last(), Some('`')) => {
1259 if self.chars.next_if(|(_, c)| *c == '{').is_some() {
1260 pair_stack.push('$');
1261 }
1262 }
1263 Some((_, '{')) if matches!(pair_stack.last(), Some('$' | '{') | None) => {
1264 pair_stack.push('{');
1265 }
1266 Some((_, '}')) if matches!(pair_stack.last(), Some('$' | '{')) => {
1267 pair_stack.pop();
1268 }
1269 Some((_, '/'))
1270 if !matches!(pair_stack.last(), Some('\'' | '"' | '`' | '/' | '*')) =>
1271 {
1272 if let Some((_, c)) = self.chars.next_if(|(_, c)| *c == '/' || *c == '*') {
1273 pair_stack.push(c);
1274 }
1275 }
1276 Some((_, '\n')) => {
1277 if let Some('/' | '\'' | '"') = pair_stack.last() {
1278 pair_stack.pop();
1279 }
1280 }
1281 Some((_, '*')) => {
1282 if self
1283 .chars
1284 .next_if(|(_, c)| *c == '/' && matches!(pair_stack.last(), Some('*')))
1285 .is_some()
1286 {
1287 pair_stack.pop();
1288 }
1289 }
1290 Some((_, '\\')) if matches!(pair_stack.last(), Some('\'' | '"' | '`')) => {
1291 self.chars.next();
1292 }
1293 Some(..) => continue,
1294 None => break,
1295 }
1296 }
1297
1298 self.state.has_front_matter = true;
1299 Ok(FrontMatter {
1300 raw: unsafe { self.source.get_unchecked(start..end) },
1301 start,
1302 })
1303 }
1304
1305 fn parse_identifier(&mut self) -> PResult<&'s str> {
1306 fn is_identifier_char(c: char) -> bool {
1307 c.is_ascii_alphanumeric() || c == '-' || c == '_' || !c.is_ascii() || c == '\\'
1308 }
1309
1310 let Some((start, _)) = self.chars.next_if(|(_, c)| is_identifier_char(*c)) else {
1311 return Err(self.emit_error(SyntaxErrorKind::ExpectIdentifier));
1312 };
1313 let mut end = start;
1314
1315 while let Some((i, _)) = self.chars.next_if(|(_, c)| is_identifier_char(*c)) {
1316 end = i;
1317 }
1318
1319 unsafe { Ok(self.source.get_unchecked(start..=end)) }
1320 }
1321
1322 fn parse_inside(&mut self, open: char, close: char, inclusive: bool) -> PResult<&'s str> {
1324 let Some(start) = self
1325 .chars
1326 .next_if(|(_, c)| *c == open)
1327 .map(|(i, c)| if inclusive { i } else { i + c.len_utf8() })
1328 else {
1329 return Err(self.emit_error(SyntaxErrorKind::ExpectChar(open)));
1330 };
1331 let mut end = start;
1332 let mut stack = 0u8;
1333 for (i, c) in self.chars.by_ref() {
1334 if c == open {
1335 stack += 1;
1336 } else if c == close {
1337 if stack == 0 {
1338 end = if inclusive { i + close.len_utf8() } else { i };
1339 break;
1340 }
1341 stack -= 1;
1342 }
1343 }
1344 Ok(unsafe { self.source.get_unchecked(start..end) })
1345 }
1346
1347 fn parse_jinja_block_children<T, F>(&mut self, children_parser: &mut F) -> PResult<Vec<T>>
1348 where
1349 T: HasJinjaFlowControl<'s>,
1350 F: FnMut(&mut Self) -> PResult<T>,
1351 {
1352 let mut children = vec![];
1353 loop {
1354 match self.chars.peek() {
1355 Some((_, '{')) => {
1356 let mut chars = self.chars.clone();
1357 chars.next();
1358 if chars.next_if(|(_, c)| *c == '%').is_some() {
1359 break;
1360 }
1361 children.push(children_parser(self)?);
1362 }
1363 Some(..) => {
1364 children.push(children_parser(self)?);
1365 }
1366 None => return Err(self.emit_error(SyntaxErrorKind::ExpectJinjaBlockEnd)),
1367 }
1368 }
1369 Ok(children)
1370 }
1371
1372 fn parse_jinja_comment(&mut self) -> PResult<JinjaComment<'s>> {
1373 let Some((start, _)) = self
1374 .chars
1375 .next_if(|(_, c)| *c == '{')
1376 .and_then(|_| self.chars.next_if(|(_, c)| *c == '#'))
1377 else {
1378 return Err(self.emit_error(SyntaxErrorKind::ExpectComment));
1379 };
1380 let start = start + 1;
1381
1382 let mut end = start;
1383 loop {
1384 match self.chars.next() {
1385 Some((i, '#')) => {
1386 let mut chars = self.chars.clone();
1387 if chars.next_if(|(_, c)| *c == '}').is_some() {
1388 end = i;
1389 self.chars = chars;
1390 break;
1391 }
1392 }
1393 Some(..) => continue,
1394 None => break,
1395 }
1396 }
1397
1398 Ok(JinjaComment {
1399 raw: unsafe { self.source.get_unchecked(start..end) },
1400 })
1401 }
1402
1403 fn parse_jinja_tag(&mut self) -> PResult<JinjaTag<'s>> {
1404 let Some((start, _)) = self
1405 .chars
1406 .next_if(|(_, c)| *c == '{')
1407 .and_then(|_| self.chars.next_if(|(_, c)| *c == '%'))
1408 else {
1409 return Err(self.emit_error(SyntaxErrorKind::ExpectJinjaTag));
1410 };
1411 let start = start + 1;
1412
1413 let mut end = start;
1414 loop {
1415 match self.chars.next() {
1416 Some((i, '%')) => {
1417 if self.chars.next_if(|(_, c)| *c == '}').is_some() {
1418 end = i;
1419 break;
1420 }
1421 }
1422 Some(..) => continue,
1423 None => break,
1424 }
1425 }
1426
1427 Ok(JinjaTag {
1428 content: unsafe { self.source.get_unchecked(start..end) },
1429 start,
1430 })
1431 }
1432
1433 fn parse_jinja_tag_or_block<T, F>(
1434 &mut self,
1435 first_tag: Option<JinjaTag<'s>>,
1436 children_parser: &mut F,
1437 ) -> PResult<T::Intermediate>
1438 where
1439 T: HasJinjaFlowControl<'s>,
1440 F: FnMut(&mut Self) -> PResult<T>,
1441 {
1442 let first_tag = if let Some(first_tag) = first_tag {
1443 first_tag
1444 } else {
1445 self.parse_jinja_tag()?
1446 };
1447 let tag_name = parse_jinja_tag_name(&first_tag);
1448
1449 if matches!(
1450 tag_name,
1451 "for"
1452 | "if"
1453 | "macro"
1454 | "call"
1455 | "filter"
1456 | "block"
1457 | "apply"
1458 | "autoescape"
1459 | "embed"
1460 | "with"
1461 | "trans"
1462 | "raw"
1463 ) || tag_name == "set" && !first_tag.content.contains('=')
1464 {
1465 let mut body = vec![JinjaTagOrChildren::Tag(first_tag)];
1466
1467 loop {
1468 let mut children = self.parse_jinja_block_children(children_parser)?;
1469 if !children.is_empty() {
1470 if let Some(JinjaTagOrChildren::Children(nodes)) = body.last_mut() {
1471 nodes.append(&mut children);
1472 } else {
1473 body.push(JinjaTagOrChildren::Children(children));
1474 }
1475 }
1476 if let Ok(next_tag) = self.parse_jinja_tag() {
1477 let next_tag_name = parse_jinja_tag_name(&next_tag);
1478 if next_tag_name
1479 .strip_prefix("end")
1480 .is_some_and(|name| name == tag_name)
1481 {
1482 body.push(JinjaTagOrChildren::Tag(next_tag));
1483 break;
1484 }
1485 if (tag_name == "if" || tag_name == "for")
1486 && matches!(next_tag_name, "elif" | "elseif" | "else")
1487 {
1488 body.push(JinjaTagOrChildren::Tag(next_tag));
1489 } else if let Some(JinjaTagOrChildren::Children(nodes)) = body.last_mut() {
1490 nodes.push(
1491 self.with_taken(|parser| {
1492 parser.parse_jinja_tag_or_block(Some(next_tag), children_parser)
1493 })
1494 .map(|(kind, raw)| T::build(kind, raw))?,
1495 );
1496 } else {
1497 body.push(JinjaTagOrChildren::Children(vec![
1498 self.with_taken(|parser| {
1499 parser.parse_jinja_tag_or_block(Some(next_tag), children_parser)
1500 })
1501 .map(|(kind, raw)| T::build(kind, raw))?,
1502 ]));
1503 }
1504 } else {
1505 break;
1506 }
1507 }
1508 Ok(T::from_block(JinjaBlock { body }))
1509 } else {
1510 Ok(T::from_tag(first_tag))
1511 }
1512 }
1513
1514 fn parse_mustache_block_or_interpolation(&mut self) -> PResult<NodeKind<'s>> {
1515 let mut controls = vec![];
1516 let (raw, _) = self.parse_mustache_interpolation()?;
1517 let (content, wc_before, wc_after) = strip_hbs_whitespace_control(raw);
1518 if let Some((prefix, rest)) = content
1519 .split_at_checked(1)
1520 .filter(|(c, _)| matches!(*c, "#" | "^" | "$" | "<"))
1521 {
1522 let (prefix, rest) = if rest.strip_prefix(['>', '*']).is_some() {
1523 content.split_at(2)
1524 } else {
1525 (prefix, rest)
1526 };
1527 let trimmed_rest = rest.trim_ascii();
1528 let (block_name, rest) = if let Some((name, rest)) =
1529 trimmed_rest.split_once(|c: char| c.is_ascii_whitespace())
1530 {
1531 (name, Some(rest))
1532 } else {
1533 (trimmed_rest, None)
1534 };
1535 controls.push(MustacheBlockControl {
1536 name: block_name,
1537 prefix,
1538 content: rest,
1539 wc_before,
1540 wc_after,
1541 });
1542 let mut children = vec![vec![]];
1543 loop {
1544 let chars = self.chars.clone();
1545 if let Some((content, _)) = self.parse_mustache_interpolation().ok()
1546 && let (content, wc_before, wc_after) = strip_hbs_whitespace_control(content)
1547 && content
1548 .strip_prefix('/')
1549 .is_some_and(|s| s.trim_ascii() == block_name)
1550 {
1551 controls.push(MustacheBlockControl {
1552 name: block_name,
1553 prefix: "/",
1554 content: None,
1555 wc_before,
1556 wc_after,
1557 });
1558 break;
1559 } else {
1560 self.chars = chars;
1561 }
1562 let node = self.parse_node()?;
1563 if let NodeKind::MustacheInterpolation(interpolation) = &node.kind
1564 && let ("else", wc_before, wc_after) =
1565 strip_hbs_whitespace_control(interpolation.content)
1566 {
1567 controls.push(MustacheBlockControl {
1568 name: "else",
1569 prefix: "",
1570 content: None,
1571 wc_before,
1572 wc_after,
1573 });
1574 children.push(vec![]);
1575 } else if let Some(nodes) = children.last_mut() {
1576 nodes.push(node);
1577 }
1578 }
1579 Ok(NodeKind::MustacheBlock(MustacheBlock {
1580 controls,
1581 children,
1582 }))
1583 } else {
1584 Ok(NodeKind::MustacheInterpolation(MustacheInterpolation {
1585 content: raw,
1586 }))
1587 }
1588 }
1589
1590 fn parse_mustache_interpolation(&mut self) -> PResult<(&'s str, usize)> {
1591 let Some((start, _)) = self
1592 .chars
1593 .next_if(|(_, c)| *c == '{')
1594 .and_then(|_| self.chars.next_if(|(_, c)| *c == '{'))
1595 else {
1596 return Err(self.emit_error(SyntaxErrorKind::ExpectMustacheInterpolation));
1597 };
1598 let start = start + 1;
1599
1600 let mut braces_stack = 0usize;
1601 let mut end = start;
1602 loop {
1603 match self.chars.next() {
1604 Some((_, '{')) => braces_stack += 1,
1605 Some((i, '}')) => {
1606 if braces_stack == 0 {
1607 if self.chars.next_if(|(_, c)| *c == '}').is_some() {
1608 end = i;
1609 break;
1610 }
1611 } else {
1612 braces_stack -= 1;
1613 }
1614 }
1615 Some(..) => continue,
1616 None => break,
1617 }
1618 }
1619
1620 Ok((unsafe { self.source.get_unchecked(start..end) }, start))
1621 }
1622
1623 fn parse_native_attr(&mut self) -> PResult<NativeAttribute<'s>> {
1624 let name = self.parse_attr_name()?;
1625 self.skip_ws();
1626 let mut quote = None;
1627 let value = if self.chars.next_if(|(_, c)| *c == '=').is_some() {
1628 self.skip_ws();
1629 quote = self
1630 .chars
1631 .peek()
1632 .and_then(|(_, c)| (*c == '\'' || *c == '"').then_some(*c));
1633 Some(self.parse_attr_value()?)
1634 } else {
1635 None
1636 };
1637 Ok(NativeAttribute { name, value, quote })
1638 }
1639
1640 fn parse_node(&mut self) -> PResult<Node<'s>> {
1641 let (kind, raw) = self.with_taken(Parser::parse_node_kind)?;
1642 Ok(Node { kind, raw })
1643 }
1644
1645 fn parse_node_kind(&mut self) -> PResult<NodeKind<'s>> {
1646 match self.chars.peek() {
1647 Some((_, '<')) => {
1648 let mut chars = self.chars.clone();
1649 chars.next();
1650 match chars.next() {
1651 Some((_, c))
1652 if is_html_tag_name_char(c)
1653 || is_special_tag_name_char(c, self.language) =>
1654 {
1655 self.parse_element().map(NodeKind::Element)
1656 }
1657 Some((_, '!')) => {
1658 if matches!(
1659 self.language,
1660 Language::Html
1661 | Language::Astro
1662 | Language::Jinja
1663 | Language::Vento
1664 | Language::Mustache
1665 | Language::Xml
1666 ) {
1667 self.try_parse(Parser::parse_comment)
1668 .map(NodeKind::Comment)
1669 .or_else(|_| {
1670 self.try_parse(Parser::parse_doctype).map(NodeKind::Doctype)
1671 })
1672 .or_else(|_| {
1673 self.try_parse(Parser::parse_cdata).map(NodeKind::Cdata)
1674 })
1675 .or_else(|_| self.parse_text_node().map(NodeKind::Text))
1676 } else {
1677 self.parse_comment().map(NodeKind::Comment)
1678 }
1679 }
1680 Some((_, '?')) if self.language == Language::Xml => {
1681 self.parse_xml_decl().map(NodeKind::XmlDecl)
1682 }
1683 _ => self.parse_text_node().map(NodeKind::Text),
1684 }
1685 }
1686 Some((_, '{')) => {
1687 let mut chars = self.chars.clone();
1688 chars.next();
1689 match chars.next() {
1690 Some((_, '{')) => {
1691 match self.language {
1692 Language::Html | Language::Xml => {
1693 self.parse_text_node().map(NodeKind::Text)
1694 }
1695 Language::Vue | Language::Jinja => self
1696 .parse_mustache_interpolation()
1697 .map(|(expr, start)| match self.language {
1698 Language::Vue => {
1699 NodeKind::VueInterpolation(VueInterpolation { expr, start })
1700 }
1701 Language::Jinja => {
1702 NodeKind::JinjaInterpolation(JinjaInterpolation {
1703 expr,
1704 start,
1705 })
1706 }
1707 _ => unreachable!(),
1708 }),
1709 Language::Svelte => self
1710 .parse_svelte_interpolation()
1711 .map(NodeKind::SvelteInterpolation),
1712 Language::Astro => self.parse_astro_expr().map(NodeKind::AstroExpr),
1713 Language::Angular => self
1714 .try_parse(|parser| {
1715 parser.parse_mustache_interpolation().map(|(expr, start)| {
1716 NodeKind::AngularInterpolation(AngularInterpolation {
1717 expr,
1718 start,
1719 })
1720 })
1721 })
1722 .or_else(|_| self.parse_text_node().map(NodeKind::Text)),
1723 Language::Vento => self.parse_vento_tag_or_block(None),
1724 Language::Mustache => self.parse_mustache_block_or_interpolation(),
1725 }
1726 }
1727 Some((_, '#')) if matches!(self.language, Language::Svelte) => {
1728 match chars.next() {
1729 Some((_, 'i')) => {
1730 self.parse_svelte_if_block().map(NodeKind::SvelteIfBlock)
1731 }
1732 Some((_, 'e')) => self
1733 .parse_svelte_each_block()
1734 .map(NodeKind::SvelteEachBlock),
1735 Some((_, 'a')) => self
1736 .parse_svelte_await_block()
1737 .map(NodeKind::SvelteAwaitBlock),
1738 Some((_, 'k')) => {
1739 self.parse_svelte_key_block().map(NodeKind::SvelteKeyBlock)
1740 }
1741 Some((_, 's')) => self
1742 .parse_svelte_snippet_block()
1743 .map(NodeKind::SvelteSnippetBlock),
1744 _ => self.parse_text_node().map(NodeKind::Text),
1745 }
1746 }
1747 Some((_, '#')) if matches!(self.language, Language::Jinja) => {
1748 self.parse_jinja_comment().map(NodeKind::JinjaComment)
1749 }
1750 Some((_, '@')) => self.parse_svelte_at_tag().map(NodeKind::SvelteAtTag),
1751 Some((_, '%')) if matches!(self.language, Language::Jinja) => {
1752 self.parse_jinja_tag_or_block(None, &mut Parser::parse_node)
1753 }
1754 _ => match self.language {
1755 Language::Svelte => self
1756 .parse_svelte_interpolation()
1757 .map(NodeKind::SvelteInterpolation),
1758 Language::Astro => self.parse_astro_expr().map(NodeKind::AstroExpr),
1759 _ => self.parse_text_node().map(NodeKind::Text),
1760 },
1761 }
1762 }
1763 Some((_, '-'))
1764 if matches!(
1765 self.language,
1766 Language::Astro | Language::Jinja | Language::Vento | Language::Mustache
1767 ) && !self.state.has_front_matter =>
1768 {
1769 let mut chars = self.chars.clone();
1770 chars.next();
1771 if let Some(((_, '-'), (_, '-'))) = chars.next().zip(chars.next()) {
1772 self.parse_front_matter().map(NodeKind::FrontMatter)
1773 } else {
1774 self.parse_text_node().map(NodeKind::Text)
1775 }
1776 }
1777 Some((_, '@')) if matches!(self.language, Language::Angular) => {
1778 let mut chars = self.chars.clone();
1779 chars.next();
1780 match chars.next() {
1781 Some((_, 'i')) => self.parse_angular_if().map(NodeKind::AngularIf),
1782 Some((_, 'f')) => self.parse_angular_for().map(NodeKind::AngularFor),
1783 Some((_, 's')) => self.parse_angular_switch().map(NodeKind::AngularSwitch),
1784 Some((_, 'l')) => self.parse_angular_let().map(NodeKind::AngularLet),
1785 Some((_, 'd')) => self
1786 .parse_angular_defer()
1787 .map(NodeKind::AngularGenericBlocks),
1788 _ => self.parse_text_node().map(NodeKind::Text),
1789 }
1790 }
1791 Some(..) => self.parse_text_node().map(NodeKind::Text),
1792 None => Err(self.emit_error(SyntaxErrorKind::ExpectElement)),
1793 }
1794 }
1795
1796 fn parse_raw_text_node(&mut self, tag_name: &str) -> PResult<TextNode<'s>> {
1797 let start = self
1798 .chars
1799 .peek()
1800 .map(|(i, _)| *i)
1801 .unwrap_or(self.source.len());
1802
1803 let allow_nested = tag_name.eq_ignore_ascii_case("pre");
1804 let mut nested = 0u16;
1805 let mut line_breaks = 0;
1806 let end;
1807 loop {
1808 match self.chars.peek() {
1809 Some((i, '<')) => {
1810 let i = *i;
1811 let mut chars = self.chars.clone();
1812 chars.next();
1813 if chars.next_if(|(_, c)| *c == '/').is_some()
1814 && chars
1815 .by_ref()
1816 .zip(tag_name.chars())
1817 .all(|((_, a), b)| a.eq_ignore_ascii_case(&b))
1818 {
1819 if nested == 0 {
1820 end = i;
1821 break;
1822 } else {
1823 nested -= 1;
1824 self.chars = chars;
1825 continue;
1826 }
1827 } else if allow_nested
1828 && chars
1829 .by_ref()
1830 .zip(tag_name.chars())
1831 .all(|((_, a), b)| a.eq_ignore_ascii_case(&b))
1832 {
1833 nested += 1;
1834 self.chars = chars;
1835 continue;
1836 }
1837 self.chars.next();
1838 }
1839 Some((_, c)) => {
1840 if *c == '\n' {
1841 line_breaks += 1;
1842 }
1843 self.chars.next();
1844 }
1845 None => {
1846 end = self.source.len();
1847 break;
1848 }
1849 }
1850 }
1851
1852 Ok(TextNode {
1853 raw: unsafe { self.source.get_unchecked(start..end) },
1854 line_breaks,
1855 start,
1856 })
1857 }
1858
1859 pub fn parse_root(&mut self) -> PResult<Root<'s>> {
1860 let mut children = vec![];
1861 while self.chars.peek().is_some() {
1862 children.push(self.parse_node()?);
1863 }
1864
1865 Ok(Root { children })
1866 }
1867
1868 fn parse_svelte_at_tag(&mut self) -> PResult<SvelteAtTag<'s>> {
1869 if self
1870 .chars
1871 .next_if(|(_, c)| *c == '{')
1872 .and_then(|_| self.chars.next_if(|(_, c)| *c == '@'))
1873 .is_none()
1874 {
1875 return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteAtTag));
1876 };
1877 let name = self.parse_identifier()?;
1878 self.skip_ws();
1879 let expr = self.parse_svelte_or_astro_expr()?;
1880 Ok(SvelteAtTag { name, expr })
1881 }
1882
1883 fn parse_svelte_attachment(&mut self) -> PResult<SvelteAttachment<'s>> {
1884 if self
1885 .chars
1886 .next_if(|(_, c)| *c == '{')
1887 .map(|_| self.skip_ws())
1888 .and_then(|_| self.chars.next_if(|(_, c)| *c == '@'))
1889 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'a'))
1890 .and_then(|_| self.chars.next_if(|(_, c)| *c == 't'))
1891 .and_then(|_| self.chars.next_if(|(_, c)| *c == 't'))
1892 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'a'))
1893 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'c'))
1894 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'h'))
1895 .is_some()
1896 {
1897 self.parse_svelte_or_astro_expr()
1898 .map(|expr| SvelteAttachment { expr })
1899 } else {
1900 Err(self.emit_error(SyntaxErrorKind::ExpectSvelteAttachment))
1901 }
1902 }
1903
1904 fn parse_svelte_attr(&mut self) -> PResult<SvelteAttribute<'s>> {
1905 let name = if self.chars.next_if(|(_, c)| *c == '{').is_some() {
1906 None
1907 } else {
1908 let name = self.parse_attr_name()?;
1909 self.skip_ws();
1910 if self
1911 .chars
1912 .next_if(|(_, c)| *c == '=')
1913 .map(|_| self.skip_ws())
1914 .and_then(|_| self.chars.next_if(|(_, c)| *c == '{'))
1915 .is_some()
1916 {
1917 Some(name)
1918 } else {
1919 return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteAttr));
1920 }
1921 };
1922
1923 self.parse_svelte_or_astro_expr()
1924 .map(|expr| SvelteAttribute { name, expr })
1925 }
1926
1927 fn parse_svelte_await_block(&mut self) -> PResult<Box<SvelteAwaitBlock<'s>>> {
1928 if self
1929 .chars
1930 .next_if(|(_, c)| *c == '{')
1931 .and_then(|_| self.chars.next_if(|(_, c)| *c == '#'))
1932 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'a'))
1933 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'w'))
1934 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'a'))
1935 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'i'))
1936 .and_then(|_| self.chars.next_if(|(_, c)| *c == 't'))
1937 .and_then(|_| self.chars.next_if(|(_, c)| c.is_ascii_whitespace()))
1938 .is_none()
1939 {
1940 return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteIfBlock));
1941 };
1942 self.skip_ws();
1943
1944 let expr = {
1945 let start = self
1946 .chars
1947 .peek()
1948 .map(|(i, _)| *i)
1949 .unwrap_or(self.source.len());
1950 let mut end = start;
1951 let mut braces_stack = 0u8;
1952 loop {
1953 match self.chars.peek() {
1954 Some((i, c)) if c.is_ascii_whitespace() => {
1955 let i = *i;
1956 self.skip_ws();
1957 let mut chars = self.chars.clone();
1958 match chars.next() {
1959 Some((_, 't')) => {
1960 if chars
1961 .next_if(|(_, c)| *c == 'h')
1962 .and_then(|_| chars.next_if(|(_, c)| *c == 'e'))
1963 .and_then(|_| chars.next_if(|(_, c)| *c == 'n'))
1964 .is_some()
1965 {
1966 end = i;
1967 break;
1968 }
1969 }
1970 Some((_, 'c')) => {
1971 if chars
1972 .next_if(|(_, c)| *c == 'a')
1973 .and_then(|_| chars.next_if(|(_, c)| *c == 't'))
1974 .and_then(|_| chars.next_if(|(_, c)| *c == 'c'))
1975 .and_then(|_| chars.next_if(|(_, c)| *c == 'h'))
1976 .is_some()
1977 {
1978 end = i;
1979 break;
1980 }
1981 }
1982 _ => {}
1983 }
1984 }
1985 Some((i, '{')) => {
1986 braces_stack += 1;
1987 end = *i;
1988 self.chars.next();
1989 }
1990 Some((i, '}')) => {
1991 end = *i;
1992 if braces_stack == 0 {
1993 break;
1994 }
1995 self.chars.next();
1996 braces_stack -= 1;
1997 }
1998 Some((i, _)) => {
1999 end = *i;
2000 self.chars.next();
2001 }
2002 None => break,
2003 }
2004 }
2005 (unsafe { self.source.get_unchecked(start..end) }, start)
2006 };
2007
2008 self.skip_ws();
2009 let then_binding = if self
2010 .chars
2011 .next_if(|(_, c)| *c == 't')
2012 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'h'))
2013 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'e'))
2014 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'n'))
2015 .is_some()
2016 {
2017 self.skip_ws();
2018 Some(match self.chars.peek() {
2019 Some((_, '}')) => None,
2020 _ => Some(self.parse_svelte_binding()?),
2021 })
2022 } else {
2023 None
2024 };
2025
2026 self.skip_ws();
2027 let catch_binding = if self
2028 .chars
2029 .next_if(|(_, c)| *c == 'c')
2030 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'a'))
2031 .and_then(|_| self.chars.next_if(|(_, c)| *c == 't'))
2032 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'c'))
2033 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'h'))
2034 .is_some()
2035 {
2036 self.skip_ws();
2037 Some(match self.chars.peek() {
2038 Some((_, '}')) => None,
2039 _ => Some(self.parse_svelte_binding()?),
2040 })
2041 } else {
2042 None
2043 };
2044
2045 self.skip_ws();
2046 if self.chars.next_if(|(_, c)| *c == '}').is_none() {
2047 return Err(self.emit_error(SyntaxErrorKind::ExpectChar('}')));
2048 }
2049
2050 let children = self.parse_svelte_block_children()?;
2051
2052 let then_block = if self
2053 .try_parse(|parser| {
2054 parser
2055 .chars
2056 .next_if(|(_, c)| *c == '{')
2057 .and_then(|_| parser.chars.next_if(|(_, c)| *c == ':'))
2058 .and_then(|_| parser.chars.next_if(|(_, c)| *c == 't'))
2059 .and_then(|_| parser.chars.next_if(|(_, c)| *c == 'h'))
2060 .and_then(|_| parser.chars.next_if(|(_, c)| *c == 'e'))
2061 .and_then(|_| parser.chars.next_if(|(_, c)| *c == 'n'))
2062 .ok_or_else(|| parser.emit_error(SyntaxErrorKind::ExpectSvelteThenBlock))
2063 })
2064 .is_ok()
2065 {
2066 self.skip_ws();
2067 let binding = match self.chars.peek() {
2068 Some((_, '}')) => None,
2069 _ => {
2070 let binding = self.parse_svelte_binding()?;
2071 self.skip_ws();
2072 Some(binding)
2073 }
2074 };
2075 if self.chars.next_if(|(_, c)| *c == '}').is_none() {
2076 return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteThenBlock));
2077 }
2078 let children = self.parse_svelte_block_children()?;
2079 Some(SvelteThenBlock { binding, children })
2080 } else {
2081 None
2082 };
2083
2084 let catch_block = if self
2085 .try_parse(|parser| {
2086 parser
2087 .chars
2088 .next_if(|(_, c)| *c == '{')
2089 .and_then(|_| parser.chars.next_if(|(_, c)| *c == ':'))
2090 .and_then(|_| parser.chars.next_if(|(_, c)| *c == 'c'))
2091 .and_then(|_| parser.chars.next_if(|(_, c)| *c == 'a'))
2092 .and_then(|_| parser.chars.next_if(|(_, c)| *c == 't'))
2093 .and_then(|_| parser.chars.next_if(|(_, c)| *c == 'c'))
2094 .and_then(|_| parser.chars.next_if(|(_, c)| *c == 'h'))
2095 .ok_or_else(|| parser.emit_error(SyntaxErrorKind::ExpectSvelteCatchBlock))
2096 })
2097 .is_ok()
2098 {
2099 self.skip_ws();
2100 let binding = match self.chars.peek() {
2101 Some((_, '}')) => None,
2102 _ => {
2103 let binding = self.parse_svelte_binding()?;
2104 self.skip_ws();
2105 Some(binding)
2106 }
2107 };
2108 if self.chars.next_if(|(_, c)| *c == '}').is_none() {
2109 return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteCatchBlock));
2110 }
2111 let children = self.parse_svelte_block_children()?;
2112 Some(SvelteCatchBlock { binding, children })
2113 } else {
2114 None
2115 };
2116
2117 if self
2118 .chars
2119 .next_if(|(_, c)| *c == '{')
2120 .map(|_| self.skip_ws())
2121 .and_then(|_| self.chars.next_if(|(_, c)| *c == '/'))
2122 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'a'))
2123 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'w'))
2124 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'a'))
2125 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'i'))
2126 .and_then(|_| self.chars.next_if(|(_, c)| *c == 't'))
2127 .map(|_| self.skip_ws())
2128 .and_then(|_| self.chars.next_if(|(_, c)| *c == '}'))
2129 .is_some()
2130 {
2131 Ok(Box::new(SvelteAwaitBlock {
2132 expr,
2133 then_binding,
2134 catch_binding,
2135 children,
2136 then_block,
2137 catch_block,
2138 }))
2139 } else {
2140 Err(self.emit_error(SyntaxErrorKind::ExpectSvelteBlockEnd))
2141 }
2142 }
2143
2144 fn parse_svelte_binding(&mut self) -> PResult<(&'s str, usize)> {
2145 match self.chars.peek() {
2146 Some((start, '{')) => {
2147 let start = start + 1;
2148 self.parse_inside('{', '}', true)
2149 .map(|binding| (binding, start))
2150 }
2151 Some((start, '[')) => {
2152 let start = start + 1;
2153 self.parse_inside('[', ']', true)
2154 .map(|binding| (binding, start))
2155 }
2156 Some((start, _)) => {
2157 let start = *start;
2158 self.parse_identifier().map(|ident| (ident, start))
2159 }
2160 _ => Err(self.emit_error(SyntaxErrorKind::ExpectIdentifier)),
2161 }
2162 }
2163
2164 fn parse_svelte_block_children(&mut self) -> PResult<Vec<Node<'s>>> {
2165 let mut children = vec![];
2166 loop {
2167 match self.chars.peek() {
2168 Some((_, '{')) => {
2169 let mut chars = self.chars.clone();
2170 chars.next();
2171 while chars.next_if(|(_, c)| c.is_ascii_whitespace()).is_some() {}
2172 if chars.next_if(|(_, c)| *c == '/' || *c == ':').is_some() {
2173 break;
2174 }
2175 children.push(self.parse_node()?);
2176 }
2177 Some(..) => {
2178 children.push(self.parse_node()?);
2179 }
2180 None => return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteBlockEnd)),
2181 }
2182 }
2183 Ok(children)
2184 }
2185
2186 fn parse_svelte_each_block(&mut self) -> PResult<SvelteEachBlock<'s>> {
2187 if self
2188 .chars
2189 .next_if(|(_, c)| *c == '{')
2190 .and_then(|_| self.chars.next_if(|(_, c)| *c == '#'))
2191 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'e'))
2192 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'a'))
2193 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'c'))
2194 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'h'))
2195 .and_then(|_| self.chars.next_if(|(_, c)| c.is_ascii_whitespace()))
2196 .is_none()
2197 {
2198 return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteIfBlock));
2199 };
2200 self.skip_ws();
2201
2202 let mut binding = None;
2203 let expr = {
2204 let start = self
2205 .chars
2206 .peek()
2207 .map(|(i, _)| *i)
2208 .unwrap_or(self.source.len());
2209 let mut end = start;
2210 let mut pair_stack = vec![];
2211 loop {
2212 match self.chars.peek() {
2213 Some((i, c)) if c.is_ascii_whitespace() => {
2214 end = *i;
2215 self.skip_ws();
2216 let mut chars = self.chars.clone();
2217 if chars
2218 .next_if(|(_, c)| *c == 'a')
2219 .and_then(|_| chars.next_if(|(_, c)| *c == 's'))
2220 .and_then(|_| chars.next_if(|(_, c)| c.is_ascii_whitespace()))
2221 .is_some()
2222 {
2223 self.chars = chars;
2224 self.skip_ws();
2225 binding = Some(self.parse_svelte_binding()?);
2226
2227 let mut chars = self.chars.clone();
2229 while chars.next_if(|(_, c)| c.is_ascii_whitespace()).is_some() {}
2230 if matches!(chars.peek(), Some((_, '}' | '(' | ','))) {
2231 break;
2232 }
2233 }
2234 }
2235 Some((_, '(')) => {
2236 pair_stack.push('(');
2237 self.chars.next();
2238 }
2239 Some((i, ')')) if matches!(pair_stack.last(), Some('(')) => {
2240 pair_stack.pop();
2241 end = *i;
2242 self.chars.next();
2243 }
2244 Some((_, '[')) => {
2245 pair_stack.push('[');
2246 self.chars.next();
2247 }
2248 Some((i, ']')) if matches!(pair_stack.last(), Some('[')) => {
2249 pair_stack.pop();
2250 end = *i;
2251 self.chars.next();
2252 }
2253 Some((_, '{')) => {
2254 pair_stack.push('{');
2255 self.chars.next();
2256 }
2257 Some((i, '}')) => {
2258 end = *i;
2259 if matches!(pair_stack.last(), Some('{')) {
2260 pair_stack.pop();
2261 self.chars.next();
2262 } else {
2263 break;
2264 }
2265 }
2266 Some((i, ',')) => {
2267 end = *i;
2268 if pair_stack.is_empty() {
2269 break;
2270 } else {
2271 self.chars.next();
2272 }
2273 }
2274 Some((i, _)) => {
2275 end = *i;
2276 self.chars.next();
2277 }
2278 None => break,
2279 }
2280 }
2281 (unsafe { self.source.get_unchecked(start..end) }, start)
2282 };
2283
2284 self.skip_ws();
2285 let index = if self.chars.next_if(|(_, c)| *c == ',').is_some() {
2286 self.skip_ws();
2287 Some(self.parse_identifier()?)
2288 } else {
2289 None
2290 };
2291
2292 self.skip_ws();
2293 let key = if let Some((start, '(')) = self.chars.peek() {
2294 let start = start + 1;
2295 Some((self.parse_inside('(', ')', false)?, start))
2296 } else {
2297 None
2298 };
2299
2300 self.skip_ws();
2301 if self.chars.next_if(|(_, c)| *c == '}').is_none() {
2302 return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteEachBlock));
2303 }
2304
2305 let children = self.parse_svelte_block_children()?;
2306
2307 let else_children = if self
2308 .try_parse(|parser| {
2309 parser
2310 .chars
2311 .next_if(|(_, c)| *c == '{')
2312 .and_then(|_| parser.chars.next_if(|(_, c)| *c == ':'))
2313 .and_then(|_| parser.chars.next_if(|(_, c)| *c == 'e'))
2314 .and_then(|_| parser.chars.next_if(|(_, c)| *c == 'l'))
2315 .and_then(|_| parser.chars.next_if(|(_, c)| *c == 's'))
2316 .and_then(|_| parser.chars.next_if(|(_, c)| *c == 'e'))
2317 .and_then(|_| {
2318 parser.skip_ws();
2319 parser.chars.next_if(|(_, c)| *c == '}')
2320 })
2321 .ok_or_else(|| parser.emit_error(SyntaxErrorKind::ExpectSvelteEachBlock))
2322 })
2323 .is_ok()
2324 {
2325 Some(self.parse_svelte_block_children()?)
2326 } else {
2327 None
2328 };
2329
2330 if self
2331 .chars
2332 .next_if(|(_, c)| *c == '{')
2333 .map(|_| self.skip_ws())
2334 .and_then(|_| self.chars.next_if(|(_, c)| *c == '/'))
2335 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'e'))
2336 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'a'))
2337 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'c'))
2338 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'h'))
2339 .map(|_| self.skip_ws())
2340 .and_then(|_| self.chars.next_if(|(_, c)| *c == '}'))
2341 .is_some()
2342 {
2343 Ok(SvelteEachBlock {
2344 expr,
2345 binding,
2346 index,
2347 key,
2348 children,
2349 else_children,
2350 })
2351 } else {
2352 Err(self.emit_error(SyntaxErrorKind::ExpectSvelteBlockEnd))
2353 }
2354 }
2355
2356 fn parse_svelte_if_block(&mut self) -> PResult<SvelteIfBlock<'s>> {
2357 if self
2358 .chars
2359 .next_if(|(_, c)| *c == '{')
2360 .and_then(|_| self.chars.next_if(|(_, c)| *c == '#'))
2361 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'i'))
2362 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'f'))
2363 .and_then(|_| self.chars.next_if(|(_, c)| c.is_ascii_whitespace()))
2364 .is_none()
2365 {
2366 return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteIfBlock));
2367 };
2368
2369 let expr = self.parse_svelte_or_astro_expr()?;
2370 let children = self.parse_svelte_block_children()?;
2371
2372 let mut else_if_blocks = vec![];
2373 let mut else_children = None;
2374 loop {
2375 if self.chars.next_if(|(_, c)| *c == '{').is_none() {
2376 return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteBlockEnd));
2377 }
2378 self.skip_ws();
2379 match self.chars.next() {
2380 Some((_, ':')) => {
2381 if self
2382 .chars
2383 .next_if(|(_, c)| *c == 'e')
2384 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'l'))
2385 .and_then(|_| self.chars.next_if(|(_, c)| *c == 's'))
2386 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'e'))
2387 .is_none()
2388 {
2389 return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteElseIfBlock));
2390 }
2391 self.skip_ws();
2392 match self.chars.next() {
2393 Some((_, 'i')) => {
2394 if self.chars.next_if(|(_, c)| *c == 'f').is_none() {
2395 return Err(
2396 self.emit_error(SyntaxErrorKind::ExpectSvelteElseIfBlock)
2397 );
2398 }
2399 let expr = self.parse_svelte_or_astro_expr()?;
2400 let children = self.parse_svelte_block_children()?;
2401 else_if_blocks.push(SvelteElseIfBlock { expr, children });
2402 }
2403 Some((_, '}')) => {
2404 else_children = Some(self.parse_svelte_block_children()?);
2405 }
2406 _ => return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteElseIfBlock)),
2407 }
2408 }
2409 Some((_, '/')) => break,
2410 _ => return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteBlockEnd)),
2411 }
2412 }
2413 if self
2414 .chars
2415 .next_if(|(_, c)| *c == 'i')
2416 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'f'))
2417 .map(|_| self.skip_ws())
2418 .and_then(|_| self.chars.next_if(|(_, c)| *c == '}'))
2419 .is_some()
2420 {
2421 Ok(SvelteIfBlock {
2422 expr,
2423 children,
2424 else_if_blocks,
2425 else_children,
2426 })
2427 } else {
2428 Err(self.emit_error(SyntaxErrorKind::ExpectSvelteBlockEnd))
2429 }
2430 }
2431
2432 fn parse_svelte_interpolation(&mut self) -> PResult<SvelteInterpolation<'s>> {
2433 if self.chars.next_if(|(_, c)| *c == '{').is_some() {
2434 Ok(SvelteInterpolation {
2435 expr: self.parse_svelte_or_astro_expr()?,
2436 })
2437 } else {
2438 Err(self.emit_error(SyntaxErrorKind::ExpectSvelteInterpolation))
2439 }
2440 }
2441
2442 fn parse_svelte_key_block(&mut self) -> PResult<SvelteKeyBlock<'s>> {
2443 if self
2444 .chars
2445 .next_if(|(_, c)| *c == '{')
2446 .and_then(|_| self.chars.next_if(|(_, c)| *c == '#'))
2447 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'k'))
2448 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'e'))
2449 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'y'))
2450 .and_then(|_| self.chars.next_if(|(_, c)| c.is_ascii_whitespace()))
2451 .is_none()
2452 {
2453 return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteKeyBlock));
2454 };
2455
2456 let expr = self.parse_svelte_or_astro_expr()?;
2457 let children = self.parse_svelte_block_children()?;
2458
2459 if self
2460 .chars
2461 .next_if(|(_, c)| *c == '{')
2462 .map(|_| self.skip_ws())
2463 .and_then(|_| self.chars.next_if(|(_, c)| *c == '/'))
2464 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'k'))
2465 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'e'))
2466 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'y'))
2467 .map(|_| self.skip_ws())
2468 .and_then(|_| self.chars.next_if(|(_, c)| *c == '}'))
2469 .is_some()
2470 {
2471 Ok(SvelteKeyBlock { expr, children })
2472 } else {
2473 Err(self.emit_error(SyntaxErrorKind::ExpectSvelteBlockEnd))
2474 }
2475 }
2476
2477 fn parse_svelte_or_astro_expr(&mut self) -> PResult<(&'s str, usize)> {
2479 self.skip_ws();
2480
2481 let start = self
2482 .chars
2483 .peek()
2484 .map(|(i, _)| *i)
2485 .unwrap_or(self.source.len());
2486 let mut end = start;
2487 let mut braces_stack = 0u8;
2488 loop {
2489 match self.chars.next() {
2490 Some((_, '{')) => {
2491 braces_stack += 1;
2492 }
2493 Some((i, '}')) => {
2494 if braces_stack == 0 {
2495 end = i;
2496 break;
2497 }
2498 braces_stack -= 1;
2499 }
2500 Some(..) => continue,
2501 None => break,
2502 }
2503 }
2504 Ok((unsafe { self.source.get_unchecked(start..end) }, start))
2505 }
2506
2507 fn parse_svelte_snippet_block(&mut self) -> PResult<SvelteSnippetBlock<'s>> {
2508 if self
2509 .chars
2510 .next_if(|(_, c)| *c == '{')
2511 .and_then(|_| self.chars.next_if(|(_, c)| *c == '#'))
2512 .and_then(|_| self.chars.next_if(|(_, c)| *c == 's'))
2513 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'n'))
2514 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'i'))
2515 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'p'))
2516 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'p'))
2517 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'e'))
2518 .and_then(|_| self.chars.next_if(|(_, c)| *c == 't'))
2519 .and_then(|_| self.chars.next_if(|(_, c)| c.is_ascii_whitespace()))
2520 .is_none()
2521 {
2522 return Err(self.emit_error(SyntaxErrorKind::ExpectSvelteSnippetBlock));
2523 };
2524
2525 let signature = self.parse_svelte_or_astro_expr()?;
2526 let children = self.parse_svelte_block_children()?;
2527
2528 if self
2529 .chars
2530 .next_if(|(_, c)| *c == '{')
2531 .map(|_| self.skip_ws())
2532 .and_then(|_| self.chars.next_if(|(_, c)| *c == '/'))
2533 .and_then(|_| self.chars.next_if(|(_, c)| *c == 's'))
2534 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'n'))
2535 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'i'))
2536 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'p'))
2537 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'p'))
2538 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'e'))
2539 .and_then(|_| self.chars.next_if(|(_, c)| *c == 't'))
2540 .map(|_| self.skip_ws())
2541 .and_then(|_| self.chars.next_if(|(_, c)| *c == '}'))
2542 .is_some()
2543 {
2544 Ok(SvelteSnippetBlock {
2545 signature,
2546 children,
2547 })
2548 } else {
2549 Err(self.emit_error(SyntaxErrorKind::ExpectSvelteBlockEnd))
2550 }
2551 }
2552
2553 fn parse_tag_name(&mut self) -> PResult<&'s str> {
2554 let (start, mut end) = match self.chars.peek() {
2555 Some((i, c)) if is_html_tag_name_char(*c) => {
2556 let c = *c;
2557 let start = *i;
2558 self.chars.next();
2559 (start, start + c.len_utf8())
2560 }
2561 Some((i, '{')) if matches!(self.language, Language::Jinja) => (*i, *i + 1),
2562 Some((_, '>')) if matches!(self.language, Language::Astro) => {
2563 return Ok("");
2565 }
2566 _ => return Err(self.emit_error(SyntaxErrorKind::ExpectTagName)),
2567 };
2568
2569 while let Some((i, c)) = self.chars.peek() {
2570 if is_html_tag_name_char(*c) {
2571 end = *i + c.len_utf8();
2572 self.chars.next();
2573 } else if *c == '{' && matches!(self.language, Language::Jinja) {
2574 let current_i = *i;
2575 let mut chars = self.chars.clone();
2576 chars.next();
2577 if chars.next_if(|(_, c)| *c == '{').is_some() {
2578 end = current_i + self.parse_mustache_interpolation()?.0.len() + "{{}}".len();
2579 } else {
2580 break;
2581 }
2582 } else {
2583 break;
2584 }
2585 }
2586
2587 unsafe { Ok(self.source.get_unchecked(start..end)) }
2588 }
2589
2590 fn parse_text_node(&mut self) -> PResult<TextNode<'s>> {
2591 let Some((start, first_char)) = self.chars.next() else {
2592 return Err(self.emit_error(SyntaxErrorKind::ExpectTextNode));
2593 };
2594
2595 let mut line_breaks = if first_char == '\n' { 1 } else { 0 };
2596 let end;
2597 loop {
2598 match self.chars.peek() {
2599 Some((i, '{')) => match self.language {
2600 Language::Html | Language::Xml => {
2601 self.chars.next();
2602 }
2603 Language::Vue | Language::Vento | Language::Mustache => {
2604 let i = *i;
2605 let mut chars = self.chars.clone();
2606 chars.next();
2607 if chars.next_if(|(_, c)| *c == '{').is_some() {
2608 end = i;
2609 break;
2610 }
2611 self.chars.next();
2612 }
2613 Language::Svelte | Language::Astro => {
2614 end = *i;
2615 break;
2616 }
2617 Language::Jinja => {
2618 let i = *i;
2619 let mut chars = self.chars.clone();
2620 chars.next();
2621 if chars
2622 .next_if(|(_, c)| *c == '%' || *c == '{' || *c == '#')
2623 .is_some()
2624 {
2625 end = i;
2626 break;
2627 }
2628 self.chars.next();
2629 }
2630 Language::Angular => {
2631 let i = *i;
2632 let mut chars = self.chars.clone();
2633 chars.next();
2634 if chars.next_if(|(_, c)| *c == '{').is_some()
2637 && chars.next_if(|(_, c)| *c == '{').is_none()
2638 {
2639 end = i;
2640 break;
2641 }
2642 self.chars.next();
2643 }
2644 },
2645 Some((i, '<')) => {
2646 let i = *i;
2647 let mut chars = self.chars.clone();
2648 chars.next();
2649 match chars.next() {
2650 Some((_, c))
2651 if is_html_tag_name_char(c)
2652 || is_special_tag_name_char(c, self.language)
2653 || c == '/'
2654 || c == '!' =>
2655 {
2656 end = i;
2657 break;
2658 }
2659 _ => {
2660 self.chars.next();
2661 }
2662 }
2663 }
2664 Some((i, '-'))
2665 if matches!(self.language, Language::Astro) && !self.state.has_front_matter =>
2666 {
2667 let i = *i;
2668 let mut chars = self.chars.clone();
2669 chars.next();
2670 if let Some(((_, '-'), (_, '-'))) = chars.next().zip(chars.next()) {
2671 end = i;
2672 break;
2673 }
2674 self.chars.next();
2675 }
2676 Some((i, '}' | '@')) if matches!(self.language, Language::Angular) => {
2677 end = *i;
2678 break;
2679 }
2680 Some((_, c)) => {
2681 if *c == '\n' {
2682 line_breaks += 1;
2683 }
2684 self.chars.next();
2685 }
2686 None => {
2687 end = self.source.len();
2688 break;
2689 }
2690 }
2691 }
2692
2693 Ok(TextNode {
2694 raw: unsafe { self.source.get_unchecked(start..end) },
2695 line_breaks,
2696 start,
2697 })
2698 }
2699
2700 fn parse_vento_block_children(&mut self) -> PResult<Vec<Node<'s>>> {
2701 let mut children = vec![];
2702 loop {
2703 match self.chars.peek() {
2704 Some((_, '{')) => {
2705 let mut chars = self.chars.clone();
2706 chars.next();
2707 if chars.next_if(|(_, c)| *c == '{').is_some() {
2708 break;
2709 }
2710 children.push(self.parse_node()?);
2711 }
2712 Some(..) => {
2713 children.push(self.parse_node()?);
2714 }
2715 None => return Err(self.emit_error(SyntaxErrorKind::ExpectVentoBlockEnd)),
2716 }
2717 }
2718 Ok(children)
2719 }
2720
2721 fn parse_vento_tag_or_block(
2722 &mut self,
2723 first_tag: Option<(&'s str, bool, bool, usize)>,
2724 ) -> PResult<NodeKind<'s>> {
2725 let (first_tag, trim_prev, trim_next, first_tag_start) = if let Some(first_tag) = first_tag
2726 {
2727 first_tag
2728 } else {
2729 let (mut first_tag, mut start) = self.parse_mustache_interpolation()?;
2730 let mut trim_prev = false;
2731 let mut trim_next = false;
2732 if let Some(tag) = first_tag.strip_prefix('-') {
2733 first_tag = tag;
2734 trim_prev = true;
2735 start += 1;
2736 }
2737 if let Some(tag) = first_tag.strip_suffix('-') {
2738 first_tag = tag;
2739 trim_next = true;
2740 }
2741 (first_tag, trim_prev, trim_next, start)
2742 };
2743
2744 if let Some(raw) = first_tag
2745 .strip_prefix('#')
2746 .and_then(|s| s.strip_suffix('#'))
2747 {
2748 return Ok(NodeKind::VentoComment(VentoComment { raw }));
2749 } else if let Some(raw) = first_tag.strip_prefix('>') {
2750 return Ok(NodeKind::VentoEval(VentoEval {
2751 raw,
2752 start: first_tag_start,
2753 }));
2754 }
2755
2756 let (tag_name, tag_rest) = helpers::parse_vento_tag(first_tag);
2757
2758 let is_function = tag_name == "function"
2759 || matches!(tag_name, "async" | "export") && tag_rest.starts_with("function");
2760 if matches!(tag_name, "for" | "if" | "layout")
2761 || matches!(tag_name, "set" | "export") && !first_tag.contains('=')
2762 || is_function
2763 {
2764 let mut body = vec![VentoTagOrChildren::Tag(VentoTag {
2765 tag: first_tag,
2766 trim_prev,
2767 trim_next,
2768 })];
2769
2770 loop {
2771 let mut children = self.parse_vento_block_children()?;
2772 if !children.is_empty() {
2773 if let Some(VentoTagOrChildren::Children(nodes)) = body.last_mut() {
2774 nodes.append(&mut children);
2775 } else {
2776 body.push(VentoTagOrChildren::Children(children));
2777 }
2778 }
2779 if let Ok((mut next_tag, mut next_tag_start)) = self.parse_mustache_interpolation()
2780 {
2781 let mut trim_prev = false;
2782 let mut trim_next = false;
2783 if let Some(tag) = next_tag.strip_prefix('-') {
2784 next_tag = tag;
2785 trim_prev = true;
2786 next_tag_start += 1;
2787 };
2788 if let Some(tag) = next_tag.strip_suffix('-') {
2789 next_tag = tag;
2790 trim_next = true;
2791 };
2792 let (next_tag_name, _) = helpers::parse_vento_tag(next_tag);
2793 if next_tag_name
2794 .trim()
2795 .strip_prefix('/')
2796 .is_some_and(|name| name == tag_name || is_function && name == "function")
2797 {
2798 body.push(VentoTagOrChildren::Tag(VentoTag {
2799 tag: next_tag,
2800 trim_prev,
2801 trim_next,
2802 }));
2803 break;
2804 }
2805 if tag_name == "if" && next_tag_name == "else" {
2806 body.push(VentoTagOrChildren::Tag(VentoTag {
2807 tag: next_tag,
2808 trim_prev,
2809 trim_next,
2810 }));
2811 } else {
2812 let node = self
2813 .with_taken(|parser| {
2814 parser.parse_vento_tag_or_block(Some((
2815 next_tag,
2816 trim_prev,
2817 trim_next,
2818 next_tag_start,
2819 )))
2820 })
2821 .map(|(kind, raw)| Node { kind, raw })?;
2822 if let Some(VentoTagOrChildren::Children(nodes)) = body.last_mut() {
2823 nodes.push(node);
2824 } else {
2825 body.push(VentoTagOrChildren::Children(vec![node]));
2826 }
2827 }
2828 } else {
2829 break;
2830 }
2831 }
2832 Ok(NodeKind::VentoBlock(VentoBlock { body }))
2833 } else if is_vento_interpolation(tag_name) {
2834 Ok(NodeKind::VentoInterpolation(VentoInterpolation {
2835 expr: first_tag,
2836 start: first_tag_start,
2837 }))
2838 } else {
2839 Ok(NodeKind::VentoTag(VentoTag {
2840 tag: first_tag,
2841 trim_prev,
2842 trim_next,
2843 }))
2844 }
2845 }
2846
2847 fn parse_vue_directive(&mut self) -> PResult<VueDirective<'s>> {
2848 let name = match self.chars.peek() {
2849 Some((_, ':')) => {
2850 self.chars.next();
2851 ":"
2852 }
2853 Some((_, '@')) => {
2854 self.chars.next();
2855 "@"
2856 }
2857 Some((_, '#')) => {
2858 self.chars.next();
2859 "#"
2860 }
2861 Some((_, 'v')) => {
2862 let mut chars = self.chars.clone();
2863 chars.next();
2864 if chars.next_if(|(_, c)| *c == '-').is_some() {
2865 self.chars = chars;
2866 self.parse_identifier()?
2867 } else {
2868 return Err(self.emit_error(SyntaxErrorKind::ExpectVueDirective));
2869 }
2870 }
2871 _ => return Err(self.emit_error(SyntaxErrorKind::ExpectVueDirective)),
2872 };
2873
2874 let arg_and_modifiers = if matches!(name, ":" | "@" | "#")
2875 || self
2876 .chars
2877 .peek()
2878 .is_some_and(|(_, c)| is_attr_name_char(*c))
2879 {
2880 Some(self.parse_attr_name()?)
2881 } else {
2882 None
2883 };
2884
2885 self.skip_ws();
2886 let value = if self.chars.next_if(|(_, c)| *c == '=').is_some() {
2887 self.skip_ws();
2888 Some(self.parse_attr_value()?)
2889 } else {
2890 None
2891 };
2892
2893 Ok(VueDirective {
2894 name,
2895 arg_and_modifiers,
2896 value,
2897 })
2898 }
2899
2900 fn parse_xml_decl(&mut self) -> PResult<XmlDecl<'s>> {
2901 if self
2902 .chars
2903 .next_if(|(_, c)| *c == '<')
2904 .and_then(|_| self.chars.next_if(|(_, c)| *c == '?'))
2905 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'x'))
2906 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'm'))
2907 .and_then(|_| self.chars.next_if(|(_, c)| *c == 'l'))
2908 .and_then(|_| self.chars.next_if(|(_, c)| c.is_ascii_whitespace()))
2909 .is_none()
2910 {
2911 return Err(self.emit_error(SyntaxErrorKind::ExpectXmlDecl));
2912 };
2913
2914 let mut attrs = vec![];
2915 loop {
2916 match self.chars.peek() {
2917 Some((_, '?')) => {
2918 self.chars.next();
2919 if self.chars.next_if(|(_, c)| *c == '>').is_some() {
2920 break;
2921 }
2922 return Err(self.emit_error(SyntaxErrorKind::ExpectChar('>')));
2923 }
2924 Some((_, c)) if c.is_ascii_whitespace() => {
2925 self.chars.next();
2926 }
2927 _ => {
2928 attrs.push(self.parse_native_attr()?);
2929 }
2930 }
2931 }
2932 Ok(XmlDecl { attrs })
2933 }
2934}
2935
2936fn is_html_tag_name_char(c: char) -> bool {
2938 c.is_ascii_alphanumeric()
2939 || c == '-'
2940 || c == '_'
2941 || c == '.'
2942 || c == ':'
2943 || !c.is_ascii()
2944 || c == '\\'
2945}
2946
2947fn is_special_tag_name_char(c: char, language: Language) -> bool {
2953 match language {
2954 Language::Astro => c == '>',
2955 Language::Jinja => c == '{',
2956 _ => false,
2957 }
2958}
2959
2960fn is_attr_name_char(c: char) -> bool {
2961 !matches!(c, '"' | '\'' | '>' | '/' | '=') && !c.is_ascii_whitespace()
2962}
2963
2964fn parse_jinja_tag_name<'s>(tag: &JinjaTag<'s>) -> &'s str {
2965 let trimmed = tag.content.trim_start_matches(['+', '-']).trim_start();
2966 trimmed
2967 .split_once(|c: char| c.is_ascii_whitespace())
2968 .map(|(name, _)| name)
2969 .unwrap_or(trimmed)
2970}
2971
2972fn is_vento_interpolation(tag_name: &str) -> bool {
2973 !matches!(
2974 tag_name,
2975 "if" | "else"
2976 | "for"
2977 | "set"
2978 | "include"
2979 | "layout"
2980 | "async"
2981 | "function"
2982 | "import"
2983 | "export"
2984 )
2985}
2986
2987fn strip_hbs_whitespace_control(text: &str) -> (&str, bool, bool) {
2988 let (text, before) = if let Some(stripped) = text.strip_prefix('~') {
2989 (stripped, true)
2990 } else {
2991 (text, false)
2992 };
2993 let (text, after) = if let Some(stripped) = text.strip_suffix('~') {
2994 (stripped, true)
2995 } else {
2996 (text, false)
2997 };
2998 (text, before, after)
2999}
3000
3001pub type PResult<T> = Result<T, SyntaxError>;
3002type AngularIfCond<'s> = ((&'s str, usize), Option<(&'s str, usize)>);
3003
3004trait HasJinjaFlowControl<'s>: Sized {
3005 type Intermediate;
3006
3007 fn build(intermediate: Self::Intermediate, raw: &'s str) -> Self;
3008 fn from_tag(tag: JinjaTag<'s>) -> Self::Intermediate;
3009 fn from_block(block: JinjaBlock<'s, Self>) -> Self::Intermediate;
3010}
3011
3012impl<'s> HasJinjaFlowControl<'s> for Node<'s> {
3013 type Intermediate = NodeKind<'s>;
3014
3015 fn build(intermediate: Self::Intermediate, raw: &'s str) -> Self {
3016 Node {
3017 kind: intermediate,
3018 raw,
3019 }
3020 }
3021
3022 fn from_tag(tag: JinjaTag<'s>) -> Self::Intermediate {
3023 NodeKind::JinjaTag(tag)
3024 }
3025
3026 fn from_block(block: JinjaBlock<'s, Self>) -> Self::Intermediate {
3027 NodeKind::JinjaBlock(block)
3028 }
3029}
3030
3031impl<'s> HasJinjaFlowControl<'s> for Attribute<'s> {
3032 type Intermediate = Attribute<'s>;
3033
3034 fn build(intermediate: Self::Intermediate, _: &'s str) -> Self {
3035 intermediate
3036 }
3037
3038 fn from_tag(tag: JinjaTag<'s>) -> Self::Intermediate {
3039 Attribute::JinjaTag(tag)
3040 }
3041
3042 fn from_block(block: JinjaBlock<'s, Self>) -> Self::Intermediate {
3043 Attribute::JinjaBlock(block)
3044 }
3045}
3046
3047pub fn parse_as_interpolated(
3048 text: &'_ str,
3049 base_start: usize,
3050 language: Language,
3051 attr: bool,
3052) -> (Vec<&'_ str>, Vec<(&'_ str, usize)>) {
3053 let mut statics = Vec::with_capacity(1);
3054 let mut dynamics = Vec::new();
3055 let mut chars = text.char_indices().peekable();
3056 let mut pos = 0;
3057 let mut brace_stack = 0u8;
3058 while let Some((i, c)) = chars.next() {
3059 match c {
3060 '{' => {
3061 if brace_stack > 0 {
3062 brace_stack += 1;
3063 continue;
3064 }
3065 match language {
3066 Language::Svelte if attr => {
3067 statics.push(unsafe { text.get_unchecked(pos..i) });
3068 pos = i;
3069 brace_stack += 1;
3070 }
3071 Language::Jinja | Language::Vento | Language::Mustache => {
3072 if chars.next_if(|(_, c)| *c == '{').is_some() {
3073 statics.push(unsafe { text.get_unchecked(pos..i) });
3074 pos = i;
3075 brace_stack += 1;
3076 }
3077 }
3078 _ => {}
3079 }
3080 }
3081 '}' => {
3082 if brace_stack > 1 {
3083 brace_stack -= 1;
3084 continue;
3085 }
3086 match language {
3087 Language::Svelte if attr => {
3088 dynamics.push((
3089 unsafe { text.get_unchecked(pos + 1..i) },
3090 base_start + pos + 1,
3091 ));
3092 pos = i + 1;
3093 brace_stack = 0;
3094 }
3095 Language::Jinja | Language::Vento | Language::Mustache => {
3096 if chars.next_if(|(_, c)| *c == '}').is_some() {
3097 dynamics.push((
3098 unsafe { text.get_unchecked(pos + 2..i) },
3099 base_start + pos + 2,
3100 ));
3101 pos = i + 2;
3102 brace_stack = 0;
3103 }
3104 }
3105 _ => {}
3106 }
3107 }
3108 _ => {}
3109 }
3110 }
3111 statics.push(unsafe { text.get_unchecked(pos..) });
3112 (statics, dynamics)
3113}