mirror of
https://github.com/kanaka/mal.git
synced 2024-10-26 22:28:26 +03:00
8a19f60386
- Reorder README to have implementation list after "learning tool" bullet. - This also moves tests/ and libs/ into impls. It would be preferrable to have these directories at the top level. However, this causes difficulties with the wasm implementations which need pre-open directories and have trouble with paths starting with "../../". So in lieu of that, symlink those directories to the top-level. - Move the run_argv_test.sh script into the tests directory for general hygiene.
137 lines
3.8 KiB
Rust
137 lines
3.8 KiB
Rust
use std::rc::Rc;
|
|
//use std::collections::HashMap;
|
|
use fnv::FnvHashMap;
|
|
|
|
#[macro_use]
|
|
extern crate lazy_static;
|
|
extern crate fnv;
|
|
extern crate itertools;
|
|
extern crate regex;
|
|
|
|
extern crate rustyline;
|
|
use rustyline::error::ReadlineError;
|
|
use rustyline::Editor;
|
|
|
|
#[macro_use]
|
|
#[allow(dead_code)]
|
|
mod types;
|
|
use crate::types::MalErr::ErrString;
|
|
use crate::types::MalVal::{Hash, Int, List, Nil, Sym, Vector};
|
|
use crate::types::{error, format_error, func, MalArgs, MalErr, MalRet, MalVal};
|
|
mod printer;
|
|
mod reader;
|
|
// TODO: figure out a way to avoid including env
|
|
#[allow(dead_code)]
|
|
mod env;
|
|
|
|
pub type Env = FnvHashMap<String, MalVal>;
|
|
|
|
// read
|
|
fn read(str: &str) -> MalRet {
|
|
reader::read_str(str.to_string())
|
|
}
|
|
|
|
// eval
|
|
fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
|
|
match ast {
|
|
Sym(sym) => Ok(env
|
|
.get(sym)
|
|
.ok_or(ErrString(format!("'{}' not found", sym)))?
|
|
.clone()),
|
|
List(v, _) => {
|
|
let mut lst: MalArgs = vec![];
|
|
for a in v.iter() {
|
|
lst.push(eval(a.clone(), env.clone())?)
|
|
}
|
|
Ok(list!(lst))
|
|
}
|
|
Vector(v, _) => {
|
|
let mut lst: MalArgs = vec![];
|
|
for a in v.iter() {
|
|
lst.push(eval(a.clone(), env.clone())?)
|
|
}
|
|
Ok(vector!(lst))
|
|
}
|
|
Hash(hm, _) => {
|
|
let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default();
|
|
for (k, v) in hm.iter() {
|
|
new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?);
|
|
}
|
|
Ok(Hash(Rc::new(new_hm), Rc::new(Nil)))
|
|
}
|
|
_ => Ok(ast.clone()),
|
|
}
|
|
}
|
|
|
|
fn eval(ast: MalVal, env: Env) -> MalRet {
|
|
match ast.clone() {
|
|
List(l, _) => {
|
|
if l.len() == 0 {
|
|
return Ok(ast);
|
|
}
|
|
match eval_ast(&ast, &env)? {
|
|
List(ref el, _) => {
|
|
let ref f = el[0].clone();
|
|
f.apply(el[1..].to_vec())
|
|
}
|
|
_ => error("expected a list"),
|
|
}
|
|
}
|
|
_ => eval_ast(&ast, &env),
|
|
}
|
|
}
|
|
|
|
// print
|
|
fn print(ast: &MalVal) -> String {
|
|
ast.pr_str(true)
|
|
}
|
|
|
|
fn rep(str: &str, env: &Env) -> Result<String, MalErr> {
|
|
let ast = read(str)?;
|
|
let exp = eval(ast, env.clone())?;
|
|
Ok(print(&exp))
|
|
}
|
|
|
|
fn int_op(op: fn(i64, i64) -> i64, a: MalArgs) -> MalRet {
|
|
match (a[0].clone(), a[1].clone()) {
|
|
(Int(a0), Int(a1)) => Ok(Int(op(a0, a1))),
|
|
_ => error("invalid int_op args"),
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
// `()` can be used when no completer is required
|
|
let mut rl = Editor::<()>::new();
|
|
if rl.load_history(".mal-history").is_err() {
|
|
eprintln!("No previous history.");
|
|
}
|
|
|
|
let mut repl_env = Env::default();
|
|
repl_env.insert("+".to_string(), func(|a: MalArgs| int_op(|i, j| i + j, a)));
|
|
repl_env.insert("-".to_string(), func(|a: MalArgs| int_op(|i, j| i - j, a)));
|
|
repl_env.insert("*".to_string(), func(|a: MalArgs| int_op(|i, j| i * j, a)));
|
|
repl_env.insert("/".to_string(), func(|a: MalArgs| int_op(|i, j| i / j, a)));
|
|
|
|
loop {
|
|
let readline = rl.readline("user> ");
|
|
match readline {
|
|
Ok(line) => {
|
|
rl.add_history_entry(&line);
|
|
rl.save_history(".mal-history").unwrap();
|
|
if line.len() > 0 {
|
|
match rep(&line, &repl_env) {
|
|
Ok(out) => println!("{}", out),
|
|
Err(e) => println!("Error: {}", format_error(e)),
|
|
}
|
|
}
|
|
}
|
|
Err(ReadlineError::Interrupted) => continue,
|
|
Err(ReadlineError::Eof) => break,
|
|
Err(err) => {
|
|
println!("Error: {:?}", err);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|