use std::collections::HashMap;
use crate::{parser, wasm::exp::JsExp, Error, Exp};
use serde::Serialize;
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
#[derive(Clone, Serialize)]
enum Mutation {
BetaReduce {
redex: u32,
alpha: u32,
},
EtaReduce {
redex: u32,
alpha: u32,
},
SubstAlpha {
alpha: u32,
name: String,
},
}
#[derive(Clone, Serialize)]
struct Step {
#[serde(skip)]
raw_exp: Exp<String>,
display_exp: JsExp,
last_action: Option<Mutation>,
id: String,
}
impl Step {
fn beta_reduce_by_id(&mut self, id: u32) -> Result<Step, Error> {
let mut raw_exp = self.raw_exp.clone();
let alpha_id = raw_exp.beta_reduce_by_id(&self.display_exp, id)?;
self.last_action = Some(Mutation::BetaReduce {
redex: id,
alpha: alpha_id,
});
let display_exp = JsExp::from_exp(&raw_exp);
let id = raw_exp.to_string();
Ok(Self {
raw_exp,
display_exp,
id,
last_action: None,
})
}
fn eta_reduce_by_id(&mut self, id: u32) -> Result<Step, Error> {
let mut raw_exp = self.raw_exp.clone();
let alpha_id = raw_exp.eta_reduce_by_id(&self.display_exp, id)?;
self.last_action = Some(Mutation::EtaReduce {
redex: id,
alpha: alpha_id,
});
let display_exp = JsExp::from_exp(&raw_exp);
let id = raw_exp.to_string();
Ok(Self {
raw_exp,
display_exp,
id,
last_action: None,
})
}
fn replace_by_alpha_id(
&mut self,
id: u32,
name: String,
exp: &Exp<String>,
) -> Result<Step, Error> {
let mut raw_exp = self.raw_exp.clone();
let var = match raw_exp.find_var_by_alpha_id(&self.display_exp, id) {
None => return Err(Error::VarNotFound(name, id)),
Some(r) => r,
};
assert!(var.into_ident().unwrap().0 == name);
*var = exp.to_owned();
self.last_action = Some(Mutation::SubstAlpha { alpha: id, name });
let display_exp = JsExp::from_exp(&raw_exp);
let id = raw_exp.to_string();
Ok(Self {
raw_exp,
display_exp,
id,
last_action: None,
})
}
}
#[wasm_bindgen]
pub struct Calculator {
steps: Vec<Step>,
defs: HashMap<String, Exp<String>>,
}
#[wasm_bindgen]
impl Calculator {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self {
steps: Vec::new(),
defs: HashMap::new(),
}
}
pub fn init(&mut self, expr: &str) -> Result<(), String> {
let (raw_exp, _) = parser::parse_exp(expr).map_err(|e| e.to_string())?;
let wasm_exp = JsExp::from_exp(&raw_exp);
let id = raw_exp.to_string();
self.steps = vec![Step {
raw_exp,
id,
display_exp: wasm_exp,
last_action: None,
}];
Ok(())
}
fn trim_steps(&mut self, step_num: usize) -> Result<Step, String> {
if step_num >= self.steps.len() {
return Err(format!("invalid step {}", step_num));
}
let last = self.steps.swap_remove(step_num);
while step_num < self.steps.len() {
self.steps.swap_remove(step_num);
}
Ok(last)
}
pub fn beta_reduce(&mut self, step: usize, redex_id: u32) -> Result<(), String> {
let mut last = self.trim_steps(step)?;
let cur = last
.beta_reduce_by_id(redex_id)
.map_err(|e| format!("化简错误:{}", e))?;
self.steps.push(last);
self.steps.push(cur);
Ok(())
}
pub fn eta_reduce(&mut self, step: usize, id: u32) -> Result<(), String> {
let mut last = self.trim_steps(step)?;
let cur = last
.eta_reduce_by_id(id)
.map_err(|e| format!("化简错误:{}", e))?;
self.steps.push(last);
self.steps.push(cur);
Ok(())
}
pub fn replace_def_alpha(
&mut self,
step: usize,
name: &str,
alpha_id: u32,
) -> Result<(), String> {
let mut last = self.trim_steps(step)?;
let exp = self
.defs
.get(name)
.ok_or(format!("expression not found name = {}", name))?;
let cur = last
.replace_by_alpha_id(alpha_id, name.to_string(), exp)
.map_err(|e| format!("化简错误:{}", e))?;
self.steps.push(last);
self.steps.push(cur);
Ok(())
}
pub fn add_defs(&mut self, content: &str) -> Result<(), String> {
let (map, _) = parser::parse_file(content).map_err(|e| e.to_string())?;
for (name, exp) in map {
self.defs.insert(name, exp);
}
Ok(())
}
pub fn history(&self) -> Result<JsValue, String> {
serde_wasm_bindgen::to_value(&self.steps).map_err(|e| e.to_string())
}
pub fn get_defs(&self) -> Result<JsValue, String> {
let mut jsmap = HashMap::new();
for (name, exp) in &self.defs {
jsmap.insert(name, JsExp::from_exp(exp));
}
serde_wasm_bindgen::to_value(&jsmap).map_err(|e| e.to_string())
}
}
impl Default for Calculator {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::Calculator;
#[test]
fn test_calculator() -> Result<(), String> {
let mut calc = Calculator::new();
calc.init("I y")?;
calc.add_defs(
r"
I = \x. x
K = \x. \y. x
",
)?;
Ok(())
}
}