lamcalc/wasm/
exp.rs

1//! Expression data for browser
2use serde::Serialize;
3
4use crate::{Error, Exp};
5
6#[derive(Serialize, Debug, Clone)]
7/// variable data
8pub struct Var {
9    /// identifier
10    pub ident: String,
11    /// de bruijn code
12    pub code: u32,
13    /// alpha-equivalence setoid id
14    pub alpha_id: u32,
15}
16
17#[derive(Serialize, Debug, Clone)]
18/// abstraction data
19pub struct Abs {
20    /// identifier
21    pub ident: String,
22    /// setoid id
23    pub alpha_id: u32,
24    /// sub expression
25    pub body: Box<JsExp>,
26    /// whether it's in a beta-redex
27    pub in_beta_redex: bool,
28    /// whether eta reduceable
29    pub eta_redex: Option<u32>,
30}
31#[derive(Serialize, Debug, Clone)]
32/// application data
33pub struct App {
34    /// the former sub expression
35    pub func: Box<JsExp>,
36    /// the latter sub expression
37    pub body: Box<JsExp>,
38    /// id of its beta_redex (greater than 0)
39    pub beta_redex: Option<u32>,
40}
41
42#[derive(Serialize, Debug, Clone)]
43/// typed data of Lambda expression
44pub enum InnerExp {
45    #[allow(missing_docs)]
46    Var(Var),
47    #[allow(missing_docs)]
48    Abs(Abs),
49    #[allow(missing_docs)]
50    App(App),
51}
52
53/// Expression data in a fronend-friendly format
54#[derive(Serialize, Debug, Clone)]
55pub struct JsExp {
56    /// whether wrapping parentheses
57    pub parentheses: bool,
58    /// inner type of this expression
59    pub inner: InnerExp,
60}
61
62impl JsExp {
63    fn init_exp(exp: &crate::Exp<String>) -> Self {
64        match exp {
65            crate::Exp::Var(v) => Self {
66                parentheses: false,
67                inner: InnerExp::Var(Var {
68                    ident: v.0.clone(),
69                    code: v.1,
70                    alpha_id: 0,
71                }),
72            },
73            crate::Exp::Abs(id, body) => Self {
74                parentheses: false,
75                inner: InnerExp::Abs(Abs {
76                    ident: id.0.clone(),
77                    alpha_id: 0,
78                    body: Box::new(Self::init_exp(body)),
79                    in_beta_redex: false,
80                    eta_redex: None,
81                }),
82            },
83            crate::Exp::App(func, body) => Self {
84                parentheses: false,
85                inner: InnerExp::App(App {
86                    func: Box::new(Self::init_exp(func)),
87                    body: Box::new(Self::init_exp(body)),
88                    beta_redex: None,
89                }),
90            },
91        }
92    }
93    fn for_each_captured_by<'a, F, D>(&'a mut self, de: u32, f: F, sum: Option<D>) -> Option<D>
94    where
95        F: Fn(&mut Var, Option<D>) -> Option<D> + Clone,
96    {
97        match &mut self.inner {
98            InnerExp::Var(var) => {
99                if var.code == de {
100                    return f(var, sum);
101                }
102                return sum;
103            }
104            InnerExp::Abs(abs) => abs.body.for_each_captured_by(de + 1, f, sum),
105            InnerExp::App(app) => {
106                let sum = app.func.for_each_captured_by(de, f.clone(), sum);
107                app.body.for_each_captured_by(de, f, sum)
108            }
109        }
110    }
111    /// 初始化后添加括号,添加 alpha-equivalence setoid id
112    fn decorate(
113        &mut self,
114        is_app_func: bool,
115        is_app_body: bool,
116        is_tail: bool,
117        counter: &mut u32,
118    ) {
119        match &mut self.inner {
120            InnerExp::Var(var) => {
121                // free variable
122                if var.alpha_id == 0 {
123                    *counter += 1;
124                    var.alpha_id = *counter
125                }
126            }
127            InnerExp::Abs(abs) => {
128                abs.in_beta_redex = is_app_func;
129                if is_app_func || !is_tail {
130                    self.parentheses = true;
131                }
132                abs.body
133                    .decorate(false, false, self.parentheses || is_tail, counter);
134                *counter += 1;
135                abs.alpha_id = *counter;
136                abs.body.for_each_captured_by(
137                    1,
138                    |v, _| {
139                        v.alpha_id = *counter;
140                        None
141                    },
142                    Some(()),
143                );
144                if let InnerExp::App(app) = &mut abs.body.inner {
145                    if app
146                        .func
147                        .for_each_captured_by(1, |_, _| Some(()), None)
148                        .is_none()
149                    {
150                        if let InnerExp::Var(var) = &app.body.inner {
151                            if var.code == 1 {
152                                *counter = *counter + 1;
153                                abs.eta_redex = Some(*counter);
154                            }
155                        }
156                    }
157                }
158            }
159            InnerExp::App(app) => {
160                if is_app_body {
161                    self.parentheses = true;
162                }
163                app.func.decorate(true, false, false, counter);
164                app.body
165                    .decorate(false, true, self.parentheses || is_tail, counter);
166                if let InnerExp::Abs(_) = app.func.inner {
167                    *counter += 1;
168                    // *redex_counter = *redex_counter + 1;
169                    app.beta_redex = Some(*counter)
170                }
171            }
172        }
173    }
174    pub(crate) fn from_exp(expr: &crate::Exp<String>) -> Self {
175        let mut exp = Self::init_exp(expr);
176        let mut counter = 0;
177        // let mut setoid_counter = 0;
178        exp.decorate(false, false, true, &mut counter);
179        exp
180    }
181    fn into_app_ref(&self) -> Result<&App, Error> {
182        match &self.inner {
183            InnerExp::App(app) => Ok(app),
184            _ => Err(Error::InvalidInnerType),
185        }
186    }
187    fn into_abs_ref(&self) -> Result<&Abs, Error> {
188        match &self.inner {
189            InnerExp::Abs(abs) => Ok(abs),
190            _ => Err(Error::InvalidInnerType),
191        }
192    }
193    fn into_var(&self) -> Result<&Var, Error> {
194        match &self.inner {
195            InnerExp::Var(v) => Ok(v),
196            _ => Err(Error::InvalidInnerType),
197        }
198    }
199}
200
201impl Exp<String> {
202    /// Resolve beta-redex based on it's `display_exp`
203    ///
204    /// this operation requires mutable reference of `display_exp`
205    /// to mark the modified part.
206    ///
207    /// return the alpha_id of reduced part.
208    pub(crate) fn beta_reduce_by_id(
209        &mut self,
210        display_exp: &JsExp,
211        id: u32,
212    ) -> Result<u32, Error> {
213        if let InnerExp::App(app) = &display_exp.inner {
214            if let Some(beta_redex) = app.beta_redex {
215                let alpha_id = app.func.into_abs_ref()?.alpha_id;
216                if beta_redex == id {
217                    if self.beta_reduce() {
218                        // display_exp.marked = true;
219                        return Ok(alpha_id);
220                    }
221                    return Err(Error::InvalidRedex(id, self.to_string()));
222                }
223            }
224        }
225        match self {
226            Exp::Var(_) => Err(Error::RedexNotFound),
227            Exp::Abs(_, body) => body.beta_reduce_by_id(&display_exp.into_abs_ref()?.body, id),
228            Exp::App(func, body) => {
229                let app = display_exp.into_app_ref()?;
230                match func.beta_reduce_by_id(&app.func, id) {
231                    Err(Error::RedexNotFound) => body.beta_reduce_by_id(&app.body, id),
232                    r => r,
233                }
234            }
235        }
236    }
237    /// return the alpha_id of reduced part.
238    pub(crate) fn eta_reduce_by_id(
239        &mut self,
240        display_exp: &JsExp,
241        id: u32,
242    ) -> Result<u32, Error> {
243        if let Exp::Abs(_, _) = self {
244            let abs = display_exp.into_abs_ref()?;
245            if abs.eta_redex.is_some() && abs.eta_redex.unwrap() == id {
246                if self.eta_reduce() {
247                    return Ok(abs.alpha_id);
248                }
249                return Err(Error::InvalidRedex(id, format!("{:?}", self)));
250            }
251        }
252        match self {
253            Exp::Var(_) => Err(Error::RedexNotFound),
254            Exp::Abs(_, body) => body.eta_reduce_by_id(&display_exp.into_abs_ref()?.body, id),
255            Exp::App(func, body) => {
256                let app = display_exp.into_app_ref()?;
257                match func.eta_reduce_by_id(&app.func, id) {
258                    Err(Error::RedexNotFound) => body.eta_reduce_by_id(&app.body, id),
259                    r => r,
260                }
261            }
262        }
263    }
264    pub(crate) fn find_var_by_alpha_id(
265        &mut self,
266        display_exp: &JsExp,
267        id: u32,
268    ) -> Option<&mut Self> {
269        match self {
270            Exp::Var(_) => {
271                if let Ok(var) = display_exp.into_var() {
272                    if var.alpha_id == id {
273                        return Some(self);
274                    }
275                }
276                None
277            }
278            Exp::Abs(_, body) => {
279                let abs = display_exp.into_abs_ref().unwrap();
280                body.find_var_by_alpha_id(&abs.body, id)
281            }
282            Exp::App(func, body) => {
283                let app = display_exp.into_app_ref().unwrap();
284                match func.find_var_by_alpha_id(&app.func, id) {
285                    None => body.find_var_by_alpha_id(&app.body, id),
286                    r => r,
287                }
288            }
289        }
290    }
291}
292
293#[cfg(test)]
294mod tests {
295    use crate::lambda;
296
297    use super::{Abs, App, InnerExp, JsExp, Var};
298    impl JsExp {
299        fn into_var_mut(&mut self) -> &mut Var {
300            match &mut self.inner {
301                InnerExp::Var(var) => var,
302                _ => panic!("not var"),
303            }
304        }
305        fn into_app_mut(&mut self) -> &mut App {
306            match &mut self.inner {
307                InnerExp::App(app) => app,
308                _ => panic!("not app"),
309            }
310        }
311        fn into_abs_mut(&mut self) -> &mut Abs {
312            match &mut self.inner {
313                InnerExp::Abs(abs) => abs,
314                _ => panic!("not app"),
315            }
316        }
317    }
318
319    #[test]
320    fn test_jsexp() {
321        let exp = lambda!(x. x (x. x) (y. x (y. y) y));
322        let mut jsexp = JsExp::from_exp(&exp);
323        dbg!(&jsexp);
324        {
325            let id = jsexp.into_abs_mut().alpha_id;
326            assert_eq!(
327                id,
328                jsexp
329                    .into_abs_mut()
330                    .body
331                    .into_app_mut()
332                    .func
333                    .into_app_mut()
334                    .func
335                    .into_var_mut()
336                    .alpha_id
337            );
338            assert_eq!(
339                id,
340                jsexp
341                    .into_abs_mut()
342                    .body
343                    .into_app_mut()
344                    .body
345                    .into_abs_mut()
346                    .body
347                    .into_app_mut()
348                    .func
349                    .into_app_mut()
350                    .func
351                    .into_var_mut()
352                    .alpha_id
353            );
354        }
355        {
356            let abs = jsexp
357                .into_abs_mut()
358                .body
359                .into_app_mut()
360                .func
361                .into_app_mut()
362                .body
363                .into_abs_mut();
364            assert_eq!(abs.alpha_id, abs.body.into_var_mut().alpha_id);
365        }
366    }
367}