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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use failure::Error;

use lamcal::{parse_str, Term, VarName};
use lamcal::environment::Binding;

use command::{cont_output, Command, Continuation};
use context::Context;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LetStatementPart {
    Undefined,
    Identifier,
    Expression,
}

impl Default for LetStatementPart {
    fn default() -> Self {
        LetStatementPart::Undefined
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct LetStatement {
    identifier: String,
    expression: Term,
}

impl LetStatement {
    pub fn unwrap(self) -> (String, Term) {
        (self.identifier, self.expression)
    }
}

impl Into<Binding> for LetStatement {
    fn into(self) -> Binding {
        Binding::new(VarName(self.identifier), self.expression)
    }
}

impl Command for LetStatement {
    type Input = Self;
    type Output = String;

    fn with_input(input: <Self as Command>::Input) -> Self {
        input
    }

    fn execute(self, ctx: &mut Context) -> Continuation<<Self as Command>::Output> {
        let message = format!("Bound: {} => {}", &self.identifier, &self.expression);
        ctx.env_mut().insert_binding(self.into());
        cont_output(message, "")
    }
}

pub fn parse_let_statement(line: &str) -> Result<LetStatement, Error> {
    let mut identifier = String::new();
    let mut expression = String::new();
    let mut part = if line.starts_with(":let ") {
        LetStatementPart::Identifier
    } else {
        LetStatementPart::Undefined
    };
    let mut chr_iter = line.chars().skip(5);
    while let Some(chr) = chr_iter.next() {
        match chr {
            '=' => {
                part = LetStatementPart::Expression;
            },
            _ => match part {
                LetStatementPart::Identifier => {
                    if !chr.is_whitespace() {
                        identifier.push(chr);
                    }
                },
                LetStatementPart::Expression => {
                    expression.push(chr);
                },
                LetStatementPart::Undefined => {
                    if !chr.is_whitespace() {
                        return Err(format_err!("Character not allowed: {}", chr));
                    }
                },
            },
        }
    }
    if identifier.contains(char::is_whitespace) {
        return Err(format_err!("Invalid identifier: {}", identifier));
    }
    let expression = parse_str(&expression)?;
    Ok(LetStatement {
        identifier,
        expression,
    })
}