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;