use crate::compiler::ir::*;
use crate::compiler::program::Program;
use crate::shell;
use crate::shell::cmd::CmdResult;
type Locals = Vec<String>;
pub struct Evaluator {
pub functions: Vec<UDF>,
pub consts: Vec<String>,
pub globs: Vec<String>,
pub expr: Vec<Expr>,
pub name_to_var: NameToIndex,
pub name_to_func: NameToIndex,
pub name_to_const: NameToIndex,
pub entrypoint_idx: u16,
}
impl Evaluator {
pub fn new(p: Program) -> Self {
let functions = p.functions;
let consts = p.consts;
let expr = p.expr;
let entrypoint_idx = p.entrypoint_idx;
let name_to_var = p.name_to_var;
let name_to_func = p.name_to_func;
let name_to_const = p.name_to_const;
Self {
globs: Vec::new(),
name_to_const,
name_to_var,
name_to_func,
functions,
consts,
expr,
entrypoint_idx,
}
}
pub fn eval(&mut self) -> CmdResult {
let args: Vec<String> = Vec::new();
let _ = self.eval_udf(0, &args);
self.eval_udf(self.entrypoint_idx, &args)?;
Ok(())
}
fn eval_index(&self, index: &Index, loc: &Locals, args: &[String]) -> String {
match index {
Index::Loc(idx) => loc[*idx as usize].to_owned(),
Index::Arg(idx) => args[*idx as usize - 1].to_owned(),
Index::Const(idx) => self.consts[*idx as usize].to_owned(),
Index::Expr(idx) => self.eval_expr_by_index(*idx, loc),
_ => todo!("eval for index {:?}", index),
}
}
fn eval_udf(&mut self, udf_idx: u16, args: &[String]) -> CmdResult {
let mut loc = Locals::new();
let func = self.functions[udf_idx as usize].clone();
for ir in func.instructions.iter().clone() {
self.eval_ir(&ir, &mut loc, args)?;
}
Ok(())
}
fn eval_ir(&mut self, ir: &IR, loc: &mut Locals, args: &[String]) -> CmdResult {
match ir {
IR::GlobalAssignement(dst_idx, src_idx) => {
let val = self.eval_index(src_idx, &loc, args);
if *dst_idx < self.globs.len() as u16 {
self.globs[*dst_idx as usize] = val;
} else {
self.globs.push(val);
}
}
IR::LocalAssignement(dst_idx, src_idx) => {
let val = self.eval_index(src_idx, &loc, args);
if (loc.len() as u8) > *dst_idx {
loc[*dst_idx as usize] = val;
} else {
loc.push(val);
}
}
IR::UdfCall(idx, arg_idxs) => {
let mut sub_args: Vec<String> = Vec::with_capacity(arg_idxs.len());
for arg_idx in arg_idxs.iter() {
sub_args.push(self.eval_index(arg_idx, &loc, args))
}
self.eval_udf(*idx, &sub_args)?;
}
IR::CmdCall(idx, arg_idxs) => {
let mut sub_args: Vec<String> = Vec::with_capacity(arg_idxs.len());
for arg_idx in arg_idxs.iter() {
sub_args.push(self.eval_index(arg_idx, &loc, args))
}
let cmd = &self.consts[*idx as usize];
let mut cmd_args: Vec<String> = Vec::new();
for idx in arg_idxs.iter() {
let arg = self.eval_index(idx, loc, &args);
cmd_args.push(arg);
}
shell::cmd::run_no_redirect(cmd, &cmd_args)?;
}
_ => todo!("eval for IRs"),
};
Ok(())
}
fn eval_expr_by_index(&self, idx: u16, loc: &Locals) -> String {
let expr = &self.expr[idx as usize];
self.eval_expr(expr, loc)
}
fn eval_expr(&self, expr: &Expr, loc: &Locals) -> String {
let mut res = String::with_capacity(2 * expr.data.len());
let mut idx = 0;
let var = &expr.data;
let len = var.len();
while idx < len {
let ch = var[idx];
if ch == GLOB_VAR_INDIC {
let msb = var[idx + 1] as u16;
let lsb = var[idx + 2] as u16;
let var_idx = (msb << 8) | lsb;
let val = &self.globs[var_idx as usize];
res.push_str(&val);
idx += 3;
} else if ch == LOCL_VAR_INDIC {
let loc_idx = var[idx + 1] as usize;
let val = &loc[loc_idx];
res.push_str(&val);
idx += 2;
} else {
res.push(ch as char);
idx += 1;
}
}
return res;
}
pub fn print(&self) {
println!("{}", self.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::runtime::tests::snap;
const DESC_1: &'static str = "
VAR_1 (at position 0) must be hello before the program starts
VAR_4 resolves to 'hello from sbash' after evaluating the program
when update_var2 is called VAR_4 becomes 'hello from bash' without the s
";
fn validate_glob_eval(p: Program) -> String {
let mut evaluator = Evaluator::new(p);
let locals: Vec<String> = Vec::new();
let mut res = String::with_capacity(120);
evaluator.eval_udf(0, &locals); let hello = &evaluator.globs[0];
res.push_str(hello);
res.push('\n');
evaluator.eval();
let var_4_new = &evaluator.globs[4];
res.push_str(&var_4_new);
res.push('\n');
let args: Vec<String> = Vec::new();
evaluator.eval_udf(2, &args);
let var_4_updated = &evaluator.globs[4];
res.push_str(&var_4_updated);
res
}
snap!(basic_eval, DESC_1, validate_glob_eval);
}