Skip to main content

fml_syntax/
ast.rs

1//! Typed views over the syntax tree.
2//!
3//! Each type wraps a [`SyntaxNode`] of one kind. Accessors return `None` when the source is
4//! incomplete, so they work on trees with errors too.
5
6use crate::{SyntaxKind, SyntaxNode, SyntaxToken};
7
8/// A typed view of a syntax node.
9pub trait AstNode: Sized {
10    fn can_cast(kind: SyntaxKind) -> bool;
11    fn cast(node: SyntaxNode) -> Option<Self>;
12    fn syntax(&self) -> &SyntaxNode;
13}
14
15macro_rules! ast_node {
16    ($(#[$meta:meta])* $name:ident, $kind:ident) => {
17        $(#[$meta])*
18        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
19        pub struct $name(SyntaxNode);
20
21        impl AstNode for $name {
22            fn can_cast(kind: SyntaxKind) -> bool {
23                kind == SyntaxKind::$kind
24            }
25
26            fn cast(node: SyntaxNode) -> Option<Self> {
27                Self::can_cast(node.kind()).then(|| Self(node))
28            }
29
30            fn syntax(&self) -> &SyntaxNode {
31                &self.0
32            }
33        }
34    };
35}
36
37fn child<N: AstNode>(node: &SyntaxNode) -> Option<N> {
38    node.children().find_map(N::cast)
39}
40
41fn children<N: AstNode>(node: &SyntaxNode) -> impl Iterator<Item = N> + use<N> {
42    node.children().filter_map(N::cast)
43}
44
45fn tokens(node: &SyntaxNode) -> impl Iterator<Item = SyntaxToken> + use<> {
46    node.children_with_tokens()
47        .filter_map(|element| element.into_token())
48        .filter(|token| !token.kind().is_trivia())
49}
50
51/// The text of the first name token directly under `node` that is not one of `skip`.
52fn name_after(node: &SyntaxNode, skip: &[&str]) -> Option<String> {
53    tokens(node)
54        .filter(|t| matches!(t.kind(), SyntaxKind::Ident | SyntaxKind::DelimitedIdent))
55        .find(|t| !skip.contains(&t.text()))
56        .map(|t| name_text(&t))
57}
58
59/// The name a token spells: the identifier, or the content of a `` `delimited` `` identifier.
60pub fn name_text(token: &SyntaxToken) -> String {
61    match token.kind() {
62        SyntaxKind::DelimitedIdent => unescape(token.text()),
63        _ => token.text().to_owned(),
64    }
65}
66
67/// Removes the quotes from a string or delimited identifier and resolves escape sequences.
68pub fn unescape(text: &str) -> String {
69    // Markdown between triple quotes is taken verbatim, line breaks included.
70    let inner = if text.starts_with("\"\"\"") && text.len() >= 6 {
71        return text[3..text.len() - 3].to_owned();
72    } else if text.len() >= 2 {
73        &text[1..text.len() - 1]
74    } else {
75        text
76    };
77    let mut out = String::with_capacity(inner.len());
78    let mut chars = inner.chars();
79    while let Some(c) = chars.next() {
80        if c != '\\' {
81            out.push(c);
82            continue;
83        }
84        match chars.next() {
85            Some('n') => out.push('\n'),
86            Some('r') => out.push('\r'),
87            Some('t') => out.push('\t'),
88            Some('f') => out.push('\u{c}'),
89            Some('u') => {
90                let hex: String = chars.by_ref().take(4).collect();
91                match u32::from_str_radix(&hex, 16).ok().and_then(char::from_u32) {
92                    Some(decoded) => out.push(decoded),
93                    None => {
94                        out.push_str("\\u");
95                        out.push_str(&hex);
96                    }
97                }
98            }
99            Some(other) => out.push(other),
100            None => out.push('\\'),
101        }
102    }
103    out
104}
105
106// ---- File level -------------------------------------------------------------------------------
107
108ast_node!(
109    /// A whole FML document.
110    SourceFile,
111    SourceFile
112);
113
114impl SourceFile {
115    pub fn metadata(&self) -> impl Iterator<Item = Metadata> + use<> {
116        children(&self.0)
117    }
118
119    pub fn concept_maps(&self) -> impl Iterator<Item = ConceptMap> + use<> {
120        children(&self.0)
121    }
122
123    pub fn map_declaration(&self) -> Option<MapDeclaration> {
124        child(&self.0)
125    }
126
127    pub fn uses(&self) -> impl Iterator<Item = UsesDeclaration> + use<> {
128        children(&self.0)
129    }
130
131    pub fn imports(&self) -> impl Iterator<Item = ImportsDeclaration> + use<> {
132        children(&self.0)
133    }
134
135    pub fn constants(&self) -> impl Iterator<Item = ConstantDeclaration> + use<> {
136        children(&self.0)
137    }
138
139    pub fn groups(&self) -> impl Iterator<Item = Group> + use<> {
140        children(&self.0)
141    }
142}
143
144ast_node!(
145    /// `/// key = value`
146    Metadata,
147    Metadata
148);
149
150impl Metadata {
151    /// The key, such as `url` or `jurisdiction.coding.system`.
152    pub fn key(&self) -> Option<QualifiedName> {
153        child(&self.0)
154    }
155
156    /// The value. `None` when the key is given without one, which removes an inherited value.
157    pub fn value(&self) -> Option<Literal> {
158        child(&self.0)
159    }
160}
161
162ast_node!(
163    /// A literal token: string, number, boolean, date or time.
164    Literal,
165    Literal
166);
167
168impl Literal {
169    pub fn token(&self) -> Option<SyntaxToken> {
170        tokens(&self.0).next()
171    }
172
173    /// The value as written, with quotes removed and escapes resolved for strings.
174    pub fn value(&self) -> Option<String> {
175        let token = self.token()?;
176        Some(match token.kind() {
177            SyntaxKind::SingleQuotedString
178            | SyntaxKind::DoubleQuotedString
179            | SyntaxKind::TripleQuotedString
180            | SyntaxKind::DelimitedIdent => unescape(token.text()),
181            _ => token.text().to_owned(),
182        })
183    }
184
185    pub fn kind(&self) -> Option<SyntaxKind> {
186        self.token().map(|t| t.kind())
187    }
188}
189
190ast_node!(
191    /// `conceptmap "url" { … }`
192    ConceptMap,
193    ConceptMap
194);
195
196impl ConceptMap {
197    pub fn url(&self) -> Option<Literal> {
198        child(&self.0)
199    }
200
201    pub fn prefixes(&self) -> impl Iterator<Item = ConceptMapPrefix> + use<> {
202        children(&self.0)
203    }
204
205    pub fn entries(&self) -> impl Iterator<Item = ConceptMapEntry> + use<> {
206        children(&self.0)
207    }
208
209    pub fn unmapped(&self) -> impl Iterator<Item = ConceptMapUnmapped> + use<> {
210        children(&self.0)
211    }
212}
213
214ast_node!(
215    /// `unmapped for s = provided`
216    ConceptMapUnmapped,
217    ConceptMapUnmapped
218);
219
220impl ConceptMapUnmapped {
221    pub fn prefix(&self) -> Option<String> {
222        name_after(&self.0, &["unmapped", "for"])
223    }
224
225    /// The unmapped mode, such as `provided`.
226    pub fn mode(&self) -> Option<String> {
227        tokens(&self.0)
228            .filter(|t| t.kind() == SyntaxKind::Ident)
229            .last()
230            .map(|t| t.text().to_owned())
231    }
232}
233
234ast_node!(
235    /// `prefix s = "url"`
236    ConceptMapPrefix,
237    ConceptMapPrefix
238);
239
240impl ConceptMapPrefix {
241    pub fn name(&self) -> Option<String> {
242        name_after(&self.0, &["prefix"])
243    }
244
245    pub fn url(&self) -> Option<Literal> {
246        child(&self.0)
247    }
248}
249
250ast_node!(
251    /// `s:male - t:M`
252    ConceptMapEntry,
253    ConceptMapEntry
254);
255
256impl ConceptMapEntry {
257    pub fn source(&self) -> Option<ConceptMapCode> {
258        children(&self.0).next()
259    }
260
261    pub fn target(&self) -> Option<ConceptMapCode> {
262        children(&self.0).nth(1)
263    }
264
265    /// The relationship operator, such as `-`, `==` or `<=`.
266    pub fn relation(&self) -> Option<String> {
267        self.0
268            .children()
269            .find(|n| n.kind() == SyntaxKind::ConceptMapRelation)
270            .map(|n| tokens(&n).map(|t| t.text().to_owned()).collect())
271    }
272}
273
274ast_node!(
275    /// `s:male`
276    ConceptMapCode,
277    ConceptMapCode
278);
279
280impl ConceptMapCode {
281    pub fn prefix(&self) -> Option<String> {
282        tokens(&self.0).next().map(|t| t.text().to_owned())
283    }
284
285    pub fn code(&self) -> Option<String> {
286        let token = tokens(&self.0).nth(2)?;
287        Some(match token.kind() {
288            SyntaxKind::Ident | SyntaxKind::Integer | SyntaxKind::Decimal => {
289                token.text().to_owned()
290            }
291            _ => unescape(token.text()),
292        })
293    }
294}
295
296ast_node!(
297    /// `map "url" = "name"`
298    MapDeclaration,
299    MapDeclaration
300);
301
302impl MapDeclaration {
303    pub fn url(&self) -> Option<Literal> {
304        children(&self.0).next()
305    }
306
307    pub fn name(&self) -> Option<Literal> {
308        children(&self.0).nth(1)
309    }
310}
311
312/// How a map uses a structure.
313#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
314pub enum ModelMode {
315    Source,
316    Queried,
317    Target,
318    Produced,
319}
320
321impl ModelMode {
322    pub fn as_str(self) -> &'static str {
323        match self {
324            ModelMode::Source => "source",
325            ModelMode::Queried => "queried",
326            ModelMode::Target => "target",
327            ModelMode::Produced => "produced",
328        }
329    }
330}
331
332ast_node!(
333    /// `uses "url" alias Name as source`
334    UsesDeclaration,
335    UsesDeclaration
336);
337
338impl UsesDeclaration {
339    pub fn url(&self) -> Option<Literal> {
340        child(&self.0)
341    }
342
343    pub fn alias(&self) -> Option<String> {
344        let alias = self
345            .0
346            .children()
347            .find(|n| n.kind() == SyntaxKind::UsesAlias)?;
348        name_after(&alias, &["alias"])
349    }
350
351    pub fn mode(&self) -> Option<ModelMode> {
352        let mut words = tokens(&self.0).map(|t| t.text().to_owned());
353        words.find(|w| w == "as")?;
354        match words.next()?.as_str() {
355            "source" => Some(ModelMode::Source),
356            "queried" => Some(ModelMode::Queried),
357            "target" => Some(ModelMode::Target),
358            "produced" => Some(ModelMode::Produced),
359            _ => None,
360        }
361    }
362}
363
364ast_node!(
365    /// `imports "url"`
366    ImportsDeclaration,
367    ImportsDeclaration
368);
369
370impl ImportsDeclaration {
371    pub fn url(&self) -> Option<Literal> {
372        child(&self.0)
373    }
374}
375
376ast_node!(
377    /// `let name = expression;`
378    ConstantDeclaration,
379    ConstantDeclaration
380);
381
382impl ConstantDeclaration {
383    pub fn name(&self) -> Option<String> {
384        name_after(&self.0, &["let"])
385    }
386
387    pub fn expression(&self) -> Option<Expr> {
388        child(&self.0)
389    }
390}
391
392// ---- Groups -----------------------------------------------------------------------------------
393
394ast_node!(
395    /// `group Name(…) extends Base <<types>> { … }`
396    Group,
397    Group
398);
399
400/// The type mode of a group: whether it is the default mapping for its types.
401#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
402pub enum GroupTypeMode {
403    /// `<<types>>`
404    Types,
405    /// `<<type+>>`
406    TypeAndTypes,
407}
408
409impl Group {
410    pub fn name(&self) -> Option<String> {
411        name_after(&self.0, &["group"])
412    }
413
414    pub fn parameters(&self) -> impl Iterator<Item = Parameter> + use<> {
415        child::<ParameterList>(&self.0)
416            .into_iter()
417            .flat_map(|list| children::<Parameter>(&list.0).collect::<Vec<_>>())
418    }
419
420    pub fn extends(&self) -> Option<String> {
421        let clause = self
422            .0
423            .children()
424            .find(|n| n.kind() == SyntaxKind::ExtendsClause)?;
425        name_after(&clause, &["extends"])
426    }
427
428    pub fn type_mode(&self) -> Option<GroupTypeMode> {
429        let mode = self
430            .0
431            .children()
432            .find(|n| n.kind() == SyntaxKind::TypeMode)?;
433        let text: String = tokens(&mode).map(|t| t.text().to_owned()).collect();
434        match text.as_str() {
435            "<<types>>" => Some(GroupTypeMode::Types),
436            "<<type+>>" => Some(GroupTypeMode::TypeAndTypes),
437            _ => None,
438        }
439    }
440
441    pub fn body(&self) -> Option<RuleBlock> {
442        child(&self.0)
443    }
444
445    pub fn rules(&self) -> impl Iterator<Item = Rule> + use<> {
446        self.body()
447            .into_iter()
448            .flat_map(|block| block.rules().collect::<Vec<_>>())
449    }
450}
451
452ast_node!(ParameterList, ParameterList);
453
454ast_node!(
455    /// `source src : Type`
456    Parameter,
457    Parameter
458);
459
460/// Whether a group parameter is an input or an output.
461#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
462pub enum ParameterMode {
463    Source,
464    Target,
465}
466
467impl Parameter {
468    pub fn mode(&self) -> Option<ParameterMode> {
469        match tokens(&self.0).next()?.text() {
470            "source" => Some(ParameterMode::Source),
471            "target" => Some(ParameterMode::Target),
472            _ => None,
473        }
474    }
475
476    pub fn name(&self) -> Option<String> {
477        tokens(&self.0)
478            .nth(1)
479            .filter(|t| matches!(t.kind(), SyntaxKind::Ident | SyntaxKind::DelimitedIdent))
480            .map(|t| name_text(&t))
481    }
482
483    pub fn type_annotation(&self) -> Option<TypeAnnotation> {
484        child(&self.0)
485    }
486}
487
488ast_node!(
489    /// `: Type`
490    TypeAnnotation,
491    TypeAnnotation
492);
493
494impl TypeAnnotation {
495    /// The type name, unquoted when it was written as a string.
496    pub fn type_name(&self) -> Option<String> {
497        if let Some(name) = child::<QualifiedName>(&self.0) {
498            return Some(name.text());
499        }
500        child::<Literal>(&self.0).and_then(|l| l.value())
501    }
502}
503
504ast_node!(
505    /// `{ rule; … }`
506    RuleBlock,
507    RuleBlock
508);
509
510impl RuleBlock {
511    pub fn rules(&self) -> impl Iterator<Item = Rule> + use<> {
512        children(&self.0)
513    }
514}
515
516// ---- Rules ------------------------------------------------------------------------------------
517
518ast_node!(
519    /// `sources -> targets then … "name";`
520    Rule,
521    Rule
522);
523
524impl Rule {
525    pub fn sources(&self) -> impl Iterator<Item = Source> + use<> {
526        self.0
527            .children()
528            .find(|n| n.kind() == SyntaxKind::SourceList)
529            .into_iter()
530            .flat_map(|list| children::<Source>(&list).collect::<Vec<_>>())
531    }
532
533    pub fn targets(&self) -> impl Iterator<Item = Target> + use<> {
534        self.0
535            .children()
536            .find(|n| n.kind() == SyntaxKind::TargetList)
537            .into_iter()
538            .flat_map(|list| children::<Target>(&list).collect::<Vec<_>>())
539    }
540
541    pub fn dependent(&self) -> Option<DependentClause> {
542        child(&self.0)
543    }
544
545    /// The fields of the batch identity form `src.a -> tgt.a : f1, f2;`.
546    pub fn identity_fields(&self) -> Vec<String> {
547        self.0
548            .children()
549            .find(|n| n.kind() == SyntaxKind::IdentityList)
550            .map(|list| {
551                tokens(&list)
552                    .filter(|t| matches!(t.kind(), SyntaxKind::Ident | SyntaxKind::DelimitedIdent))
553                    .map(|t| name_text(&t))
554                    .collect()
555            })
556            .unwrap_or_default()
557    }
558
559    /// The rule name, without quotes.
560    pub fn name(&self) -> Option<String> {
561        let name = self
562            .0
563            .children()
564            .find(|n| n.kind() == SyntaxKind::RuleName)?;
565        tokens(&name).next().map(|t| unescape(t.text()))
566    }
567}
568
569/// A source or target list mode.
570#[derive(Debug, Clone, PartialEq, Eq, Hash)]
571pub enum ListMode {
572    First,
573    NotFirst,
574    Last,
575    NotLast,
576    OnlyOne,
577    Single,
578    Collate,
579    /// `share id`
580    Share(String),
581}
582
583impl ListMode {
584    fn from_word(word: &str) -> Option<Self> {
585        Some(match word {
586            "first" => ListMode::First,
587            "not_first" => ListMode::NotFirst,
588            "last" => ListMode::Last,
589            "not_last" => ListMode::NotLast,
590            "only_one" => ListMode::OnlyOne,
591            "single" => ListMode::Single,
592            "collate" => ListMode::Collate,
593            _ => return None,
594        })
595    }
596}
597
598ast_node!(
599    /// One source of a rule.
600    Source,
601    Source
602);
603
604impl Source {
605    /// The context variable and optional element, as in `src.name`.
606    pub fn path(&self) -> Option<QualifiedName> {
607        child(&self.0)
608    }
609
610    pub fn type_annotation(&self) -> Option<TypeAnnotation> {
611        child(&self.0)
612    }
613
614    /// `(min, max)` where `max` is `*` or a number.
615    pub fn cardinality(&self) -> Option<(String, String)> {
616        let node = self
617            .0
618            .children()
619            .find(|n| n.kind() == SyntaxKind::Cardinality)?;
620        let mut parts = tokens(&node);
621        let min = parts.next()?.text().to_owned();
622        parts.next()?;
623        let max = parts.next()?.text().to_owned();
624        Some((min, max))
625    }
626
627    pub fn default_value(&self) -> Option<DefaultClause> {
628        child(&self.0)
629    }
630
631    pub fn list_mode(&self) -> Option<ListMode> {
632        let node = self
633            .0
634            .children()
635            .find(|n| n.kind() == SyntaxKind::SourceListMode)?;
636        ListMode::from_word(tokens(&node).next()?.text())
637    }
638
639    pub fn alias(&self) -> Option<String> {
640        child::<Alias>(&self.0).and_then(|a| a.name())
641    }
642
643    pub fn where_clause(&self) -> Option<Expr> {
644        self.clause(SyntaxKind::WhereClause)
645    }
646
647    pub fn check_clause(&self) -> Option<Expr> {
648        self.clause(SyntaxKind::CheckClause)
649    }
650
651    pub fn log_clause(&self) -> Option<Expr> {
652        self.clause(SyntaxKind::LogClause)
653    }
654
655    fn clause(&self, kind: SyntaxKind) -> Option<Expr> {
656        let node = self.0.children().find(|n| n.kind() == kind)?;
657        child(&node)
658    }
659}
660
661ast_node!(
662    /// `default (expression)`, or the legacy `default "value"`.
663    DefaultClause,
664    DefaultClause
665);
666
667impl DefaultClause {
668    pub fn expression(&self) -> Option<Expr> {
669        child(&self.0)
670    }
671}
672
673ast_node!(
674    /// `as name`
675    Alias,
676    Alias
677);
678
679impl Alias {
680    pub fn name(&self) -> Option<String> {
681        name_after(&self.0, &["as"])
682    }
683}
684
685ast_node!(
686    /// One target of a rule.
687    Target,
688    Target
689);
690
691impl Target {
692    /// The context variable and element, as in `tgt.name`. `None` for targets that are only a
693    /// transform (`create('X') as x`) or an expression.
694    pub fn path(&self) -> Option<QualifiedName> {
695        child(&self.0)
696    }
697
698    /// The transform: the value after `=`, or the target itself when it is an invocation or a
699    /// parenthesised expression.
700    pub fn transform(&self) -> Option<TransformValue> {
701        if let Some(transform) = self
702            .0
703            .children()
704            .find(|n| n.kind() == SyntaxKind::Transform)
705        {
706            return transform.children().find_map(TransformValue::cast);
707        }
708        self.0
709            .children()
710            .filter(|n| matches!(n.kind(), SyntaxKind::Invocation | SyntaxKind::ParenExpr))
711            .find_map(TransformValue::cast)
712    }
713
714    pub fn alias(&self) -> Option<String> {
715        child::<Alias>(&self.0).and_then(|a| a.name())
716    }
717
718    pub fn list_mode(&self) -> Option<ListMode> {
719        let node = self
720            .0
721            .children()
722            .find(|n| n.kind() == SyntaxKind::TargetListMode)?;
723        let mut words = tokens(&node);
724        let word = words.next()?;
725        if word.text() == "share" {
726            return Some(ListMode::Share(name_text(&words.next()?)));
727        }
728        ListMode::from_word(word.text())
729    }
730}
731
732/// The value a target is set to.
733#[derive(Debug, Clone, PartialEq, Eq, Hash)]
734pub enum TransformValue {
735    /// A constant.
736    Literal(Literal),
737    /// A variable or a path: the copy transform.
738    Name(QualifiedName),
739    /// A named transform such as `create('Coding')`, or a group call.
740    Invocation(Invocation),
741    /// `(fhirpath)`: the evaluate transform.
742    Expression(Expr),
743}
744
745impl TransformValue {
746    fn cast(node: SyntaxNode) -> Option<Self> {
747        Some(match node.kind() {
748            SyntaxKind::Literal => TransformValue::Literal(Literal(node)),
749            SyntaxKind::QualifiedName => TransformValue::Name(QualifiedName(node)),
750            SyntaxKind::Invocation => TransformValue::Invocation(Invocation(node)),
751            SyntaxKind::ParenExpr => TransformValue::Expression(Expr(node)),
752            _ => return None,
753        })
754    }
755
756    pub fn syntax(&self) -> &SyntaxNode {
757        match self {
758            TransformValue::Literal(n) => n.syntax(),
759            TransformValue::Name(n) => n.syntax(),
760            TransformValue::Invocation(n) => n.syntax(),
761            TransformValue::Expression(n) => n.syntax(),
762        }
763    }
764}
765
766ast_node!(
767    /// `name(arguments)`: a transform or a group call.
768    Invocation,
769    Invocation
770);
771
772impl Invocation {
773    pub fn name(&self) -> Option<String> {
774        tokens(&self.0).next().map(|t| name_text(&t))
775    }
776
777    pub fn arguments(&self) -> impl Iterator<Item = Expr> + use<> {
778        child::<ArgumentList>(&self.0)
779            .into_iter()
780            .flat_map(|list| children::<Expr>(&list.0).collect::<Vec<_>>())
781    }
782}
783
784ast_node!(ArgumentList, ArgumentList);
785
786ast_node!(
787    /// `then Group(a, b) { … }`
788    DependentClause,
789    DependentClause
790);
791
792impl DependentClause {
793    pub fn invocations(&self) -> impl Iterator<Item = Invocation> + use<> {
794        children(&self.0)
795    }
796
797    pub fn rules(&self) -> Option<RuleBlock> {
798        child(&self.0)
799    }
800}
801
802ast_node!(
803    /// `a.b.c`
804    QualifiedName,
805    QualifiedName
806);
807
808impl QualifiedName {
809    /// The segments, with delimited identifiers unquoted. A trailing `[x]` stays on its segment.
810    pub fn segments(&self) -> Vec<String> {
811        let mut segments: Vec<String> = Vec::new();
812        let mut choice = false;
813        for token in tokens(&self.0) {
814            match token.kind() {
815                SyntaxKind::Ident | SyntaxKind::DelimitedIdent if choice => {}
816                SyntaxKind::Ident | SyntaxKind::DelimitedIdent => segments.push(name_text(&token)),
817                SyntaxKind::LBracket => choice = true,
818                SyntaxKind::RBracket => {
819                    choice = false;
820                    if let Some(last) = segments.last_mut() {
821                        last.push_str("[x]");
822                    }
823                }
824                _ => {}
825            }
826        }
827        segments
828    }
829
830    /// The segments joined with `.`.
831    pub fn text(&self) -> String {
832        self.segments().join(".")
833    }
834}
835
836// ---- Expressions ------------------------------------------------------------------------------
837
838/// Any FHIRPath expression node.
839#[derive(Debug, Clone, PartialEq, Eq, Hash)]
840pub struct Expr(SyntaxNode);
841
842impl AstNode for Expr {
843    fn can_cast(kind: SyntaxKind) -> bool {
844        matches!(
845            kind,
846            SyntaxKind::Literal
847                | SyntaxKind::Quantity
848                | SyntaxKind::NameRef
849                | SyntaxKind::ExternalConstant
850                | SyntaxKind::ParenExpr
851                | SyntaxKind::MemberExpr
852                | SyntaxKind::CallExpr
853                | SyntaxKind::IndexExpr
854                | SyntaxKind::PrefixExpr
855                | SyntaxKind::BinaryExpr
856                | SyntaxKind::TypeExpr
857                | SyntaxKind::NullLiteral
858        )
859    }
860
861    fn cast(node: SyntaxNode) -> Option<Self> {
862        Self::can_cast(node.kind()).then(|| Self(node))
863    }
864
865    fn syntax(&self) -> &SyntaxNode {
866        &self.0
867    }
868}
869
870impl Expr {
871    /// The expression without the outer parentheses, if it has them.
872    pub fn unparenthesized(&self) -> Expr {
873        let mut expr = self.clone();
874        while expr.0.kind() == SyntaxKind::ParenExpr {
875            match child::<Expr>(&expr.0) {
876                Some(inner) => expr = inner,
877                None => break,
878            }
879        }
880        expr
881    }
882
883    /// The expression in canonical form, as StructureMap stores it; see
884    /// [`expression_text`](crate::expression_text).
885    pub fn canonical_text(&self) -> String {
886        crate::expression_text(&self.0)
887    }
888
889    /// The expression's source text, comments and whitespace kept as written.
890    pub fn text(&self) -> String {
891        self.0.text().to_string().trim().to_owned()
892    }
893}