instruct 0.1.0

A language to write general purpose 'makefile like' tasks which are powerful and reuseable
Documentation
use anyhow::Context;
use log::debug;

use crate::interpreter::context::ContextRef;
use crate::interpreter::stack::StackRef;
use crate::interpreter::variables::Variables;
use crate::parse::ast::{Executeable, ExecuteableType};

use super::{get_executor, DynExecutor, Executor, ExecutorError, Stack};

pub struct BlockExecutor {
    name: String,
    variables: Variables,
    executeables: Vec<Executeable>,
    executors: Vec<DynExecutor>,
    stack: Option<StackRef>,
}

impl BlockExecutor {
    pub fn new(input: Executeable) -> anyhow::Result<BlockExecutor> {
        if let ExecuteableType::Block { executeables } = input.executeable_type {
            Ok(BlockExecutor {
                name: input.name,
                variables: Variables::new(input.output_variables),
                executeables,
                executors: Vec::new(),
                stack: None,
            })
        } else {
            Err(ExecutorError::WrongExecutorType(input.executeable_type).into())
        }
    }

    pub fn error_context(&self, index: usize) -> String {
        format!("executing block '{}' at index '{}'", self.name, index)
    }
}

impl Executor for BlockExecutor {
    fn init(&mut self, mut parent_stack: StackRef, ctx: ContextRef) -> anyhow::Result<()> {
        let mut child_stack: StackRef = Stack::inherit_new(&parent_stack).into();

        for executeable in self.executeables.drain(..) {
            let mut executor = get_executor(executeable, child_stack.clone())?;
            executor.init(child_stack.clone(), ctx.clone())?;
            self.executors.push(executor);
        }

        self.variables
            .allocate_and_check_all(&mut parent_stack, &mut child_stack)?;

        self.stack = Some(child_stack);

        Ok(())
    }

    #[allow(clippy::needless_collect)]
    fn execute(&mut self, mut parent_stack: StackRef, ctx: ContextRef) -> anyhow::Result<()> {
        if let Some(mut child_stack) = self.stack.take() {
            debug!("{}: {{", &self.name);
            let executors: Vec<DynExecutor> = self.executors.drain(..).collect();
            for (counter, mut executor) in executors.into_iter().enumerate() {
                executor
                    .execute(child_stack.clone(), ctx.clone())
                    .with_context(|| self.error_context(counter))?;
            }
            self.variables
                .carry_over(&mut parent_stack, &mut child_stack)?;
            debug!("}}\n");
            Ok(())
        } else {
            Err(ExecutorError::NotInitialized.into())
        }
    }
}