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
#[macro_use] extern crate failure;
#[macro_use] extern crate unic_char_range;
extern crate unic_ucd_name;
pub mod eval;
pub mod parser;
pub mod tokenizer;
pub mod types;
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "tokenize error: {}", _0)]
TokenizeError(tokenizer::Error),
#[fail(display = "parse error: {}", _0)]
ParseError(parser::Error),
#[fail(display = "eval error: {}", _0)]
EvalError(eval::Error),
}
use eval::EvalParams;
use parser::AST;
use std::io;
pub fn parse(code: &str) -> Result<Vec<AST>, Error> {
let tokens = tokenizer::tokenize(code.chars()).map_err(|err| Error::TokenizeError(err))?;
#[cfg(feature = "debug")] println!("{:#?}", tokens);
let parsed = parser::parse(tokens).map_err(|err| Error::ParseError(err))?;
#[cfg(feature = "debug")] println!("{:#?}", parsed);
Ok(parsed)
}
pub fn eval<R, W, F>(code: &str, stdin: R, stdout: W, callback: F) -> Result<(), Error>
where R: io::BufRead,
W: io::Write,
F: FnOnce(&mut EvalParams<R, W>)
{
let parsed = parse(code)?;
let mut eval = eval::EvalParams::new(stdin, stdout);
callback(&mut eval);
eval.scope().eval_all(parsed).map_err(|err| Error::EvalError(err))?;
Ok(())
}
pub fn capture<R, F>(code: &str, stdin: R, callback: F) -> Result<String, Error>
where R: io::BufRead,
F: FnOnce(&mut EvalParams<R, &mut Vec<u8>>)
{
let mut output = Vec::new();
eval(code, stdin, &mut output, callback)?;
Ok(String::from_utf8(output).expect("Program (somehow) returned non-utf8 data"))
}
#[cfg(test)]
mod tests {
use super::*;
use types::Value;
fn run(code: &str) -> String {
capture(code, io::empty(), |_| ()).expect("Running test failed")
}
#[test]
fn run_all() {
assert_eq!(run(include_str!("../tests/fac.lol")), "120\n");
assert_eq!(run(include_str!("../tests/pow.lol")), "32\n");
}
#[test]
fn rust_callback() {
assert_eq!(
capture(include_str!("../tests/callback.lol"), io::empty(), |eval| {
eval.bind_func("LOWERIN", Box::new(|values| {
Value::Yarn(values[0].clone().cast_yarn().unwrap().to_lowercase())
}));
}).expect("Running test failed"),
"test\n"
);
}
}