use crate::core::environment::LocalEntry;
use crate::core::eval;
use crate::core::exec::{outcome_from_run, ExecOutcome};
use crate::core::Expr;
use crate::error::{Result, TemplateError};
use std::collections::HashMap;
use std::rc::Rc;
pub struct BodyInstruction {
pub args: Vec<Expr>,
}
impl BodyInstruction {
pub fn new(args: Vec<Expr>) -> Self {
BodyInstruction { args }
}
pub(crate) fn exec(&self, env: &mut crate::core::Environment) -> Result<ExecOutcome> {
exec_nested(env, &self.args)
}
}
fn exec_nested(
env: &mut crate::core::Environment,
args: &[crate::core::Expr],
) -> Result<ExecOutcome> {
let frame = env.get_current_macro_frame().ok_or_else(|| {
TemplateError::misc("Cannot use a \"nested\" instruction outside a macro.")
})?;
let mut arg_values = Vec::new();
for a in args {
arg_values.push(eval::eval(env, a)?);
}
let call_body = match &frame.call_body {
Some(b) => b.clone(),
None => return Ok(ExecOutcome::Done), };
let mut body_vars = HashMap::new();
for (i, bp) in frame.body_params.iter().enumerate() {
if let Some(v) = arg_values.get(i) {
body_vars.insert(bp.clone(), v.clone());
}
}
let prev_macro = env.macro_frames.pop();
let prev_ns = std::mem::replace(&mut env.current_ns, frame.caller_ns.clone());
let prev_local = std::mem::take(&mut env.local_stack);
let prev_macro_name =
std::mem::replace(&mut env.current_macro_name, frame.caller_macro_name.clone());
let fallback_lexical = env.lexical_template_name.clone();
let prev_lexical = std::mem::replace(
&mut env.lexical_template_name,
frame
.prev_lexical_template_name
.clone()
.unwrap_or(fallback_lexical),
);
env.local_stack = frame.caller_local_stack.clone();
if !frame.body_params.is_empty() {
env.push_local(LocalEntry::Body(Rc::new(
crate::core::environment::BodyCtx { vars: body_vars },
)));
}
let r = env.run(&call_body);
env.current_macro_name = prev_macro_name;
env.local_stack = prev_local;
env.current_ns = prev_ns;
env.lexical_template_name = prev_lexical;
if let Some(f) = prev_macro {
env.macro_frames.push(f);
}
outcome_from_run(r)
}