use crate::ast::{ActionDef, ActionsBlock, ParamDef, Spanned};
use crate::lexer::Token;
use chumsky::prelude::*;
use super::expressions::spanned_type;
use super::primitives::{
dedent, description_entry, indent, newline, skip_block_noise, spanned_ident, spanned_string,
to_ast_span, ParserInput, Span,
};
#[derive(Clone)]
enum ParamDefEntry {
Description(Spanned<String>),
Label(Spanned<String>),
IsRequired(Spanned<bool>),
IsDisplayable(Spanned<bool>),
IsUsedByPlanner,
ComplexDataTypeName(Spanned<String>),
FilterFromAgent(Spanned<bool>),
}
pub(crate) fn param_def<'tokens, 'src: 'tokens>() -> impl Parser<
'tokens,
ParserInput<'tokens, 'src>,
Spanned<ParamDef>,
extra::Err<Rich<'tokens, Token<'src>, Span>>,
> + Clone {
let param_name = choice((
select! {
Token::Ident(s) => s.to_string(),
Token::Description => "description".to_string(),
Token::Available => "available".to_string(),
Token::String => "string".to_string(),
Token::Number => "number".to_string(),
Token::Boolean => "boolean".to_string(),
Token::Object => "object".to_string(),
Token::Date => "date".to_string(),
Token::Timestamp => "timestamp".to_string(),
Token::Currency => "currency".to_string(),
Token::Id => "id".to_string(),
Token::Datetime => "datetime".to_string(),
Token::Time => "time".to_string(),
Token::Integer => "integer".to_string(),
Token::Long => "long".to_string(),
Token::List => "list".to_string(),
}
.map_with(|s, e| Spanned::new(s, to_ast_span(e.span()))),
spanned_string(),
));
let param_entry = choice((
description_entry().map(ParamDefEntry::Description),
just(Token::Label)
.ignore_then(just(Token::Colon))
.ignore_then(spanned_string())
.map(ParamDefEntry::Label),
just(Token::IsRequired)
.ignore_then(just(Token::Colon))
.ignore_then(choice((just(Token::True).to(true), just(Token::False).to(false))))
.map_with(|v, e| ParamDefEntry::IsRequired(Spanned::new(v, to_ast_span(e.span())))),
just(Token::IsDisplayable)
.ignore_then(just(Token::Colon))
.ignore_then(choice((just(Token::True).to(true), just(Token::False).to(false))))
.map_with(|v, e| ParamDefEntry::IsDisplayable(Spanned::new(v, to_ast_span(e.span())))),
just(Token::IsUsedByPlanner)
.ignore_then(just(Token::Colon))
.ignore_then(choice((just(Token::True).to(true), just(Token::False).to(false))))
.to(ParamDefEntry::IsUsedByPlanner),
just(Token::ComplexDataTypeName)
.ignore_then(just(Token::Colon))
.ignore_then(spanned_string())
.map(ParamDefEntry::ComplexDataTypeName),
just(Token::FilterFromAgent)
.ignore_then(just(Token::Colon))
.ignore_then(choice((just(Token::True).to(true), just(Token::False).to(false))))
.map_with(|v, e| {
ParamDefEntry::FilterFromAgent(Spanned::new(v, to_ast_span(e.span())))
}),
));
param_name
.then_ignore(just(Token::Colon))
.then(spanned_type())
.then(
newline()
.ignore_then(skip_block_noise())
.ignore_then(indent())
.ignore_then(
param_entry
.separated_by(skip_block_noise())
.allow_trailing()
.collect::<Vec<_>>(),
)
.then_ignore(skip_block_noise())
.then_ignore(dedent())
.or_not()
.map(|opt| opt.unwrap_or_default()),
)
.map_with(|((name, ty), entries), e| {
let mut description = None;
let mut label = None;
let mut is_required = None;
let mut is_displayable = None;
let mut complex_data_type_name = None;
let mut filter_from_agent = None;
for entry in entries {
match entry {
ParamDefEntry::Description(d) => description = Some(d),
ParamDefEntry::Label(l) => label = Some(l),
ParamDefEntry::IsRequired(v) => is_required = Some(v),
ParamDefEntry::IsDisplayable(v) => is_displayable = Some(v),
ParamDefEntry::IsUsedByPlanner => {
}
ParamDefEntry::ComplexDataTypeName(s) => complex_data_type_name = Some(s),
ParamDefEntry::FilterFromAgent(v) => filter_from_agent = Some(v),
}
}
Spanned::new(
ParamDef {
name,
ty,
description,
label,
is_required,
filter_from_agent,
is_displayable,
complex_data_type_name,
},
to_ast_span(e.span()),
)
})
}
#[derive(Clone)]
enum ActionDefEntry {
Description(Spanned<String>),
Label(Spanned<String>),
RequireUserConfirmation(Spanned<bool>),
IncludeInProgressIndicator(Spanned<bool>),
ProgressIndicatorMessage(Spanned<String>),
Target(Spanned<String>),
Inputs(Vec<Spanned<ParamDef>>),
Outputs(Vec<Spanned<ParamDef>>),
}
#[derive(Clone)]
enum InputOutputEntry {
Param(Box<Spanned<ParamDef>>),
Description,
}
fn input_output_entry<'tokens, 'src: 'tokens>() -> impl Parser<
'tokens,
ParserInput<'tokens, 'src>,
InputOutputEntry,
extra::Err<Rich<'tokens, Token<'src>, Span>>,
> + Clone {
choice((
description_entry().to(InputOutputEntry::Description),
param_def().map(|p| InputOutputEntry::Param(Box::new(p))),
))
}
fn extract_params(entries: Vec<InputOutputEntry>) -> Vec<Spanned<ParamDef>> {
entries
.into_iter()
.filter_map(|e| match e {
InputOutputEntry::Param(p) => Some(*p),
InputOutputEntry::Description => None, })
.collect()
}
pub(crate) fn action_def<'tokens, 'src: 'tokens>() -> impl Parser<
'tokens,
ParserInput<'tokens, 'src>,
Spanned<ActionDef>,
extra::Err<Rich<'tokens, Token<'src>, Span>>,
> + Clone {
spanned_ident()
.then_ignore(just(Token::Colon))
.then_ignore(newline())
.then_ignore(skip_block_noise())
.then_ignore(indent())
.labelled("action definition")
.then(
choice((
description_entry().map(ActionDefEntry::Description),
just(Token::Label)
.ignore_then(just(Token::Colon))
.ignore_then(spanned_string())
.map(ActionDefEntry::Label),
just(Token::RequireUserConfirmation)
.ignore_then(just(Token::Colon))
.ignore_then(choice((just(Token::True).to(true), just(Token::False).to(false))))
.map_with(|v, e| {
ActionDefEntry::RequireUserConfirmation(Spanned::new(
v,
to_ast_span(e.span()),
))
}),
just(Token::IncludeInProgressIndicator)
.ignore_then(just(Token::Colon))
.ignore_then(choice((just(Token::True).to(true), just(Token::False).to(false))))
.map_with(|v, e| {
ActionDefEntry::IncludeInProgressIndicator(Spanned::new(
v,
to_ast_span(e.span()),
))
}),
just(Token::ProgressIndicatorMessage)
.ignore_then(just(Token::Colon))
.ignore_then(spanned_string())
.map(ActionDefEntry::ProgressIndicatorMessage),
just(Token::Target)
.ignore_then(just(Token::Colon))
.ignore_then(spanned_string())
.map(ActionDefEntry::Target),
just(Token::Inputs)
.map_with(|_, e| e.span()) .then_ignore(just(Token::Colon))
.then_ignore(newline())
.then_ignore(skip_block_noise())
.then(
just(Token::Dedent)
.rewind()
.to(None) .or(indent()
.ignore_then(
input_output_entry()
.separated_by(skip_block_noise())
.allow_trailing()
.collect::<Vec<_>>(),
)
.then_ignore(skip_block_noise())
.then_ignore(dedent())
.map(Some)),
)
.validate(|(inputs_span, entries), _, emitter| {
if entries.is_none() {
emitter.emit(Rich::custom(inputs_span, "inputs block cannot be empty"));
}
entries.unwrap_or_default()
})
.labelled("inputs block")
.map(|entries| ActionDefEntry::Inputs(extract_params(entries))),
just(Token::Outputs)
.map_with(|_, e| e.span()) .then_ignore(just(Token::Colon))
.then_ignore(newline())
.then_ignore(skip_block_noise())
.then(
just(Token::Dedent).rewind().to(None).or(indent()
.ignore_then(
input_output_entry()
.separated_by(skip_block_noise())
.allow_trailing()
.collect::<Vec<_>>(),
)
.then_ignore(skip_block_noise())
.then_ignore(dedent())
.map(Some)),
)
.validate(|(outputs_span, entries), _, emitter| {
if entries.is_none() {
emitter
.emit(Rich::custom(outputs_span, "outputs block cannot be empty"));
}
entries.unwrap_or_default()
})
.labelled("outputs block")
.map(|entries| ActionDefEntry::Outputs(extract_params(entries))),
))
.separated_by(skip_block_noise())
.allow_trailing()
.collect::<Vec<_>>(),
)
.then_ignore(skip_block_noise())
.then_ignore(dedent())
.map_with(|(name, entries), e| {
let mut def = ActionDef {
name,
description: None,
label: None,
require_user_confirmation: None,
include_in_progress_indicator: None,
progress_indicator_message: None,
inputs: None,
outputs: None,
target: None,
};
for entry in entries {
match entry {
ActionDefEntry::Description(d) => def.description = Some(d),
ActionDefEntry::Label(l) => def.label = Some(l),
ActionDefEntry::RequireUserConfirmation(v) => {
def.require_user_confirmation = Some(v)
}
ActionDefEntry::IncludeInProgressIndicator(v) => {
def.include_in_progress_indicator = Some(v)
}
ActionDefEntry::ProgressIndicatorMessage(m) => {
def.progress_indicator_message = Some(m)
}
ActionDefEntry::Target(t) => def.target = Some(t),
ActionDefEntry::Inputs(i) => {
def.inputs = Some(Spanned::new(i, to_ast_span(e.span())))
}
ActionDefEntry::Outputs(o) => {
def.outputs = Some(Spanned::new(o, to_ast_span(e.span())))
}
}
}
Spanned::new(def, to_ast_span(e.span()))
})
}
pub(crate) fn actions_block<'tokens, 'src: 'tokens>() -> impl Parser<
'tokens,
ParserInput<'tokens, 'src>,
Spanned<ActionsBlock>,
extra::Err<Rich<'tokens, Token<'src>, Span>>,
> + Clone {
just(Token::Actions)
.ignore_then(just(Token::Colon))
.ignore_then(newline())
.ignore_then(skip_block_noise())
.ignore_then(indent())
.ignore_then(
action_def()
.separated_by(skip_block_noise())
.allow_trailing()
.collect::<Vec<_>>(),
)
.then_ignore(skip_block_noise())
.then_ignore(dedent())
.labelled("actions block")
.map_with(|actions, e| Spanned::new(ActionsBlock { actions }, to_ast_span(e.span())))
}