1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//! Simple expression construction.
//!
//! This module defines an expression type that is able to describe simple expressions composed of
//! constants, variables, and applications.
//!
//! # Usage
//!
//! To construct expressions, the API provides the functions `cst`, `var`, and `app`, which
//! construct constants, variables, and applications, respectively. The corresponding `Exp` enum
//! variants can be used directly but require a call to `Box::new` to construct each value of the
//! `App` tuple.
//!
//! These functions allow the expressions to be constructed similarly to how they would be in
//! functional languages like OCaml, for example:
//!
//! ```
//! use knube::exp::{cst, var, app};
//!
//! let e = app(app(cst("f"), cst("a")), var("x"));
//! ```
//!
//! Which would translate to the expression `((f a) x)` (OCaml style) or `f(a)(x)` (Rust style).
//! This crate uses the Rust style to display expressions.

/// An alias to make functions signatures returning expressions easier to read and write.
pub type ExpBox = Box<Exp>;

/// A simple expression, as described in the module documentation.
///
/// This recursive form is what we study in class. Other implementations might be completely
/// different.
#[derive(Debug)]
pub enum Exp {
    Cst(&'static str),
    Var(&'static str),
    App(ExpBox, ExpBox),
}

/// Constructs a constant with the given name.
///
/// Constant names are usually single letters close to the beginning of the alphabet, i.e. `a`,
/// `b`, `f`, etc.
pub fn cst(name: &'static str) -> ExpBox {
    Exp::new(Exp::Cst(name))
}

/// Constructs a variable with the given name. Variables are just identifiers, they don't hold any
/// value.
///
/// Variable names, similarly to constant names, are usually a single letter. However their name is
/// usually a letter close to the end of the alphabet, i.e. `x`, `y`, etc.
pub fn var(name: &'static str) -> ExpBox {
    Exp::new(Exp::Var(name))
}

/// Constructs an application with the given left and right expressions.
///
/// Applications would translate to functions calls, i.e. if `left` is `f` and `right` is `x`, then
/// `app(left, right)` is `(f x)` or `f(x)` depending on the style.
pub fn app(left: ExpBox, right: ExpBox) -> ExpBox {
    Exp::new(Exp::App(left, right))
}

impl Exp {
    fn new(exp: Exp) -> ExpBox {
        Box::new(exp)
    }
}

use std::fmt;

impl fmt::Display for Exp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Exp::Cst(name) => write!(f, "{}", name),
            Exp::Var(name) => write!(f, "{}", name),
            Exp::App(left, right) => write!(f, "{}({})", left, right), // Write recursively.
        }
    }
}