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