use std::fmt::{self, Display, Formatter};
use std::rc::Rc;
use super::node::Span;
use crate::antlr::hamelinparser::{
PipelineAltContextAttrs, PipelineContextAll, PipelineEOFContextAttrs,
};
use crate::antlr::interval;
use crate::antlr::resilient_parse_pipeline;
use crate::err::TranslationErrors;
use crate::tree::ast::context::{FromCst, ParseContext};
use crate::tree::ast::{ParseWithContext, ParseWithErrors, TypeCheck};
use crate::tree::typed_ast::context::StatementTranslationContext;
use crate::tree::typed_ast::pipeline::TypedPipeline;
use crate::write_utils::pad;
use std::ops::RangeInclusive;
use std::sync::Arc;
use super::command::{Command, CommandKind, SelectCommand};
use super::expression::Expression;
use super::node::Spannable;
#[derive(Debug, Clone)]
pub struct Pipeline {
pub span: Span,
pub commands: Vec<Arc<Command>>,
}
impl ParseWithErrors for Pipeline {
fn parse_with_errors(input: impl Into<String>) -> (Self, TranslationErrors) {
Self::parse_with_context(input.into(), ParseContext::new())
}
}
impl ParseWithContext for Pipeline {
fn parse_with_context(input: String, mut ctx: ParseContext) -> (Self, TranslationErrors) {
match resilient_parse_pipeline(input) {
Ok((csteof, parse_errors)) => match csteof.pipeline() {
Some(cst) => {
let ast = Self::from_cst_with_context(cst, &mut ctx);
let mut errors = ctx.take_errors();
errors.extend(parse_errors);
(ast, errors)
}
None => {
let errors = ctx.take_errors();
let mut all_errors = errors;
all_errors.extend(parse_errors);
(
Self {
span: Span::NONE,
commands: vec![],
},
all_errors,
)
}
},
Err(e) => {
let error = ctx
.error("fatal error initializing parser")
.with_source_boxed(e.into())
.emit();
(
Self {
span: Span::NONE,
commands: vec![],
},
(*error).clone().single(),
)
}
}
}
}
impl Spannable for Pipeline {
fn span(&self) -> Option<RangeInclusive<usize>> {
self.span.to_range()
}
}
impl Pipeline {
pub fn at(mut self, span: Span) -> Self {
self.span = span;
self
}
}
impl PartialEq for Pipeline {
fn eq(&self, other: &Self) -> bool {
self.commands == other.commands
}
}
impl From<Expression> for Pipeline {
fn from(expr: Expression) -> Self {
use super::clause::Assignment;
use super::identifier::{ParsedIdentifier, SimpleIdentifier};
let identifier: ParsedIdentifier = SimpleIdentifier::new("result").into();
let command = Command {
span: Span::NONE,
kind: CommandKind::Select(SelectCommand {
clauses: vec![Arc::new(Assignment::from_parts(identifier, expr).into())],
}),
};
Pipeline {
span: Span::NONE,
commands: vec![Arc::new(command)],
}
}
}
impl FromCst<Rc<PipelineContextAll<'static>>> for Pipeline {
fn from_cst_with_context(cst: Rc<PipelineContextAll<'static>>, ctx: &mut ParseContext) -> Self {
let commands: Vec<Arc<Command>> = match cst.as_ref() {
PipelineContextAll::PipelineAltContext(pctx) => pctx
.command_all()
.into_iter()
.map(|cmd_cst| Arc::new(Command::from_cst_with_context(cmd_cst.clone(), ctx)))
.collect(),
PipelineContextAll::Error(_) => vec![],
};
Pipeline {
span: interval(cst.as_ref()).into(),
commands,
}
}
}
impl Pipeline {
pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: usize) -> fmt::Result {
for (i, cmd) in self.commands.iter().enumerate() {
if i > 0 {
writeln!(f)?;
pad(f, indentation)?;
write!(f, "| ")?;
}
let cmd_indent = if i == 0 { indentation } else { indentation + 2 };
cmd.fmt_indented(f, cmd_indent)?;
}
Ok(())
}
}
impl Display for Pipeline {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.fmt_indented(f, 0)
}
}
impl TypeCheck for Pipeline {
type Output = TypedPipeline;
fn type_check_with_context(
ast: Arc<Self>,
ctx: &mut StatementTranslationContext,
) -> Self::Output {
TypedPipeline::from_ast_with_context(ast, ctx)
}
}