use crate::prelude::*;
use std::collections::HashMap;
enum PendingRight {
Add(Obj),
Sub(Obj),
Mul(Obj),
Div(Obj),
}
#[derive(Copy, Clone)]
enum BinaryCombineOp {
Add,
Sub,
Mul,
Div,
}
impl Runtime {
fn evaluate_symbol_obj_iterative(
&mut self,
initial: Obj,
eval_stmt: &EvalStmt,
) -> Result<Obj, RuntimeError> {
let mut pending: Vec<PendingRight> = Vec::new();
let mut cur = initial;
loop {
cur = self.peel_fn_obj_dispatch_loop(cur, eval_stmt)?;
match cur {
Obj::Add(add) => {
pending.push(PendingRight::Add(*add.right));
cur = *add.left;
continue;
}
Obj::Sub(sub) => {
pending.push(PendingRight::Sub(*sub.right));
cur = *sub.left;
continue;
}
Obj::Mul(mul) => {
pending.push(PendingRight::Mul(*mul.right));
cur = *mul.left;
continue;
}
Obj::Div(div) => {
pending.push(PendingRight::Div(*div.right));
cur = *div.left;
continue;
}
Obj::Number(acc_num) => {
let mut acc = Obj::Number(acc_num);
while let Some(pend) = pending.pop() {
let (combine_op, right_obj) = match pend {
PendingRight::Add(o) => (BinaryCombineOp::Add, o),
PendingRight::Sub(o) => (BinaryCombineOp::Sub, o),
PendingRight::Mul(o) => (BinaryCombineOp::Mul, o),
PendingRight::Div(o) => (BinaryCombineOp::Div, o),
};
let right_eval =
self.evaluate_symbol_obj_iterative(right_obj, eval_stmt)?;
acc =
self.combine_two_numeric_objs(acc, right_eval, combine_op, eval_stmt)?;
}
return Ok(acc);
}
_ => {
if pending.is_empty() {
return Ok(cur);
}
return Err(RuntimeError::from(
RuntimeErrorStruct::exec_stmt_with_message_and_cause(
eval_stmt.clone().into(),
"eval: non-numeric intermediate with pending binary operation"
.to_string(),
None,
vec![],
),
));
}
}
}
}
fn combine_two_numeric_objs(
&mut self,
left: Obj,
right: Obj,
combine_op: BinaryCombineOp,
eval_stmt: &EvalStmt,
) -> Result<Obj, RuntimeError> {
let combined = match combine_op {
BinaryCombineOp::Add => Obj::Add(crate::obj::Add::new(left, right)),
BinaryCombineOp::Sub => Obj::Sub(crate::obj::Sub::new(left, right)),
BinaryCombineOp::Mul => Obj::Mul(crate::obj::Mul::new(left, right)),
BinaryCombineOp::Div => Obj::Div(crate::obj::Div::new(left, right)),
};
let calculated = combined.evaluate_to_normalized_decimal_number();
match calculated {
Some(number) => Ok(Obj::Number(number)),
None => Err(RuntimeError::from(
RuntimeErrorStruct::exec_stmt_with_message_and_cause(
eval_stmt.clone().into(),
"eval: failed to combine numeric sub-expression".to_string(),
None,
vec![],
),
)),
}
}
fn peel_fn_obj_dispatch_loop(
&mut self,
mut cur: Obj,
eval_stmt: &EvalStmt,
) -> Result<Obj, RuntimeError> {
while let Obj::FnObj(ref fn_obj) = cur {
cur = self.dispatch_algo_one_return_expr(fn_obj, eval_stmt)?;
}
Ok(cur)
}
fn dispatch_algo_one_return_expr(
&mut self,
fn_obj_to_evaluate: &FnObj,
eval_stmt: &EvalStmt,
) -> Result<Obj, RuntimeError> {
let fn_name = fn_obj_to_evaluate.head.to_string();
let mut flattened_number_args: Vec<Obj> = Vec::new();
for arg_group in fn_obj_to_evaluate.body.iter() {
for arg in arg_group.iter() {
let evaluated_arg_obj =
self.evaluate_symbol_obj_iterative((**arg).clone(), eval_stmt)?;
match evaluated_arg_obj {
Obj::Number(number) => {
flattened_number_args.push(Obj::Number(number));
}
_ => {
return Err(RuntimeError::from(
RuntimeErrorStruct::exec_stmt_with_message_and_cause(
eval_stmt.clone().into(),
"eval: function arguments must evaluate to Number".to_string(),
None,
vec![],
),
));
}
}
}
}
let algo_definition = match self.get_algo_definition_by_name(&fn_name) {
Some(definition) => definition.clone(),
None => {
return Err(RuntimeError::from(
RuntimeErrorStruct::exec_stmt_with_message_and_cause(
eval_stmt.clone().into(),
format!("eval: algorithm `{}` is not defined", fn_name),
None,
vec![],
),
));
}
};
if flattened_number_args.len() != algo_definition.params.len() {
return Err(RuntimeError::from(
RuntimeErrorStruct::exec_stmt_with_message_and_cause(
eval_stmt.clone().into(),
format!(
"eval: argument count mismatch (expected {}, got {})",
algo_definition.params.len(),
flattened_number_args.len()
),
None,
vec![],
),
));
}
let mut param_to_arg_map: HashMap<String, Obj> = HashMap::new();
for (param_name, arg_obj) in algo_definition
.params
.iter()
.zip(flattened_number_args.iter())
{
param_to_arg_map.insert(param_name.clone(), arg_obj.clone());
}
for algo_case in algo_definition.cases.iter() {
let instantiated_case_condition =
self.inst_atomic_fact(&algo_case.condition, ¶m_to_arg_map)?;
let verify_result = self
.verify_atomic_fact(&instantiated_case_condition, &VerifyState::new(0, false))
.map_err(|verify_error| {
RuntimeError::from(RuntimeErrorStruct::exec_stmt_with_message_and_cause(
eval_stmt.clone().into(),
"eval: failed to verify case condition".to_string(),
Some(verify_error),
vec![],
))
})?;
if verify_result.is_true() {
return self.inst_obj(&algo_case.return_stmt.value, ¶m_to_arg_map);
}
if verify_result.is_unknown() {
let reversed_case_condition = instantiated_case_condition.make_reversed();
let verify_reversed_result = self
.verify_atomic_fact(&reversed_case_condition, &VerifyState::new(0, false))
.map_err(|verify_error| {
RuntimeError::from(RuntimeErrorStruct::exec_stmt_with_message_and_cause(
eval_stmt.clone().into(),
"eval: failed to verify reversed case condition".to_string(),
Some(verify_error),
vec![],
))
})?;
if verify_reversed_result.is_unknown() {
return Err(RuntimeError::from(
RuntimeErrorStruct::exec_stmt_with_message_and_cause(
eval_stmt.clone().into(),
format!(
"eval: case `{}` is unknown and its reverse is also unknown",
instantiated_case_condition
),
None,
vec![],
),
));
}
}
}
if let Some(default_return_stmt) = &algo_definition.default_return {
self.inst_obj(&default_return_stmt.value, ¶m_to_arg_map)
} else {
Err(RuntimeError::from(
RuntimeErrorStruct::exec_stmt_with_message_and_cause(
eval_stmt.clone().into(),
"eval: no case matched and no default return".to_string(),
None,
vec![],
),
))
}
}
pub fn _exec_eval_stmt(&mut self, stmt: &EvalStmt) -> Result<StmtResult, RuntimeError> {
self.verify_obj_well_defined_and_store_cache(
&stmt.obj_to_eval,
&VerifyState::new(0, false),
)?;
if !matches!(stmt.obj_to_eval, Obj::FnObj(_)) {
return Err(RuntimeError::from(
RuntimeErrorStruct::exec_stmt_with_message_and_cause(
stmt.clone().into(),
"eval: obj_to_eval must be a fnObj".to_string(),
None,
vec![],
),
));
}
let eval_result = self.run_in_local_env(|rt| {
rt.evaluate_symbol_obj_iterative(stmt.obj_to_eval.clone(), stmt)
});
let evaluated_obj = eval_result?;
let evaluated_equal_fact = Fact::AtomicFact(AtomicFact::EqualFact(EqualFact::new(
stmt.obj_to_eval.clone(),
evaluated_obj,
stmt.line_file.clone(),
)));
let mut infer_result = InferResult::new();
infer_result.new_fact(&evaluated_equal_fact);
self.store_fact_without_well_defined_verified_and_infer(evaluated_equal_fact)?;
Ok((NonFactualStmtSuccess::new(stmt.clone().into(), infer_result, vec![])).into())
}
}