use std::str::FromStr;
#[cfg(feature = "unstable-python")]
pub use python::py_document;
use super::Diagnostic;
use super::Span;
use super::SupportedVersion;
use super::lexer::PreambleToken;
use super::parser::Event;
use super::parser::ParseDiagnostic;
use super::parser::Parser;
use super::tree::SyntaxKind;
use crate::lexer::VersionStatementToken;
pub mod v1;
mod macros {
macro_rules! expected {
($parser:ident, $marker:ident, $token:expr_2021) => {
if let Err(e) = $parser.expect($token) {
return Err(($marker, e));
}
};
($parser:ident, $marker:ident, $token:expr_2021, $name:literal) => {
if let Err(e) = $parser.expect_with_name($token, $name) {
return Err(($marker, e));
}
};
}
macro_rules! expected_in {
($parser:ident, $marker:ident, $set:ident $(, $names:literal)+ $(,)?) => {
if let Err(e) = $parser.expect_in($set, &[$($names),+]) {
return Err(($marker, e));
}
};
}
macro_rules! expected_fn {
($parser:ident, $marker:ident, $func:ident) => {
let inner = $parser.start();
if let Err((inner, e)) = $func($parser, inner) {
inner.abandon($parser);
return Err(($marker, e));
}
};
($parser:ident, $func:ident) => {
let inner = $parser.start();
if let Err((inner, e)) = $func($parser, inner) {
inner.abandon($parser);
return Err(e);
}
};
}
pub(crate) use expected;
pub(crate) use expected_fn;
pub(crate) use expected_in;
}
type PreambleParser<'a> = Parser<'a, PreambleToken>;
pub fn document(
mut parser: PreambleParser<'_>,
fallback_version: Option<SupportedVersion>,
) -> (Vec<Event>, Vec<Diagnostic>) {
let root = parser.start();
let (mut parser, diagnostic) = match parser.peek() {
Some((PreambleToken::VersionKeyword, _)) => {
match version_statement(parser, fallback_version) {
(parser, None) => {
let mut parser = parser.morph();
v1::items(&mut parser);
root.complete(&mut parser, SyntaxKind::RootNode);
let output = parser.finish();
return (output.events, output.diagnostics);
}
(parser, Some(diag)) => (parser, diag),
}
}
found => {
let mut diagnostic = Diagnostic::error("missing version statement")
.with_help(
"omitting the version statement declares the document as WDL draft-2, which \
is not supported",
)
.with_fix("upgrade WDL draft-2 documents to v1.0 or later");
if let Some((_, span)) = found {
diagnostic = diagnostic.with_label(
"WDL v1.0+ documents must begin with a version statement",
span,
);
}
(parser, diagnostic.into())
}
};
parser.diagnostic(diagnostic);
parser.consume_remainder();
root.complete(&mut parser, SyntaxKind::RootNode);
let output = parser.finish();
(output.events, output.diagnostics)
}
fn unsupported_version(version: &str, span: Span) -> Diagnostic {
Diagnostic::error(format!("unsupported WDL version `{version}`"))
.with_label("this version of WDL is not supported", span)
.with_fix("either use a supported WDL version or configure a fallback version")
}
fn version_statement(
mut parser: Parser<'_, PreambleToken>,
fallback_version: Option<SupportedVersion>,
) -> (Parser<'_, PreambleToken>, Option<ParseDiagnostic>) {
let marker = parser.start();
parser.require(PreambleToken::VersionKeyword);
let mut parser: Parser<'_, VersionStatementToken> = parser.morph();
match parser.expect(VersionStatementToken::Version) {
Ok(span) => match SupportedVersion::from_str(parser.source(span)) {
Ok(version) => parser.set_version(version),
Err(_) => {
if let Some(fallback) = fallback_version {
parser.set_version(fallback);
} else {
let diagnostic = unsupported_version(parser.source(span), span).into();
parser.diagnostic(diagnostic);
}
}
},
Err(e) => {
marker.abandon(&mut parser);
return (parser.morph(), Some(e));
}
}
marker.complete(&mut parser, SyntaxKind::VersionStatementNode);
(parser.morph(), None)
}
#[cfg(feature = "unstable-python")]
mod python {
use pyo3::prelude::*;
use super::*;
use crate::lexer::Lexer;
use crate::parser::PyEvent;
#[pyfunction(name = "document")]
pub fn py_document(
source: &str,
fallback_version: Option<SupportedVersion>,
) -> (Vec<PyEvent>, Vec<Diagnostic>) {
let (events, diagnostics) = document(Parser::new(Lexer::new(source)), fallback_version);
let events = events.into_iter().map(PyEvent::from_event).collect();
(events, diagnostics)
}
}