use super::macros::expected;
use super::macros::expected_fn;
use crate::Diagnostic;
use crate::Span;
use crate::grammar::macros::expected_in;
use crate::lexer::TokenSet;
use crate::lexer::v1::BraceCommandToken;
use crate::lexer::v1::DQStringToken;
use crate::lexer::v1::HeredocToken;
use crate::lexer::v1::SQStringToken;
use crate::lexer::v1::Token;
use crate::parser;
use crate::parser::CompletedMarker;
use crate::parser::Event;
use crate::parser::Interpolator;
use crate::parser::Marker;
use crate::parser::ParserToken;
use crate::parser::expected_found;
use crate::parser::expected_one_of;
use crate::parser::unmatched;
use crate::parser::unterminated_braced_command;
use crate::parser::unterminated_heredoc;
use crate::parser::unterminated_string;
use crate::tree::SyntaxKind;
pub type Parser<'a> = parser::Parser<'a, Token>;
const TOP_EXPECTED_SET: TokenSet = TokenSet::new(&[
Token::ImportKeyword as u8,
Token::StructKeyword as u8,
Token::TaskKeyword as u8,
Token::WorkflowKeyword as u8,
]);
const TOP_EXPECTED_NAMES: &[&str] = &[
"import statement",
"struct definition",
"task definition",
"workflow definition",
];
const TOP_RECOVERY_SET: TokenSet = TOP_EXPECTED_SET;
const PRIMITIVE_TYPE_SET: TokenSet = TokenSet::new(&[
Token::BooleanTypeKeyword as u8,
Token::IntTypeKeyword as u8,
Token::FloatTypeKeyword as u8,
Token::StringTypeKeyword as u8,
Token::FileTypeKeyword as u8,
Token::DirectoryTypeKeyword as u8,
]);
const TYPE_EXPECTED_SET: TokenSet = PRIMITIVE_TYPE_SET.union(TokenSet::new(&[
Token::MapTypeKeyword as u8,
Token::ArrayTypeKeyword as u8,
Token::PairTypeKeyword as u8,
Token::ObjectTypeKeyword as u8,
Token::Ident as u8,
]));
const STRUCT_ITEM_RECOVERY_SET: TokenSet = TYPE_EXPECTED_SET.union(TokenSet::new(&[
Token::MetaKeyword as u8,
Token::ParameterMetaKeyword as u8,
Token::CloseBrace as u8,
]));
const INPUT_ITEM_RECOVERY_SET: TokenSet =
TYPE_EXPECTED_SET.union(TokenSet::new(&[Token::CloseBrace as u8]));
const OUTPUT_ITEM_RECOVERY_SET: TokenSet =
TYPE_EXPECTED_SET.union(TokenSet::new(&[Token::CloseBrace as u8]));
const RUNTIME_ITEM_RECOVERY_SET: TokenSet =
ANY_IDENT.union(TokenSet::new(&[Token::CloseBrace as u8]));
const REQUIREMENTS_ITEM_RECOVERY_SET: TokenSet =
ANY_IDENT.union(TokenSet::new(&[Token::CloseBrace as u8]));
const HINTS_ITEM_RECOVERY_SET: TokenSet =
ANY_IDENT.union(TokenSet::new(&[Token::CloseBrace as u8]));
const LITERAL_INPUT_ITEM_RECOVERY_SET: TokenSet =
ANY_IDENT.union(TokenSet::new(&[Token::CloseBrace as u8]));
const LITERAL_OUTPUT_ITEM_RECOVERY_SET: TokenSet =
ANY_IDENT.union(TokenSet::new(&[Token::CloseBrace as u8]));
const STRUCT_ITEM_EXPECTED_NAMES: &[&str] = &[
"metadata section",
"parameter metadata section",
"struct member declaration",
];
const TASK_ITEM_EXPECTED_SET: TokenSet = TYPE_EXPECTED_SET.union(TokenSet::new(&[
Token::InputKeyword as u8,
Token::CommandKeyword as u8,
Token::OutputKeyword as u8,
Token::RequirementsKeyword as u8,
Token::HintsKeyword as u8,
Token::RuntimeKeyword as u8,
Token::MetaKeyword as u8,
Token::ParameterMetaKeyword as u8,
]));
const TASK_ITEM_EXPECTED_NAMES: &[&str] = &[
"input section",
"command section",
"output section",
"runtime section",
"metadata section",
"parameter metadata section",
"private declaration",
];
const TASK_ITEM_RECOVERY_SET: TokenSet =
TASK_ITEM_EXPECTED_SET.union(TokenSet::new(&[Token::CloseBrace as u8]));
const WORKFLOW_ITEM_EXPECTED_SET: TokenSet = TYPE_EXPECTED_SET.union(TokenSet::new(&[
Token::InputKeyword as u8,
Token::OutputKeyword as u8,
Token::MetaKeyword as u8,
Token::ParameterMetaKeyword as u8,
Token::HintsKeyword as u8,
Token::IfKeyword as u8,
Token::ScatterKeyword as u8,
Token::CallKeyword as u8,
]));
const WORKFLOW_ITEM_EXPECTED_NAMES: &[&str] = &[
"input section",
"output section",
"runtime section",
"metadata section",
"parameter metadata section",
"conditional statement",
"scatter statement",
"call statement",
"private declaration",
];
const WORKFLOW_ITEM_RECOVERY_SET: TokenSet =
WORKFLOW_ITEM_EXPECTED_SET.union(TokenSet::new(&[Token::CloseBrace as u8]));
const WORKFLOW_STATEMENT_RECOVERY_SET: TokenSet = TokenSet::new(&[
Token::IfKeyword as u8,
Token::CallKeyword as u8,
Token::ScatterKeyword as u8,
Token::CloseBrace as u8,
]);
const CALL_INPUT_ITEM_RECOVERY_SET: TokenSet = ANY_IDENT.union(TokenSet::new(&[
Token::Comma as u8,
Token::CloseBrace as u8,
]));
const METADATA_VALUE_EXPECTED_SET: TokenSet = TokenSet::new(&[
Token::Minus as u8,
Token::Integer as u8,
Token::Float as u8,
Token::SingleQuote as u8,
Token::DoubleQuote as u8,
Token::OpenHeredoc as u8,
Token::TrueKeyword as u8,
Token::FalseKeyword as u8,
Token::OpenBrace as u8,
Token::OpenBracket as u8,
]);
const METADATA_VALUE_EXPECTED_NAMES: &[&str] = &[
"number",
"string",
"boolean",
"metadata object",
"metadata array",
"null",
];
const METADATA_SECTION_RECOVERY_SET: TokenSet =
ANY_IDENT.union(TokenSet::new(&[Token::CloseBrace as u8]));
const METADATA_OBJECT_RECOVERY_SET: TokenSet =
METADATA_SECTION_RECOVERY_SET.union(TokenSet::new(&[
Token::Comma as u8,
Token::CloseBrace as u8,
]));
const METADATA_ARRAY_RECOVERY_SET: TokenSet = METADATA_VALUE_EXPECTED_SET.union(TokenSet::new(&[
Token::Comma as u8,
Token::CloseBracket as u8,
]));
const WORKFLOW_HINT_VALUE_EXPECTED_NAMES: &[&str] = &[
"number",
"string",
"boolean",
"literal object",
"literal array",
];
const WORKFLOW_HINTS_OBJECT_RECOVERY_SET: TokenSet = METADATA_OBJECT_RECOVERY_SET;
const WORKFLOW_HINTS_ARRAY_RECOVERY_SET: TokenSet = METADATA_ARRAY_RECOVERY_SET;
const ATOM_EXPECTED_SET: TokenSet = ANY_IDENT.union(TokenSet::new(&[
Token::Integer as u8,
Token::Float as u8,
Token::TrueKeyword as u8,
Token::FalseKeyword as u8,
Token::SingleQuote as u8,
Token::DoubleQuote as u8,
Token::OpenHeredoc as u8,
Token::OpenBracket as u8,
Token::OpenBrace as u8,
Token::OpenParen as u8,
Token::ObjectKeyword as u8,
Token::IfKeyword as u8,
Token::NoneKeyword as u8,
Token::HintsKeyword as u8,
Token::InputKeyword as u8,
Token::OutputKeyword as u8,
]));
const PREFIX_OPERATOR_EXPECTED_SET: TokenSet =
TokenSet::new(&[Token::Exclamation as u8, Token::Minus as u8]);
const INFIX_OPERATOR_EXPECTED_SET: TokenSet = TokenSet::new(&[
Token::LogicalOr as u8,
Token::LogicalAnd as u8,
Token::Plus as u8,
Token::Minus as u8,
Token::Asterisk as u8,
Token::Exponentiation as u8,
Token::Slash as u8,
Token::Percent as u8,
Token::Equal as u8,
Token::NotEqual as u8,
Token::Less as u8,
Token::LessEqual as u8,
Token::Greater as u8,
Token::GreaterEqual as u8,
]);
const POSTFIX_OPERATOR_EXPECTED_SET: TokenSet = TokenSet::new(&[
Token::OpenParen as u8,
Token::OpenBracket as u8,
Token::Dot as u8,
]);
const EXPR_RECOVERY_SET: TokenSet = ATOM_EXPECTED_SET.union(PREFIX_OPERATOR_EXPECTED_SET);
const MAP_RECOVERY_SET: TokenSet = TokenSet::new(&[Token::Comma as u8, Token::CloseBrace as u8]);
const LITERAL_OBJECT_RECOVERY_SET: TokenSet =
TokenSet::new(&[Token::Comma as u8, Token::CloseBrace as u8]);
const ANY_IDENT: TokenSet = TokenSet::new(&[
Token::Ident as u8,
Token::ArrayTypeKeyword as u8,
Token::BooleanTypeKeyword as u8,
Token::DirectoryTypeKeyword as u8,
Token::FileTypeKeyword as u8,
Token::FloatTypeKeyword as u8,
Token::IntTypeKeyword as u8,
Token::MapTypeKeyword as u8,
Token::ObjectTypeKeyword as u8,
Token::PairTypeKeyword as u8,
Token::StringTypeKeyword as u8,
Token::AfterKeyword as u8,
Token::AliasKeyword as u8,
Token::AsKeyword as u8,
Token::CallKeyword as u8,
Token::CommandKeyword as u8,
Token::ElseKeyword as u8,
Token::EnvKeyword as u8,
Token::FalseKeyword as u8,
Token::HintsKeyword as u8,
Token::IfKeyword as u8,
Token::InKeyword as u8,
Token::ImportKeyword as u8,
Token::InputKeyword as u8,
Token::MetaKeyword as u8,
Token::NoneKeyword as u8,
Token::NullKeyword as u8,
Token::ObjectKeyword as u8,
Token::OutputKeyword as u8,
Token::ParameterMetaKeyword as u8,
Token::RequirementsKeyword as u8,
Token::HintsKeyword as u8,
Token::RuntimeKeyword as u8,
Token::ScatterKeyword as u8,
Token::StructKeyword as u8,
Token::TaskKeyword as u8,
Token::ThenKeyword as u8,
Token::TrueKeyword as u8,
Token::VersionKeyword as u8,
Token::WorkflowKeyword as u8,
]);
macro_rules! braced_items {
($parser:ident, $marker:ident, $delimiter:expr_2021, $recovery:expr_2021, $cb:expr_2021) => {
if let Err(e) = $parser.matching_delimited(
Token::OpenBrace,
Token::CloseBrace,
$delimiter,
$recovery,
$cb,
) {
return Err(($marker, e));
}
};
}
macro_rules! bracketed_items {
($parser:ident, $marker:ident, $delimiter:expr_2021, $recovery:expr_2021, $cb:expr_2021) => {
if let Err(e) = $parser.matching_delimited(
Token::OpenBracket,
Token::CloseBracket,
$delimiter,
$recovery,
$cb,
) {
return Err(($marker, e));
}
};
}
macro_rules! paren_items {
($parser:ident, $marker:ident, $delimiter:expr_2021, $recovery:expr_2021, $cb:expr_2021) => {
if let Err(e) = $parser.matching_delimited(
Token::OpenParen,
Token::CloseParen,
$delimiter,
$recovery,
$cb,
) {
return Err(($marker, e));
}
};
}
macro_rules! bracketed {
($parser:ident, $marker:ident, $cb:expr_2021) => {
if let Err(e) = $parser.matching(Token::OpenBracket, Token::CloseBracket, false, $cb) {
return Err(($marker, e));
}
};
}
macro_rules! paren {
($parser:ident, $marker:ident, $cb:expr_2021) => {
if let Err(e) = $parser.matching(Token::OpenParen, Token::CloseParen, false, $cb) {
return Err(($marker, e));
}
};
}
pub fn items(parser: &mut Parser<'_>) {
parser.push_recovery_set(TOP_RECOVERY_SET);
while parser.peek().is_some() {
let marker = parser.start();
if let Err((marker, e)) = item(parser, marker) {
parser.recover(e);
marker.abandon(parser);
}
}
parser.pop_recovery_set();
assert!(parser.next().is_none(), "parser is not finished");
}
fn item(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
match parser.peek() {
Some((Token::ImportKeyword, _)) => import_statement(parser, marker),
Some((Token::StructKeyword, _)) => struct_definition(parser, marker),
Some((Token::TaskKeyword, _)) => task_definition(parser, marker),
Some((Token::WorkflowKeyword, _)) => workflow_definition(parser, marker),
found => {
let (found, span) = found
.map(|(t, s)| (Some(t.describe()), s))
.unwrap_or_else(|| (None, parser.span()));
Err((marker, expected_one_of(TOP_EXPECTED_NAMES, found, span)))
}
}
}
fn import_statement(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::ImportKeyword);
expected_fn!(parser, marker, string);
if parser.next_if(Token::AsKeyword) {
expected!(parser, marker, Token::Ident, "import namespace");
}
while let Some((Token::AliasKeyword, _)) = parser.peek() {
expected_fn!(parser, marker, import_alias);
}
marker.complete(parser, SyntaxKind::ImportStatementNode);
Ok(())
}
fn import_alias(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::AliasKeyword);
expected!(parser, marker, Token::Ident, "source type name");
expected!(parser, marker, Token::AsKeyword);
expected!(parser, marker, Token::Ident, "target type name");
marker.complete(parser, SyntaxKind::ImportAliasNode);
Ok(())
}
fn struct_definition(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::StructKeyword);
expected!(parser, marker, Token::Ident, "struct name");
braced_items!(parser, marker, None, STRUCT_ITEM_RECOVERY_SET, struct_item);
marker.complete(parser, SyntaxKind::StructDefinitionNode);
Ok(())
}
fn struct_item(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
match parser.peek() {
Some((Token::MetaKeyword, _)) => metadata_section(parser, marker),
Some((Token::ParameterMetaKeyword, _)) => parameter_metadata_section(parser, marker),
Some((t, _)) if TYPE_EXPECTED_SET.contains(t.into_raw()) => {
struct_member_decl(parser, marker)
}
found => {
let (found, span) = found
.map(|(t, s)| (Some(t.describe()), s))
.unwrap_or_else(|| (None, parser.span()));
Err((
marker,
expected_one_of(STRUCT_ITEM_EXPECTED_NAMES, found, span),
))
}
}
}
fn struct_member_decl(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
expected_fn!(parser, marker, ty);
expected_in!(parser, marker, ANY_IDENT, "struct member name");
parser.update_last_token_kind(SyntaxKind::Ident);
marker.complete(parser, SyntaxKind::UnboundDeclNode);
Ok(())
}
fn task_definition(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::TaskKeyword);
expected!(parser, marker, Token::Ident, "task name");
braced_items!(parser, marker, None, TASK_ITEM_RECOVERY_SET, task_item);
marker.complete(parser, SyntaxKind::TaskDefinitionNode);
Ok(())
}
fn workflow_definition(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::WorkflowKeyword);
expected!(parser, marker, Token::Ident, "workflow name");
braced_items!(
parser,
marker,
None,
WORKFLOW_ITEM_RECOVERY_SET,
workflow_item
);
marker.complete(parser, SyntaxKind::WorkflowDefinitionNode);
Ok(())
}
fn ty(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
match parser.peek() {
Some((Token::MapTypeKeyword, _)) => map_type(parser, marker),
Some((Token::ArrayTypeKeyword, _)) => array_type(parser, marker),
Some((Token::PairTypeKeyword, _)) => pair_type(parser, marker),
Some((Token::ObjectTypeKeyword, _)) => object_type(parser, marker),
Some((Token::Ident, _)) => type_ref(parser, marker),
Some((t, _)) if PRIMITIVE_TYPE_SET.contains(t.into_raw()) => primitive_type(parser, marker),
found => {
let (found, span) = found
.map(|(t, s)| (Some(t.describe()), s))
.unwrap_or_else(|| (None, parser.span()));
Err((marker, expected_found("type", found, span)))
}
}
}
fn map_type(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
fn parse(parser: &mut Parser<'_>, _: Span) -> Result<(), Diagnostic> {
expected_fn!(parser, primitive_type);
parser.expect(Token::Comma)?;
expected_fn!(parser, ty);
Ok(())
}
parser.require(Token::MapTypeKeyword);
bracketed!(parser, marker, parse);
parser.next_if(Token::QuestionMark);
marker.complete(parser, SyntaxKind::MapTypeNode);
Ok(())
}
fn array_type(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
fn parse(parser: &mut Parser<'_>, _: Span) -> Result<(), Diagnostic> {
expected_fn!(parser, ty);
Ok(())
}
parser.require(Token::ArrayTypeKeyword);
bracketed!(parser, marker, parse);
parser.next_if(Token::Plus);
parser.next_if(Token::QuestionMark);
marker.complete(parser, SyntaxKind::ArrayTypeNode);
Ok(())
}
fn pair_type(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
fn parse(parser: &mut Parser<'_>, _: Span) -> Result<(), Diagnostic> {
expected_fn!(parser, ty);
parser.expect(Token::Comma)?;
expected_fn!(parser, ty);
Ok(())
}
parser.require(Token::PairTypeKeyword);
bracketed!(parser, marker, parse);
parser.next_if(Token::QuestionMark);
marker.complete(parser, SyntaxKind::PairTypeNode);
Ok(())
}
fn object_type(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::ObjectTypeKeyword);
parser.next_if(Token::QuestionMark);
marker.complete(parser, SyntaxKind::ObjectTypeNode);
Ok(())
}
fn type_ref(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::Ident);
parser.next_if(Token::QuestionMark);
marker.complete(parser, SyntaxKind::TypeRefNode);
Ok(())
}
fn primitive_type(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
expected_in!(
parser,
marker,
PRIMITIVE_TYPE_SET,
"Boolean",
"Directory",
"File",
"Float",
"Int",
"String",
);
parser.next_if(Token::QuestionMark);
marker.complete(parser, SyntaxKind::PrimitiveTypeNode);
Ok(())
}
fn task_item(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
match parser.peek() {
Some((Token::InputKeyword, _)) => input_section(parser, marker, true),
Some((Token::CommandKeyword, _)) => command_section(parser, marker),
Some((Token::OutputKeyword, _)) => output_section(parser, marker),
Some((Token::RuntimeKeyword, _)) => runtime_section(parser, marker),
Some((Token::RequirementsKeyword, _)) => requirements_section(parser, marker),
Some((Token::HintsKeyword, _)) => task_hints_section(parser, marker),
Some((Token::MetaKeyword, _)) => metadata_section(parser, marker),
Some((Token::ParameterMetaKeyword, _)) => parameter_metadata_section(parser, marker),
Some((t, _)) if t == Token::EnvKeyword || TYPE_EXPECTED_SET.contains(t.into_raw()) => {
bound_decl(parser, marker, false, true)
}
found => {
let (found, span) = found
.map(|(t, s)| (Some(t.describe()), s))
.unwrap_or_else(|| (None, parser.span()));
Err((
marker,
expected_one_of(TASK_ITEM_EXPECTED_NAMES, found, span),
))
}
}
}
fn workflow_item(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
match parser.peek() {
Some((Token::InputKeyword, _)) => input_section(parser, marker, false),
Some((Token::OutputKeyword, _)) => output_section(parser, marker),
Some((Token::MetaKeyword, _)) => metadata_section(parser, marker),
Some((Token::ParameterMetaKeyword, _)) => parameter_metadata_section(parser, marker),
Some((Token::HintsKeyword, _)) => workflow_hints_section(parser, marker),
Some((Token::IfKeyword, _)) => conditional_statement(parser, marker),
Some((Token::ScatterKeyword, _)) => scatter_statement(parser, marker),
Some((Token::CallKeyword, _)) => call_statement(parser, marker),
Some((t, _)) if TYPE_EXPECTED_SET.contains(t.into_raw()) => {
bound_decl(parser, marker, false, false)
}
found => {
let (found, span) = found
.map(|(t, s)| (Some(t.describe()), s))
.unwrap_or_else(|| (None, parser.span()));
Err((
marker,
expected_one_of(WORKFLOW_ITEM_EXPECTED_NAMES, found, span),
))
}
}
}
fn workflow_statement(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
match parser.peek() {
Some((Token::IfKeyword, _)) => conditional_statement(parser, marker),
Some((Token::ScatterKeyword, _)) => scatter_statement(parser, marker),
Some((Token::CallKeyword, _)) => call_statement(parser, marker),
Some((t, _)) if TYPE_EXPECTED_SET.contains(t.into_raw()) => {
bound_decl(parser, marker, false, false)
}
found => {
let (found, span) = found
.map(|(t, s)| (Some(t.describe()), s))
.unwrap_or_else(|| (None, parser.span()));
Err((marker, expected_found("workflow statement", found, span)))
}
}
}
fn input_section(
parser: &mut Parser<'_>,
marker: Marker,
allow_env: bool,
) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::InputKeyword);
braced_items!(
parser,
marker,
None,
INPUT_ITEM_RECOVERY_SET,
|parser, marker| input_item(parser, marker, allow_env)
);
marker.complete(parser, SyntaxKind::InputSectionNode);
Ok(())
}
fn input_item(
parser: &mut Parser<'_>,
marker: Marker,
allow_env: bool,
) -> Result<(), (Marker, Diagnostic)> {
if allow_env {
parser.next_if(Token::EnvKeyword);
}
expected_fn!(parser, marker, ty);
expected_in!(parser, marker, ANY_IDENT, "input name");
parser.update_last_token_kind(SyntaxKind::Ident);
let kind = if parser.next_if(Token::Assignment) {
expected_fn!(parser, marker, expr);
SyntaxKind::BoundDeclNode
} else {
SyntaxKind::UnboundDeclNode
};
marker.complete(parser, kind);
Ok(())
}
fn command_section(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::CommandKeyword);
if let Some((Token::OpenBrace, _)) = parser.peek() {
let start = parser.next().expect("should have token").1;
if let Err(e) =
parser.interpolate(|interpolator| interpolate_brace_command(start, interpolator))
{
return Err((marker, e));
}
} else {
let start = match parser.expect(Token::OpenHeredoc) {
Ok(span) => span,
Err(e) => return Err((marker, e)),
};
if let Err(e) = parser.interpolate(|interpolator| {
interpolate_heredoc(start, HeredocContext::Command, interpolator)
}) {
return Err((marker, e));
}
}
marker.complete(parser, SyntaxKind::CommandSectionNode);
Ok(())
}
fn interpolate_brace_command(
start: Span,
mut interpolator: Interpolator<'_, BraceCommandToken>,
) -> (Parser<'_>, Result<(), Diagnostic>) {
let mut text = None;
let mut end = None;
while let Some((Ok(token), span)) = interpolator.next() {
match token {
BraceCommandToken::PlaceholderStart => {
if let Some(span) = text.take() {
interpolator.event(Event::Token {
kind: SyntaxKind::LiteralCommandText,
span,
})
}
let marker = interpolator.start();
interpolator.event(Event::Token {
kind: SyntaxKind::PlaceholderOpen,
span,
});
let mut parser = interpolator.into_parser();
if let Err((marker, e)) = placeholder_expr(&mut parser, marker, span) {
marker.abandon(&mut parser);
parser.recover_with_set(e, TokenSet::new(&[Token::CloseBrace as u8]));
parser.next_if(Token::CloseBrace);
}
interpolator = parser.into_interpolator();
}
BraceCommandToken::Escape
| BraceCommandToken::Text
| BraceCommandToken::DollarSign
| BraceCommandToken::Tilde => {
text = match text {
Some(prev) => Some(Span::new(prev.start(), prev.len() + span.len())),
None => Some(span),
};
}
BraceCommandToken::End => {
end = Some(span);
break;
}
}
}
if let Some(span) = text.take() {
interpolator.event(Event::Token {
kind: SyntaxKind::LiteralCommandText,
span,
})
}
match end {
Some(span) => {
interpolator.event(Event::Token {
kind: SyntaxKind::CloseBrace,
span,
});
(interpolator.into_parser(), Ok(()))
}
None => {
(
interpolator.into_parser(),
Err(unterminated_braced_command(
Token::OpenBrace.describe(),
start,
)),
)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum HeredocContext {
Command,
String,
MetadataString,
}
pub(crate) fn interpolate_heredoc(
start: Span,
context: HeredocContext,
mut interpolator: Interpolator<'_, HeredocToken>,
) -> (Parser<'_>, Result<(), Diagnostic>) {
fn interpolate<'a>(
mut interpolator: Interpolator<'a, HeredocToken>,
open: Span,
text: &mut Option<Span>,
context: HeredocContext,
) -> Interpolator<'a, HeredocToken> {
if let Some(span) = text.take() {
interpolator.event(Event::Token {
kind: if context == HeredocContext::Command {
SyntaxKind::LiteralCommandText
} else {
SyntaxKind::LiteralStringText
},
span,
})
}
let marker = interpolator.start();
interpolator.event(Event::Token {
kind: SyntaxKind::PlaceholderOpen,
span: open,
});
let mut parser = interpolator.into_parser();
if let Err((marker, e)) = placeholder_expr(&mut parser, marker, open) {
marker.abandon(&mut parser);
parser.recover_with_set(
e,
TokenSet::new(&[Token::CloseBrace as u8, Token::CloseHeredoc as u8]),
);
parser.next_if(Token::CloseBrace);
}
parser.into_interpolator()
}
let mut text = None;
let mut end = None;
while let Some((Ok(token), span)) = interpolator.next() {
match token {
HeredocToken::TildePlaceholderStart
if matches!(context, HeredocContext::Command | HeredocContext::String) =>
{
interpolator = interpolate(interpolator, span, &mut text, context);
}
HeredocToken::DollarPlaceholderStart if context == HeredocContext::String => {
interpolator = interpolate(interpolator, span, &mut text, context);
}
HeredocToken::Escape
| HeredocToken::Text
| HeredocToken::SingleCloseAngle
| HeredocToken::DoubleCloseAngle
| HeredocToken::Tilde
| HeredocToken::DollarSign
| HeredocToken::TildePlaceholderStart
| HeredocToken::DollarPlaceholderStart => {
text = match text {
Some(prev) => Some(Span::new(prev.start(), prev.len() + span.len())),
None => Some(span),
};
}
HeredocToken::End => {
end = Some(span);
break;
}
}
}
if let Some(span) = text.take() {
interpolator.event(Event::Token {
kind: if context == HeredocContext::Command {
SyntaxKind::LiteralCommandText
} else {
SyntaxKind::LiteralStringText
},
span,
})
}
match end {
Some(span) => {
interpolator.event(Event::Token {
kind: SyntaxKind::CloseHeredoc,
span,
});
(interpolator.into_parser(), Ok(()))
}
None => {
let span = Span::new(interpolator.span().start(), 0);
interpolator.event(Event::Token {
kind: SyntaxKind::CloseHeredoc,
span,
});
(
interpolator.into_parser(),
Err(unterminated_heredoc(
Token::OpenHeredoc.describe(),
start,
context == HeredocContext::Command,
)),
)
}
}
}
fn output_section(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::OutputKeyword);
braced_items!(
parser,
marker,
None,
OUTPUT_ITEM_RECOVERY_SET,
|parser, marker| bound_decl(parser, marker, true, false)
);
marker.complete(parser, SyntaxKind::OutputSectionNode);
Ok(())
}
fn runtime_section(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::RuntimeKeyword);
braced_items!(
parser,
marker,
None,
RUNTIME_ITEM_RECOVERY_SET,
runtime_item
);
marker.complete(parser, SyntaxKind::RuntimeSectionNode);
Ok(())
}
fn runtime_item(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
expected_in!(parser, marker, ANY_IDENT, "runtime key");
parser.update_last_token_kind(SyntaxKind::Ident);
expected!(parser, marker, Token::Colon);
expected_fn!(parser, marker, expr);
marker.complete(parser, SyntaxKind::RuntimeItemNode);
Ok(())
}
fn requirements_section(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::RequirementsKeyword);
braced_items!(
parser,
marker,
None,
REQUIREMENTS_ITEM_RECOVERY_SET,
requirements_item
);
marker.complete(parser, SyntaxKind::RequirementsSectionNode);
Ok(())
}
fn requirements_item(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
expected_in!(parser, marker, ANY_IDENT, "requirements key");
parser.update_last_token_kind(SyntaxKind::Ident);
expected!(parser, marker, Token::Colon);
expected_fn!(parser, marker, expr);
marker.complete(parser, SyntaxKind::RequirementsItemNode);
Ok(())
}
fn task_hints_section(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::HintsKeyword);
braced_items!(
parser,
marker,
None,
HINTS_ITEM_RECOVERY_SET,
task_hints_item
);
marker.complete(parser, SyntaxKind::TaskHintsSectionNode);
Ok(())
}
fn workflow_hints_section(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::HintsKeyword);
braced_items!(
parser,
marker,
None,
HINTS_ITEM_RECOVERY_SET,
workflow_hints_item
);
marker.complete(parser, SyntaxKind::WorkflowHintsSectionNode);
Ok(())
}
fn task_hints_item(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
expected_in!(parser, marker, ANY_IDENT, "hint key");
parser.update_last_token_kind(SyntaxKind::Ident);
expected!(parser, marker, Token::Colon);
expected_fn!(parser, marker, expr);
marker.complete(parser, SyntaxKind::TaskHintsItemNode);
Ok(())
}
fn workflow_hints_item(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<(), (Marker, Diagnostic)> {
expected_in!(parser, marker, ANY_IDENT, "hint key");
parser.update_last_token_kind(SyntaxKind::Ident);
expected!(parser, marker, Token::Colon);
expected_fn!(parser, marker, workflow_hints_value);
marker.complete(parser, SyntaxKind::WorkflowHintsItemNode);
Ok(())
}
fn workflow_hints_value(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<(), (Marker, Diagnostic)> {
match parser.peek() {
Some((Token::Minus, _)) | Some((Token::Integer, _)) | Some((Token::Float, _)) => {
number(parser, marker, true)?;
Ok(())
}
Some((Token::SingleQuote, _)) => {
single_quote_string(parser, marker, false)?;
Ok(())
}
Some((Token::DoubleQuote, _)) => {
double_quote_string(parser, marker, false)?;
Ok(())
}
Some((Token::OpenHeredoc, _)) => {
multiline_string(parser, marker, false)?;
Ok(())
}
Some((Token::TrueKeyword, _)) | Some((Token::FalseKeyword, _)) => {
boolean(parser, marker)?;
Ok(())
}
Some((Token::OpenBrace, _)) => workflow_hints_object(parser, marker),
Some((Token::OpenBracket, _)) => workflow_hints_array(parser, marker),
found => {
let (found, span) = found
.map(|(t, s)| (Some(t.describe()), s))
.unwrap_or_else(|| (None, parser.span()));
Err((
marker,
expected_one_of(WORKFLOW_HINT_VALUE_EXPECTED_NAMES, found, span),
))
}
}
}
fn workflow_hints_object(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<(), (Marker, Diagnostic)> {
braced_items!(
parser,
marker,
Some(Token::Comma),
WORKFLOW_HINTS_OBJECT_RECOVERY_SET,
workflow_hints_object_item
);
marker.complete(parser, SyntaxKind::WorkflowHintsObjectNode);
Ok(())
}
fn workflow_hints_object_item(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<(), (Marker, Diagnostic)> {
expected_in!(parser, marker, ANY_IDENT, "object key");
parser.update_last_token_kind(SyntaxKind::Ident);
expected!(parser, marker, Token::Colon);
expected_fn!(parser, marker, workflow_hints_value);
marker.complete(parser, SyntaxKind::WorkflowHintsObjectItemNode);
Ok(())
}
fn workflow_hints_array(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<(), (Marker, Diagnostic)> {
bracketed_items!(
parser,
marker,
Some(Token::Comma),
WORKFLOW_HINTS_ARRAY_RECOVERY_SET,
workflow_hints_value
);
marker.complete(parser, SyntaxKind::WorkflowHintsArrayNode);
Ok(())
}
fn metadata_section(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::MetaKeyword);
braced_items!(
parser,
marker,
None,
METADATA_SECTION_RECOVERY_SET,
metadata_object_item
);
marker.complete(parser, SyntaxKind::MetadataSectionNode);
Ok(())
}
fn metadata_object_item(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<(), (Marker, Diagnostic)> {
expected_in!(parser, marker, ANY_IDENT, "metadata key");
parser.update_last_token_kind(SyntaxKind::Ident);
expected!(parser, marker, Token::Colon);
expected_fn!(parser, marker, metadata_value);
marker.complete(parser, SyntaxKind::MetadataObjectItemNode);
Ok(())
}
fn metadata_value(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
match parser.peek() {
Some((Token::Minus, _)) | Some((Token::Integer, _)) | Some((Token::Float, _)) => {
number(parser, marker, true)?;
Ok(())
}
Some((Token::SingleQuote, _)) => {
single_quote_string(parser, marker, false)?;
Ok(())
}
Some((Token::DoubleQuote, _)) => {
double_quote_string(parser, marker, false)?;
Ok(())
}
Some((Token::OpenHeredoc, _)) => {
multiline_string(parser, marker, false)?;
Ok(())
}
Some((Token::TrueKeyword, _)) | Some((Token::FalseKeyword, _)) => {
boolean(parser, marker)?;
Ok(())
}
Some((Token::NullKeyword, _)) => null(parser, marker),
Some((Token::OpenBrace, _)) => metadata_object(parser, marker),
Some((Token::OpenBracket, _)) => metadata_array(parser, marker),
found => {
let (found, span) = found
.map(|(t, s)| (Some(t.describe()), s))
.unwrap_or_else(|| (None, parser.span()));
Err((
marker,
expected_one_of(METADATA_VALUE_EXPECTED_NAMES, found, span),
))
}
}
}
fn none(parser: &mut Parser<'_>, marker: Marker) -> Result<CompletedMarker, (Marker, Diagnostic)> {
parser.require(Token::NoneKeyword);
Ok(marker.complete(parser, SyntaxKind::LiteralNoneNode))
}
fn number(
parser: &mut Parser<'_>,
marker: Marker,
accept_minus: bool,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
if accept_minus {
parser.next_if(Token::Minus);
}
let kind = match parser.expect_in(
TokenSet::new(&[Token::Integer as u8, Token::Float as u8]),
&["number"],
) {
Ok((Token::Integer, _)) => SyntaxKind::LiteralIntegerNode,
Ok((Token::Float, _)) => SyntaxKind::LiteralFloatNode,
Ok(_) => unreachable!(),
Err(e) => return Err((marker, e)),
};
Ok(marker.complete(parser, kind))
}
fn placeholder_options(
parser: &mut Parser<'_>,
mut marker: Marker,
) -> Result<(), (Marker, Diagnostic)> {
loop {
if let Some(peek) = parser.peek2() {
match (peek.first, peek.second) {
((Token::Ident, span), (Token::Assignment, _)) => {
let kind = match parser.source(span) {
"sep" => SyntaxKind::PlaceholderSepOptionNode,
"default" => SyntaxKind::PlaceholderDefaultOptionNode,
_ => {
marker.abandon(parser);
return Ok(());
}
};
parser.next();
expected!(parser, marker, Token::Assignment);
expected_fn!(parser, marker, string);
marker.complete(parser, kind);
marker = parser.start();
continue;
}
((t @ Token::TrueKeyword, _), (Token::Assignment, _))
| ((t @ Token::FalseKeyword, _), (Token::Assignment, _)) => {
parser.next();
expected!(parser, marker, Token::Assignment);
expected_fn!(parser, marker, string);
expected!(
parser,
marker,
if t == Token::TrueKeyword {
Token::FalseKeyword
} else {
Token::TrueKeyword
}
);
expected!(parser, marker, Token::Assignment);
expected_fn!(parser, marker, string);
marker.complete(parser, SyntaxKind::PlaceholderTrueFalseOptionNode);
marker = parser.start();
continue;
}
_ => {
}
}
}
marker.abandon(parser);
return Ok(());
}
}
fn placeholder_expr(
parser: &mut Parser<'_>,
marker: Marker,
open_span: Span,
) -> Result<(), (Marker, Diagnostic)> {
expected_fn!(parser, marker, placeholder_options);
expected_fn!(parser, marker, expr);
match parser.peek() {
Some((Token::CloseBrace, _)) => {
parser.next();
marker.complete(parser, SyntaxKind::PlaceholderNode);
Ok(())
}
found => {
let (found, span) = found
.map(|(t, s)| (t.describe(), s))
.unwrap_or_else(|| ("end of input", parser.span()));
Err((
marker,
unmatched(
"placeholder start",
open_span,
Token::CloseBrace.describe(),
found,
span,
),
))
}
}
}
pub(crate) fn interpolate_sq_string(
start: Span,
allow_placeholders: bool,
mut interpolator: Interpolator<'_, SQStringToken>,
) -> (Parser<'_>, Result<(), Diagnostic>) {
let mut text = None;
let mut end = None;
while let Some((Ok(token), span)) = interpolator.next() {
match token {
SQStringToken::PlaceholderStart if allow_placeholders => {
if let Some(span) = text.take() {
interpolator.event(Event::Token {
kind: SyntaxKind::LiteralStringText,
span,
})
}
let marker = interpolator.start();
interpolator.event(Event::Token {
kind: SyntaxKind::PlaceholderOpen,
span,
});
let mut parser = interpolator.into_parser();
if let Err((marker, e)) = placeholder_expr(&mut parser, marker, span) {
marker.abandon(&mut parser);
parser.recover_with_set(
e,
TokenSet::new(&[Token::CloseBrace as u8, Token::SingleQuote as u8]),
);
parser.next_if(Token::CloseBrace);
}
interpolator = parser.into_interpolator();
}
SQStringToken::PlaceholderStart
| SQStringToken::Escape
| SQStringToken::Text
| SQStringToken::DollarSign
| SQStringToken::Tilde => {
text = match text {
Some(prev) => Some(Span::new(prev.start(), prev.len() + span.len())),
None => Some(span),
};
}
SQStringToken::End => {
end = Some(span);
break;
}
}
}
if let Some(span) = text.take() {
interpolator.event(Event::Token {
kind: SyntaxKind::LiteralStringText,
span,
})
}
match end {
Some(span) => {
interpolator.event(Event::Token {
kind: SyntaxKind::SingleQuote,
span,
});
(interpolator.into_parser(), Ok(()))
}
None => {
let span = Span::new(interpolator.span().start(), 0);
interpolator.event(Event::Token {
kind: SyntaxKind::SingleQuote,
span,
});
(interpolator.into_parser(), Err(unterminated_string(start)))
}
}
}
fn string(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
match parser.peek() {
Some((Token::SingleQuote, _)) => single_quote_string(parser, marker, true),
Some((Token::DoubleQuote, _)) => double_quote_string(parser, marker, true),
Some((Token::OpenHeredoc, _)) => multiline_string(parser, marker, true),
found => {
let (found, span) = found
.map(|(t, s)| (Some(t.describe()), s))
.unwrap_or_else(|| (None, parser.span()));
Err((marker, expected_found("string", found, span)))
}
}
}
fn single_quote_string(
parser: &mut Parser<'_>,
marker: Marker,
allow_placeholders: bool,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
let start = parser.require(Token::SingleQuote);
if let Err(e) = parser.interpolate(|i| interpolate_sq_string(start, allow_placeholders, i)) {
return Err((marker, e));
}
Ok(marker.complete(parser, SyntaxKind::LiteralStringNode))
}
pub(crate) fn interpolate_dq_string(
start: Span,
allow_placeholders: bool,
mut interpolator: Interpolator<'_, DQStringToken>,
) -> (Parser<'_>, Result<(), Diagnostic>) {
let mut text = None;
let mut end = None;
while let Some((Ok(token), span)) = interpolator.next() {
match token {
DQStringToken::PlaceholderStart if allow_placeholders => {
if let Some(span) = text.take() {
interpolator.event(Event::Token {
kind: SyntaxKind::LiteralStringText,
span,
})
}
let marker = interpolator.start();
interpolator.event(Event::Token {
kind: SyntaxKind::PlaceholderOpen,
span,
});
let mut parser = interpolator.into_parser();
if let Err((marker, e)) = placeholder_expr(&mut parser, marker, span) {
marker.abandon(&mut parser);
parser.recover_with_set(
e,
TokenSet::new(&[Token::CloseBrace as u8, Token::DoubleQuote as u8]),
);
parser.next_if(Token::CloseBrace);
}
interpolator = parser.into_interpolator();
}
DQStringToken::PlaceholderStart
| DQStringToken::Escape
| DQStringToken::Text
| DQStringToken::DollarSign
| DQStringToken::Tilde => {
text = match text {
Some(prev) => Some(Span::new(prev.start(), prev.len() + span.len())),
None => Some(span),
};
}
DQStringToken::End => {
end = Some(span);
break;
}
}
}
if let Some(span) = text.take() {
interpolator.event(Event::Token {
kind: SyntaxKind::LiteralStringText,
span,
})
}
match end {
Some(span) => {
interpolator.event(Event::Token {
kind: SyntaxKind::DoubleQuote,
span,
});
(interpolator.into_parser(), Ok(()))
}
None => {
let span = Span::new(interpolator.span().start(), 0);
interpolator.event(Event::Token {
kind: SyntaxKind::DoubleQuote,
span,
});
(interpolator.into_parser(), Err(unterminated_string(start)))
}
}
}
fn double_quote_string(
parser: &mut Parser<'_>,
marker: Marker,
allow_placeholders: bool,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
let start = parser.require(Token::DoubleQuote);
if let Err(e) = parser.interpolate(|i| interpolate_dq_string(start, allow_placeholders, i)) {
return Err((marker, e));
}
Ok(marker.complete(parser, SyntaxKind::LiteralStringNode))
}
fn multiline_string(
parser: &mut Parser<'_>,
marker: Marker,
allow_placeholders: bool,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
let start = parser.require(Token::OpenHeredoc);
if let Err(e) = parser.interpolate(|i| {
interpolate_heredoc(
start,
if allow_placeholders {
HeredocContext::String
} else {
HeredocContext::MetadataString
},
i,
)
}) {
return Err((marker, e));
}
Ok(marker.complete(parser, SyntaxKind::LiteralStringNode))
}
fn boolean(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
parser.require_in(TokenSet::new(&[
Token::TrueKeyword as u8,
Token::FalseKeyword as u8,
]));
Ok(marker.complete(parser, SyntaxKind::LiteralBooleanNode))
}
fn null(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::NullKeyword);
marker.complete(parser, SyntaxKind::LiteralNullNode);
Ok(())
}
fn metadata_object(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
braced_items!(
parser,
marker,
Some(Token::Comma),
METADATA_OBJECT_RECOVERY_SET,
metadata_object_item
);
marker.complete(parser, SyntaxKind::MetadataObjectNode);
Ok(())
}
fn metadata_array(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
bracketed_items!(
parser,
marker,
Some(Token::Comma),
METADATA_ARRAY_RECOVERY_SET,
metadata_value
);
marker.complete(parser, SyntaxKind::MetadataArrayNode);
Ok(())
}
fn parameter_metadata_section(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::ParameterMetaKeyword);
braced_items!(
parser,
marker,
None,
METADATA_SECTION_RECOVERY_SET,
metadata_object_item
);
marker.complete(parser, SyntaxKind::ParameterMetadataSectionNode);
Ok(())
}
fn bound_decl(
parser: &mut Parser<'_>,
marker: Marker,
output: bool,
allow_env: bool,
) -> Result<(), (Marker, Diagnostic)> {
if allow_env {
parser.next_if(Token::EnvKeyword);
}
expected_fn!(parser, marker, ty);
if output {
expected_in!(parser, marker, ANY_IDENT, "output name");
parser.update_last_token_kind(SyntaxKind::Ident);
} else {
expected!(parser, marker, Token::Ident, "declaration name");
}
expected!(parser, marker, Token::Assignment);
expected_fn!(parser, marker, expr);
marker.complete(parser, SyntaxKind::BoundDeclNode);
Ok(())
}
fn conditional_statement(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::IfKeyword);
paren!(parser, marker, |parser, _| {
expected_fn!(parser, expr);
Ok(())
});
braced_items!(
parser,
marker,
None,
WORKFLOW_STATEMENT_RECOVERY_SET,
workflow_statement
);
marker.complete(parser, SyntaxKind::ConditionalStatementNode);
Ok(())
}
fn scatter_statement(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::ScatterKeyword);
paren!(parser, marker, |parser, _| {
parser.expect_with_name(Token::Ident, "scatter variable name")?;
parser.expect(Token::InKeyword)?;
expected_fn!(parser, expr);
Ok(())
});
braced_items!(
parser,
marker,
None,
WORKFLOW_STATEMENT_RECOVERY_SET,
workflow_statement
);
marker.complete(parser, SyntaxKind::ScatterStatementNode);
Ok(())
}
fn call_statement(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::CallKeyword);
expected_fn!(parser, marker, call_target);
if let Some((Token::AsKeyword, _)) = parser.peek() {
expected_fn!(parser, marker, call_alias);
}
while let Some((Token::AfterKeyword, _)) = parser.peek() {
expected_fn!(parser, marker, call_after_clause);
}
if let Some((Token::OpenBrace, _)) = parser.peek() {
let open_span = parser.require(Token::OpenBrace);
if parser.next_if(Token::InputKeyword) {
expected!(parser, marker, Token::Colon);
}
parser.delimited(
Token::CloseBrace,
Some(Token::Comma),
CALL_INPUT_ITEM_RECOVERY_SET,
call_input_item,
);
parser.consume_close_token(Token::OpenBrace, open_span, Token::CloseBrace);
}
marker.complete(parser, SyntaxKind::CallStatementNode);
Ok(())
}
fn call_target(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
expected!(parser, marker, Token::Ident, "call target name");
while parser.next_if(Token::Dot) {
expected!(parser, marker, Token::Ident, "call target name");
}
marker.complete(parser, SyntaxKind::CallTargetNode);
Ok(())
}
fn call_alias(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::AsKeyword);
expected!(parser, marker, Token::Ident, "call output name");
marker.complete(parser, SyntaxKind::CallAliasNode);
Ok(())
}
fn call_after_clause(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
parser.require(Token::AfterKeyword);
expected!(parser, marker, Token::Ident, "task name");
marker.complete(parser, SyntaxKind::CallAfterNode);
Ok(())
}
fn call_input_item(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
expected_in!(parser, marker, ANY_IDENT, "call input key");
parser.update_last_token_kind(SyntaxKind::Ident);
if parser.next_if(Token::Assignment) {
expected_fn!(parser, marker, expr);
}
marker.complete(parser, SyntaxKind::CallInputItemNode);
Ok(())
}
#[inline]
pub fn expr(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
expr_with_precedence(parser, marker, 0)?;
Ok(())
}
fn expr_with_precedence(
parser: &mut Parser<'_>,
marker: Marker,
min_precedence: u8,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
let mut lhs = match parser.peek() {
Some((token, _)) if ATOM_EXPECTED_SET.contains(token.into_raw()) => {
let lhs = parser.start();
match atom_expr(parser, lhs, token) {
Ok(lhs) => lhs,
Err((lhs, e)) => {
lhs.abandon(parser);
return Err((marker, e));
}
}
}
Some((token, _)) if PREFIX_OPERATOR_EXPECTED_SET.contains(token.into_raw()) => {
let prefix = parser.start();
parser.next();
let rhs = parser.start();
let (precedence, kind, associativity) = prefix_precedence(token);
match expr_with_precedence(
parser,
rhs,
match associativity {
Associativity::Left => precedence + 1,
Associativity::Right => precedence,
},
) {
Ok(_) => prefix.complete(parser, kind),
Err((rhs, e)) => {
prefix.abandon(parser);
rhs.abandon(parser);
return Err((marker, e));
}
}
}
found => {
let (found, span) = found
.map(|(t, s)| (Some(t.describe()), s))
.unwrap_or_else(|| (None, parser.span()));
return Err((marker, expected_found("expression", found, span)));
}
};
lhs = lhs.extend_to(parser, marker);
loop {
match parser.peek() {
Some((token, _)) if INFIX_OPERATOR_EXPECTED_SET.contains(token.into_raw()) => {
let (precedence, kind, associativity) = infix_precedence(token);
if precedence < min_precedence {
break;
}
let infix = lhs.precede(parser);
parser.next();
let rhs = parser.start();
if let Err((rhs, e)) = expr_with_precedence(
parser,
rhs,
match associativity {
Associativity::Left => precedence + 1,
Associativity::Right => precedence,
},
) {
rhs.abandon(parser);
return Err((infix, e));
}
lhs = infix.complete(parser, kind);
}
Some((token, _)) if POSTFIX_OPERATOR_EXPECTED_SET.contains(token.into_raw()) => {
let precedence = postfix_precedence(token);
if precedence < min_precedence {
break;
}
if token == Token::OpenParen {
break;
}
let postfix = lhs.precede(parser);
let res = match token {
Token::OpenBracket => index_expr(parser, postfix),
Token::Dot => access_expr(parser, postfix),
_ => panic!("unexpected postfix operator"),
};
lhs = match res {
Ok(marker) => marker,
Err((postfix, e)) => {
return Err((postfix, e));
}
};
}
_ => break,
}
}
Ok(lhs)
}
fn atom_expr(
parser: &mut Parser<'_>,
marker: Marker,
peeked: Token,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
match peeked {
Token::NoneKeyword => none(parser, marker),
Token::Float | Token::Integer => number(parser, marker, false),
Token::TrueKeyword | Token::FalseKeyword => boolean(parser, marker),
Token::SingleQuote => single_quote_string(parser, marker, true),
Token::DoubleQuote => double_quote_string(parser, marker, true),
Token::OpenHeredoc => multiline_string(parser, marker, true),
Token::OpenBracket => array(parser, marker),
Token::OpenBrace => map(parser, marker),
Token::OpenParen => pair_or_paren_expr(parser, marker),
Token::ObjectKeyword => object(parser, marker),
Token::IfKeyword => if_expr(parser, marker),
Token::HintsKeyword => literal_hints(parser, marker),
Token::InputKeyword => literal_input(parser, marker),
Token::OutputKeyword => literal_output(parser, marker),
t if ANY_IDENT.contains(t.into_raw()) => name_ref_expr(parser, marker),
_ => unreachable!(),
}
}
fn array(parser: &mut Parser<'_>, marker: Marker) -> Result<CompletedMarker, (Marker, Diagnostic)> {
bracketed_items!(parser, marker, Some(Token::Comma), EXPR_RECOVERY_SET, expr);
Ok(marker.complete(parser, SyntaxKind::LiteralArrayNode))
}
fn map(parser: &mut Parser<'_>, marker: Marker) -> Result<CompletedMarker, (Marker, Diagnostic)> {
braced_items!(
parser,
marker,
Some(Token::Comma),
MAP_RECOVERY_SET,
map_item
);
Ok(marker.complete(parser, SyntaxKind::LiteralMapNode))
}
fn map_item(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
expected_fn!(parser, marker, expr);
expected!(parser, marker, Token::Colon);
expected_fn!(parser, marker, expr);
marker.complete(parser, SyntaxKind::LiteralMapItemNode);
Ok(())
}
fn pair_or_paren_expr(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
let open_span = match parser.expect(Token::OpenParen) {
Ok(span) => span,
Err(e) => return Err((marker, e)),
};
expected_fn!(parser, marker, expr);
if parser.next_if(Token::CloseParen) {
return Ok(marker.complete(parser, SyntaxKind::ParenthesizedExprNode));
}
expected!(parser, marker, Token::Comma);
expected_fn!(parser, marker, expr);
match parser.next() {
Some((Token::CloseParen, _)) => Ok(marker.complete(parser, SyntaxKind::LiteralPairNode)),
found => {
let (found, span) = found
.map(|(t, s)| (t.describe(), s))
.unwrap_or_else(|| ("end of input", parser.span()));
Err((
marker,
unmatched(
Token::OpenParen.describe(),
open_span,
Token::CloseParen.describe(),
found,
span,
),
))
}
}
}
fn object(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
parser.require(Token::ObjectKeyword);
braced_items!(
parser,
marker,
Some(Token::Comma),
LITERAL_OBJECT_RECOVERY_SET,
object_item
);
Ok(marker.complete(parser, SyntaxKind::LiteralObjectNode))
}
fn object_item(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
expected_in!(parser, marker, ANY_IDENT, "object key");
parser.update_last_token_kind(SyntaxKind::Ident);
expected!(parser, marker, Token::Colon);
expected_fn!(parser, marker, expr);
marker.complete(parser, SyntaxKind::LiteralObjectItemNode);
Ok(())
}
fn name_ref_expr(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
expected_in!(parser, marker, ANY_IDENT, "identifier");
parser.update_last_token_kind(SyntaxKind::Ident);
if let Some((Token::OpenParen, _)) = parser.peek() {
return call_expr(parser, marker);
}
if let Some((Token::OpenBrace, _)) = parser.peek() {
braced_items!(
parser,
marker,
Some(Token::Comma),
LITERAL_OBJECT_RECOVERY_SET, literal_struct_item
);
return Ok(marker.complete(parser, SyntaxKind::LiteralStructNode));
}
Ok(marker.complete(parser, SyntaxKind::NameRefExprNode))
}
fn literal_struct_item(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<(), (Marker, Diagnostic)> {
expected_in!(parser, marker, ANY_IDENT, "struct member name");
parser.update_last_token_kind(SyntaxKind::Ident);
expected!(parser, marker, Token::Colon);
expected_fn!(parser, marker, expr);
marker.complete(parser, SyntaxKind::LiteralStructItemNode);
Ok(())
}
fn if_expr(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
parser.require(Token::IfKeyword);
expected_fn!(parser, marker, expr);
expected!(parser, marker, Token::ThenKeyword);
expected_fn!(parser, marker, expr);
expected!(parser, marker, Token::ElseKeyword);
expected_fn!(parser, marker, expr);
Ok(marker.complete(parser, SyntaxKind::IfExprNode))
}
fn literal_hints(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
parser.require(Token::HintsKeyword);
braced_items!(
parser,
marker,
Some(Token::Comma),
HINTS_ITEM_RECOVERY_SET,
literal_hints_item
);
Ok(marker.complete(parser, SyntaxKind::LiteralHintsNode))
}
fn literal_hints_item(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
expected_in!(parser, marker, ANY_IDENT, "hint key");
parser.update_last_token_kind(SyntaxKind::Ident);
expected!(parser, marker, Token::Colon);
expected_fn!(parser, marker, expr);
marker.complete(parser, SyntaxKind::LiteralHintsItemNode);
Ok(())
}
fn literal_input(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
parser.require(Token::InputKeyword);
braced_items!(
parser,
marker,
Some(Token::Comma),
LITERAL_INPUT_ITEM_RECOVERY_SET,
literal_input_item
);
Ok(marker.complete(parser, SyntaxKind::LiteralInputNode))
}
fn literal_input_item(parser: &mut Parser<'_>, marker: Marker) -> Result<(), (Marker, Diagnostic)> {
expected_in!(parser, marker, ANY_IDENT, "input key");
parser.update_last_token_kind(SyntaxKind::Ident);
while parser.next_if(Token::Dot) {
expected_in!(parser, marker, ANY_IDENT, "struct member name");
parser.update_last_token_kind(SyntaxKind::Ident);
}
expected!(parser, marker, Token::Colon);
expected_fn!(parser, marker, expr);
marker.complete(parser, SyntaxKind::LiteralInputItemNode);
Ok(())
}
fn literal_output(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
parser.require(Token::OutputKeyword);
braced_items!(
parser,
marker,
Some(Token::Comma),
LITERAL_OUTPUT_ITEM_RECOVERY_SET,
literal_output_item
);
Ok(marker.complete(parser, SyntaxKind::LiteralOutputNode))
}
fn literal_output_item(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<(), (Marker, Diagnostic)> {
expected_in!(parser, marker, ANY_IDENT, "output key");
parser.update_last_token_kind(SyntaxKind::Ident);
while parser.next_if(Token::Dot) {
expected_in!(parser, marker, ANY_IDENT, "struct member name");
parser.update_last_token_kind(SyntaxKind::Ident);
}
expected!(parser, marker, Token::Colon);
expected_fn!(parser, marker, expr);
marker.complete(parser, SyntaxKind::LiteralOutputItemNode);
Ok(())
}
fn call_expr(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
paren_items!(parser, marker, Some(Token::Comma), EXPR_RECOVERY_SET, expr);
Ok(marker.complete(parser, SyntaxKind::CallExprNode))
}
fn index_expr(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
bracketed!(parser, marker, |parser, _| {
expected_fn!(parser, expr);
Ok(())
});
Ok(marker.complete(parser, SyntaxKind::IndexExprNode))
}
fn access_expr(
parser: &mut Parser<'_>,
marker: Marker,
) -> Result<CompletedMarker, (Marker, Diagnostic)> {
parser.require(Token::Dot);
expected_in!(parser, marker, ANY_IDENT, "name");
parser.update_last_token_kind(SyntaxKind::Ident);
Ok(marker.complete(parser, SyntaxKind::AccessExprNode))
}
enum Associativity {
Left,
Right,
}
fn prefix_precedence(token: Token) -> (u8, SyntaxKind, Associativity) {
use Associativity::*;
use SyntaxKind::*;
match token {
Token::Exclamation => (8, LogicalNotExprNode, Right),
Token::Minus => (8, NegationExprNode, Right),
_ => panic!("unknown prefix operator token"),
}
}
fn infix_precedence(token: Token) -> (u8, SyntaxKind, Associativity) {
use Associativity::*;
use SyntaxKind::*;
match token {
Token::LogicalOr => (1, LogicalOrExprNode, Left),
Token::LogicalAnd => (2, LogicalAndExprNode, Left),
Token::Equal => (3, EqualityExprNode, Left),
Token::NotEqual => (3, InequalityExprNode, Left),
Token::Less => (4, LessExprNode, Left),
Token::LessEqual => (4, LessEqualExprNode, Left),
Token::Greater => (4, GreaterExprNode, Left),
Token::GreaterEqual => (4, GreaterEqualExprNode, Left),
Token::Plus => (5, AdditionExprNode, Left),
Token::Minus => (5, SubtractionExprNode, Left),
Token::Asterisk => (6, MultiplicationExprNode, Left),
Token::Slash => (6, DivisionExprNode, Left),
Token::Percent => (6, ModuloExprNode, Left),
Token::Exponentiation => (7, ExponentiationExprNode, Left),
_ => panic!("unknown infix operator token"),
}
}
fn postfix_precedence(token: Token) -> u8 {
match token {
Token::OpenParen => 9,
Token::OpenBracket => 10,
Token::Dot => 11,
_ => panic!("unknown postfix operator token"),
}
}