Skip to main content

fml_syntax/
lib.rs

1//! Lossless syntax tree, error-tolerant parser and formatter for the
2//! [FHIR Mapping Language](https://build.fhir.org/ig/HL7/mapping-language-ig/).
3//!
4//! ```
5//! let parse = fml_syntax::parse(r#"
6//! uses "http://hl7.org/fhir/StructureDefinition/Patient" as source
7//! uses "http://hl7.org/fhir/StructureDefinition/Patient" as target
8//!
9//! group Copy(source src : Patient, target tgt : Patient) {
10//!   src.name -> tgt.name;
11//! }
12//! "#);
13//! assert!(parse.diagnostics().is_empty());
14//!
15//! let file = parse.tree();
16//! let group = file.groups().next().unwrap();
17//! assert_eq!(group.name().as_deref(), Some("Copy"));
18//! ```
19//!
20//! The tree keeps every byte of the input, comments and whitespace included, so
21//! `parse(text).syntax().to_string() == text` for any input.
22
23pub mod ast;
24mod diagnostic;
25mod format;
26mod kind;
27mod lexer;
28mod parser;
29
30use rowan::GreenNode;
31
32pub use diagnostic::{Diagnostic, LineCol, LineIndex, Severity};
33pub use format::{
34    FormatError, FormatOptions, expression_text, format, format_tree, quote_fhirpath_string,
35};
36pub use kind::{FmlLanguage, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken};
37pub use lexer::{Token, tokenize};
38pub use rowan::{Direction, NodeOrToken, TextRange, TextSize};
39
40/// The result of parsing: a syntax tree that covers the whole input, plus the problems found.
41#[derive(Debug, Clone)]
42pub struct Parse {
43    green: GreenNode,
44    diagnostics: Vec<Diagnostic>,
45}
46
47impl Parse {
48    /// The root of the untyped syntax tree.
49    pub fn syntax(&self) -> SyntaxNode {
50        SyntaxNode::new_root(self.green.clone())
51    }
52
53    /// The typed root of the tree.
54    pub fn tree(&self) -> ast::SourceFile {
55        ast::SourceFile::cast(self.syntax()).expect("the root is always a source file")
56    }
57
58    /// Lexical and syntax errors, ordered by position.
59    pub fn diagnostics(&self) -> &[Diagnostic] {
60        &self.diagnostics
61    }
62
63    pub fn has_errors(&self) -> bool {
64        self.diagnostics
65            .iter()
66            .any(|d| d.severity == Severity::Error)
67    }
68}
69
70/// Parses FML source text. Never fails; see [`Parse::diagnostics`].
71pub fn parse(text: &str) -> Parse {
72    let (green, diagnostics) = parser::parse_source(text);
73    Parse { green, diagnostics }
74}
75
76pub use ast::AstNode;