lisp/
lisp.rs

1use std::fmt;
2use std::io::prelude::*;
3use std::convert::{TryFrom, TryInto};
4
5use combinedfun as cf;
6use cf::types::VerboseError;
7use cf::str_parsers as sp;
8
9#[derive(Clone, Debug)]
10pub enum Value {
11    Str(String),
12    Int(i32),
13    Float(f32),
14    List(Vec<Value>),
15}
16
17impl fmt::Display for Value {
18    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
19        match *self {
20            Value::Str(ref s) => write!(f, "{:?}", s),
21            Value::Int(ref i) => write!(f, "{:?}", i),
22            Value::Float(ref fl) => write!(f, "{:?}", fl),
23            Value::List(ref l) => {
24                write!(f, "(")?;
25                let mut first = true;
26                for i in l {
27                    if first {
28                        first = false;
29                    } else {
30                        write!(f, " ")?;
31                    }
32                    write!(f, "{}", i)?;
33                }
34                write!(f, ")")?;
35                Ok(())
36            },
37        }
38    }
39}
40
41type ParserInput<'a> = cf::Span<&'a str, cf::Pos>;
42pub type ParserError<'a> = VerboseError<'a, ParserInput<'a>, (), str>;
43
44type ParserResult<'a, Out> = Result<(ParserInput<'a>, Out), ParserError<'a>>;
45
46fn parse_partial(i: ParserInput) -> ParserResult<Value> {
47    fn string(i: ParserInput) -> ParserResult<String> {
48        (
49            -cf::tag("\"")
50            >> cf::record_while(|&c: &char| c != '"', ..)
51            >> -cf::tag("\"")
52            >> ToOwned::to_owned
53        ).parse_partial(i)
54    }
55
56    fn consume_digits<'a, R: cf::RangeLike>(range: R) -> cf::parser!(<ParserInput<'a>, (), ParserError<'a>>) {
57        cf::consume_while(|&c: &char| c.is_digit(10), range)
58    }
59
60    fn float_e(i: ParserInput) -> ParserResult<()> {
61        (
62            -cf::tag("e")
63            >> -(cf::tag("-") | ())
64            >> -consume_digits(1..)
65            >> cf::epsilon()
66        ).parse_partial(i)
67    }
68
69    // NOTE: You could combine `float` and `int` to one parser that may return either, by capturing
70    // wether or not an "e" or a dot occured, and choosing the right method based on that.
71    // Actually, we already have the handy `Number` type below anyways.
72
73    fn float(i: ParserInput) -> ParserResult<f32> {
74        (
75            (
76                -(cf::tag("-") | ())
77                >> (
78                    -cf::tag(".") >> -consume_digits(1..) >> cf::epsilon()
79                    |
80                    -consume_digits(1..) >> (
81                        -cf::tag(".") >> -consume_digits(0..) >> -(cf::f(float_e) | ()) >> cf::epsilon()
82                        |
83                        -cf::f(float_e) >> cf::epsilon()
84                    )
85                )
86            ).record()
87            >> (|s: &str| s.parse::<f32>().unwrap())
88        ).parse_partial(i)
89    }
90
91    fn int(i: ParserInput) -> ParserResult<i32> {
92        (
93            (
94                -(cf::tag("-") | ())
95                >> cf::consume_while(|&c: &char| c.is_digit(10), 1..)
96            ).record()
97            >> (|s: &str| s.parse::<i32>().unwrap())
98        ).parse_partial(i)
99    }
100
101    fn list(i: ParserInput) -> ParserResult<Vec<Value>> {
102        (
103            -cf::tag("(")
104            >> cf::f(parse_partial) / sp::ws(1..) * Vec::new * ..
105            >> -sp::ws(..)
106            >> -cf::tag(")")
107        ).parse_partial(i)
108    }
109
110    (
111        -sp::ws(..)
112        >> (
113            cf::f(string) >> Value::Str
114            | cf::f(float) >> Value::Float
115            | cf::f(int) >> Value::Int
116            | cf::f(list) >> Value::List
117        )
118    ).parse_partial(i)
119}
120
121pub fn parse(i: &str) -> Result<Value, ParserError> {
122    (cf::f(parse_partial) >> -sp::ws(..)).parse(ParserInput::new(i))
123}
124
125#[derive(Clone, Debug)]
126pub enum LispError {
127    EmptyCommand,
128    ExpectedCommand(Value),
129    ExpectedCommandWithArgs(Value),
130    ExpectedNumber(Value),
131    WrongArgCountDivision(Vec<Value>),
132    WrongArgCountQuote(Vec<Value>),
133    UndefinedFunction(String),
134}
135
136enum Number {
137    Int(i32),
138    Float(f32),
139}
140
141impl From<Number> for Value {
142    fn from(number: Number) -> Self {
143        match number {
144            Number::Int(i) => Value::Int(i),
145            Number::Float(f) => Value::Float(f),
146        }
147    }
148}
149
150impl<'a> TryFrom<&'a Value> for Number {
151    type Error = LispError;
152
153    fn try_from(value: &'a Value) -> Result<Number, LispError> {
154        match *value {
155            Value::Int(i) => Ok(Number::Int(i)),
156            Value::Float(f) => Ok(Number::Float(f)),
157            ref inp @ Value::List(_) => match evaluate(inp)? {
158                Value::Int(i) => Ok(Number::Int(i)),
159                Value::Float(f) => Ok(Number::Float(f)),
160                ref val => Err(LispError::ExpectedNumber(val.clone())),
161            },
162            ref val => Err(LispError::ExpectedNumber(val.clone())),
163        }
164    }
165}
166
167impl<'a> TryFrom<&'a Value> for f32 {
168    type Error = LispError;
169
170    fn try_from(value: &'a Value) -> Result<f32, LispError> {
171        match value.try_into()? {
172            Number::Float(f) => Ok(f),
173            Number::Int(i) => Ok(i as f32),
174        }
175    }
176}
177
178pub fn evaluate(v: &Value) -> Result<Value, LispError> {
179    macro_rules! cmd {
180        ($elements:expr, neutral = $neutral:expr, $op:tt) => {
181                $elements.iter().skip(1).fold(Ok(Number::Int($neutral)), |acc, next| match acc? {  // there's probably a way to stop early but I'm just too lazy to look it up
182                    Number::Int(i) => match next.try_into()? {
183                        Number::Int(i2) => Ok(Number::Int(i $op i2)),
184                        Number::Float(f) => Ok(Number::Float(i as f32 $op f)),
185                    },
186                    Number::Float(f) => Ok(Number::Float(f $op f32::try_from(next)?)),
187                }).map(Into::into)
188        };
189    }
190
191    match *v {
192        Value::List(ref elements) => {
193            if let Some(first) = elements.first() {
194                match *first {
195                    Value::Str(ref s) => match &**s {
196                        "+" => cmd!(elements, neutral = 0, +),
197                        "-" => cmd!(elements, neutral = 0, +),
198                        "*" => cmd!(elements, neutral = 0, +),
199                        "/" => if elements.len() == 3 {
200                            Ok(Value::Float(f32::try_from(&elements[1])? / f32::try_from(&elements[2])?))
201                        } else {
202                            Err(LispError::WrongArgCountDivision(elements.clone()))
203                        },
204                        "quote" => if elements.len() == 2 {
205                            Ok(elements[1].clone())
206                        } else {
207                            Err(LispError::WrongArgCountQuote(elements.clone()))
208                        },
209                        s => Err(LispError::UndefinedFunction(s.to_owned())),
210                    },
211                    ref x => Err(LispError::ExpectedCommand(x.clone())),
212                }
213            } else {
214                Err(LispError::EmptyCommand)
215            }
216        },
217        ref v => Err(LispError::ExpectedCommandWithArgs(v.clone())),
218    }
219}
220
221fn main() {
222    let mut input = String::new();
223    std::io::stdin().read_to_string(&mut input).unwrap();
224
225    let expression = match parse(&input) {
226        Ok(expression) => expression,
227        Err(error) => {
228            eprintln!("Encountered an error while parsing:\n{:#?}", error);
229            return;
230        },
231    };
232    let result = evaluate(&expression);
233    match result {
234        Ok(value) => println!("{}", value),
235        Err(error) => eprintln!("Encountered an error while evaluating:\n{:#?}", error),
236    }
237}