1use std::num::NonZeroUsize;
6use std::ops::Deref;
7use std::path::Path;
8use std::str::FromStr;
9
10use ecow::EcoString;
11use unscanny::Scanner;
12
13use crate::package::PackageSpec;
14use crate::{is_ident, is_newline, Span, SyntaxKind, SyntaxNode};
15
16pub trait AstNode<'a>: Sized {
18 fn from_untyped(node: &'a SyntaxNode) -> Option<Self>;
20
21 fn to_untyped(self) -> &'a SyntaxNode;
23
24 fn span(self) -> Span {
26 self.to_untyped().span()
27 }
28}
29
30macro_rules! node {
31 ($(#[$attr:meta])* $name:ident) => {
32 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
33 #[repr(transparent)]
34 $(#[$attr])*
35 pub struct $name<'a>(&'a SyntaxNode);
36
37 impl<'a> AstNode<'a> for $name<'a> {
38 #[inline]
39 fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
40 if node.kind() == SyntaxKind::$name {
41 Some(Self(node))
42 } else {
43 Option::None
44 }
45 }
46
47 #[inline]
48 fn to_untyped(self) -> &'a SyntaxNode {
49 self.0
50 }
51 }
52
53 impl Default for $name<'_> {
54 #[inline]
55 fn default() -> Self {
56 static PLACEHOLDER: SyntaxNode
57 = SyntaxNode::placeholder(SyntaxKind::$name);
58 Self(&PLACEHOLDER)
59 }
60 }
61 };
62}
63
64node! {
65 Markup
67}
68
69impl<'a> Markup<'a> {
70 pub fn exprs(self) -> impl DoubleEndedIterator<Item = Expr<'a>> {
72 let mut was_stmt = false;
73 self.0
74 .children()
75 .filter(move |node| {
76 let kind = node.kind();
78 let keep = !was_stmt || node.kind() != SyntaxKind::Space;
79 was_stmt = kind.is_stmt();
80 keep
81 })
82 .filter_map(Expr::cast_with_space)
83 }
84}
85
86#[derive(Debug, Copy, Clone, Hash)]
88pub enum Expr<'a> {
89 Text(Text<'a>),
91 Space(Space<'a>),
94 Linebreak(Linebreak<'a>),
96 Parbreak(Parbreak<'a>),
98 Escape(Escape<'a>),
100 Shorthand(Shorthand<'a>),
103 SmartQuote(SmartQuote<'a>),
105 Strong(Strong<'a>),
107 Emph(Emph<'a>),
109 Raw(Raw<'a>),
111 Link(Link<'a>),
113 Label(Label<'a>),
115 Ref(Ref<'a>),
117 Heading(Heading<'a>),
119 List(ListItem<'a>),
121 Enum(EnumItem<'a>),
123 Term(TermItem<'a>),
125 Equation(Equation<'a>),
127 Math(Math<'a>),
129 MathText(MathText<'a>),
131 MathIdent(MathIdent<'a>),
133 MathShorthand(MathShorthand<'a>),
135 MathAlignPoint(MathAlignPoint<'a>),
137 MathDelimited(MathDelimited<'a>),
139 MathAttach(MathAttach<'a>),
141 MathPrimes(MathPrimes<'a>),
143 MathFrac(MathFrac<'a>),
145 MathRoot(MathRoot<'a>),
147 Ident(Ident<'a>),
149 None(None<'a>),
151 Auto(Auto<'a>),
153 Bool(Bool<'a>),
155 Int(Int<'a>),
157 Float(Float<'a>),
159 Numeric(Numeric<'a>),
161 Str(Str<'a>),
163 Code(CodeBlock<'a>),
165 Content(ContentBlock<'a>),
167 Parenthesized(Parenthesized<'a>),
169 Array(Array<'a>),
171 Dict(Dict<'a>),
173 Unary(Unary<'a>),
175 Binary(Binary<'a>),
177 FieldAccess(FieldAccess<'a>),
179 FuncCall(FuncCall<'a>),
181 Closure(Closure<'a>),
183 Let(LetBinding<'a>),
185 DestructAssign(DestructAssignment<'a>),
187 Set(SetRule<'a>),
189 Show(ShowRule<'a>),
191 Contextual(Contextual<'a>),
193 Conditional(Conditional<'a>),
195 While(WhileLoop<'a>),
197 For(ForLoop<'a>),
199 Import(ModuleImport<'a>),
201 Include(ModuleInclude<'a>),
203 Break(LoopBreak<'a>),
205 Continue(LoopContinue<'a>),
207 Return(FuncReturn<'a>),
209}
210
211impl<'a> Expr<'a> {
212 fn cast_with_space(node: &'a SyntaxNode) -> Option<Self> {
213 match node.kind() {
214 SyntaxKind::Space => node.cast().map(Self::Space),
215 _ => Self::from_untyped(node),
216 }
217 }
218}
219
220impl<'a> AstNode<'a> for Expr<'a> {
221 fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
222 match node.kind() {
223 SyntaxKind::Linebreak => node.cast().map(Self::Linebreak),
224 SyntaxKind::Parbreak => node.cast().map(Self::Parbreak),
225 SyntaxKind::Text => node.cast().map(Self::Text),
226 SyntaxKind::Escape => node.cast().map(Self::Escape),
227 SyntaxKind::Shorthand => node.cast().map(Self::Shorthand),
228 SyntaxKind::SmartQuote => node.cast().map(Self::SmartQuote),
229 SyntaxKind::Strong => node.cast().map(Self::Strong),
230 SyntaxKind::Emph => node.cast().map(Self::Emph),
231 SyntaxKind::Raw => node.cast().map(Self::Raw),
232 SyntaxKind::Link => node.cast().map(Self::Link),
233 SyntaxKind::Label => node.cast().map(Self::Label),
234 SyntaxKind::Ref => node.cast().map(Self::Ref),
235 SyntaxKind::Heading => node.cast().map(Self::Heading),
236 SyntaxKind::ListItem => node.cast().map(Self::List),
237 SyntaxKind::EnumItem => node.cast().map(Self::Enum),
238 SyntaxKind::TermItem => node.cast().map(Self::Term),
239 SyntaxKind::Equation => node.cast().map(Self::Equation),
240 SyntaxKind::Math => node.cast().map(Self::Math),
241 SyntaxKind::MathText => node.cast().map(Self::MathText),
242 SyntaxKind::MathIdent => node.cast().map(Self::MathIdent),
243 SyntaxKind::MathShorthand => node.cast().map(Self::MathShorthand),
244 SyntaxKind::MathAlignPoint => node.cast().map(Self::MathAlignPoint),
245 SyntaxKind::MathDelimited => node.cast().map(Self::MathDelimited),
246 SyntaxKind::MathAttach => node.cast().map(Self::MathAttach),
247 SyntaxKind::MathPrimes => node.cast().map(Self::MathPrimes),
248 SyntaxKind::MathFrac => node.cast().map(Self::MathFrac),
249 SyntaxKind::MathRoot => node.cast().map(Self::MathRoot),
250 SyntaxKind::Ident => node.cast().map(Self::Ident),
251 SyntaxKind::None => node.cast().map(Self::None),
252 SyntaxKind::Auto => node.cast().map(Self::Auto),
253 SyntaxKind::Bool => node.cast().map(Self::Bool),
254 SyntaxKind::Int => node.cast().map(Self::Int),
255 SyntaxKind::Float => node.cast().map(Self::Float),
256 SyntaxKind::Numeric => node.cast().map(Self::Numeric),
257 SyntaxKind::Str => node.cast().map(Self::Str),
258 SyntaxKind::CodeBlock => node.cast().map(Self::Code),
259 SyntaxKind::ContentBlock => node.cast().map(Self::Content),
260 SyntaxKind::Parenthesized => node.cast().map(Self::Parenthesized),
261 SyntaxKind::Array => node.cast().map(Self::Array),
262 SyntaxKind::Dict => node.cast().map(Self::Dict),
263 SyntaxKind::Unary => node.cast().map(Self::Unary),
264 SyntaxKind::Binary => node.cast().map(Self::Binary),
265 SyntaxKind::FieldAccess => node.cast().map(Self::FieldAccess),
266 SyntaxKind::FuncCall => node.cast().map(Self::FuncCall),
267 SyntaxKind::Closure => node.cast().map(Self::Closure),
268 SyntaxKind::LetBinding => node.cast().map(Self::Let),
269 SyntaxKind::DestructAssignment => node.cast().map(Self::DestructAssign),
270 SyntaxKind::SetRule => node.cast().map(Self::Set),
271 SyntaxKind::ShowRule => node.cast().map(Self::Show),
272 SyntaxKind::Contextual => node.cast().map(Self::Contextual),
273 SyntaxKind::Conditional => node.cast().map(Self::Conditional),
274 SyntaxKind::WhileLoop => node.cast().map(Self::While),
275 SyntaxKind::ForLoop => node.cast().map(Self::For),
276 SyntaxKind::ModuleImport => node.cast().map(Self::Import),
277 SyntaxKind::ModuleInclude => node.cast().map(Self::Include),
278 SyntaxKind::LoopBreak => node.cast().map(Self::Break),
279 SyntaxKind::LoopContinue => node.cast().map(Self::Continue),
280 SyntaxKind::FuncReturn => node.cast().map(Self::Return),
281 _ => Option::None,
282 }
283 }
284
285 fn to_untyped(self) -> &'a SyntaxNode {
286 match self {
287 Self::Text(v) => v.to_untyped(),
288 Self::Space(v) => v.to_untyped(),
289 Self::Linebreak(v) => v.to_untyped(),
290 Self::Parbreak(v) => v.to_untyped(),
291 Self::Escape(v) => v.to_untyped(),
292 Self::Shorthand(v) => v.to_untyped(),
293 Self::SmartQuote(v) => v.to_untyped(),
294 Self::Strong(v) => v.to_untyped(),
295 Self::Emph(v) => v.to_untyped(),
296 Self::Raw(v) => v.to_untyped(),
297 Self::Link(v) => v.to_untyped(),
298 Self::Label(v) => v.to_untyped(),
299 Self::Ref(v) => v.to_untyped(),
300 Self::Heading(v) => v.to_untyped(),
301 Self::List(v) => v.to_untyped(),
302 Self::Enum(v) => v.to_untyped(),
303 Self::Term(v) => v.to_untyped(),
304 Self::Equation(v) => v.to_untyped(),
305 Self::Math(v) => v.to_untyped(),
306 Self::MathText(v) => v.to_untyped(),
307 Self::MathIdent(v) => v.to_untyped(),
308 Self::MathShorthand(v) => v.to_untyped(),
309 Self::MathAlignPoint(v) => v.to_untyped(),
310 Self::MathDelimited(v) => v.to_untyped(),
311 Self::MathAttach(v) => v.to_untyped(),
312 Self::MathPrimes(v) => v.to_untyped(),
313 Self::MathFrac(v) => v.to_untyped(),
314 Self::MathRoot(v) => v.to_untyped(),
315 Self::Ident(v) => v.to_untyped(),
316 Self::None(v) => v.to_untyped(),
317 Self::Auto(v) => v.to_untyped(),
318 Self::Bool(v) => v.to_untyped(),
319 Self::Int(v) => v.to_untyped(),
320 Self::Float(v) => v.to_untyped(),
321 Self::Numeric(v) => v.to_untyped(),
322 Self::Str(v) => v.to_untyped(),
323 Self::Code(v) => v.to_untyped(),
324 Self::Content(v) => v.to_untyped(),
325 Self::Array(v) => v.to_untyped(),
326 Self::Dict(v) => v.to_untyped(),
327 Self::Parenthesized(v) => v.to_untyped(),
328 Self::Unary(v) => v.to_untyped(),
329 Self::Binary(v) => v.to_untyped(),
330 Self::FieldAccess(v) => v.to_untyped(),
331 Self::FuncCall(v) => v.to_untyped(),
332 Self::Closure(v) => v.to_untyped(),
333 Self::Let(v) => v.to_untyped(),
334 Self::DestructAssign(v) => v.to_untyped(),
335 Self::Set(v) => v.to_untyped(),
336 Self::Show(v) => v.to_untyped(),
337 Self::Contextual(v) => v.to_untyped(),
338 Self::Conditional(v) => v.to_untyped(),
339 Self::While(v) => v.to_untyped(),
340 Self::For(v) => v.to_untyped(),
341 Self::Import(v) => v.to_untyped(),
342 Self::Include(v) => v.to_untyped(),
343 Self::Break(v) => v.to_untyped(),
344 Self::Continue(v) => v.to_untyped(),
345 Self::Return(v) => v.to_untyped(),
346 }
347 }
348}
349
350impl Expr<'_> {
351 pub fn hash(self) -> bool {
353 matches!(
354 self,
355 Self::Ident(_)
356 | Self::None(_)
357 | Self::Auto(_)
358 | Self::Bool(_)
359 | Self::Int(_)
360 | Self::Float(_)
361 | Self::Numeric(_)
362 | Self::Str(_)
363 | Self::Code(_)
364 | Self::Content(_)
365 | Self::Array(_)
366 | Self::Dict(_)
367 | Self::Parenthesized(_)
368 | Self::FieldAccess(_)
369 | Self::FuncCall(_)
370 | Self::Let(_)
371 | Self::Set(_)
372 | Self::Show(_)
373 | Self::Contextual(_)
374 | Self::Conditional(_)
375 | Self::While(_)
376 | Self::For(_)
377 | Self::Import(_)
378 | Self::Include(_)
379 | Self::Break(_)
380 | Self::Continue(_)
381 | Self::Return(_)
382 )
383 }
384
385 pub fn is_literal(self) -> bool {
387 matches!(
388 self,
389 Self::None(_)
390 | Self::Auto(_)
391 | Self::Bool(_)
392 | Self::Int(_)
393 | Self::Float(_)
394 | Self::Numeric(_)
395 | Self::Str(_)
396 )
397 }
398}
399
400impl Default for Expr<'_> {
401 fn default() -> Self {
402 Expr::None(None::default())
403 }
404}
405
406node! {
407 Text
409}
410
411impl<'a> Text<'a> {
412 pub fn get(self) -> &'a EcoString {
414 self.0.text()
415 }
416}
417
418node! {
419 Space
422}
423
424node! {
425 Linebreak
427}
428
429node! {
430 Parbreak
432}
433
434node! {
435 Escape
437}
438
439impl Escape<'_> {
440 pub fn get(self) -> char {
442 let mut s = Scanner::new(self.0.text());
443 s.expect('\\');
444 if s.eat_if("u{") {
445 let hex = s.eat_while(char::is_ascii_hexdigit);
446 u32::from_str_radix(hex, 16)
447 .ok()
448 .and_then(std::char::from_u32)
449 .unwrap_or_default()
450 } else {
451 s.eat().unwrap_or_default()
452 }
453 }
454}
455
456node! {
457 Shorthand
460}
461
462impl Shorthand<'_> {
463 pub const LIST: &'static [(&'static str, char)] = &[
465 ("...", '…'),
466 ("~", '\u{00A0}'),
467 ("-", '\u{2212}'), ("--", '\u{2013}'),
469 ("---", '\u{2014}'),
470 ("-?", '\u{00AD}'),
471 ];
472
473 pub fn get(self) -> char {
475 let text = self.0.text();
476 Self::LIST
477 .iter()
478 .find(|&&(s, _)| s == text)
479 .map_or_else(char::default, |&(_, c)| c)
480 }
481}
482
483node! {
484 SmartQuote
486}
487
488impl SmartQuote<'_> {
489 pub fn double(self) -> bool {
491 self.0.text() == "\""
492 }
493}
494
495node! {
496 Strong
498}
499
500impl<'a> Strong<'a> {
501 pub fn body(self) -> Markup<'a> {
503 self.0.cast_first_match().unwrap_or_default()
504 }
505}
506
507node! {
508 Emph
510}
511
512impl<'a> Emph<'a> {
513 pub fn body(self) -> Markup<'a> {
515 self.0.cast_first_match().unwrap_or_default()
516 }
517}
518
519node! {
520 Raw
522}
523
524impl<'a> Raw<'a> {
525 pub fn lines(self) -> impl DoubleEndedIterator<Item = Text<'a>> {
527 self.0.children().filter_map(SyntaxNode::cast)
528 }
529
530 pub fn lang(self) -> Option<RawLang<'a>> {
532 let delim: RawDelim = self.0.cast_first_match()?;
534 if delim.0.len() < 3 {
535 return Option::None;
536 }
537
538 self.0.cast_first_match()
539 }
540
541 pub fn block(self) -> bool {
543 self.0
544 .cast_first_match()
545 .is_some_and(|delim: RawDelim| delim.0.len() >= 3)
546 && self.0.children().any(|e| {
547 e.kind() == SyntaxKind::RawTrimmed && e.text().chars().any(is_newline)
548 })
549 }
550}
551
552node! {
553 RawLang
555}
556
557impl<'a> RawLang<'a> {
558 pub fn get(self) -> &'a EcoString {
560 self.0.text()
561 }
562}
563
564node! {
565 RawDelim
567}
568
569node! {
570 Link
572}
573
574impl<'a> Link<'a> {
575 pub fn get(self) -> &'a EcoString {
577 self.0.text()
578 }
579}
580
581node! {
582 Label
584}
585
586impl<'a> Label<'a> {
587 pub fn get(self) -> &'a str {
589 self.0.text().trim_start_matches('<').trim_end_matches('>')
590 }
591}
592
593node! {
594 Ref
596}
597
598impl<'a> Ref<'a> {
599 pub fn target(self) -> &'a str {
601 self.0
602 .children()
603 .find(|node| node.kind() == SyntaxKind::RefMarker)
604 .map(|node| node.text().trim_start_matches('@'))
605 .unwrap_or_default()
606 }
607
608 pub fn supplement(self) -> Option<ContentBlock<'a>> {
610 self.0.cast_last_match()
611 }
612}
613
614node! {
615 Heading
617}
618
619impl<'a> Heading<'a> {
620 pub fn body(self) -> Markup<'a> {
622 self.0.cast_first_match().unwrap_or_default()
623 }
624
625 pub fn depth(self) -> NonZeroUsize {
627 self.0
628 .children()
629 .find(|node| node.kind() == SyntaxKind::HeadingMarker)
630 .and_then(|node| node.len().try_into().ok())
631 .unwrap_or(NonZeroUsize::new(1).unwrap())
632 }
633}
634
635node! {
636 ListItem
638}
639
640impl<'a> ListItem<'a> {
641 pub fn body(self) -> Markup<'a> {
643 self.0.cast_first_match().unwrap_or_default()
644 }
645}
646
647node! {
648 EnumItem
650}
651
652impl<'a> EnumItem<'a> {
653 pub fn number(self) -> Option<usize> {
655 self.0.children().find_map(|node| match node.kind() {
656 SyntaxKind::EnumMarker => node.text().trim_end_matches('.').parse().ok(),
657 _ => Option::None,
658 })
659 }
660
661 pub fn body(self) -> Markup<'a> {
663 self.0.cast_first_match().unwrap_or_default()
664 }
665}
666
667node! {
668 TermItem
670}
671
672impl<'a> TermItem<'a> {
673 pub fn term(self) -> Markup<'a> {
675 self.0.cast_first_match().unwrap_or_default()
676 }
677
678 pub fn description(self) -> Markup<'a> {
680 self.0.cast_last_match().unwrap_or_default()
681 }
682}
683
684node! {
685 Equation
687}
688
689impl<'a> Equation<'a> {
690 pub fn body(self) -> Math<'a> {
692 self.0.cast_first_match().unwrap_or_default()
693 }
694
695 pub fn block(self) -> bool {
697 let is_space = |node: Option<&SyntaxNode>| {
698 node.map(SyntaxNode::kind) == Some(SyntaxKind::Space)
699 };
700 is_space(self.0.children().nth(1)) && is_space(self.0.children().nth_back(1))
701 }
702}
703
704node! {
705 Math
707}
708
709impl<'a> Math<'a> {
710 pub fn exprs(self) -> impl DoubleEndedIterator<Item = Expr<'a>> {
712 self.0.children().filter_map(Expr::cast_with_space)
713 }
714}
715
716node! {
717 MathText
719}
720
721pub enum MathTextKind<'a> {
723 Character(char),
724 Number(&'a EcoString),
725}
726
727impl<'a> MathText<'a> {
728 pub fn get(self) -> MathTextKind<'a> {
730 let text = self.0.text();
731 let mut chars = text.chars();
732 let c = chars.next().unwrap();
733 if c.is_numeric() {
734 MathTextKind::Number(text)
737 } else {
738 assert!(chars.next().is_none());
739 MathTextKind::Character(c)
740 }
741 }
742}
743
744node! {
745 MathIdent
747}
748
749impl<'a> MathIdent<'a> {
750 pub fn get(self) -> &'a EcoString {
752 self.0.text()
753 }
754
755 pub fn as_str(self) -> &'a str {
757 self.get()
758 }
759}
760
761impl Deref for MathIdent<'_> {
762 type Target = str;
763
764 fn deref(&self) -> &Self::Target {
767 self.as_str()
768 }
769}
770
771node! {
772 MathShorthand
774}
775
776impl MathShorthand<'_> {
777 pub const LIST: &'static [(&'static str, char)] = &[
779 ("...", '…'),
780 ("-", '−'),
781 ("*", '∗'),
782 ("~", '∼'),
783 ("!=", '≠'),
784 (":=", '≔'),
785 ("::=", '⩴'),
786 ("=:", '≕'),
787 ("<<", '≪'),
788 ("<<<", '⋘'),
789 (">>", '≫'),
790 (">>>", '⋙'),
791 ("<=", '≤'),
792 (">=", '≥'),
793 ("->", '→'),
794 ("-->", '⟶'),
795 ("|->", '↦'),
796 (">->", '↣'),
797 ("->>", '↠'),
798 ("<-", '←'),
799 ("<--", '⟵'),
800 ("<-<", '↢'),
801 ("<<-", '↞'),
802 ("<->", '↔'),
803 ("<-->", '⟷'),
804 ("~>", '⇝'),
805 ("~~>", '⟿'),
806 ("<~", '⇜'),
807 ("<~~", '⬳'),
808 ("=>", '⇒'),
809 ("|=>", '⤇'),
810 ("==>", '⟹'),
811 ("<==", '⟸'),
812 ("<=>", '⇔'),
813 ("<==>", '⟺'),
814 ("[|", '⟦'),
815 ("|]", '⟧'),
816 ("||", '‖'),
817 ];
818
819 pub fn get(self) -> char {
821 let text = self.0.text();
822 Self::LIST
823 .iter()
824 .find(|&&(s, _)| s == text)
825 .map_or_else(char::default, |&(_, c)| c)
826 }
827}
828
829node! {
830 MathAlignPoint
832}
833
834node! {
835 MathDelimited
837}
838
839impl<'a> MathDelimited<'a> {
840 pub fn open(self) -> Expr<'a> {
842 self.0.cast_first_match().unwrap_or_default()
843 }
844
845 pub fn body(self) -> Math<'a> {
847 self.0.cast_first_match().unwrap_or_default()
848 }
849
850 pub fn close(self) -> Expr<'a> {
852 self.0.cast_last_match().unwrap_or_default()
853 }
854}
855
856node! {
857 MathAttach
859}
860
861impl<'a> MathAttach<'a> {
862 pub fn base(self) -> Expr<'a> {
864 self.0.cast_first_match().unwrap_or_default()
865 }
866
867 pub fn bottom(self) -> Option<Expr<'a>> {
869 self.0
870 .children()
871 .skip_while(|node| !matches!(node.kind(), SyntaxKind::Underscore))
872 .find_map(SyntaxNode::cast)
873 }
874
875 pub fn top(self) -> Option<Expr<'a>> {
877 self.0
878 .children()
879 .skip_while(|node| !matches!(node.kind(), SyntaxKind::Hat))
880 .find_map(SyntaxNode::cast)
881 }
882
883 pub fn primes(self) -> Option<MathPrimes<'a>> {
885 self.0
886 .children()
887 .skip_while(|node| node.cast::<Expr<'_>>().is_none())
888 .nth(1)
889 .and_then(|n| n.cast())
890 }
891}
892
893node! {
894 MathPrimes
896}
897
898impl MathPrimes<'_> {
899 pub fn count(self) -> usize {
901 self.0
902 .children()
903 .filter(|node| matches!(node.kind(), SyntaxKind::Prime))
904 .count()
905 }
906}
907
908node! {
909 MathFrac
911}
912
913impl<'a> MathFrac<'a> {
914 pub fn num(self) -> Expr<'a> {
916 self.0.cast_first_match().unwrap_or_default()
917 }
918
919 pub fn denom(self) -> Expr<'a> {
921 self.0.cast_last_match().unwrap_or_default()
922 }
923}
924
925node! {
926 MathRoot
928}
929
930impl<'a> MathRoot<'a> {
931 pub fn index(self) -> Option<usize> {
933 match self.0.children().next().map(|node| node.text().as_str()) {
934 Some("∜") => Some(4),
935 Some("∛") => Some(3),
936 Some("√") => Option::None,
937 _ => Option::None,
938 }
939 }
940
941 pub fn radicand(self) -> Expr<'a> {
943 self.0.cast_first_match().unwrap_or_default()
944 }
945}
946
947node! {
948 Ident
950}
951
952impl<'a> Ident<'a> {
953 pub fn get(self) -> &'a EcoString {
955 self.0.text()
956 }
957
958 pub fn as_str(self) -> &'a str {
960 self.get()
961 }
962}
963
964impl Deref for Ident<'_> {
965 type Target = str;
966
967 fn deref(&self) -> &Self::Target {
970 self.as_str()
971 }
972}
973
974node! {
975 None
977}
978
979node! {
980 Auto
982}
983
984node! {
985 Bool
987}
988
989impl Bool<'_> {
990 pub fn get(self) -> bool {
992 self.0.text() == "true"
993 }
994}
995
996node! {
997 Int
999}
1000
1001impl Int<'_> {
1002 pub fn get(self) -> i64 {
1004 let text = self.0.text();
1005 if let Some(rest) = text.strip_prefix("0x") {
1006 i64::from_str_radix(rest, 16)
1007 } else if let Some(rest) = text.strip_prefix("0o") {
1008 i64::from_str_radix(rest, 8)
1009 } else if let Some(rest) = text.strip_prefix("0b") {
1010 i64::from_str_radix(rest, 2)
1011 } else {
1012 text.parse()
1013 }
1014 .unwrap_or_default()
1015 }
1016}
1017
1018node! {
1019 Float
1021}
1022
1023impl Float<'_> {
1024 pub fn get(self) -> f64 {
1026 self.0.text().parse().unwrap_or_default()
1027 }
1028}
1029
1030node! {
1031 Numeric
1033}
1034
1035impl Numeric<'_> {
1036 pub fn get(self) -> (f64, Unit) {
1038 let text = self.0.text();
1039 let count = text
1040 .chars()
1041 .rev()
1042 .take_while(|c| matches!(c, 'a'..='z' | '%'))
1043 .count();
1044
1045 let split = text.len() - count;
1046 let value = text[..split].parse().unwrap_or_default();
1047 let unit = match &text[split..] {
1048 "pt" => Unit::Pt,
1049 "mm" => Unit::Mm,
1050 "cm" => Unit::Cm,
1051 "in" => Unit::In,
1052 "deg" => Unit::Deg,
1053 "rad" => Unit::Rad,
1054 "em" => Unit::Em,
1055 "fr" => Unit::Fr,
1056 "%" => Unit::Percent,
1057 _ => Unit::Percent,
1058 };
1059
1060 (value, unit)
1061 }
1062}
1063
1064#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1066pub enum Unit {
1067 Pt,
1069 Mm,
1071 Cm,
1073 In,
1075 Rad,
1077 Deg,
1079 Em,
1081 Fr,
1083 Percent,
1085}
1086
1087node! {
1088 Str
1090}
1091
1092impl Str<'_> {
1093 pub fn get(self) -> EcoString {
1095 let text = self.0.text();
1096 let unquoted = &text[1..text.len() - 1];
1097 if !unquoted.contains('\\') {
1098 return unquoted.into();
1099 }
1100
1101 let mut out = EcoString::with_capacity(unquoted.len());
1102 let mut s = Scanner::new(unquoted);
1103
1104 while let Some(c) = s.eat() {
1105 if c != '\\' {
1106 out.push(c);
1107 continue;
1108 }
1109
1110 let start = s.locate(-1);
1111 match s.eat() {
1112 Some('\\') => out.push('\\'),
1113 Some('"') => out.push('"'),
1114 Some('n') => out.push('\n'),
1115 Some('r') => out.push('\r'),
1116 Some('t') => out.push('\t'),
1117 Some('u') if s.eat_if('{') => {
1118 let sequence = s.eat_while(char::is_ascii_hexdigit);
1119 s.eat_if('}');
1120
1121 match u32::from_str_radix(sequence, 16)
1122 .ok()
1123 .and_then(std::char::from_u32)
1124 {
1125 Some(c) => out.push(c),
1126 Option::None => out.push_str(s.from(start)),
1127 }
1128 }
1129 _ => out.push_str(s.from(start)),
1130 }
1131 }
1132
1133 out
1134 }
1135}
1136
1137node! {
1138 CodeBlock
1140}
1141
1142impl<'a> CodeBlock<'a> {
1143 pub fn body(self) -> Code<'a> {
1145 self.0.cast_first_match().unwrap_or_default()
1146 }
1147}
1148
1149node! {
1150 Code
1152}
1153
1154impl<'a> Code<'a> {
1155 pub fn exprs(self) -> impl DoubleEndedIterator<Item = Expr<'a>> {
1157 self.0.children().filter_map(SyntaxNode::cast)
1158 }
1159}
1160
1161node! {
1162 ContentBlock
1164}
1165
1166impl<'a> ContentBlock<'a> {
1167 pub fn body(self) -> Markup<'a> {
1169 self.0.cast_first_match().unwrap_or_default()
1170 }
1171}
1172
1173node! {
1174 Parenthesized
1176}
1177
1178impl<'a> Parenthesized<'a> {
1179 pub fn expr(self) -> Expr<'a> {
1183 self.0.cast_first_match().unwrap_or_default()
1184 }
1185
1186 pub fn pattern(self) -> Pattern<'a> {
1190 self.0.cast_first_match().unwrap_or_default()
1191 }
1192}
1193
1194node! {
1195 Array
1197}
1198
1199impl<'a> Array<'a> {
1200 pub fn items(self) -> impl DoubleEndedIterator<Item = ArrayItem<'a>> {
1202 self.0.children().filter_map(SyntaxNode::cast)
1203 }
1204}
1205
1206#[derive(Debug, Copy, Clone, Hash)]
1208pub enum ArrayItem<'a> {
1209 Pos(Expr<'a>),
1211 Spread(Spread<'a>),
1213}
1214
1215impl<'a> AstNode<'a> for ArrayItem<'a> {
1216 fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
1217 match node.kind() {
1218 SyntaxKind::Spread => node.cast().map(Self::Spread),
1219 _ => node.cast().map(Self::Pos),
1220 }
1221 }
1222
1223 fn to_untyped(self) -> &'a SyntaxNode {
1224 match self {
1225 Self::Pos(v) => v.to_untyped(),
1226 Self::Spread(v) => v.to_untyped(),
1227 }
1228 }
1229}
1230
1231node! {
1232 Dict
1234}
1235
1236impl<'a> Dict<'a> {
1237 pub fn items(self) -> impl DoubleEndedIterator<Item = DictItem<'a>> {
1239 self.0.children().filter_map(SyntaxNode::cast)
1240 }
1241}
1242
1243#[derive(Debug, Copy, Clone, Hash)]
1245pub enum DictItem<'a> {
1246 Named(Named<'a>),
1248 Keyed(Keyed<'a>),
1250 Spread(Spread<'a>),
1252}
1253
1254impl<'a> AstNode<'a> for DictItem<'a> {
1255 fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
1256 match node.kind() {
1257 SyntaxKind::Named => node.cast().map(Self::Named),
1258 SyntaxKind::Keyed => node.cast().map(Self::Keyed),
1259 SyntaxKind::Spread => node.cast().map(Self::Spread),
1260 _ => Option::None,
1261 }
1262 }
1263
1264 fn to_untyped(self) -> &'a SyntaxNode {
1265 match self {
1266 Self::Named(v) => v.to_untyped(),
1267 Self::Keyed(v) => v.to_untyped(),
1268 Self::Spread(v) => v.to_untyped(),
1269 }
1270 }
1271}
1272
1273node! {
1274 Named
1276}
1277
1278impl<'a> Named<'a> {
1279 pub fn name(self) -> Ident<'a> {
1281 self.0.cast_first_match().unwrap_or_default()
1282 }
1283
1284 pub fn expr(self) -> Expr<'a> {
1289 self.0.cast_last_match().unwrap_or_default()
1290 }
1291
1292 pub fn pattern(self) -> Pattern<'a> {
1297 self.0.cast_last_match().unwrap_or_default()
1298 }
1299}
1300
1301node! {
1302 Keyed
1304}
1305
1306impl<'a> Keyed<'a> {
1307 pub fn key(self) -> Expr<'a> {
1309 self.0.cast_first_match().unwrap_or_default()
1310 }
1311
1312 pub fn expr(self) -> Expr<'a> {
1317 self.0.cast_last_match().unwrap_or_default()
1318 }
1319}
1320
1321node! {
1322 Spread
1324}
1325
1326impl<'a> Spread<'a> {
1327 pub fn expr(self) -> Expr<'a> {
1332 self.0.cast_first_match().unwrap_or_default()
1333 }
1334
1335 pub fn sink_ident(self) -> Option<Ident<'a>> {
1340 self.0.cast_first_match()
1341 }
1342
1343 pub fn sink_expr(self) -> Option<Expr<'a>> {
1348 self.0.cast_first_match()
1349 }
1350}
1351
1352node! {
1353 Unary
1355}
1356
1357impl<'a> Unary<'a> {
1358 pub fn op(self) -> UnOp {
1360 self.0
1361 .children()
1362 .find_map(|node| UnOp::from_kind(node.kind()))
1363 .unwrap_or(UnOp::Pos)
1364 }
1365
1366 pub fn expr(self) -> Expr<'a> {
1368 self.0.cast_last_match().unwrap_or_default()
1369 }
1370}
1371
1372#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1374pub enum UnOp {
1375 Pos,
1377 Neg,
1379 Not,
1381}
1382
1383impl UnOp {
1384 pub fn from_kind(token: SyntaxKind) -> Option<Self> {
1386 Some(match token {
1387 SyntaxKind::Plus => Self::Pos,
1388 SyntaxKind::Minus => Self::Neg,
1389 SyntaxKind::Not => Self::Not,
1390 _ => return Option::None,
1391 })
1392 }
1393
1394 pub fn precedence(self) -> usize {
1396 match self {
1397 Self::Pos | Self::Neg => 7,
1398 Self::Not => 4,
1399 }
1400 }
1401
1402 pub fn as_str(self) -> &'static str {
1404 match self {
1405 Self::Pos => "+",
1406 Self::Neg => "-",
1407 Self::Not => "not",
1408 }
1409 }
1410}
1411
1412node! {
1413 Binary
1415}
1416
1417impl<'a> Binary<'a> {
1418 pub fn op(self) -> BinOp {
1420 let mut not = false;
1421 self.0
1422 .children()
1423 .find_map(|node| match node.kind() {
1424 SyntaxKind::Not => {
1425 not = true;
1426 Option::None
1427 }
1428 SyntaxKind::In if not => Some(BinOp::NotIn),
1429 _ => BinOp::from_kind(node.kind()),
1430 })
1431 .unwrap_or(BinOp::Add)
1432 }
1433
1434 pub fn lhs(self) -> Expr<'a> {
1436 self.0.cast_first_match().unwrap_or_default()
1437 }
1438
1439 pub fn rhs(self) -> Expr<'a> {
1441 self.0.cast_last_match().unwrap_or_default()
1442 }
1443}
1444
1445#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1447pub enum BinOp {
1448 Add,
1450 Sub,
1452 Mul,
1454 Div,
1456 And,
1458 Or,
1460 Eq,
1462 Neq,
1464 Lt,
1466 Leq,
1468 Gt,
1470 Geq,
1472 Assign,
1474 In,
1476 NotIn,
1478 AddAssign,
1480 SubAssign,
1482 MulAssign,
1484 DivAssign,
1486}
1487
1488impl BinOp {
1489 pub fn from_kind(token: SyntaxKind) -> Option<Self> {
1491 Some(match token {
1492 SyntaxKind::Plus => Self::Add,
1493 SyntaxKind::Minus => Self::Sub,
1494 SyntaxKind::Star => Self::Mul,
1495 SyntaxKind::Slash => Self::Div,
1496 SyntaxKind::And => Self::And,
1497 SyntaxKind::Or => Self::Or,
1498 SyntaxKind::EqEq => Self::Eq,
1499 SyntaxKind::ExclEq => Self::Neq,
1500 SyntaxKind::Lt => Self::Lt,
1501 SyntaxKind::LtEq => Self::Leq,
1502 SyntaxKind::Gt => Self::Gt,
1503 SyntaxKind::GtEq => Self::Geq,
1504 SyntaxKind::Eq => Self::Assign,
1505 SyntaxKind::In => Self::In,
1506 SyntaxKind::PlusEq => Self::AddAssign,
1507 SyntaxKind::HyphEq => Self::SubAssign,
1508 SyntaxKind::StarEq => Self::MulAssign,
1509 SyntaxKind::SlashEq => Self::DivAssign,
1510 _ => return Option::None,
1511 })
1512 }
1513
1514 pub fn precedence(self) -> usize {
1516 match self {
1517 Self::Mul => 6,
1518 Self::Div => 6,
1519 Self::Add => 5,
1520 Self::Sub => 5,
1521 Self::Eq => 4,
1522 Self::Neq => 4,
1523 Self::Lt => 4,
1524 Self::Leq => 4,
1525 Self::Gt => 4,
1526 Self::Geq => 4,
1527 Self::In => 4,
1528 Self::NotIn => 4,
1529 Self::And => 3,
1530 Self::Or => 2,
1531 Self::Assign => 1,
1532 Self::AddAssign => 1,
1533 Self::SubAssign => 1,
1534 Self::MulAssign => 1,
1535 Self::DivAssign => 1,
1536 }
1537 }
1538
1539 pub fn assoc(self) -> Assoc {
1541 match self {
1542 Self::Add => Assoc::Left,
1543 Self::Sub => Assoc::Left,
1544 Self::Mul => Assoc::Left,
1545 Self::Div => Assoc::Left,
1546 Self::And => Assoc::Left,
1547 Self::Or => Assoc::Left,
1548 Self::Eq => Assoc::Left,
1549 Self::Neq => Assoc::Left,
1550 Self::Lt => Assoc::Left,
1551 Self::Leq => Assoc::Left,
1552 Self::Gt => Assoc::Left,
1553 Self::Geq => Assoc::Left,
1554 Self::In => Assoc::Left,
1555 Self::NotIn => Assoc::Left,
1556 Self::Assign => Assoc::Right,
1557 Self::AddAssign => Assoc::Right,
1558 Self::SubAssign => Assoc::Right,
1559 Self::MulAssign => Assoc::Right,
1560 Self::DivAssign => Assoc::Right,
1561 }
1562 }
1563
1564 pub fn as_str(self) -> &'static str {
1566 match self {
1567 Self::Add => "+",
1568 Self::Sub => "-",
1569 Self::Mul => "*",
1570 Self::Div => "/",
1571 Self::And => "and",
1572 Self::Or => "or",
1573 Self::Eq => "==",
1574 Self::Neq => "!=",
1575 Self::Lt => "<",
1576 Self::Leq => "<=",
1577 Self::Gt => ">",
1578 Self::Geq => ">=",
1579 Self::In => "in",
1580 Self::NotIn => "not in",
1581 Self::Assign => "=",
1582 Self::AddAssign => "+=",
1583 Self::SubAssign => "-=",
1584 Self::MulAssign => "*=",
1585 Self::DivAssign => "/=",
1586 }
1587 }
1588}
1589
1590#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1592pub enum Assoc {
1593 Left,
1595 Right,
1597}
1598
1599node! {
1600 FieldAccess
1602}
1603
1604impl<'a> FieldAccess<'a> {
1605 pub fn target(self) -> Expr<'a> {
1607 self.0.cast_first_match().unwrap_or_default()
1608 }
1609
1610 pub fn field(self) -> Ident<'a> {
1612 self.0.cast_last_match().unwrap_or_default()
1613 }
1614}
1615
1616node! {
1617 FuncCall
1619}
1620
1621impl<'a> FuncCall<'a> {
1622 pub fn callee(self) -> Expr<'a> {
1624 self.0.cast_first_match().unwrap_or_default()
1625 }
1626
1627 pub fn args(self) -> Args<'a> {
1629 self.0.cast_last_match().unwrap_or_default()
1630 }
1631}
1632
1633node! {
1634 Args
1636}
1637
1638impl<'a> Args<'a> {
1639 pub fn items(self) -> impl DoubleEndedIterator<Item = Arg<'a>> {
1641 self.0.children().filter_map(SyntaxNode::cast)
1642 }
1643
1644 pub fn trailing_comma(self) -> bool {
1646 self.0
1647 .children()
1648 .rev()
1649 .skip(1)
1650 .find(|n| !n.kind().is_trivia())
1651 .is_some_and(|n| n.kind() == SyntaxKind::Comma)
1652 }
1653}
1654
1655#[derive(Debug, Copy, Clone, Hash)]
1657pub enum Arg<'a> {
1658 Pos(Expr<'a>),
1660 Named(Named<'a>),
1662 Spread(Spread<'a>),
1664}
1665
1666impl<'a> AstNode<'a> for Arg<'a> {
1667 fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
1668 match node.kind() {
1669 SyntaxKind::Named => node.cast().map(Self::Named),
1670 SyntaxKind::Spread => node.cast().map(Self::Spread),
1671 _ => node.cast().map(Self::Pos),
1672 }
1673 }
1674
1675 fn to_untyped(self) -> &'a SyntaxNode {
1676 match self {
1677 Self::Pos(v) => v.to_untyped(),
1678 Self::Named(v) => v.to_untyped(),
1679 Self::Spread(v) => v.to_untyped(),
1680 }
1681 }
1682}
1683
1684node! {
1685 Closure
1687}
1688
1689impl<'a> Closure<'a> {
1690 pub fn name(self) -> Option<Ident<'a>> {
1694 self.0.children().next()?.cast()
1695 }
1696
1697 pub fn params(self) -> Params<'a> {
1699 self.0.cast_first_match().unwrap_or_default()
1700 }
1701
1702 pub fn body(self) -> Expr<'a> {
1704 self.0.cast_last_match().unwrap_or_default()
1705 }
1706}
1707
1708node! {
1709 Params
1711}
1712
1713impl<'a> Params<'a> {
1714 pub fn children(self) -> impl DoubleEndedIterator<Item = Param<'a>> {
1716 self.0.children().filter_map(SyntaxNode::cast)
1717 }
1718}
1719
1720#[derive(Debug, Copy, Clone, Hash)]
1722pub enum Param<'a> {
1723 Pos(Pattern<'a>),
1725 Named(Named<'a>),
1727 Spread(Spread<'a>),
1729}
1730
1731impl<'a> AstNode<'a> for Param<'a> {
1732 fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
1733 match node.kind() {
1734 SyntaxKind::Named => node.cast().map(Self::Named),
1735 SyntaxKind::Spread => node.cast().map(Self::Spread),
1736 _ => node.cast().map(Self::Pos),
1737 }
1738 }
1739
1740 fn to_untyped(self) -> &'a SyntaxNode {
1741 match self {
1742 Self::Pos(v) => v.to_untyped(),
1743 Self::Named(v) => v.to_untyped(),
1744 Self::Spread(v) => v.to_untyped(),
1745 }
1746 }
1747}
1748
1749#[derive(Debug, Copy, Clone, Hash)]
1751pub enum Pattern<'a> {
1752 Normal(Expr<'a>),
1754 Placeholder(Underscore<'a>),
1756 Parenthesized(Parenthesized<'a>),
1758 Destructuring(Destructuring<'a>),
1760}
1761
1762impl<'a> AstNode<'a> for Pattern<'a> {
1763 fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
1764 match node.kind() {
1765 SyntaxKind::Underscore => node.cast().map(Self::Placeholder),
1766 SyntaxKind::Parenthesized => node.cast().map(Self::Parenthesized),
1767 SyntaxKind::Destructuring => node.cast().map(Self::Destructuring),
1768 _ => node.cast().map(Self::Normal),
1769 }
1770 }
1771
1772 fn to_untyped(self) -> &'a SyntaxNode {
1773 match self {
1774 Self::Normal(v) => v.to_untyped(),
1775 Self::Placeholder(v) => v.to_untyped(),
1776 Self::Parenthesized(v) => v.to_untyped(),
1777 Self::Destructuring(v) => v.to_untyped(),
1778 }
1779 }
1780}
1781
1782impl<'a> Pattern<'a> {
1783 pub fn bindings(self) -> Vec<Ident<'a>> {
1785 match self {
1786 Self::Normal(Expr::Ident(ident)) => vec![ident],
1787 Self::Parenthesized(v) => v.pattern().bindings(),
1788 Self::Destructuring(v) => v.bindings(),
1789 _ => vec![],
1790 }
1791 }
1792}
1793
1794impl Default for Pattern<'_> {
1795 fn default() -> Self {
1796 Self::Normal(Expr::default())
1797 }
1798}
1799
1800node! {
1801 Underscore
1803}
1804
1805node! {
1806 Destructuring
1808}
1809
1810impl<'a> Destructuring<'a> {
1811 pub fn items(self) -> impl DoubleEndedIterator<Item = DestructuringItem<'a>> {
1813 self.0.children().filter_map(SyntaxNode::cast)
1814 }
1815
1816 pub fn bindings(self) -> Vec<Ident<'a>> {
1818 self.items()
1819 .flat_map(|binding| match binding {
1820 DestructuringItem::Pattern(pattern) => pattern.bindings(),
1821 DestructuringItem::Named(named) => named.pattern().bindings(),
1822 DestructuringItem::Spread(spread) => {
1823 spread.sink_ident().into_iter().collect()
1824 }
1825 })
1826 .collect()
1827 }
1828}
1829
1830#[derive(Debug, Copy, Clone, Hash)]
1832pub enum DestructuringItem<'a> {
1833 Pattern(Pattern<'a>),
1835 Named(Named<'a>),
1837 Spread(Spread<'a>),
1839}
1840
1841impl<'a> AstNode<'a> for DestructuringItem<'a> {
1842 fn from_untyped(node: &'a SyntaxNode) -> Option<Self> {
1843 match node.kind() {
1844 SyntaxKind::Named => node.cast().map(Self::Named),
1845 SyntaxKind::Spread => node.cast().map(Self::Spread),
1846 _ => node.cast().map(Self::Pattern),
1847 }
1848 }
1849
1850 fn to_untyped(self) -> &'a SyntaxNode {
1851 match self {
1852 Self::Pattern(v) => v.to_untyped(),
1853 Self::Named(v) => v.to_untyped(),
1854 Self::Spread(v) => v.to_untyped(),
1855 }
1856 }
1857}
1858
1859node! {
1860 LetBinding
1862}
1863
1864#[derive(Debug)]
1866pub enum LetBindingKind<'a> {
1867 Normal(Pattern<'a>),
1869 Closure(Ident<'a>),
1871}
1872
1873impl<'a> LetBindingKind<'a> {
1874 pub fn bindings(self) -> Vec<Ident<'a>> {
1876 match self {
1877 LetBindingKind::Normal(pattern) => pattern.bindings(),
1878 LetBindingKind::Closure(ident) => vec![ident],
1879 }
1880 }
1881}
1882
1883impl<'a> LetBinding<'a> {
1884 pub fn kind(self) -> LetBindingKind<'a> {
1886 match self.0.cast_first_match::<Pattern>() {
1887 Some(Pattern::Normal(Expr::Closure(closure))) => {
1888 LetBindingKind::Closure(closure.name().unwrap_or_default())
1889 }
1890 pattern => LetBindingKind::Normal(pattern.unwrap_or_default()),
1891 }
1892 }
1893
1894 pub fn init(self) -> Option<Expr<'a>> {
1896 match self.kind() {
1897 LetBindingKind::Normal(Pattern::Normal(_) | Pattern::Parenthesized(_)) => {
1898 self.0.children().filter_map(SyntaxNode::cast).nth(1)
1899 }
1900 LetBindingKind::Normal(_) => self.0.cast_first_match(),
1901 LetBindingKind::Closure(_) => self.0.cast_first_match(),
1902 }
1903 }
1904}
1905
1906node! {
1907 DestructAssignment
1909}
1910
1911impl<'a> DestructAssignment<'a> {
1912 pub fn pattern(self) -> Pattern<'a> {
1914 self.0.cast_first_match::<Pattern>().unwrap_or_default()
1915 }
1916
1917 pub fn value(self) -> Expr<'a> {
1919 self.0.cast_last_match().unwrap_or_default()
1920 }
1921}
1922
1923node! {
1924 SetRule
1926}
1927
1928impl<'a> SetRule<'a> {
1929 pub fn target(self) -> Expr<'a> {
1931 self.0.cast_first_match().unwrap_or_default()
1932 }
1933
1934 pub fn args(self) -> Args<'a> {
1936 self.0.cast_last_match().unwrap_or_default()
1937 }
1938
1939 pub fn condition(self) -> Option<Expr<'a>> {
1941 self.0
1942 .children()
1943 .skip_while(|child| child.kind() != SyntaxKind::If)
1944 .find_map(SyntaxNode::cast)
1945 }
1946}
1947
1948node! {
1949 ShowRule
1951}
1952
1953impl<'a> ShowRule<'a> {
1954 pub fn selector(self) -> Option<Expr<'a>> {
1956 self.0
1957 .children()
1958 .rev()
1959 .skip_while(|child| child.kind() != SyntaxKind::Colon)
1960 .find_map(SyntaxNode::cast)
1961 }
1962
1963 pub fn transform(self) -> Expr<'a> {
1965 self.0.cast_last_match().unwrap_or_default()
1966 }
1967}
1968
1969node! {
1970 Contextual
1972}
1973
1974impl<'a> Contextual<'a> {
1975 pub fn body(self) -> Expr<'a> {
1977 self.0.cast_first_match().unwrap_or_default()
1978 }
1979}
1980
1981node! {
1982 Conditional
1984}
1985
1986impl<'a> Conditional<'a> {
1987 pub fn condition(self) -> Expr<'a> {
1989 self.0.cast_first_match().unwrap_or_default()
1990 }
1991
1992 pub fn if_body(self) -> Expr<'a> {
1994 self.0
1995 .children()
1996 .filter_map(SyntaxNode::cast)
1997 .nth(1)
1998 .unwrap_or_default()
1999 }
2000
2001 pub fn else_body(self) -> Option<Expr<'a>> {
2003 self.0.children().filter_map(SyntaxNode::cast).nth(2)
2004 }
2005}
2006
2007node! {
2008 WhileLoop
2010}
2011
2012impl<'a> WhileLoop<'a> {
2013 pub fn condition(self) -> Expr<'a> {
2015 self.0.cast_first_match().unwrap_or_default()
2016 }
2017
2018 pub fn body(self) -> Expr<'a> {
2020 self.0.cast_last_match().unwrap_or_default()
2021 }
2022}
2023
2024node! {
2025 ForLoop
2027}
2028
2029impl<'a> ForLoop<'a> {
2030 pub fn pattern(self) -> Pattern<'a> {
2032 self.0.cast_first_match().unwrap_or_default()
2033 }
2034
2035 pub fn iterable(self) -> Expr<'a> {
2037 self.0
2038 .children()
2039 .skip_while(|&c| c.kind() != SyntaxKind::In)
2040 .find_map(SyntaxNode::cast)
2041 .unwrap_or_default()
2042 }
2043
2044 pub fn body(self) -> Expr<'a> {
2046 self.0.cast_last_match().unwrap_or_default()
2047 }
2048}
2049
2050node! {
2051 ModuleImport
2053}
2054
2055impl<'a> ModuleImport<'a> {
2056 pub fn source(self) -> Expr<'a> {
2058 self.0.cast_first_match().unwrap_or_default()
2059 }
2060
2061 pub fn imports(self) -> Option<Imports<'a>> {
2063 self.0.children().find_map(|node| match node.kind() {
2064 SyntaxKind::Star => Some(Imports::Wildcard),
2065 SyntaxKind::ImportItems => node.cast().map(Imports::Items),
2066 _ => Option::None,
2067 })
2068 }
2069
2070 pub fn bare_name(self) -> Result<EcoString, BareImportError> {
2078 match self.source() {
2079 Expr::Ident(ident) => Ok(ident.get().clone()),
2080 Expr::FieldAccess(access) => Ok(access.field().get().clone()),
2081 Expr::Str(string) => {
2082 let string = string.get();
2083 let name = if string.starts_with('@') {
2084 PackageSpec::from_str(&string)
2085 .map_err(|_| BareImportError::PackageInvalid)?
2086 .name
2087 } else {
2088 Path::new(string.as_str())
2089 .file_stem()
2090 .and_then(|path| path.to_str())
2091 .ok_or(BareImportError::PathInvalid)?
2092 .into()
2093 };
2094
2095 if !is_ident(&name) {
2096 return Err(BareImportError::PathInvalid);
2097 }
2098
2099 Ok(name)
2100 }
2101 _ => Err(BareImportError::Dynamic),
2102 }
2103 }
2104
2105 pub fn new_name(self) -> Option<Ident<'a>> {
2108 self.0
2109 .children()
2110 .skip_while(|child| child.kind() != SyntaxKind::As)
2111 .find_map(SyntaxNode::cast)
2112 }
2113}
2114
2115#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
2117pub enum BareImportError {
2118 Dynamic,
2120 PathInvalid,
2123 PackageInvalid,
2125}
2126
2127#[derive(Debug, Copy, Clone, Hash)]
2129pub enum Imports<'a> {
2130 Wildcard,
2132 Items(ImportItems<'a>),
2134}
2135
2136node! {
2137 ImportItems
2139}
2140
2141impl<'a> ImportItems<'a> {
2142 pub fn iter(self) -> impl DoubleEndedIterator<Item = ImportItem<'a>> {
2144 self.0.children().filter_map(|child| match child.kind() {
2145 SyntaxKind::RenamedImportItem => child.cast().map(ImportItem::Renamed),
2146 SyntaxKind::ImportItemPath => child.cast().map(ImportItem::Simple),
2147 _ => Option::None,
2148 })
2149 }
2150}
2151
2152node! {
2153 ImportItemPath
2155}
2156
2157impl<'a> ImportItemPath<'a> {
2158 pub fn iter(self) -> impl DoubleEndedIterator<Item = Ident<'a>> {
2160 self.0.children().filter_map(SyntaxNode::cast)
2161 }
2162
2163 pub fn name(self) -> Ident<'a> {
2165 self.iter().last().unwrap_or_default()
2166 }
2167}
2168
2169#[derive(Debug, Copy, Clone, Hash)]
2171pub enum ImportItem<'a> {
2172 Simple(ImportItemPath<'a>),
2175 Renamed(RenamedImportItem<'a>),
2178}
2179
2180impl<'a> ImportItem<'a> {
2181 pub fn path(self) -> ImportItemPath<'a> {
2183 match self {
2184 Self::Simple(path) => path,
2185 Self::Renamed(renamed_item) => renamed_item.path(),
2186 }
2187 }
2188
2189 pub fn original_name(self) -> Ident<'a> {
2192 match self {
2193 Self::Simple(path) => path.name(),
2194 Self::Renamed(renamed_item) => renamed_item.original_name(),
2195 }
2196 }
2197
2198 pub fn bound_name(self) -> Ident<'a> {
2201 match self {
2202 Self::Simple(path) => path.name(),
2203 Self::Renamed(renamed_item) => renamed_item.new_name(),
2204 }
2205 }
2206}
2207
2208node! {
2209 RenamedImportItem
2211}
2212
2213impl<'a> RenamedImportItem<'a> {
2214 pub fn path(self) -> ImportItemPath<'a> {
2216 self.0.cast_first_match().unwrap_or_default()
2217 }
2218
2219 pub fn original_name(self) -> Ident<'a> {
2221 self.path().name()
2222 }
2223
2224 pub fn new_name(self) -> Ident<'a> {
2226 self.0
2227 .children()
2228 .filter_map(SyntaxNode::cast)
2229 .last()
2230 .unwrap_or_default()
2231 }
2232}
2233
2234node! {
2235 ModuleInclude
2237}
2238
2239impl<'a> ModuleInclude<'a> {
2240 pub fn source(self) -> Expr<'a> {
2242 self.0.cast_last_match().unwrap_or_default()
2243 }
2244}
2245
2246node! {
2247 LoopBreak
2249}
2250
2251node! {
2252 LoopContinue
2254}
2255
2256node! {
2257 FuncReturn
2259}
2260
2261impl<'a> FuncReturn<'a> {
2262 pub fn body(self) -> Option<Expr<'a>> {
2264 self.0.cast_last_match()
2265 }
2266}
2267
2268#[cfg(test)]
2269mod tests {
2270 use super::*;
2271
2272 #[test]
2273 fn test_expr_default() {
2274 assert!(Expr::default().to_untyped().cast::<Expr>().is_some());
2275 }
2276}