wdl_grammar/
grammar.rs

1//! Module for the WDL grammar functions.
2
3use super::Diagnostic;
4use super::Span;
5use super::lexer::PreambleToken;
6use super::parser::Event;
7use super::parser::Marker;
8use super::parser::Parser;
9use super::tree::SyntaxKind;
10use crate::SupportedVersion;
11use crate::lexer::VersionStatementToken;
12
13pub mod v1;
14
15/// Helper macros for the parser implementation.
16mod macros {
17    /// A macro for expecting the next token be a particular token.
18    ///
19    /// Returns a diagnostic if the token is not the specified token.
20    macro_rules! expected {
21        ($parser:ident, $marker:ident, $token:expr_2021) => {
22            if let Err(e) = $parser.expect($token) {
23                return Err(($marker, e));
24            }
25        };
26        ($parser:ident, $marker:ident, $token:expr_2021, $name:literal) => {
27            if let Err(e) = $parser.expect_with_name($token, $name) {
28                return Err(($marker, e));
29            }
30        };
31    }
32
33    /// A macro for expecting the next token be in the given token set.
34    ///
35    /// Returns an error if the token is not the specified token.
36    macro_rules! expected_in {
37        ($parser:ident, $marker:ident, $set:ident $(, $names:literal)+ $(,)?) => {
38            if let Err(e) = $parser.expect_in($set, &[$($names),+]) {
39                return Err(($marker, e));
40            }
41        };
42    }
43
44    /// A macro for expecting that a given function parses the next node.
45    ///
46    /// Returns an error if the given function returns an error.
47    macro_rules! expected_fn {
48        ($parser:ident, $marker:ident, $func:ident) => {
49            let inner = $parser.start();
50            if let Err((inner, e)) = $func($parser, inner) {
51                inner.abandon($parser);
52                return Err(($marker, e));
53            }
54        };
55        ($parser:ident, $func:ident) => {
56            let inner = $parser.start();
57            if let Err((inner, e)) = $func($parser, inner) {
58                inner.abandon($parser);
59                return Err(e);
60            }
61        };
62    }
63
64    pub(crate) use expected;
65    pub(crate) use expected_fn;
66    pub(crate) use expected_in;
67}
68
69/// A parser type used for parsing the document preamble.
70type PreambleParser<'a> = Parser<'a, PreambleToken>;
71
72/// Parses a WDL document.
73///
74/// Returns the parser events that result from parsing the document.
75pub fn document(source: &str, mut parser: PreambleParser<'_>) -> (Vec<Event>, Vec<Diagnostic>) {
76    let root = parser.start();
77    // Look for a starting `version` keyword token
78    // If this fails, an error is emitted and we'll skip parsing the remainder of
79    // the file.
80    let (mut parser, diagnostic) = match parser.peek() {
81        Some((PreambleToken::VersionKeyword, _)) => {
82            let marker = parser.start();
83            let (mut parser, res) = version_statement(parser, marker);
84            match res {
85                Ok(span) => {
86                    // A version statement was successfully parsed, check to see if the
87                    // version is supported by this implementation
88                    let version = &source[span.start()..span.end()];
89
90                    match version.parse::<SupportedVersion>() {
91                        Ok(_) => {
92                            let mut parser = parser.morph();
93                            v1::items(&mut parser);
94                            root.complete(&mut parser, SyntaxKind::RootNode);
95                            let output = parser.finish();
96                            return (output.events, output.diagnostics);
97                        }
98                        _ => (
99                            parser,
100                            Diagnostic::error(format!("unsupported WDL version `{version}`"))
101                                .with_label("this version of WDL is not supported", span),
102                        ),
103                    }
104                }
105                Err((marker, e)) => {
106                    marker.abandon(&mut parser);
107                    (parser, e)
108                }
109            }
110        }
111        found => {
112            let mut diagnostic =
113                Diagnostic::error("a WDL document must start with a version statement");
114
115            if let Some((_, span)) = found {
116                diagnostic =
117                    diagnostic.with_label("a version statement must come before this", span);
118            }
119
120            (parser, diagnostic)
121        }
122    };
123
124    // At this point, the parse cannot continue; but we still want the tree to cover
125    // every span of the source, so we will insert a special "unparsed" token for
126    // the remaining source.
127    parser.diagnostic(diagnostic);
128    parser.consume_remainder();
129    root.complete(&mut parser, SyntaxKind::RootNode);
130    let output = parser.finish();
131    (output.events, output.diagnostics)
132}
133
134/// Parses the version statement of a WDL source file.
135///
136/// Returns the source span of the version token if present.
137pub fn version_statement(
138    mut parser: Parser<'_, PreambleToken>,
139    marker: Marker,
140) -> (
141    Parser<'_, PreambleToken>,
142    Result<Span, (Marker, Diagnostic)>,
143) {
144    parser.require(PreambleToken::VersionKeyword);
145
146    let mut parser: Parser<'_, VersionStatementToken> = parser.morph();
147    let span = match parser.expect(VersionStatementToken::Version) {
148        Ok(span) => span,
149        Err(e) => return (parser.morph(), Err((marker, e))),
150    };
151
152    marker.complete(&mut parser, SyntaxKind::VersionStatementNode);
153    (parser.morph(), Ok(span))
154}