1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-11 13:55:55 +03:00

Merge pull request #448 from rofrol/rust-rustfmt

rust: rustfmt
This commit is contained in:
Joel Martin 2019-09-18 12:42:25 -05:00 committed by GitHub
commit 26e29fe36e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 2535 additions and 2429 deletions

View File

@ -1,6 +1,6 @@
use std::rc::Rc;
use std::fs::File;
use std::io::Read;
use std::rc::Rc;
use std::sync::Mutex;
use std::time::{SystemTime, UNIX_EPOCH};
@ -8,21 +8,19 @@ extern crate rustyline;
use rustyline::error::ReadlineError;
use rustyline::Editor;
use types::{MalVal,MalArgs,MalRet,error,func,hash_map,_assoc,_dissoc,atom};
use types::MalVal::{Nil,Bool,Int,Str,Sym,List,Vector,Hash,Func,MalFunc,Atom};
use types::MalErr::{ErrMalVal};
use reader::read_str;
use printer::pr_seq;
use reader::read_str;
use types::MalErr::ErrMalVal;
use types::MalVal::{Atom, Bool, Func, Hash, Int, List, MalFunc, Nil, Str, Sym, Vector};
use types::{MalArgs, MalRet, MalVal, _assoc, _dissoc, atom, error, func, hash_map};
macro_rules! fn_t_int_int {
($ret:ident, $fn:expr) => {{
|a:MalArgs| {
match (a[0].clone(), a[1].clone()) {
(Int(a0), Int(a1)) => Ok($ret($fn(a0, a1))),
_ => error("expecting (int,int) args"),
}
}
}};
($ret:ident, $fn:expr) => {{
|a: MalArgs| match (a[0].clone(), a[1].clone()) {
(Int(a0), Int(a1)) => Ok($ret($fn(a0, a1))),
_ => error("expecting (int,int) args"),
}
}};
}
macro_rules! fn_is_type {
@ -38,290 +36,299 @@ macro_rules! fn_is_type {
}
macro_rules! fn_str {
($fn:expr) => {{
|a:MalArgs| {
match a[0].clone() {
Str(a0) => $fn(a0),
_ => error("expecting (str) arg"),
}
}
}};
($fn:expr) => {{
|a: MalArgs| match a[0].clone() {
Str(a0) => $fn(a0),
_ => error("expecting (str) arg"),
}
}};
}
fn symbol(a: MalArgs) -> MalRet {
match a[0] {
Str(ref s) => Ok(Sym(s.to_string())),
_ => error("illegal symbol call")
}
match a[0] {
Str(ref s) => Ok(Sym(s.to_string())),
_ => error("illegal symbol call"),
}
}
fn readline(a: MalArgs) -> MalRet {
lazy_static! {
static ref RL: Mutex<Editor<()>> = Mutex::new(Editor::<()>::new());
}
//let mut rl = Editor::<()>::new();
lazy_static! {
static ref RL: Mutex<Editor<()>> = Mutex::new(Editor::<()>::new());
}
//let mut rl = Editor::<()>::new();
match a[0] {
Str(ref p) => {
//match rl.readline(p) {
match RL.lock().unwrap().readline(p) {
Ok(line) => Ok(Str(line)),
Err(ReadlineError::Eof) => Ok(Nil),
Err(e) => error(&format!("{:?}", e))
}
},
_ => error("readline: prompt is not Str"),
}
match a[0] {
Str(ref p) => {
//match rl.readline(p) {
match RL.lock().unwrap().readline(p) {
Ok(line) => Ok(Str(line)),
Err(ReadlineError::Eof) => Ok(Nil),
Err(e) => error(&format!("{:?}", e)),
}
}
_ => error("readline: prompt is not Str"),
}
}
fn slurp(f: String) -> MalRet {
let mut s = String::new();
match File::open(f).and_then(|mut f| f.read_to_string(&mut s)) {
Ok(_) => Ok(Str(s)),
Err(e) => error(&e.to_string()),
}
let mut s = String::new();
match File::open(f).and_then(|mut f| f.read_to_string(&mut s)) {
Ok(_) => Ok(Str(s)),
Err(e) => error(&e.to_string()),
}
}
fn time_ms(_a: MalArgs) -> MalRet {
let ms_e = match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(d) => d,
Err(e) => return error(&format!("{:?}", e)),
};
Ok(Int(ms_e.as_secs() as i64 * 1000 +
ms_e.subsec_nanos() as i64 / 1_000_000))
let ms_e = match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(d) => d,
Err(e) => return error(&format!("{:?}", e)),
};
Ok(Int(
ms_e.as_secs() as i64 * 1000 + ms_e.subsec_nanos() as i64 / 1_000_000
))
}
fn get(a: MalArgs) -> MalRet {
match (a[0].clone(), a[1].clone()) {
(Nil, _) => Ok(Nil),
(Hash(ref hm,_), Str(ref s)) => {
match hm.get(s) {
Some(mv) => Ok(mv.clone()),
None => Ok(Nil),
}
},
_ => error("illegal get args")
}
match (a[0].clone(), a[1].clone()) {
(Nil, _) => Ok(Nil),
(Hash(ref hm, _), Str(ref s)) => match hm.get(s) {
Some(mv) => Ok(mv.clone()),
None => Ok(Nil),
},
_ => error("illegal get args"),
}
}
fn assoc(a: MalArgs) -> MalRet {
match a[0] {
Hash(ref hm,_) => _assoc((**hm).clone(), a[1..].to_vec()),
_ => error("assoc on non-Hash Map")
}
match a[0] {
Hash(ref hm, _) => _assoc((**hm).clone(), a[1..].to_vec()),
_ => error("assoc on non-Hash Map"),
}
}
fn dissoc(a: MalArgs) -> MalRet {
match a[0] {
Hash(ref hm,_) => _dissoc((**hm).clone(), a[1..].to_vec()),
_ => error("dissoc on non-Hash Map")
}
match a[0] {
Hash(ref hm, _) => _dissoc((**hm).clone(), a[1..].to_vec()),
_ => error("dissoc on non-Hash Map"),
}
}
fn contains_q(a: MalArgs) -> MalRet {
match (a[0].clone(), a[1].clone()) {
(Hash(ref hm,_), Str(ref s)) => {
Ok(Bool(hm.contains_key(s)))
},
_ => error("illegal get args")
}
match (a[0].clone(), a[1].clone()) {
(Hash(ref hm, _), Str(ref s)) => Ok(Bool(hm.contains_key(s))),
_ => error("illegal get args"),
}
}
fn keys(a: MalArgs) -> MalRet {
match a[0] {
Hash(ref hm,_) => {
Ok(list!(hm.keys().map(|k|{Str(k.to_string())}).collect()))
},
_ => error("keys requires Hash Map")
}
match a[0] {
Hash(ref hm, _) => Ok(list!(hm.keys().map(|k| { Str(k.to_string()) }).collect())),
_ => error("keys requires Hash Map"),
}
}
fn vals(a: MalArgs) -> MalRet {
match a[0] {
Hash(ref hm,_) => {
Ok(list!(hm.values().map(|v|{v.clone()}).collect()))
},
_ => error("keys requires Hash Map")
}
match a[0] {
Hash(ref hm, _) => Ok(list!(hm.values().map(|v| { v.clone() }).collect())),
_ => error("keys requires Hash Map"),
}
}
fn cons(a: MalArgs) -> MalRet {
match a[1].clone() {
List(v,_) | Vector(v,_) => {
let mut new_v = vec![a[0].clone()];
new_v.extend_from_slice(&v);
Ok(list!(new_v.to_vec()))
},
_ => error("cons expects seq as second arg"),
}
match a[1].clone() {
List(v, _) | Vector(v, _) => {
let mut new_v = vec![a[0].clone()];
new_v.extend_from_slice(&v);
Ok(list!(new_v.to_vec()))
}
_ => error("cons expects seq as second arg"),
}
}
fn concat(a: MalArgs) -> MalRet {
let mut new_v = vec![];
for seq in a.iter() {
match seq {
List(v,_) | Vector(v,_) => new_v.extend_from_slice(v),
_ => return error("non-seq passed to concat"),
let mut new_v = vec![];
for seq in a.iter() {
match seq {
List(v, _) | Vector(v, _) => new_v.extend_from_slice(v),
_ => return error("non-seq passed to concat"),
}
}
}
Ok(list!(new_v.to_vec()))
Ok(list!(new_v.to_vec()))
}
fn nth(a: MalArgs) -> MalRet {
match (a[0].clone(), a[1].clone()) {
(List(seq,_), Int(idx)) | (Vector(seq,_), Int(idx)) => {
if seq.len() <= idx as usize {
return error("nth: index out of range");
}
Ok(seq[idx as usize].clone())
match (a[0].clone(), a[1].clone()) {
(List(seq, _), Int(idx)) | (Vector(seq, _), Int(idx)) => {
if seq.len() <= idx as usize {
return error("nth: index out of range");
}
Ok(seq[idx as usize].clone())
}
_ => error("invalid args to nth"),
}
_ => error("invalid args to nth"),
}
}
fn first(a: MalArgs) -> MalRet {
match a[0].clone() {
List(ref seq,_) | Vector(ref seq,_) if seq.len() == 0 => Ok(Nil),
List(ref seq,_) | Vector(ref seq,_) => Ok(seq[0].clone()),
Nil => Ok(Nil),
_ => error("invalid args to first"),
}
match a[0].clone() {
List(ref seq, _) | Vector(ref seq, _) if seq.len() == 0 => Ok(Nil),
List(ref seq, _) | Vector(ref seq, _) => Ok(seq[0].clone()),
Nil => Ok(Nil),
_ => error("invalid args to first"),
}
}
fn rest(a: MalArgs) -> MalRet {
match a[0].clone() {
List(ref seq,_) | Vector(ref seq,_) => {
if seq.len() > 1 {
Ok(list!(seq[1..].to_vec()))
} else {
Ok(list![])
}
},
Nil => Ok(list![]),
_ => error("invalid args to first"),
}
match a[0].clone() {
List(ref seq, _) | Vector(ref seq, _) => {
if seq.len() > 1 {
Ok(list!(seq[1..].to_vec()))
} else {
Ok(list![])
}
}
Nil => Ok(list![]),
_ => error("invalid args to first"),
}
}
fn apply(a: MalArgs) -> MalRet {
match a[a.len()-1] {
List(ref v,_) | Vector(ref v,_) => {
let f = &a[0];
let mut fargs = a[1..a.len()-1].to_vec();
fargs.extend_from_slice(&v);
f.apply(fargs)
},
_ => error("apply called with non-seq"),
}
match a[a.len() - 1] {
List(ref v, _) | Vector(ref v, _) => {
let f = &a[0];
let mut fargs = a[1..a.len() - 1].to_vec();
fargs.extend_from_slice(&v);
f.apply(fargs)
}
_ => error("apply called with non-seq"),
}
}
fn map(a: MalArgs) -> MalRet {
match a[1] {
List(ref v,_) | Vector(ref v,_) => {
let mut res = vec![];
for mv in v.iter() {
res.push(a[0].apply(vec![mv.clone()])?)
}
Ok(list!(res))
},
_ => error("map called with non-seq"),
}
match a[1] {
List(ref v, _) | Vector(ref v, _) => {
let mut res = vec![];
for mv in v.iter() {
res.push(a[0].apply(vec![mv.clone()])?)
}
Ok(list!(res))
}
_ => error("map called with non-seq"),
}
}
fn conj(a: MalArgs) -> MalRet {
match a[0] {
List(ref v,_) => {
let sl = a[1..].iter().rev().map(|a|{a.clone()}).collect::<Vec<MalVal>>();
Ok(list!([&sl[..],v].concat()))
},
Vector(ref v,_) => Ok(vector!([v,&a[1..]].concat())),
_ => error("conj: called with non-seq"),
}
match a[0] {
List(ref v, _) => {
let sl = a[1..]
.iter()
.rev()
.map(|a| a.clone())
.collect::<Vec<MalVal>>();
Ok(list!([&sl[..], v].concat()))
}
Vector(ref v, _) => Ok(vector!([v, &a[1..]].concat())),
_ => error("conj: called with non-seq"),
}
}
fn seq(a: MalArgs) -> MalRet {
match a[0] {
List(ref v,_) | Vector(ref v,_) if v.len() == 0 => Ok(Nil),
List(ref v,_) | Vector(ref v,_) => Ok(list!(v.to_vec())),
Str(ref s) if s.len() == 0 => Ok(Nil),
Str(ref s) if !a[0].keyword_q() => {
Ok(list!(s.chars().map(|c|{Str(c.to_string())}).collect()))
},
Nil => Ok(Nil),
_ => error("seq: called with non-seq"),
}
match a[0] {
List(ref v, _) | Vector(ref v, _) if v.len() == 0 => Ok(Nil),
List(ref v, _) | Vector(ref v, _) => Ok(list!(v.to_vec())),
Str(ref s) if s.len() == 0 => Ok(Nil),
Str(ref s) if !a[0].keyword_q() => {
Ok(list!(s.chars().map(|c| { Str(c.to_string()) }).collect()))
}
Nil => Ok(Nil),
_ => error("seq: called with non-seq"),
}
}
pub fn ns() -> Vec<(&'static str, MalVal)> {
vec![
("=", func(|a|{Ok(Bool(a[0] == a[1]))})),
("throw", func(|a|{Err(ErrMalVal(a[0].clone()))})),
("nil?", func(fn_is_type!(Nil))),
("true?", func(fn_is_type!(Bool(true)))),
("false?", func(fn_is_type!(Bool(false)))),
("symbol", func(symbol)),
("symbol?", func(fn_is_type!(Sym(_)))),
("string?", func(fn_is_type!(Str(ref s) if !s.starts_with("\u{29e}")))),
("keyword", func(|a|{a[0].keyword()})),
("keyword?", func(fn_is_type!(Str(ref s) if s.starts_with("\u{29e}")))),
("number?", func(fn_is_type!(Int(_)))),
("fn?", func(fn_is_type!(MalFunc{is_macro,..} if !is_macro,Func(_,_)))),
("macro?", func(fn_is_type!(MalFunc{is_macro,..} if is_macro))),
("pr-str", func(|a|Ok(Str(pr_seq(&a, true, "", "", " "))))),
("str", func(|a|Ok(Str(pr_seq(&a, false, "", "", ""))))),
("prn", func(|a|{println!("{}", pr_seq(&a, true, "", "", " ")); Ok(Nil)})),
("println", func(|a|{println!("{}", pr_seq(&a, false, "", "", " ")); Ok(Nil)})),
("read-string", func(fn_str!(|s|{read_str(s)}))),
("readline", func(readline)),
("slurp", func(fn_str!(|f|{slurp(f)}))),
("<", func(fn_t_int_int!(Bool,|i,j|{i<j}))),
("<=", func(fn_t_int_int!(Bool,|i,j|{i<=j}))),
(">", func(fn_t_int_int!(Bool,|i,j|{i>j}))),
(">=", func(fn_t_int_int!(Bool,|i,j|{i>=j}))),
("+", func(fn_t_int_int!(Int,|i,j|{i+j}))),
("-", func(fn_t_int_int!(Int,|i,j|{i-j}))),
("*", func(fn_t_int_int!(Int,|i,j|{i*j}))),
("/", func(fn_t_int_int!(Int,|i,j|{i/j}))),
("time-ms", func(time_ms)),
("sequential?", func(fn_is_type!(List(_,_),Vector(_,_)))),
("list", func(|a|{Ok(list!(a))})),
("list?", func(fn_is_type!(List(_,_)))),
("vector", func(|a|{Ok(vector!(a))})),
("vector?", func(fn_is_type!(Vector(_,_)))),
("hash-map", func(|a|{hash_map(a)})),
("map?", func(fn_is_type!(Hash(_,_)))),
("assoc", func(assoc)),
("dissoc", func(dissoc)),
("get", func(get)),
("contains?", func(contains_q)),
("keys", func(keys)),
("vals", func(vals)),
("cons", func(cons)),
("concat", func(concat)),
("empty?", func(|a|{a[0].empty_q()})),
("nth", func(nth)),
("first", func(first)),
("rest", func(rest)),
("count", func(|a|{a[0].count()})),
("apply", func(apply)),
("map", func(map)),
("conj", func(conj)),
("seq", func(seq)),
("meta", func(|a|{a[0].get_meta()})),
("with-meta", func(|a|{a[0].clone().with_meta(&a[1])})),
("atom", func(|a|{Ok(atom(&a[0]))})),
("atom?", func(fn_is_type!(Atom(_)))),
("deref", func(|a|{a[0].deref()})),
("reset!", func(|a|{a[0].reset_bang(&a[1])})),
("swap!", func(|a|{a[0].swap_bang(&a[1..].to_vec())})),
]
vec![
("=", func(|a| Ok(Bool(a[0] == a[1])))),
("throw", func(|a| Err(ErrMalVal(a[0].clone())))),
("nil?", func(fn_is_type!(Nil))),
("true?", func(fn_is_type!(Bool(true)))),
("false?", func(fn_is_type!(Bool(false)))),
("symbol", func(symbol)),
("symbol?", func(fn_is_type!(Sym(_)))),
(
"string?",
func(fn_is_type!(Str(ref s) if !s.starts_with("\u{29e}"))),
),
("keyword", func(|a| a[0].keyword())),
(
"keyword?",
func(fn_is_type!(Str(ref s) if s.starts_with("\u{29e}"))),
),
("number?", func(fn_is_type!(Int(_)))),
(
"fn?",
func(fn_is_type!(MalFunc{is_macro,..} if !is_macro,Func(_,_))),
),
(
"macro?",
func(fn_is_type!(MalFunc{is_macro,..} if is_macro)),
),
("pr-str", func(|a| Ok(Str(pr_seq(&a, true, "", "", " "))))),
("str", func(|a| Ok(Str(pr_seq(&a, false, "", "", ""))))),
(
"prn",
func(|a| {
println!("{}", pr_seq(&a, true, "", "", " "));
Ok(Nil)
}),
),
(
"println",
func(|a| {
println!("{}", pr_seq(&a, false, "", "", " "));
Ok(Nil)
}),
),
("read-string", func(fn_str!(|s| { read_str(s) }))),
("readline", func(readline)),
("slurp", func(fn_str!(|f| { slurp(f) }))),
("<", func(fn_t_int_int!(Bool, |i, j| { i < j }))),
("<=", func(fn_t_int_int!(Bool, |i, j| { i <= j }))),
(">", func(fn_t_int_int!(Bool, |i, j| { i > j }))),
(">=", func(fn_t_int_int!(Bool, |i, j| { i >= j }))),
("+", func(fn_t_int_int!(Int, |i, j| { i + j }))),
("-", func(fn_t_int_int!(Int, |i, j| { i - j }))),
("*", func(fn_t_int_int!(Int, |i, j| { i * j }))),
("/", func(fn_t_int_int!(Int, |i, j| { i / j }))),
("time-ms", func(time_ms)),
("sequential?", func(fn_is_type!(List(_, _), Vector(_, _)))),
("list", func(|a| Ok(list!(a)))),
("list?", func(fn_is_type!(List(_, _)))),
("vector", func(|a| Ok(vector!(a)))),
("vector?", func(fn_is_type!(Vector(_, _)))),
("hash-map", func(|a| hash_map(a))),
("map?", func(fn_is_type!(Hash(_, _)))),
("assoc", func(assoc)),
("dissoc", func(dissoc)),
("get", func(get)),
("contains?", func(contains_q)),
("keys", func(keys)),
("vals", func(vals)),
("cons", func(cons)),
("concat", func(concat)),
("empty?", func(|a| a[0].empty_q())),
("nth", func(nth)),
("first", func(first)),
("rest", func(rest)),
("count", func(|a| a[0].count())),
("apply", func(apply)),
("map", func(map)),
("conj", func(conj)),
("seq", func(seq)),
("meta", func(|a| a[0].get_meta())),
("with-meta", func(|a| a[0].clone().with_meta(&a[1]))),
("atom", func(|a| Ok(atom(&a[0])))),
("atom?", func(fn_is_type!(Atom(_)))),
("deref", func(|a| a[0].deref())),
("reset!", func(|a| a[0].reset_bang(&a[1]))),
("swap!", func(|a| a[0].swap_bang(&a[1..].to_vec()))),
]
}
// vim: ts=2:sw=2:expandtab

View File

@ -1,16 +1,16 @@
use std::rc::Rc;
use std::cell::RefCell;
use std::rc::Rc;
//use std::collections::HashMap;
use fnv::FnvHashMap;
use types::{MalVal,MalRet,MalErr,error};
use types::MalVal::{Nil,Sym,List,Vector};
use types::MalErr::{ErrString};
use types::MalErr::ErrString;
use types::MalVal::{List, Nil, Sym, Vector};
use types::{error, MalErr, MalRet, MalVal};
#[derive(Debug)]
pub struct EnvStruct {
data: RefCell<FnvHashMap<String,MalVal>>,
pub outer: Option<Env>,
data: RefCell<FnvHashMap<String, MalVal>>,
pub outer: Option<Env>,
}
pub type Env = Rc<EnvStruct>;
@ -19,66 +19,67 @@ pub type Env = Rc<EnvStruct>;
// a deftype (i.e. Env)
pub fn env_new(outer: Option<Env>) -> Env {
Rc::new(EnvStruct{data: RefCell::new(FnvHashMap::default()), outer: outer})
Rc::new(EnvStruct {
data: RefCell::new(FnvHashMap::default()),
outer: outer,
})
}
// TODO: mbinds and exprs as & types
pub fn env_bind(outer: Option<Env>, mbinds: MalVal,
exprs: Vec<MalVal>) -> Result<Env,MalErr> {
let env = env_new(outer);
match mbinds {
List(binds,_) | Vector(binds,_) => {
for (i, b) in binds.iter().enumerate() {
match b {
Sym(s) if s == "&" => {
env_set(&env, binds[i+1].clone(), list!(exprs[i..].to_vec()))?;
break;
},
_ => {
env_set(&env, b.clone(), exprs[i].clone())?;
},
pub fn env_bind(outer: Option<Env>, mbinds: MalVal, exprs: Vec<MalVal>) -> Result<Env, MalErr> {
let env = env_new(outer);
match mbinds {
List(binds, _) | Vector(binds, _) => {
for (i, b) in binds.iter().enumerate() {
match b {
Sym(s) if s == "&" => {
env_set(&env, binds[i + 1].clone(), list!(exprs[i..].to_vec()))?;
break;
}
_ => {
env_set(&env, b.clone(), exprs[i].clone())?;
}
}
}
Ok(env)
}
}
Ok(env)
},
_ => Err(ErrString("env_bind binds not List/Vector".to_string())),
}
_ => Err(ErrString("env_bind binds not List/Vector".to_string())),
}
}
pub fn env_find(env: &Env, key: &str) -> Option<Env> {
match (env.data.borrow().contains_key(key), env.outer.clone()) {
(true, _) => Some(env.clone()),
(false, Some(o)) => env_find(&o, key),
_ => None,
}
match (env.data.borrow().contains_key(key), env.outer.clone()) {
(true, _) => Some(env.clone()),
(false, Some(o)) => env_find(&o, key),
_ => None,
}
}
pub fn env_get(env: &Env, key: &MalVal) -> MalRet {
match key {
Sym(ref s) => {
match env_find(env, s) {
Some(e) => Ok(e.data.borrow().get(s)
.ok_or(ErrString(format!("'{}' not found", s)))?
.clone()),
_ => error(&format!("'{}' not found", s)),
}
},
_ => error("Env.get called with non-Str"),
}
match key {
Sym(ref s) => match env_find(env, s) {
Some(e) => Ok(e
.data
.borrow()
.get(s)
.ok_or(ErrString(format!("'{}' not found", s)))?
.clone()),
_ => error(&format!("'{}' not found", s)),
},
_ => error("Env.get called with non-Str"),
}
}
pub fn env_set(env: &Env, key: MalVal, val: MalVal) -> MalRet {
match key {
Sym(ref s) => {
env.data.borrow_mut().insert(s.to_string(), val.clone());
Ok(val)
},
_ => error("Env.set called with non-Str")
}
match key {
Sym(ref s) => {
env.data.borrow_mut().insert(s.to_string(), val.clone());
Ok(val)
}
_ => error("Env.set called with non-Str"),
}
}
pub fn env_sets(env: &Env, key: &str, val: MalVal) {
env.data.borrow_mut().insert(key.to_string(), val);
env.data.borrow_mut().insert(key.to_string(), val);
}
// vim: ts=2:sw=2:expandtab

View File

@ -1,60 +1,61 @@
use types::MalVal;
use types::MalVal::{Nil,Bool,Int,Str,Sym,List,Vector,Hash,Func,MalFunc,Atom};
use types::MalVal::{Atom, Bool, Func, Hash, Int, List, MalFunc, Nil, Str, Sym, Vector};
fn escape_str(s: &str) -> String {
s.chars().map(|c| {
match c {
'"' => "\\\"".to_string(),
'\n' => "\\n".to_string(),
'\\' => "\\\\".to_string(),
_ => c.to_string(),
}
}).collect::<Vec<String>>().join("")
s.chars()
.map(|c| match c {
'"' => "\\\"".to_string(),
'\n' => "\\n".to_string(),
'\\' => "\\\\".to_string(),
_ => c.to_string(),
})
.collect::<Vec<String>>()
.join("")
}
impl MalVal {
pub fn pr_str(&self, print_readably: bool) -> String {
match self {
Nil => String::from("nil"),
Bool(true) => String::from("true"),
Bool(false) => String::from("false"),
Int(i) => format!("{}", i),
//Float(f) => format!("{}", f),
Str(s) => {
if s.starts_with("\u{29e}") {
format!(":{}", &s[2..])
} else if print_readably {
format!("\"{}\"", escape_str(s))
} else {
s.clone()
pub fn pr_str(&self, print_readably: bool) -> String {
match self {
Nil => String::from("nil"),
Bool(true) => String::from("true"),
Bool(false) => String::from("false"),
Int(i) => format!("{}", i),
//Float(f) => format!("{}", f),
Str(s) => {
if s.starts_with("\u{29e}") {
format!(":{}", &s[2..])
} else if print_readably {
format!("\"{}\"", escape_str(s))
} else {
s.clone()
}
}
Sym(s) => s.clone(),
List(l, _) => pr_seq(&**l, print_readably, "(", ")", " "),
Vector(l, _) => pr_seq(&**l, print_readably, "[", "]", " "),
Hash(hm, _) => {
let l: Vec<MalVal> = hm
.iter()
.flat_map(|(k, v)| vec![Str(k.to_string()), v.clone()])
.collect();
pr_seq(&l, print_readably, "{", "}", " ")
}
Func(f, _) => format!("#<fn {:?}>", f),
MalFunc {
ast: a, params: p, ..
} => format!("(fn* {} {})", p.pr_str(true), a.pr_str(true)),
Atom(a) => format!("(atom {})", a.borrow().pr_str(true)),
}
}
Sym(s) => s.clone(),
List(l,_) => pr_seq(&**l, print_readably, "(", ")", " "),
Vector(l,_) => pr_seq(&**l, print_readably, "[", "]", " "),
Hash(hm,_) => {
let l: Vec<MalVal> = hm
.iter()
.flat_map(|(k, v)| { vec![Str(k.to_string()), v.clone()] })
.collect();
pr_seq(&l, print_readably, "{", "}", " ")
},
Func(f,_) => format!("#<fn {:?}>", f),
MalFunc{ast: a, params: p, ..} => {
format!("(fn* {} {})", p.pr_str(true), a.pr_str(true))
},
Atom(a) => format!("(atom {})", a.borrow().pr_str(true)),
}
}
}
pub fn pr_seq(seq: &Vec<MalVal>, print_readably: bool,
start: &str, end: &str, join: &str) -> String {
let strs: Vec<String> = seq
.iter()
.map(|x| x.pr_str(print_readably))
.collect();
format!("{}{}{}", start, strs.join(join), end)
pub fn pr_seq(
seq: &Vec<MalVal>,
print_readably: bool,
start: &str,
end: &str,
join: &str,
) -> String {
let strs: Vec<String> = seq.iter().map(|x| x.pr_str(print_readably)).collect();
format!("{}{}{}", start, strs.join(join), end)
}
// vim: ts=2:sw=2:expandtab

View File

@ -1,9 +1,9 @@
use regex::{Captures, Regex};
use std::rc::Rc;
use regex::{Regex,Captures};
use types::{MalVal,MalRet,MalErr,error,hash_map};
use types::MalVal::{Nil,Bool,Int,Str,Sym,List,Vector};
use types::MalErr::ErrString;
use types::MalVal::{Bool, Int, List, Nil, Str, Sym, Vector};
use types::{error, hash_map, MalErr, MalRet, MalVal};
#[derive(Debug, Clone)]
struct Reader {
@ -12,130 +12,145 @@ struct Reader {
}
impl Reader {
fn next(&mut self) -> Result<String,MalErr> {
self.pos = self.pos + 1;
Ok(self.tokens.get(self.pos-1)
.ok_or(ErrString("underflow".to_string()))?.to_string())
}
fn peek(&self) -> Result<String,MalErr> {
Ok(self.tokens.get(self.pos)
.ok_or(ErrString("underflow".to_string()))?.to_string())
}
fn next(&mut self) -> Result<String, MalErr> {
self.pos = self.pos + 1;
Ok(self
.tokens
.get(self.pos - 1)
.ok_or(ErrString("underflow".to_string()))?
.to_string())
}
fn peek(&self) -> Result<String, MalErr> {
Ok(self
.tokens
.get(self.pos)
.ok_or(ErrString("underflow".to_string()))?
.to_string())
}
}
fn tokenize(str: &str) -> Vec<String> {
lazy_static! {
static ref RE: Regex = Regex::new(r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]+)"###).unwrap();
}
lazy_static! {
static ref RE: Regex = Regex::new(
r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]+)"###
)
.unwrap();
}
let mut res = vec![];
for cap in RE.captures_iter(str) {
if cap[1].starts_with(";") { continue }
res.push(String::from(&cap[1]));
}
res
let mut res = vec![];
for cap in RE.captures_iter(str) {
if cap[1].starts_with(";") {
continue;
}
res.push(String::from(&cap[1]));
}
res
}
fn unescape_str(s: &str) -> String {
lazy_static! {
static ref RE: Regex = Regex::new(r#"\\(.)"#).unwrap();
}
RE.replace_all(&s, |caps: &Captures| {
format!("{}", if &caps[1] == "n" { "\n" } else { &caps[1] })
}).to_string()
lazy_static! {
static ref RE: Regex = Regex::new(r#"\\(.)"#).unwrap();
}
RE.replace_all(&s, |caps: &Captures| {
format!("{}", if &caps[1] == "n" { "\n" } else { &caps[1] })
})
.to_string()
}
fn read_atom(rdr: &mut Reader) -> MalRet {
lazy_static! {
static ref INT_RE: Regex = Regex::new(r"^-?[0-9]+$").unwrap();
static ref STR_RE: Regex = Regex::new(r#""(?:\\.|[^\\"])*""#).unwrap();
}
let token = rdr.next()?;
match &token[..] {
"nil" => Ok(Nil),
"false" => Ok(Bool(false)),
"true" => Ok(Bool(true)),
_ => {
if INT_RE.is_match(&token) {
Ok(Int(token.parse().unwrap()))
} else if STR_RE.is_match(&token) {
Ok(Str(unescape_str(&token[1..token.len()-1])))
} else if token.starts_with("\"") {
error("expected '\"', got EOF")
} else if token.starts_with(":") {
Ok(Str(format!("\u{29e}{}", &token[1..])))
} else {
Ok(Sym(token.to_string()))
}
lazy_static! {
static ref INT_RE: Regex = Regex::new(r"^-?[0-9]+$").unwrap();
static ref STR_RE: Regex = Regex::new(r#""(?:\\.|[^\\"])*""#).unwrap();
}
let token = rdr.next()?;
match &token[..] {
"nil" => Ok(Nil),
"false" => Ok(Bool(false)),
"true" => Ok(Bool(true)),
_ => {
if INT_RE.is_match(&token) {
Ok(Int(token.parse().unwrap()))
} else if STR_RE.is_match(&token) {
Ok(Str(unescape_str(&token[1..token.len() - 1])))
} else if token.starts_with("\"") {
error("expected '\"', got EOF")
} else if token.starts_with(":") {
Ok(Str(format!("\u{29e}{}", &token[1..])))
} else {
Ok(Sym(token.to_string()))
}
}
}
}
}
fn read_seq(rdr: &mut Reader, end: &str) -> MalRet {
let mut seq : Vec<MalVal> = vec![];
rdr.next()?;
loop {
let token = match rdr.peek() {
Ok(t) => t,
Err(_) => return error(&format!("expected '{}', got EOF", end))
};
if token == end { break }
seq.push(read_form(rdr)?)
}
let _ = rdr.next();
match end {
")" => Ok(list!(seq)),
"]" => Ok(vector!(seq)),
"}" => hash_map(seq),
_ => error("read_seq unknown end value"),
}
let mut seq: Vec<MalVal> = vec![];
rdr.next()?;
loop {
let token = match rdr.peek() {
Ok(t) => t,
Err(_) => return error(&format!("expected '{}', got EOF", end)),
};
if token == end {
break;
}
seq.push(read_form(rdr)?)
}
let _ = rdr.next();
match end {
")" => Ok(list!(seq)),
"]" => Ok(vector!(seq)),
"}" => hash_map(seq),
_ => error("read_seq unknown end value"),
}
}
fn read_form(rdr: &mut Reader) -> MalRet {
let token = rdr.peek()?;
match &token[..] {
"'" => {
let _ = rdr.next();
Ok(list![Sym("quote".to_string()), read_form(rdr)?])
},
"`" => {
let _ = rdr.next();
Ok(list![Sym("quasiquote".to_string()), read_form(rdr)?])
},
"~" => {
let _ = rdr.next();
Ok(list![Sym("unquote".to_string()), read_form(rdr)?])
},
"~@" => {
let _ = rdr.next();
Ok(list![Sym("splice-unquote".to_string()), read_form(rdr)?])
},
"^" => {
let _ = rdr.next();
let meta = read_form(rdr)?;
Ok(list![Sym("with-meta".to_string()), read_form(rdr)?, meta])
},
"@" => {
let _ = rdr.next();
Ok(list![Sym("deref".to_string()), read_form(rdr)?])
},
")" => error("unexpected ')'"),
"(" => read_seq(rdr, ")"),
"]" => error("unexpected ']'"),
"[" => read_seq(rdr, "]"),
"}" => error("unexpected '}'"),
"{" => read_seq(rdr, "}"),
_ => read_atom(rdr),
}
let token = rdr.peek()?;
match &token[..] {
"'" => {
let _ = rdr.next();
Ok(list![Sym("quote".to_string()), read_form(rdr)?])
}
"`" => {
let _ = rdr.next();
Ok(list![Sym("quasiquote".to_string()), read_form(rdr)?])
}
"~" => {
let _ = rdr.next();
Ok(list![Sym("unquote".to_string()), read_form(rdr)?])
}
"~@" => {
let _ = rdr.next();
Ok(list![Sym("splice-unquote".to_string()), read_form(rdr)?])
}
"^" => {
let _ = rdr.next();
let meta = read_form(rdr)?;
Ok(list![Sym("with-meta".to_string()), read_form(rdr)?, meta])
}
"@" => {
let _ = rdr.next();
Ok(list![Sym("deref".to_string()), read_form(rdr)?])
}
")" => error("unexpected ')'"),
"(" => read_seq(rdr, ")"),
"]" => error("unexpected ']'"),
"[" => read_seq(rdr, "]"),
"}" => error("unexpected '}'"),
"{" => read_seq(rdr, "}"),
_ => read_atom(rdr),
}
}
pub fn read_str(str: String) -> MalRet {
let tokens = tokenize(&str);
//println!("tokens: {:?}", tokens);
if tokens.len() == 0 {
return error("no input");
}
read_form(&mut Reader { pos: 0, tokens: tokens })
let tokens = tokenize(&str);
//println!("tokens: {:?}", tokens);
if tokens.len() == 0 {
return error("no input");
}
read_form(&mut Reader {
pos: 0,
tokens: tokens,
})
}
// vim: ts=2:sw=2:expandtab

View File

@ -4,30 +4,28 @@ use rustyline::error::ReadlineError;
use rustyline::Editor;
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.");
}
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 {
println!("{}", line);
}
},
Err(ReadlineError::Interrupted) => continue,
Err(ReadlineError::Eof) => break,
Err(err) => {
println!("Error: {:?}", err);
break
}
// `()` 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.");
}
}
}
// vim: ts=2:sw=2:expandtab
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 {
println!("{}", line);
}
}
Err(ReadlineError::Interrupted) => continue,
Err(ReadlineError::Eof) => break,
Err(err) => {
println!("Error: {:?}", err);
break;
}
}
}
}

View File

@ -1,8 +1,8 @@
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate itertools;
extern crate fnv;
extern crate itertools;
extern crate regex;
extern crate rustyline;
use rustyline::error::ReadlineError;
@ -11,43 +11,41 @@ use rustyline::Editor;
#[macro_use]
#[allow(dead_code)]
mod types;
use types::{format_error};
mod reader;
use types::format_error;
mod printer;
mod reader;
// TODO: figure out a way to avoid including env
#[allow(dead_code)]
mod env;
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.");
}
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 reader::read_str(line) {
Ok(mv) => {
println!("{}", mv.pr_str(true));
},
Err(e) => println!("Error: {}", format_error(e)),
}
}
},
Err(ReadlineError::Interrupted) => continue,
Err(ReadlineError::Eof) => break,
Err(err) => {
println!("Error: {:?}", err);
break
}
// `()` 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.");
}
}
}
// vim: ts=2:sw=2:expandtab
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 reader::read_str(line) {
Ok(mv) => {
println!("{}", mv.pr_str(true));
}
Err(e) => println!("Error: {}", format_error(e)),
}
}
}
Err(ReadlineError::Interrupted) => continue,
Err(ReadlineError::Eof) => break,
Err(err) => {
println!("Error: {:?}", err);
break;
}
}
}
}

View File

@ -4,9 +4,9 @@ use fnv::FnvHashMap;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate itertools;
extern crate fnv;
extern crate itertools;
extern crate regex;
extern crate rustyline;
use rustyline::error::ReadlineError;
@ -15,121 +15,122 @@ use rustyline::Editor;
#[macro_use]
#[allow(dead_code)]
mod types;
use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error,func};
use types::MalVal::{Nil,Int,Sym,List,Vector,Hash};
use types::MalErr::{ErrString};
mod reader;
use types::MalErr::ErrString;
use types::MalVal::{Hash, Int, List, Nil, Sym, Vector};
use 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>;
pub type Env = FnvHashMap<String, MalVal>;
// read
fn read(str: &str) -> MalRet {
reader::read_str(str.to_string())
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()),
}
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")
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),
}
_ => eval_ast(&ast, &env),
}
}
// print
fn print(ast: &MalVal) -> String {
ast.pr_str(true)
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 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 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
}
// `()` 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.");
}
}
}
// vim: ts=2:sw=2:expandtab
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;
}
}
}
}

View File

@ -5,9 +5,9 @@ use itertools::Itertools;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate itertools;
extern crate fnv;
extern crate itertools;
extern crate regex;
extern crate rustyline;
use rustyline::error::ReadlineError;
@ -16,144 +16,147 @@ use rustyline::Editor;
#[macro_use]
#[allow(dead_code)]
mod types;
use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error,func};
use types::MalVal::{Nil,Int,Sym,List,Vector,Hash};
mod reader;
mod printer;
use types::MalVal::{Hash, Int, List, Nil, Sym, Vector};
use types::{error, format_error, func, MalArgs, MalErr, MalRet, MalVal};
mod env;
use env::{Env,env_new,env_get,env_set,env_sets};
mod printer;
mod reader;
use env::{env_get, env_new, env_set, env_sets, Env};
// read
fn read(str: &str) -> MalRet {
reader::read_str(str.to_string())
reader::read_str(str.to_string())
}
// eval
fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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()),
}
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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); }
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
},
Sym(ref a0sym) if a0sym == "let*" => {
let let_env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds,_) | Vector(ref binds,_) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(&let_env, b.clone(),
eval(e.clone(), let_env.clone())?);
},
_ => {
return error("let* with non-Sym binding");
}
match ast.clone() {
List(l, _) => {
if l.len() == 0 {
return Ok(ast);
}
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
}
}
},
_ => {
return error("let* with non-List bindings");
Sym(ref a0sym) if a0sym == "let*" => {
let let_env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds, _) | Vector(ref binds, _) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(
&let_env,
b.clone(),
eval(e.clone(), let_env.clone())?,
);
}
_ => {
return error("let* with non-Sym binding");
}
}
}
}
_ => {
return error("let* with non-List bindings");
}
};
eval(a2, let_env)
}
_ => 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(a2, let_env)
},
_ => {
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),
}
_ => eval_ast(&ast, &env),
}
}
// print
fn print(ast: &MalVal) -> String {
ast.pr_str(true)
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 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 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 repl_env = env_new(None);
env_sets(&repl_env, "+", func(|a:MalArgs|{int_op(|i,j|{i+j},a)}));
env_sets(&repl_env, "-", func(|a:MalArgs|{int_op(|i,j|{i-j},a)}));
env_sets(&repl_env, "*", func(|a:MalArgs|{int_op(|i,j|{i*j},a)}));
env_sets(&repl_env, "/", 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
}
// `()` 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.");
}
}
}
// vim: ts=2:sw=2:expandtab
let repl_env = env_new(None);
env_sets(&repl_env, "+", func(|a: MalArgs| int_op(|i, j| i + j, a)));
env_sets(&repl_env, "-", func(|a: MalArgs| int_op(|i, j| i - j, a)));
env_sets(&repl_env, "*", func(|a: MalArgs| int_op(|i, j| i * j, a)));
env_sets(&repl_env, "/", 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;
}
}
}
}

View File

@ -5,9 +5,9 @@ use itertools::Itertools;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate itertools;
extern crate fnv;
extern crate itertools;
extern crate regex;
extern crate rustyline;
use rustyline::error::ReadlineError;
@ -15,168 +15,170 @@ use rustyline::Editor;
#[macro_use]
mod types;
use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error};
use types::MalVal::{Nil,Bool,Sym,List,Vector,Hash,MalFunc};
mod reader;
mod printer;
use types::MalVal::{Bool, Hash, List, MalFunc, Nil, Sym, Vector};
use types::{error, format_error, MalArgs, MalErr, MalRet, MalVal};
mod env;
use env::{Env,env_new,env_get,env_set,env_sets};
mod printer;
mod reader;
use env::{env_get, env_new, env_set, env_sets, Env};
#[macro_use]
mod core;
// read
fn read(str: &str) -> MalRet {
reader::read_str(str.to_string())
reader::read_str(str.to_string())
}
// eval
fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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()),
}
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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); }
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
},
Sym(ref a0sym) if a0sym == "let*" => {
let let_env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds,_) | Vector(ref binds,_) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(&let_env, b.clone(),
eval(e.clone(), let_env.clone())?);
},
_ => {
return error("let* with non-Sym binding");
}
match ast.clone() {
List(l, _) => {
if l.len() == 0 {
return Ok(ast);
}
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
}
}
},
_ => {
return error("let* with non-List bindings");
Sym(ref a0sym) if a0sym == "let*" => {
let let_env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds, _) | Vector(ref binds, _) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(
&let_env,
b.clone(),
eval(e.clone(), let_env.clone())?,
);
}
_ => {
return error("let* with non-Sym binding");
}
}
}
}
_ => {
return error("let* with non-List bindings");
}
};
eval(a2, let_env)
}
Sym(ref a0sym) if a0sym == "do" => match eval_ast(&list!(l[1..].to_vec()), &env)? {
List(el, _) => Ok(el.last().unwrap_or(&Nil).clone()),
_ => error("invalid do form"),
},
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => eval(l[3].clone(), env.clone()),
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => eval(l[2].clone(), env.clone()),
_ => Ok(Nil),
}
}
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc {
eval: eval,
ast: Rc::new(a2),
env: env,
params: Rc::new(a1),
is_macro: false,
meta: Rc::new(Nil),
})
}
_ => 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(a2, let_env)
},
Sym(ref a0sym) if a0sym == "do" => {
match eval_ast(&list!(l[1..].to_vec()), &env)? {
List(el,_) => Ok(el.last().unwrap_or(&Nil).clone()),
_ => error("invalid do form"),
}
},
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => {
eval(l[3].clone(), env.clone())
},
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => {
eval(l[2].clone(), env.clone())
},
_ => Ok(Nil)
}
},
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc{eval: eval, ast: Rc::new(a2), env: env,
params: Rc::new(a1), is_macro: false,
meta: Rc::new(Nil)})
},
_ => {
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),
}
_ => eval_ast(&ast, &env),
}
}
// print
fn print(ast: &MalVal) -> String {
ast.pr_str(true)
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 rep(str: &str, env: &Env) -> Result<String, MalErr> {
let ast = read(str)?;
let exp = eval(ast, env.clone())?;
Ok(print(&exp))
}
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.");
}
// core.rs: defined using rust
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
// core.mal: defined using the language itself
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
// main repl loop
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
}
// `()` 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.");
}
}
}
// vim: ts=2:sw=2:expandtab
// core.rs: defined using rust
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
// core.mal: defined using the language itself
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
// main repl loop
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;
}
}
}
}

View File

@ -5,9 +5,9 @@ use itertools::Itertools;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate itertools;
extern crate fnv;
extern crate itertools;
extern crate regex;
extern crate rustyline;
use rustyline::error::ReadlineError;
@ -15,195 +15,206 @@ use rustyline::Editor;
#[macro_use]
mod types;
use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error};
use types::MalVal::{Nil,Bool,Sym,List,Vector,Hash,Func,MalFunc};
mod reader;
mod printer;
use types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Sym, Vector};
use types::{error, format_error, MalArgs, MalErr, MalRet, MalVal};
mod env;
use env::{Env,env_new,env_bind,env_get,env_set,env_sets};
mod printer;
mod reader;
use env::{env_bind, env_get, env_new, env_set, env_sets, Env};
#[macro_use]
mod core;
// read
fn read(str: &str) -> MalRet {
reader::read_str(str.to_string())
reader::read_str(str.to_string())
}
// eval
fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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()),
}
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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(mut ast: MalVal, mut env: Env) -> MalRet {
let ret: MalRet;
let ret: MalRet;
'tco: loop {
ret = match ast.clone() {
List(l,_) => {
if l.len() == 0 { return Ok(ast); }
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
},
Sym(ref a0sym) if a0sym == "let*" => {
env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds,_) | Vector(ref binds,_) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(&env, b.clone(),
eval(e.clone(), env.clone())?);
},
_ => {
return error("let* with non-Sym binding");
}
'tco: loop {
ret = match ast.clone() {
List(l, _) => {
if l.len() == 0 {
return Ok(ast);
}
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
}
Sym(ref a0sym) if a0sym == "let*" => {
env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds, _) | Vector(ref binds, _) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(
&env,
b.clone(),
eval(e.clone(), env.clone())?,
);
}
_ => {
return error("let* with non-Sym binding");
}
}
}
}
_ => {
return error("let* with non-List bindings");
}
};
ast = a2;
continue 'tco;
}
Sym(ref a0sym) if a0sym == "do" => {
match eval_ast(&list!(l[1..l.len() - 1].to_vec()), &env)? {
List(_, _) => {
ast = l.last().unwrap_or(&Nil).clone();
continue 'tco;
}
_ => error("invalid do form"),
}
}
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => {
ast = l[3].clone();
continue 'tco;
}
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => {
ast = l[2].clone();
continue 'tco;
}
_ => Ok(Nil),
}
}
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc {
eval: eval,
ast: Rc::new(a2),
env: env,
params: Rc::new(a1),
is_macro: false,
meta: Rc::new(Nil),
})
}
_ => match eval_ast(&ast, &env)? {
List(ref el, _) => {
let ref f = el[0].clone();
let args = el[1..].to_vec();
match f {
Func(_, _) => f.apply(args),
MalFunc {
ast: mast,
env: menv,
params,
..
} => {
let a = &**mast;
let p = &**params;
env = env_bind(Some(menv.clone()), p.clone(), args)?;
ast = a.clone();
continue 'tco;
}
_ => error("attempt to call non-function"),
}
}
_ => error("expected a list"),
},
}
}
},
_ => {
return error("let* with non-List bindings");
}
};
ast = a2;
continue 'tco;
},
Sym(ref a0sym) if a0sym == "do" => {
match eval_ast(&list!(l[1..l.len()-1].to_vec()), &env)? {
List(_,_) => {
ast = l.last().unwrap_or(&Nil).clone();
continue 'tco;
},
_ => error("invalid do form"),
}
},
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => {
ast = l[3].clone();
continue 'tco;
},
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => {
ast = l[2].clone();
continue 'tco;
},
_ => Ok(Nil)
}
},
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc{eval: eval, ast: Rc::new(a2), env: env,
params: Rc::new(a1), is_macro: false,
meta: Rc::new(Nil)})
},
_ => {
match eval_ast(&ast, &env)? {
List(ref el,_) => {
let ref f = el[0].clone();
let args = el[1..].to_vec();
match f {
Func(_,_) => f.apply(args),
MalFunc{ast: mast, env: menv, params, ..} => {
let a = &**mast;
let p = &**params;
env = env_bind(Some(menv.clone()), p.clone(), args)?;
ast = a.clone();
continue 'tco;
},
_ => error("attempt to call non-function"),
}
},
_ => {
error("expected a list")
}
}
}
}
},
_ => eval_ast(&ast, &env),
};
_ => eval_ast(&ast, &env),
};
break;
break;
} // end 'tco loop
} // end 'tco loop
ret
ret
}
// print
fn print(ast: &MalVal) -> String {
ast.pr_str(true)
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 rep(str: &str, env: &Env) -> Result<String, MalErr> {
let ast = read(str)?;
let exp = eval(ast, env.clone())?;
Ok(print(&exp))
}
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.");
}
// core.rs: defined using rust
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
// core.mal: defined using the language itself
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
// main repl loop
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
}
// `()` 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.");
}
}
}
// vim: ts=2:sw=2:expandtab
// core.rs: defined using rust
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
// core.mal: defined using the language itself
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
// main repl loop
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;
}
}
}
}

View File

@ -5,9 +5,9 @@ use itertools::Itertools;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate itertools;
extern crate fnv;
extern crate itertools;
extern crate regex;
extern crate rustyline;
use rustyline::error::ReadlineError;
@ -15,218 +15,232 @@ use rustyline::Editor;
#[macro_use]
mod types;
use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error};
use types::MalVal::{Nil,Bool,Str,Sym,List,Vector,Hash,Func,MalFunc};
mod reader;
mod printer;
use types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Str, Sym, Vector};
use types::{error, format_error, MalArgs, MalErr, MalRet, MalVal};
mod env;
use env::{Env,env_new,env_bind,env_get,env_set,env_sets};
mod printer;
mod reader;
use env::{env_bind, env_get, env_new, env_set, env_sets, Env};
#[macro_use]
mod core;
// read
fn read(str: &str) -> MalRet {
reader::read_str(str.to_string())
reader::read_str(str.to_string())
}
// eval
fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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()),
}
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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(mut ast: MalVal, mut env: Env) -> MalRet {
let ret: MalRet;
let ret: MalRet;
'tco: loop {
ret = match ast.clone() {
List(l,_) => {
if l.len() == 0 { return Ok(ast); }
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
},
Sym(ref a0sym) if a0sym == "let*" => {
env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds,_) | Vector(ref binds,_) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(&env, b.clone(),
eval(e.clone(), env.clone())?);
},
_ => {
return error("let* with non-Sym binding");
}
'tco: loop {
ret = match ast.clone() {
List(l, _) => {
if l.len() == 0 {
return Ok(ast);
}
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
}
Sym(ref a0sym) if a0sym == "let*" => {
env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds, _) | Vector(ref binds, _) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(
&env,
b.clone(),
eval(e.clone(), env.clone())?,
);
}
_ => {
return error("let* with non-Sym binding");
}
}
}
}
_ => {
return error("let* with non-List bindings");
}
};
ast = a2;
continue 'tco;
}
Sym(ref a0sym) if a0sym == "do" => {
match eval_ast(&list!(l[1..l.len() - 1].to_vec()), &env)? {
List(_, _) => {
ast = l.last().unwrap_or(&Nil).clone();
continue 'tco;
}
_ => error("invalid do form"),
}
}
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => {
ast = l[3].clone();
continue 'tco;
}
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => {
ast = l[2].clone();
continue 'tco;
}
_ => Ok(Nil),
}
}
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc {
eval: eval,
ast: Rc::new(a2),
env: env,
params: Rc::new(a1),
is_macro: false,
meta: Rc::new(Nil),
})
}
Sym(ref a0sym) if a0sym == "eval" => {
ast = eval(l[1].clone(), env.clone())?;
while let Some(ref e) = env.clone().outer {
env = e.clone();
}
continue 'tco;
}
_ => match eval_ast(&ast, &env)? {
List(ref el, _) => {
let ref f = el[0].clone();
let args = el[1..].to_vec();
match f {
Func(_, _) => f.apply(args),
MalFunc {
ast: mast,
env: menv,
params,
..
} => {
let a = &**mast;
let p = &**params;
env = env_bind(Some(menv.clone()), p.clone(), args)?;
ast = a.clone();
continue 'tco;
}
_ => error("attempt to call non-function"),
}
}
_ => error("expected a list"),
},
}
}
},
_ => {
return error("let* with non-List bindings");
}
};
ast = a2;
continue 'tco;
},
Sym(ref a0sym) if a0sym == "do" => {
match eval_ast(&list!(l[1..l.len()-1].to_vec()), &env)? {
List(_,_) => {
ast = l.last().unwrap_or(&Nil).clone();
continue 'tco;
},
_ => error("invalid do form"),
}
},
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => {
ast = l[3].clone();
continue 'tco;
},
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => {
ast = l[2].clone();
continue 'tco;
},
_ => Ok(Nil)
}
},
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc{eval: eval, ast: Rc::new(a2), env: env,
params: Rc::new(a1), is_macro: false,
meta: Rc::new(Nil)})
},
Sym(ref a0sym) if a0sym == "eval" => {
ast = eval(l[1].clone(), env.clone())?;
while let Some(ref e) = env.clone().outer {
env = e.clone();
}
continue 'tco;
},
_ => {
match eval_ast(&ast, &env)? {
List(ref el,_) => {
let ref f = el[0].clone();
let args = el[1..].to_vec();
match f {
Func(_,_) => f.apply(args),
MalFunc{ast: mast, env: menv, params, ..} => {
let a = &**mast;
let p = &**params;
env = env_bind(Some(menv.clone()), p.clone(), args)?;
ast = a.clone();
continue 'tco;
},
_ => error("attempt to call non-function"),
}
},
_ => {
error("expected a list")
}
}
}
}
},
_ => eval_ast(&ast, &env),
};
_ => eval_ast(&ast, &env),
};
break;
break;
} // end 'tco loop
} // end 'tco loop
ret
ret
}
// print
fn print(ast: &MalVal) -> String {
ast.pr_str(true)
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 rep(str: &str, env: &Env) -> Result<String, MalErr> {
let ast = read(str)?;
let exp = eval(ast, env.clone())?;
Ok(print(&exp))
}
fn main() {
let mut args = std::env::args();
let arg1 = args.nth(1);
let mut args = std::env::args();
let arg1 = args.nth(1);
// `()` 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.");
}
// core.rs: defined using rust
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
// core.mal: defined using the language itself
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", &repl_env);
// Invoked with arguments
if let Some(f) = arg1 {
match rep(&format!("(load-file \"{}\")",f), &repl_env) {
Ok(_) => std::process::exit(0),
Err(e) => {
println!("Error: {}", format_error(e));
std::process::exit(1);
}
// `()` 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.");
}
}
// main repl loop
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)),
}
// core.rs: defined using rust
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
// core.mal: defined using the language itself
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
let _ = rep(
"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))",
&repl_env,
);
// Invoked with arguments
if let Some(f) = arg1 {
match rep(&format!("(load-file \"{}\")", f), &repl_env) {
Ok(_) => std::process::exit(0),
Err(e) => {
println!("Error: {}", format_error(e));
std::process::exit(1);
}
}
},
Err(ReadlineError::Interrupted) => continue,
Err(ReadlineError::Eof) => break,
Err(err) => {
println!("Error: {:?}", err);
break
}
}
}
}
// vim: ts=2:sw=2:expandtab
// main repl loop
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;
}
}
}
}

View File

@ -5,9 +5,9 @@ use itertools::Itertools;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate itertools;
extern crate fnv;
extern crate itertools;
extern crate regex;
extern crate rustyline;
use rustyline::error::ReadlineError;
@ -15,260 +15,268 @@ use rustyline::Editor;
#[macro_use]
mod types;
use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error};
use types::MalVal::{Nil,Bool,Str,Sym,List,Vector,Hash,Func,MalFunc};
mod reader;
mod printer;
use types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Str, Sym, Vector};
use types::{error, format_error, MalArgs, MalErr, MalRet, MalVal};
mod env;
use env::{Env,env_new,env_bind,env_get,env_set,env_sets};
mod printer;
mod reader;
use env::{env_bind, env_get, env_new, env_set, env_sets, Env};
#[macro_use]
mod core;
// read
fn read(str: &str) -> MalRet {
reader::read_str(str.to_string())
reader::read_str(str.to_string())
}
// eval
fn quasiquote(ast: &MalVal) -> MalVal {
match ast {
List(ref v,_) | Vector(ref v,_) if v.len() > 0 => {
let a0 = &v[0];
match a0 {
Sym(ref s) if s == "unquote" => v[1].clone(),
_ => {
match a0 {
List(ref v0,_) | Vector(ref v0,_) if v0.len() > 0 => {
match v0[0] {
Sym(ref s) if s == "splice-unquote" => {
list![Sym("concat".to_string()),
v0[1].clone(),
quasiquote(&list!(v[1..].to_vec()))]
},
_ => {
list![Sym("cons".to_string()),
match ast {
List(ref v, _) | Vector(ref v, _) if v.len() > 0 => {
let a0 = &v[0];
match a0 {
Sym(ref s) if s == "unquote" => v[1].clone(),
_ => match a0 {
List(ref v0, _) | Vector(ref v0, _) if v0.len() > 0 => match v0[0] {
Sym(ref s) if s == "splice-unquote" => list![
Sym("concat".to_string()),
v0[1].clone(),
quasiquote(&list!(v[1..].to_vec()))
],
_ => list![
Sym("cons".to_string()),
quasiquote(a0),
quasiquote(&list!(v[1..].to_vec()))
],
},
_ => list![
Sym("cons".to_string()),
quasiquote(a0),
quasiquote(&list!(v[1..].to_vec()))]
quasiquote(&list!(v[1..].to_vec()))
],
},
}
},
_ => {
list![Sym("cons".to_string()),
quasiquote(a0),
quasiquote(&list!(v[1..].to_vec()))]
}
}
}
}
},
_ => list![Sym("quote".to_string()), ast.clone()]
}
_ => list![Sym("quote".to_string()), ast.clone()],
}
}
fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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()),
}
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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(mut ast: MalVal, mut env: Env) -> MalRet {
let ret: MalRet;
let ret: MalRet;
'tco: loop {
ret = match ast.clone() {
List(l,_) => {
if l.len() == 0 { return Ok(ast); }
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
},
Sym(ref a0sym) if a0sym == "let*" => {
env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds,_) | Vector(ref binds,_) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(&env, b.clone(),
eval(e.clone(), env.clone())?);
},
_ => {
return error("let* with non-Sym binding");
}
'tco: loop {
ret = match ast.clone() {
List(l, _) => {
if l.len() == 0 {
return Ok(ast);
}
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
}
Sym(ref a0sym) if a0sym == "let*" => {
env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds, _) | Vector(ref binds, _) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(
&env,
b.clone(),
eval(e.clone(), env.clone())?,
);
}
_ => {
return error("let* with non-Sym binding");
}
}
}
}
_ => {
return error("let* with non-List bindings");
}
};
ast = a2;
continue 'tco;
}
Sym(ref a0sym) if a0sym == "quote" => Ok(l[1].clone()),
Sym(ref a0sym) if a0sym == "quasiquote" => {
ast = quasiquote(&l[1]);
continue 'tco;
}
Sym(ref a0sym) if a0sym == "do" => {
match eval_ast(&list!(l[1..l.len() - 1].to_vec()), &env)? {
List(_, _) => {
ast = l.last().unwrap_or(&Nil).clone();
continue 'tco;
}
_ => error("invalid do form"),
}
}
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => {
ast = l[3].clone();
continue 'tco;
}
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => {
ast = l[2].clone();
continue 'tco;
}
_ => Ok(Nil),
}
}
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc {
eval: eval,
ast: Rc::new(a2),
env: env,
params: Rc::new(a1),
is_macro: false,
meta: Rc::new(Nil),
})
}
Sym(ref a0sym) if a0sym == "eval" => {
ast = eval(l[1].clone(), env.clone())?;
while let Some(ref e) = env.clone().outer {
env = e.clone();
}
continue 'tco;
}
_ => match eval_ast(&ast, &env)? {
List(ref el, _) => {
let ref f = el[0].clone();
let args = el[1..].to_vec();
match f {
Func(_, _) => f.apply(args),
MalFunc {
ast: mast,
env: menv,
params,
..
} => {
let a = &**mast;
let p = &**params;
env = env_bind(Some(menv.clone()), p.clone(), args)?;
ast = a.clone();
continue 'tco;
}
_ => error("attempt to call non-function"),
}
}
_ => error("expected a list"),
},
}
}
},
_ => {
return error("let* with non-List bindings");
}
};
ast = a2;
continue 'tco;
},
Sym(ref a0sym) if a0sym == "quote" => {
Ok(l[1].clone())
},
Sym(ref a0sym) if a0sym == "quasiquote" => {
ast = quasiquote(&l[1]);
continue 'tco;
},
Sym(ref a0sym) if a0sym == "do" => {
match eval_ast(&list!(l[1..l.len()-1].to_vec()), &env)? {
List(_,_) => {
ast = l.last().unwrap_or(&Nil).clone();
continue 'tco;
},
_ => error("invalid do form"),
}
},
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => {
ast = l[3].clone();
continue 'tco;
},
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => {
ast = l[2].clone();
continue 'tco;
},
_ => Ok(Nil)
}
},
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc{eval: eval, ast: Rc::new(a2), env: env,
params: Rc::new(a1), is_macro: false,
meta: Rc::new(Nil)})
},
Sym(ref a0sym) if a0sym == "eval" => {
ast = eval(l[1].clone(), env.clone())?;
while let Some(ref e) = env.clone().outer {
env = e.clone();
}
continue 'tco;
},
_ => {
match eval_ast(&ast, &env)? {
List(ref el,_) => {
let ref f = el[0].clone();
let args = el[1..].to_vec();
match f {
Func(_,_) => f.apply(args),
MalFunc{ast: mast, env: menv, params, ..} => {
let a = &**mast;
let p = &**params;
env = env_bind(Some(menv.clone()), p.clone(), args)?;
ast = a.clone();
continue 'tco;
},
_ => error("attempt to call non-function"),
}
},
_ => {
error("expected a list")
}
}
}
}
},
_ => eval_ast(&ast, &env),
};
_ => eval_ast(&ast, &env),
};
break;
break;
} // end 'tco loop
} // end 'tco loop
ret
ret
}
// print
fn print(ast: &MalVal) -> String {
ast.pr_str(true)
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 rep(str: &str, env: &Env) -> Result<String, MalErr> {
let ast = read(str)?;
let exp = eval(ast, env.clone())?;
Ok(print(&exp))
}
fn main() {
let mut args = std::env::args();
let arg1 = args.nth(1);
let mut args = std::env::args();
let arg1 = args.nth(1);
// `()` 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.");
}
// core.rs: defined using rust
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
// core.mal: defined using the language itself
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", &repl_env);
// Invoked with arguments
if let Some(f) = arg1 {
match rep(&format!("(load-file \"{}\")",f), &repl_env) {
Ok(_) => std::process::exit(0),
Err(e) => {
println!("Error: {}", format_error(e));
std::process::exit(1);
}
// `()` 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.");
}
}
// main repl loop
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)),
}
// core.rs: defined using rust
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
// core.mal: defined using the language itself
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
let _ = rep(
"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))",
&repl_env,
);
// Invoked with arguments
if let Some(f) = arg1 {
match rep(&format!("(load-file \"{}\")", f), &repl_env) {
Ok(_) => std::process::exit(0),
Err(e) => {
println!("Error: {}", format_error(e));
std::process::exit(1);
}
}
},
Err(ReadlineError::Interrupted) => continue,
Err(ReadlineError::Eof) => break,
Err(err) => {
println!("Error: {:?}", err);
break
}
}
}
}
// vim: ts=2:sw=2:expandtab
// main repl loop
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;
}
}
}
}

View File

@ -5,9 +5,9 @@ use itertools::Itertools;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate itertools;
extern crate fnv;
extern crate itertools;
extern crate regex;
extern crate rustyline;
use rustyline::error::ReadlineError;
@ -15,329 +15,342 @@ use rustyline::Editor;
#[macro_use]
mod types;
use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error};
use types::MalVal::{Nil,Bool,Str,Sym,List,Vector,Hash,Func,MalFunc};
mod reader;
mod printer;
use types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Str, Sym, Vector};
use types::{error, format_error, MalArgs, MalErr, MalRet, MalVal};
mod env;
use env::{Env,env_new,env_bind,env_find,env_get,env_set,env_sets};
mod printer;
mod reader;
use env::{env_bind, env_find, env_get, env_new, env_set, env_sets, Env};
#[macro_use]
mod core;
// read
fn read(str: &str) -> MalRet {
reader::read_str(str.to_string())
reader::read_str(str.to_string())
}
// eval
fn quasiquote(ast: &MalVal) -> MalVal {
match ast {
List(ref v,_) | Vector(ref v,_) if v.len() > 0 => {
let a0 = &v[0];
match a0 {
Sym(ref s) if s == "unquote" => v[1].clone(),
_ => {
match a0 {
List(ref v0,_) | Vector(ref v0,_) if v0.len() > 0 => {
match v0[0] {
Sym(ref s) if s == "splice-unquote" => {
list![Sym("concat".to_string()),
v0[1].clone(),
quasiquote(&list!(v[1..].to_vec()))]
},
_ => {
list![Sym("cons".to_string()),
match ast {
List(ref v, _) | Vector(ref v, _) if v.len() > 0 => {
let a0 = &v[0];
match a0 {
Sym(ref s) if s == "unquote" => v[1].clone(),
_ => match a0 {
List(ref v0, _) | Vector(ref v0, _) if v0.len() > 0 => match v0[0] {
Sym(ref s) if s == "splice-unquote" => list![
Sym("concat".to_string()),
v0[1].clone(),
quasiquote(&list!(v[1..].to_vec()))
],
_ => list![
Sym("cons".to_string()),
quasiquote(a0),
quasiquote(&list!(v[1..].to_vec()))
],
},
_ => list![
Sym("cons".to_string()),
quasiquote(a0),
quasiquote(&list!(v[1..].to_vec()))]
quasiquote(&list!(v[1..].to_vec()))
],
},
}
},
_ => {
list![Sym("cons".to_string()),
quasiquote(a0),
quasiquote(&list!(v[1..].to_vec()))]
}
}
}
}
},
_ => list![Sym("quote".to_string()), ast.clone()]
}
_ => list![Sym("quote".to_string()), ast.clone()],
}
}
fn is_macro_call(ast: &MalVal, env: &Env) -> Option<(MalVal,MalArgs)> {
match ast {
List(v,_) => {
match v[0] {
Sym(ref s) => {
match env_find(env, s) {
Some(e) => {
match env_get(&e, &v[0]) {
Ok(f @ MalFunc{is_macro: true, ..}) => {
Some((f, v[1..].to_vec()))
fn is_macro_call(ast: &MalVal, env: &Env) -> Option<(MalVal, MalArgs)> {
match ast {
List(v, _) => match v[0] {
Sym(ref s) => match env_find(env, s) {
Some(e) => match env_get(&e, &v[0]) {
Ok(f @ MalFunc { is_macro: true, .. }) => Some((f, v[1..].to_vec())),
_ => None,
},
_ => None,
}
},
_ => None,
}
},
_ => None,
}
},
_ => None,
}
}
}
fn macroexpand(mut ast: MalVal, env: &Env) -> (bool, MalRet) {
let mut was_expanded = false;
while let Some((mf, args)) = is_macro_call(&ast, env) {
//println!("macroexpand 1: {:?}", ast);
ast = match mf.apply(args) {
Err(e) => return (false, Err(e)),
Ok(a) => a,
};
//println!("macroexpand 2: {:?}", ast);
was_expanded = true;
}
((was_expanded, Ok(ast)))
let mut was_expanded = false;
while let Some((mf, args)) = is_macro_call(&ast, env) {
//println!("macroexpand 1: {:?}", ast);
ast = match mf.apply(args) {
Err(e) => return (false, Err(e)),
Ok(a) => a,
};
//println!("macroexpand 2: {:?}", ast);
was_expanded = true;
}
((was_expanded, Ok(ast)))
}
fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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()),
}
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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(mut ast: MalVal, mut env: Env) -> MalRet {
let ret: MalRet;
let ret: MalRet;
'tco: loop {
ret = match ast.clone() {
List(l,_) => {
if l.len() == 0 { return Ok(ast); }
match macroexpand(ast.clone(), &env) {
(true, Ok(new_ast)) => {
ast = new_ast;
continue 'tco;
}
(_, Err(e)) => return Err(e),
_ => (),
}
if l.len() == 0 { return Ok(ast); }
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
},
Sym(ref a0sym) if a0sym == "let*" => {
env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds,_) | Vector(ref binds,_) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(&env, b.clone(),
eval(e.clone(), env.clone())?);
},
_ => {
return error("let* with non-Sym binding");
}
'tco: loop {
ret = match ast.clone() {
List(l, _) => {
if l.len() == 0 {
return Ok(ast);
}
match macroexpand(ast.clone(), &env) {
(true, Ok(new_ast)) => {
ast = new_ast;
continue 'tco;
}
(_, Err(e)) => return Err(e),
_ => (),
}
if l.len() == 0 {
return Ok(ast);
}
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
}
Sym(ref a0sym) if a0sym == "let*" => {
env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds, _) | Vector(ref binds, _) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(
&env,
b.clone(),
eval(e.clone(), env.clone())?,
);
}
_ => {
return error("let* with non-Sym binding");
}
}
}
}
_ => {
return error("let* with non-List bindings");
}
};
ast = a2;
continue 'tco;
}
Sym(ref a0sym) if a0sym == "quote" => Ok(l[1].clone()),
Sym(ref a0sym) if a0sym == "quasiquote" => {
ast = quasiquote(&l[1]);
continue 'tco;
}
Sym(ref a0sym) if a0sym == "defmacro!" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
let r = eval(a2, env.clone())?;
match r {
MalFunc {
eval,
ast,
env,
params,
..
} => Ok(env_set(
&env,
a1.clone(),
MalFunc {
eval: eval,
ast: ast.clone(),
env: env.clone(),
params: params.clone(),
is_macro: true,
meta: Rc::new(Nil),
},
)?),
_ => error("set_macro on non-function"),
}
}
Sym(ref a0sym) if a0sym == "macroexpand" => {
match macroexpand(l[1].clone(), &env) {
(_, Ok(new_ast)) => Ok(new_ast),
(_, e) => return e,
}
}
Sym(ref a0sym) if a0sym == "do" => {
match eval_ast(&list!(l[1..l.len() - 1].to_vec()), &env)? {
List(_, _) => {
ast = l.last().unwrap_or(&Nil).clone();
continue 'tco;
}
_ => error("invalid do form"),
}
}
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => {
ast = l[3].clone();
continue 'tco;
}
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => {
ast = l[2].clone();
continue 'tco;
}
_ => Ok(Nil),
}
}
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc {
eval: eval,
ast: Rc::new(a2),
env: env,
params: Rc::new(a1),
is_macro: false,
meta: Rc::new(Nil),
})
}
Sym(ref a0sym) if a0sym == "eval" => {
ast = eval(l[1].clone(), env.clone())?;
while let Some(ref e) = env.clone().outer {
env = e.clone();
}
continue 'tco;
}
_ => match eval_ast(&ast, &env)? {
List(ref el, _) => {
let ref f = el[0].clone();
let args = el[1..].to_vec();
match f {
Func(_, _) => f.apply(args),
MalFunc {
ast: mast,
env: menv,
params,
..
} => {
let a = &**mast;
let p = &**params;
env = env_bind(Some(menv.clone()), p.clone(), args)?;
ast = a.clone();
continue 'tco;
}
_ => error("attempt to call non-function"),
}
}
_ => error("expected a list"),
},
}
}
},
_ => {
return error("let* with non-List bindings");
}
};
ast = a2;
continue 'tco;
},
Sym(ref a0sym) if a0sym == "quote" => {
Ok(l[1].clone())
},
Sym(ref a0sym) if a0sym == "quasiquote" => {
ast = quasiquote(&l[1]);
continue 'tco;
},
Sym(ref a0sym) if a0sym == "defmacro!" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
let r = eval(a2, env.clone())?;
match r {
MalFunc{eval, ast, env, params, ..} => {
Ok(env_set(&env, a1.clone(),
MalFunc{eval: eval, ast: ast.clone(), env: env.clone(),
params: params.clone(), is_macro: true,
meta: Rc::new(Nil)})?)
},
_ => error("set_macro on non-function"),
}
},
Sym(ref a0sym) if a0sym == "macroexpand" => {
match macroexpand(l[1].clone(), &env) {
(_, Ok(new_ast)) => Ok(new_ast),
(_, e) => return e,
}
},
Sym(ref a0sym) if a0sym == "do" => {
match eval_ast(&list!(l[1..l.len()-1].to_vec()), &env)? {
List(_,_) => {
ast = l.last().unwrap_or(&Nil).clone();
continue 'tco;
},
_ => error("invalid do form"),
}
},
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => {
ast = l[3].clone();
continue 'tco;
},
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => {
ast = l[2].clone();
continue 'tco;
},
_ => Ok(Nil)
}
},
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc{eval: eval, ast: Rc::new(a2), env: env,
params: Rc::new(a1), is_macro: false,
meta: Rc::new(Nil)})
},
Sym(ref a0sym) if a0sym == "eval" => {
ast = eval(l[1].clone(), env.clone())?;
while let Some(ref e) = env.clone().outer {
env = e.clone();
}
continue 'tco;
},
_ => {
match eval_ast(&ast, &env)? {
List(ref el,_) => {
let ref f = el[0].clone();
let args = el[1..].to_vec();
match f {
Func(_,_) => f.apply(args),
MalFunc{ast: mast, env: menv, params, ..} => {
let a = &**mast;
let p = &**params;
env = env_bind(Some(menv.clone()), p.clone(), args)?;
ast = a.clone();
continue 'tco;
},
_ => error("attempt to call non-function"),
}
},
_ => {
error("expected a list")
}
}
}
}
},
_ => eval_ast(&ast, &env),
};
_ => eval_ast(&ast, &env),
};
break;
break;
} // end 'tco loop
} // end 'tco loop
ret
ret
}
// print
fn print(ast: &MalVal) -> String {
ast.pr_str(true)
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 rep(str: &str, env: &Env) -> Result<String, MalErr> {
let ast = read(str)?;
let exp = eval(ast, env.clone())?;
Ok(print(&exp))
}
fn main() {
let mut args = std::env::args();
let arg1 = args.nth(1);
let mut args = std::env::args();
let arg1 = args.nth(1);
// `()` 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.");
}
// core.rs: defined using rust
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
// core.mal: defined using the language itself
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", &repl_env);
let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", &repl_env);
// Invoked with arguments
if let Some(f) = arg1 {
match rep(&format!("(load-file \"{}\")",f), &repl_env) {
Ok(_) => std::process::exit(0),
Err(e) => {
println!("Error: {}", format_error(e));
std::process::exit(1);
}
// `()` 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.");
}
}
// main repl loop
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)),
}
// core.rs: defined using rust
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
// core.mal: defined using the language itself
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
let _ = rep(
"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))",
&repl_env,
);
let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", &repl_env);
// Invoked with arguments
if let Some(f) = arg1 {
match rep(&format!("(load-file \"{}\")", f), &repl_env) {
Ok(_) => std::process::exit(0),
Err(e) => {
println!("Error: {}", format_error(e));
std::process::exit(1);
}
}
},
Err(ReadlineError::Interrupted) => continue,
Err(ReadlineError::Eof) => break,
Err(err) => {
println!("Error: {:?}", err);
break
}
}
}
}
// vim: ts=2:sw=2:expandtab
// main repl loop
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;
}
}
}
}

View File

@ -5,9 +5,9 @@ use itertools::Itertools;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate itertools;
extern crate fnv;
extern crate itertools;
extern crate regex;
extern crate rustyline;
use rustyline::error::ReadlineError;
@ -15,350 +15,363 @@ use rustyline::Editor;
#[macro_use]
mod types;
use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error};
use types::MalVal::{Nil,Bool,Str,Sym,List,Vector,Hash,Func,MalFunc};
use types::MalErr::{ErrString,ErrMalVal};
mod reader;
mod printer;
use types::MalErr::{ErrMalVal, ErrString};
use types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Str, Sym, Vector};
use types::{error, format_error, MalArgs, MalErr, MalRet, MalVal};
mod env;
use env::{Env,env_new,env_bind,env_find,env_get,env_set,env_sets};
mod printer;
mod reader;
use env::{env_bind, env_find, env_get, env_new, env_set, env_sets, Env};
#[macro_use]
mod core;
// read
fn read(str: &str) -> MalRet {
reader::read_str(str.to_string())
reader::read_str(str.to_string())
}
// eval
fn quasiquote(ast: &MalVal) -> MalVal {
match ast {
List(ref v,_) | Vector(ref v,_) if v.len() > 0 => {
let a0 = &v[0];
match a0 {
Sym(ref s) if s == "unquote" => v[1].clone(),
_ => {
match a0 {
List(ref v0,_) | Vector(ref v0,_) if v0.len() > 0 => {
match v0[0] {
Sym(ref s) if s == "splice-unquote" => {
list![Sym("concat".to_string()),
v0[1].clone(),
quasiquote(&list!(v[1..].to_vec()))]
},
_ => {
list![Sym("cons".to_string()),
match ast {
List(ref v, _) | Vector(ref v, _) if v.len() > 0 => {
let a0 = &v[0];
match a0 {
Sym(ref s) if s == "unquote" => v[1].clone(),
_ => match a0 {
List(ref v0, _) | Vector(ref v0, _) if v0.len() > 0 => match v0[0] {
Sym(ref s) if s == "splice-unquote" => list![
Sym("concat".to_string()),
v0[1].clone(),
quasiquote(&list!(v[1..].to_vec()))
],
_ => list![
Sym("cons".to_string()),
quasiquote(a0),
quasiquote(&list!(v[1..].to_vec()))
],
},
_ => list![
Sym("cons".to_string()),
quasiquote(a0),
quasiquote(&list!(v[1..].to_vec()))]
quasiquote(&list!(v[1..].to_vec()))
],
},
}
},
_ => {
list![Sym("cons".to_string()),
quasiquote(a0),
quasiquote(&list!(v[1..].to_vec()))]
}
}
}
}
},
_ => list![Sym("quote".to_string()), ast.clone()]
}
_ => list![Sym("quote".to_string()), ast.clone()],
}
}
fn is_macro_call(ast: &MalVal, env: &Env) -> Option<(MalVal,MalArgs)> {
match ast {
List(v,_) => {
match v[0] {
Sym(ref s) => {
match env_find(env, s) {
Some(e) => {
match env_get(&e, &v[0]) {
Ok(f @ MalFunc{is_macro: true, ..}) => {
Some((f, v[1..].to_vec()))
fn is_macro_call(ast: &MalVal, env: &Env) -> Option<(MalVal, MalArgs)> {
match ast {
List(v, _) => match v[0] {
Sym(ref s) => match env_find(env, s) {
Some(e) => match env_get(&e, &v[0]) {
Ok(f @ MalFunc { is_macro: true, .. }) => Some((f, v[1..].to_vec())),
_ => None,
},
_ => None,
}
},
_ => None,
}
},
_ => None,
}
},
_ => None,
}
}
}
fn macroexpand(mut ast: MalVal, env: &Env) -> (bool, MalRet) {
let mut was_expanded = false;
while let Some((mf, args)) = is_macro_call(&ast, env) {
//println!("macroexpand 1: {:?}", ast);
ast = match mf.apply(args) {
Err(e) => return (false, Err(e)),
Ok(a) => a,
};
//println!("macroexpand 2: {:?}", ast);
was_expanded = true;
}
((was_expanded, Ok(ast)))
let mut was_expanded = false;
while let Some((mf, args)) = is_macro_call(&ast, env) {
//println!("macroexpand 1: {:?}", ast);
ast = match mf.apply(args) {
Err(e) => return (false, Err(e)),
Ok(a) => a,
};
//println!("macroexpand 2: {:?}", ast);
was_expanded = true;
}
((was_expanded, Ok(ast)))
}
fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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()),
}
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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(mut ast: MalVal, mut env: Env) -> MalRet {
let ret: MalRet;
let ret: MalRet;
'tco: loop {
ret = match ast.clone() {
List(l,_) => {
if l.len() == 0 { return Ok(ast); }
match macroexpand(ast.clone(), &env) {
(true, Ok(new_ast)) => {
ast = new_ast;
continue 'tco;
}
(_, Err(e)) => return Err(e),
_ => (),
}
if l.len() == 0 { return Ok(ast); }
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
},
Sym(ref a0sym) if a0sym == "let*" => {
env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds,_) | Vector(ref binds,_) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(&env, b.clone(),
eval(e.clone(), env.clone())?);
},
_ => {
return error("let* with non-Sym binding");
}
'tco: loop {
ret = match ast.clone() {
List(l, _) => {
if l.len() == 0 {
return Ok(ast);
}
match macroexpand(ast.clone(), &env) {
(true, Ok(new_ast)) => {
ast = new_ast;
continue 'tco;
}
(_, Err(e)) => return Err(e),
_ => (),
}
if l.len() == 0 {
return Ok(ast);
}
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
}
Sym(ref a0sym) if a0sym == "let*" => {
env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds, _) | Vector(ref binds, _) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(
&env,
b.clone(),
eval(e.clone(), env.clone())?,
);
}
_ => {
return error("let* with non-Sym binding");
}
}
}
}
_ => {
return error("let* with non-List bindings");
}
};
ast = a2;
continue 'tco;
}
Sym(ref a0sym) if a0sym == "quote" => Ok(l[1].clone()),
Sym(ref a0sym) if a0sym == "quasiquote" => {
ast = quasiquote(&l[1]);
continue 'tco;
}
Sym(ref a0sym) if a0sym == "defmacro!" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
let r = eval(a2, env.clone())?;
match r {
MalFunc {
eval,
ast,
env,
params,
..
} => Ok(env_set(
&env,
a1.clone(),
MalFunc {
eval: eval,
ast: ast.clone(),
env: env.clone(),
params: params.clone(),
is_macro: true,
meta: Rc::new(Nil),
},
)?),
_ => error("set_macro on non-function"),
}
}
Sym(ref a0sym) if a0sym == "macroexpand" => {
match macroexpand(l[1].clone(), &env) {
(_, Ok(new_ast)) => Ok(new_ast),
(_, e) => return e,
}
}
Sym(ref a0sym) if a0sym == "try*" => match eval(l[1].clone(), env.clone()) {
Err(ref e) if l.len() >= 3 => {
let exc = match e {
ErrMalVal(mv) => mv.clone(),
ErrString(s) => Str(s.to_string()),
};
match l[2].clone() {
List(c, _) => {
let catch_env = env_bind(
Some(env.clone()),
list!(vec![c[1].clone()]),
vec![exc],
)?;
eval(c[2].clone(), catch_env)
}
_ => error("invalid catch block"),
}
}
res => res,
},
Sym(ref a0sym) if a0sym == "do" => {
match eval_ast(&list!(l[1..l.len() - 1].to_vec()), &env)? {
List(_, _) => {
ast = l.last().unwrap_or(&Nil).clone();
continue 'tco;
}
_ => error("invalid do form"),
}
}
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => {
ast = l[3].clone();
continue 'tco;
}
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => {
ast = l[2].clone();
continue 'tco;
}
_ => Ok(Nil),
}
}
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc {
eval: eval,
ast: Rc::new(a2),
env: env,
params: Rc::new(a1),
is_macro: false,
meta: Rc::new(Nil),
})
}
Sym(ref a0sym) if a0sym == "eval" => {
ast = eval(l[1].clone(), env.clone())?;
while let Some(ref e) = env.clone().outer {
env = e.clone();
}
continue 'tco;
}
_ => match eval_ast(&ast, &env)? {
List(ref el, _) => {
let ref f = el[0].clone();
let args = el[1..].to_vec();
match f {
Func(_, _) => f.apply(args),
MalFunc {
ast: mast,
env: menv,
params,
..
} => {
let a = &**mast;
let p = &**params;
env = env_bind(Some(menv.clone()), p.clone(), args)?;
ast = a.clone();
continue 'tco;
}
_ => error("attempt to call non-function"),
}
}
_ => error("expected a list"),
},
}
}
},
_ => {
return error("let* with non-List bindings");
}
};
ast = a2;
continue 'tco;
},
Sym(ref a0sym) if a0sym == "quote" => {
Ok(l[1].clone())
},
Sym(ref a0sym) if a0sym == "quasiquote" => {
ast = quasiquote(&l[1]);
continue 'tco;
},
Sym(ref a0sym) if a0sym == "defmacro!" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
let r = eval(a2, env.clone())?;
match r {
MalFunc{eval, ast, env, params, ..} => {
Ok(env_set(&env, a1.clone(),
MalFunc{eval: eval, ast: ast.clone(), env: env.clone(),
params: params.clone(), is_macro: true,
meta: Rc::new(Nil)})?)
},
_ => error("set_macro on non-function"),
}
},
Sym(ref a0sym) if a0sym == "macroexpand" => {
match macroexpand(l[1].clone(), &env) {
(_, Ok(new_ast)) => Ok(new_ast),
(_, e) => return e,
}
},
Sym(ref a0sym) if a0sym == "try*" => {
match eval(l[1].clone(), env.clone()) {
Err(ref e) if l.len() >= 3 => {
let exc = match e {
ErrMalVal(mv) => mv.clone(),
ErrString(s) => Str(s.to_string()),
};
match l[2].clone() {
List(c,_) => {
let catch_env = env_bind(Some(env.clone()),
list!(vec![c[1].clone()]),
vec![exc])?;
eval(c[2].clone(), catch_env)
},
_ => error("invalid catch block"),
}
},
res => res,
}
},
Sym(ref a0sym) if a0sym == "do" => {
match eval_ast(&list!(l[1..l.len()-1].to_vec()), &env)? {
List(_,_) => {
ast = l.last().unwrap_or(&Nil).clone();
continue 'tco;
},
_ => error("invalid do form"),
}
},
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => {
ast = l[3].clone();
continue 'tco;
},
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => {
ast = l[2].clone();
continue 'tco;
},
_ => Ok(Nil)
}
},
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc{eval: eval, ast: Rc::new(a2), env: env,
params: Rc::new(a1), is_macro: false,
meta: Rc::new(Nil)})
},
Sym(ref a0sym) if a0sym == "eval" => {
ast = eval(l[1].clone(), env.clone())?;
while let Some(ref e) = env.clone().outer {
env = e.clone();
}
continue 'tco;
},
_ => {
match eval_ast(&ast, &env)? {
List(ref el,_) => {
let ref f = el[0].clone();
let args = el[1..].to_vec();
match f {
Func(_,_) => f.apply(args),
MalFunc{ast: mast, env: menv, params, ..} => {
let a = &**mast;
let p = &**params;
env = env_bind(Some(menv.clone()), p.clone(), args)?;
ast = a.clone();
continue 'tco;
},
_ => error("attempt to call non-function"),
}
},
_ => {
error("expected a list")
}
}
}
}
},
_ => eval_ast(&ast, &env),
};
_ => eval_ast(&ast, &env),
};
break;
break;
} // end 'tco loop
} // end 'tco loop
ret
ret
}
// print
fn print(ast: &MalVal) -> String {
ast.pr_str(true)
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 rep(str: &str, env: &Env) -> Result<String, MalErr> {
let ast = read(str)?;
let exp = eval(ast, env.clone())?;
Ok(print(&exp))
}
fn main() {
let mut args = std::env::args();
let arg1 = args.nth(1);
let mut args = std::env::args();
let arg1 = args.nth(1);
// `()` 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.");
}
// core.rs: defined using rust
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
// core.mal: defined using the language itself
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", &repl_env);
let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", &repl_env);
// Invoked with arguments
if let Some(f) = arg1 {
match rep(&format!("(load-file \"{}\")",f), &repl_env) {
Ok(_) => std::process::exit(0),
Err(e) => {
println!("Error: {}", format_error(e));
std::process::exit(1);
}
// `()` 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.");
}
}
// main repl loop
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)),
}
// core.rs: defined using rust
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
// core.mal: defined using the language itself
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
let _ = rep(
"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))",
&repl_env,
);
let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", &repl_env);
// Invoked with arguments
if let Some(f) = arg1 {
match rep(&format!("(load-file \"{}\")", f), &repl_env) {
Ok(_) => std::process::exit(0),
Err(e) => {
println!("Error: {}", format_error(e));
std::process::exit(1);
}
}
},
Err(ReadlineError::Interrupted) => continue,
Err(ReadlineError::Eof) => break,
Err(err) => {
println!("Error: {:?}", err);
break
}
}
}
}
// vim: ts=2:sw=2:expandtab
// main repl loop
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;
}
}
}
}

View File

@ -7,9 +7,9 @@ use itertools::Itertools;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate itertools;
extern crate fnv;
extern crate itertools;
extern crate regex;
extern crate rustyline;
use rustyline::error::ReadlineError;
@ -17,352 +17,365 @@ use rustyline::Editor;
#[macro_use]
mod types;
use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error};
use types::MalVal::{Nil,Bool,Str,Sym,List,Vector,Hash,Func,MalFunc};
use types::MalErr::{ErrString,ErrMalVal};
mod reader;
mod printer;
use types::MalErr::{ErrMalVal, ErrString};
use types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Str, Sym, Vector};
use types::{error, format_error, MalArgs, MalErr, MalRet, MalVal};
mod env;
use env::{Env,env_new,env_bind,env_find,env_get,env_set,env_sets};
mod printer;
mod reader;
use env::{env_bind, env_find, env_get, env_new, env_set, env_sets, Env};
#[macro_use]
mod core;
// read
fn read(str: &str) -> MalRet {
reader::read_str(str.to_string())
reader::read_str(str.to_string())
}
// eval
fn quasiquote(ast: &MalVal) -> MalVal {
match ast {
List(ref v,_) | Vector(ref v,_) if v.len() > 0 => {
let a0 = &v[0];
match a0 {
Sym(ref s) if s == "unquote" => v[1].clone(),
_ => {
match a0 {
List(ref v0,_) | Vector(ref v0,_) if v0.len() > 0 => {
match v0[0] {
Sym(ref s) if s == "splice-unquote" => {
list![Sym("concat".to_string()),
v0[1].clone(),
quasiquote(&list!(v[1..].to_vec()))]
},
_ => {
list![Sym("cons".to_string()),
match ast {
List(ref v, _) | Vector(ref v, _) if v.len() > 0 => {
let a0 = &v[0];
match a0 {
Sym(ref s) if s == "unquote" => v[1].clone(),
_ => match a0 {
List(ref v0, _) | Vector(ref v0, _) if v0.len() > 0 => match v0[0] {
Sym(ref s) if s == "splice-unquote" => list![
Sym("concat".to_string()),
v0[1].clone(),
quasiquote(&list!(v[1..].to_vec()))
],
_ => list![
Sym("cons".to_string()),
quasiquote(a0),
quasiquote(&list!(v[1..].to_vec()))
],
},
_ => list![
Sym("cons".to_string()),
quasiquote(a0),
quasiquote(&list!(v[1..].to_vec()))]
quasiquote(&list!(v[1..].to_vec()))
],
},
}
},
_ => {
list![Sym("cons".to_string()),
quasiquote(a0),
quasiquote(&list!(v[1..].to_vec()))]
}
}
}
}
},
_ => list![Sym("quote".to_string()), ast.clone()]
}
_ => list![Sym("quote".to_string()), ast.clone()],
}
}
fn is_macro_call(ast: &MalVal, env: &Env) -> Option<(MalVal,MalArgs)> {
match ast {
List(v,_) => {
match v[0] {
Sym(ref s) => {
match env_find(env, s) {
Some(e) => {
match env_get(&e, &v[0]) {
Ok(f @ MalFunc{is_macro: true, ..}) => {
Some((f, v[1..].to_vec()))
fn is_macro_call(ast: &MalVal, env: &Env) -> Option<(MalVal, MalArgs)> {
match ast {
List(v, _) => match v[0] {
Sym(ref s) => match env_find(env, s) {
Some(e) => match env_get(&e, &v[0]) {
Ok(f @ MalFunc { is_macro: true, .. }) => Some((f, v[1..].to_vec())),
_ => None,
},
_ => None,
}
},
_ => None,
}
},
_ => None,
}
},
_ => None,
}
}
}
fn macroexpand(mut ast: MalVal, env: &Env) -> (bool, MalRet) {
let mut was_expanded = false;
while let Some((mf, args)) = is_macro_call(&ast, env) {
//println!("macroexpand 1: {:?}", ast);
ast = match mf.apply(args) {
Err(e) => return (false, Err(e)),
Ok(a) => a,
};
//println!("macroexpand 2: {:?}", ast);
was_expanded = true;
}
((was_expanded, Ok(ast)))
let mut was_expanded = false;
while let Some((mf, args)) = is_macro_call(&ast, env) {
//println!("macroexpand 1: {:?}", ast);
ast = match mf.apply(args) {
Err(e) => return (false, Err(e)),
Ok(a) => a,
};
//println!("macroexpand 2: {:?}", ast);
was_expanded = true;
}
((was_expanded, Ok(ast)))
}
fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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()),
}
match ast {
Sym(_) => Ok(env_get(&env, &ast)?),
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(mut ast: MalVal, mut env: Env) -> MalRet {
let ret: MalRet;
let ret: MalRet;
'tco: loop {
ret = match ast.clone() {
List(l,_) => {
if l.len() == 0 { return Ok(ast); }
match macroexpand(ast.clone(), &env) {
(true, Ok(new_ast)) => {
ast = new_ast;
continue 'tco;
}
(_, Err(e)) => return Err(e),
_ => (),
}
if l.len() == 0 { return Ok(ast); }
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
},
Sym(ref a0sym) if a0sym == "let*" => {
env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds,_) | Vector(ref binds,_) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(&env, b.clone(),
eval(e.clone(), env.clone())?);
},
_ => {
return error("let* with non-Sym binding");
}
'tco: loop {
ret = match ast.clone() {
List(l, _) => {
if l.len() == 0 {
return Ok(ast);
}
match macroexpand(ast.clone(), &env) {
(true, Ok(new_ast)) => {
ast = new_ast;
continue 'tco;
}
(_, Err(e)) => return Err(e),
_ => (),
}
if l.len() == 0 {
return Ok(ast);
}
let a0 = &l[0];
match a0 {
Sym(ref a0sym) if a0sym == "def!" => {
env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
}
Sym(ref a0sym) if a0sym == "let*" => {
env = env_new(Some(env.clone()));
let (a1, a2) = (l[1].clone(), l[2].clone());
match a1 {
List(ref binds, _) | Vector(ref binds, _) => {
for (b, e) in binds.iter().tuples() {
match b {
Sym(_) => {
let _ = env_set(
&env,
b.clone(),
eval(e.clone(), env.clone())?,
);
}
_ => {
return error("let* with non-Sym binding");
}
}
}
}
_ => {
return error("let* with non-List bindings");
}
};
ast = a2;
continue 'tco;
}
Sym(ref a0sym) if a0sym == "quote" => Ok(l[1].clone()),
Sym(ref a0sym) if a0sym == "quasiquote" => {
ast = quasiquote(&l[1]);
continue 'tco;
}
Sym(ref a0sym) if a0sym == "defmacro!" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
let r = eval(a2, env.clone())?;
match r {
MalFunc {
eval,
ast,
env,
params,
..
} => Ok(env_set(
&env,
a1.clone(),
MalFunc {
eval: eval,
ast: ast.clone(),
env: env.clone(),
params: params.clone(),
is_macro: true,
meta: Rc::new(Nil),
},
)?),
_ => error("set_macro on non-function"),
}
}
Sym(ref a0sym) if a0sym == "macroexpand" => {
match macroexpand(l[1].clone(), &env) {
(_, Ok(new_ast)) => Ok(new_ast),
(_, e) => return e,
}
}
Sym(ref a0sym) if a0sym == "try*" => match eval(l[1].clone(), env.clone()) {
Err(ref e) if l.len() >= 3 => {
let exc = match e {
ErrMalVal(mv) => mv.clone(),
ErrString(s) => Str(s.to_string()),
};
match l[2].clone() {
List(c, _) => {
let catch_env = env_bind(
Some(env.clone()),
list!(vec![c[1].clone()]),
vec![exc],
)?;
eval(c[2].clone(), catch_env)
}
_ => error("invalid catch block"),
}
}
res => res,
},
Sym(ref a0sym) if a0sym == "do" => {
match eval_ast(&list!(l[1..l.len() - 1].to_vec()), &env)? {
List(_, _) => {
ast = l.last().unwrap_or(&Nil).clone();
continue 'tco;
}
_ => error("invalid do form"),
}
}
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => {
ast = l[3].clone();
continue 'tco;
}
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => {
ast = l[2].clone();
continue 'tco;
}
_ => Ok(Nil),
}
}
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc {
eval: eval,
ast: Rc::new(a2),
env: env,
params: Rc::new(a1),
is_macro: false,
meta: Rc::new(Nil),
})
}
Sym(ref a0sym) if a0sym == "eval" => {
ast = eval(l[1].clone(), env.clone())?;
while let Some(ref e) = env.clone().outer {
env = e.clone();
}
continue 'tco;
}
_ => match eval_ast(&ast, &env)? {
List(ref el, _) => {
let ref f = el[0].clone();
let args = el[1..].to_vec();
match f {
Func(_, _) => f.apply(args),
MalFunc {
ast: mast,
env: menv,
params,
..
} => {
let a = &**mast;
let p = &**params;
env = env_bind(Some(menv.clone()), p.clone(), args)?;
ast = a.clone();
continue 'tco;
}
_ => error("attempt to call non-function"),
}
}
_ => error("expected a list"),
},
}
}
},
_ => {
return error("let* with non-List bindings");
}
};
ast = a2;
continue 'tco;
},
Sym(ref a0sym) if a0sym == "quote" => {
Ok(l[1].clone())
},
Sym(ref a0sym) if a0sym == "quasiquote" => {
ast = quasiquote(&l[1]);
continue 'tco;
},
Sym(ref a0sym) if a0sym == "defmacro!" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
let r = eval(a2, env.clone())?;
match r {
MalFunc{eval, ast, env, params, ..} => {
Ok(env_set(&env, a1.clone(),
MalFunc{eval: eval, ast: ast.clone(), env: env.clone(),
params: params.clone(), is_macro: true,
meta: Rc::new(Nil)})?)
},
_ => error("set_macro on non-function"),
}
},
Sym(ref a0sym) if a0sym == "macroexpand" => {
match macroexpand(l[1].clone(), &env) {
(_, Ok(new_ast)) => Ok(new_ast),
(_, e) => return e,
}
},
Sym(ref a0sym) if a0sym == "try*" => {
match eval(l[1].clone(), env.clone()) {
Err(ref e) if l.len() >= 3 => {
let exc = match e {
ErrMalVal(mv) => mv.clone(),
ErrString(s) => Str(s.to_string()),
};
match l[2].clone() {
List(c,_) => {
let catch_env = env_bind(Some(env.clone()),
list!(vec![c[1].clone()]),
vec![exc])?;
eval(c[2].clone(), catch_env)
},
_ => error("invalid catch block"),
}
},
res => res,
}
},
Sym(ref a0sym) if a0sym == "do" => {
match eval_ast(&list!(l[1..l.len()-1].to_vec()), &env)? {
List(_,_) => {
ast = l.last().unwrap_or(&Nil).clone();
continue 'tco;
},
_ => error("invalid do form"),
}
},
Sym(ref a0sym) if a0sym == "if" => {
let cond = eval(l[1].clone(), env.clone())?;
match cond {
Bool(false) | Nil if l.len() >= 4 => {
ast = l[3].clone();
continue 'tco;
},
Bool(false) | Nil => Ok(Nil),
_ if l.len() >= 3 => {
ast = l[2].clone();
continue 'tco;
},
_ => Ok(Nil)
}
},
Sym(ref a0sym) if a0sym == "fn*" => {
let (a1, a2) = (l[1].clone(), l[2].clone());
Ok(MalFunc{eval: eval, ast: Rc::new(a2), env: env,
params: Rc::new(a1), is_macro: false,
meta: Rc::new(Nil)})
},
Sym(ref a0sym) if a0sym == "eval" => {
ast = eval(l[1].clone(), env.clone())?;
while let Some(ref e) = env.clone().outer {
env = e.clone();
}
continue 'tco;
},
_ => {
match eval_ast(&ast, &env)? {
List(ref el,_) => {
let ref f = el[0].clone();
let args = el[1..].to_vec();
match f {
Func(_,_) => f.apply(args),
MalFunc{ast: mast, env: menv, params, ..} => {
let a = &**mast;
let p = &**params;
env = env_bind(Some(menv.clone()), p.clone(), args)?;
ast = a.clone();
continue 'tco;
},
_ => error("attempt to call non-function"),
}
},
_ => {
error("expected a list")
}
}
}
}
},
_ => eval_ast(&ast, &env),
};
_ => eval_ast(&ast, &env),
};
break;
break;
} // end 'tco loop
} // end 'tco loop
ret
ret
}
// print
fn print(ast: &MalVal) -> String {
ast.pr_str(true)
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 rep(str: &str, env: &Env) -> Result<String, MalErr> {
let ast = read(str)?;
let exp = eval(ast, env.clone())?;
Ok(print(&exp))
}
fn main() {
let mut args = std::env::args();
let arg1 = args.nth(1);
let mut args = std::env::args();
let arg1 = args.nth(1);
// `()` 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.");
}
// core.rs: defined using rust
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
// core.mal: defined using the language itself
let _ = rep("(def! *host-language* \"rust\")", &repl_env);
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", &repl_env);
let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", &repl_env);
// Invoked with arguments
if let Some(f) = arg1 {
match rep(&format!("(load-file \"{}\")",f), &repl_env) {
Ok(_) => std::process::exit(0),
Err(e) => {
println!("Error: {}", format_error(e));
std::process::exit(1);
}
// `()` 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.");
}
}
// main repl loop
let _ = rep("(println (str \"Mal [\" *host-language* \"]\"))", &repl_env);
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)),
}
// core.rs: defined using rust
let repl_env = env_new(None);
for (k, v) in core::ns() {
env_sets(&repl_env, k, v);
}
env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
// core.mal: defined using the language itself
let _ = rep("(def! *host-language* \"rust\")", &repl_env);
let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
let _ = rep(
"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))",
&repl_env,
);
let _ = rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", &repl_env);
// Invoked with arguments
if let Some(f) = arg1 {
match rep(&format!("(load-file \"{}\")", f), &repl_env) {
Ok(_) => std::process::exit(0),
Err(e) => {
println!("Error: {}", format_error(e));
std::process::exit(1);
}
}
},
Err(ReadlineError::Interrupted) => continue,
Err(ReadlineError::Eof) => break,
Err(err) => {
println!("Error: {:?}", err);
break
}
}
}
}
// vim: ts=2:sw=2:expandtab
// main repl loop
let _ = rep("(println (str \"Mal [\" *host-language* \"]\"))", &repl_env);
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;
}
}
}
}

View File

@ -1,12 +1,12 @@
use std::rc::Rc;
use std::cell::RefCell;
use std::rc::Rc;
//use std::collections::HashMap;
use fnv::FnvHashMap;
use itertools::Itertools;
use types::MalErr::{ErrString,ErrMalVal};
use types::MalVal::{Nil,Bool,Int,Str,Sym,List,Vector,Hash,Func,MalFunc,Atom};
use env::{Env,env_bind};
use env::{env_bind, Env};
use types::MalErr::{ErrMalVal, ErrString};
use types::MalVal::{Atom, Bool, Func, Hash, Int, List, MalFunc, Nil, Str, Sym, Vector};
#[derive(Debug, Clone)]
pub enum MalVal {
@ -21,24 +21,24 @@ pub enum MalVal {
Hash(Rc<FnvHashMap<String, MalVal>>, Rc<MalVal>),
Func(fn(MalArgs) -> MalRet, Rc<MalVal>),
MalFunc {
eval: fn(ast: MalVal, env: Env) -> MalRet,
ast: Rc<MalVal>,
env: Env,
params: Rc<MalVal>,
is_macro: bool,
meta: Rc<MalVal>,
eval: fn(ast: MalVal, env: Env) -> MalRet,
ast: Rc<MalVal>,
env: Env,
params: Rc<MalVal>,
is_macro: bool,
meta: Rc<MalVal>,
},
Atom(Rc<RefCell<MalVal>>),
}
#[derive(Debug)]
pub enum MalErr {
ErrString(String),
ErrMalVal(MalVal),
ErrString(String),
ErrMalVal(MalVal),
}
pub type MalArgs = Vec<MalVal>;
pub type MalRet = Result<MalVal,MalErr>;
pub type MalRet = Result<MalVal, MalErr>;
// type utility macros
@ -65,168 +65,176 @@ macro_rules! vector {
// type utility functions
pub fn error(s: &str) -> MalRet {
Err(ErrString(s.to_string()))
Err(ErrString(s.to_string()))
}
pub fn format_error(e: MalErr) -> String {
match e {
ErrString(s) => s.clone(),
ErrMalVal(mv) => mv.pr_str(true),
}
match e {
ErrString(s) => s.clone(),
ErrMalVal(mv) => mv.pr_str(true),
}
}
pub fn atom(mv: &MalVal) -> MalVal {
Atom(Rc::new(RefCell::new(mv.clone())))
Atom(Rc::new(RefCell::new(mv.clone())))
}
impl MalVal {
pub fn keyword(&self) -> MalRet {
match self {
Str(s) if s.starts_with("\u{29e}") => Ok(Str(s.to_string())),
Str(s) => Ok(Str(format!("\u{29e}{}", s))),
_ => error("invalid type for keyword"),
pub fn keyword(&self) -> MalRet {
match self {
Str(s) if s.starts_with("\u{29e}") => Ok(Str(s.to_string())),
Str(s) => Ok(Str(format!("\u{29e}{}", s))),
_ => error("invalid type for keyword"),
}
}
}
pub fn empty_q(&self) -> MalRet {
match self {
List(l,_) | Vector(l,_) => Ok(Bool(l.len() == 0)),
Nil => Ok(Bool(true)),
_ => error("invalid type for empty?"),
pub fn empty_q(&self) -> MalRet {
match self {
List(l, _) | Vector(l, _) => Ok(Bool(l.len() == 0)),
Nil => Ok(Bool(true)),
_ => error("invalid type for empty?"),
}
}
}
pub fn count(&self) -> MalRet {
match self {
List(l,_) | Vector(l,_) => Ok(Int(l.len() as i64)),
Nil => Ok(Int(0)),
_ => error("invalid type for count"),
pub fn count(&self) -> MalRet {
match self {
List(l, _) | Vector(l, _) => Ok(Int(l.len() as i64)),
Nil => Ok(Int(0)),
_ => error("invalid type for count"),
}
}
}
pub fn apply(&self, args: MalArgs) -> MalRet {
match *self {
Func(f,_) => f(args),
MalFunc{eval, ref ast, ref env, ref params, ..} => {
let a = &**ast;
let p = &**params;
let fn_env = env_bind(Some(env.clone()), p.clone(), args)?;
Ok(eval(a.clone(), fn_env)?)
}
_ => error("attempt to call non-function"),
pub fn apply(&self, args: MalArgs) -> MalRet {
match *self {
Func(f, _) => f(args),
MalFunc {
eval,
ref ast,
ref env,
ref params,
..
} => {
let a = &**ast;
let p = &**params;
let fn_env = env_bind(Some(env.clone()), p.clone(), args)?;
Ok(eval(a.clone(), fn_env)?)
}
_ => error("attempt to call non-function"),
}
}
}
pub fn keyword_q(&self) -> bool {
match self {
Str(s) if s.starts_with("\u{29e}") => true,
_ => false,
pub fn keyword_q(&self) -> bool {
match self {
Str(s) if s.starts_with("\u{29e}") => true,
_ => false,
}
}
}
pub fn deref(&self) -> MalRet {
match self {
Atom(a) => Ok(a.borrow().clone()),
_ => error("attempt to deref a non-Atom"),
pub fn deref(&self) -> MalRet {
match self {
Atom(a) => Ok(a.borrow().clone()),
_ => error("attempt to deref a non-Atom"),
}
}
}
pub fn reset_bang(&self, new: &MalVal) -> MalRet {
match self {
Atom(a) => {
*a.borrow_mut() = new.clone();
Ok(new.clone())
},
_ => error("attempt to reset! a non-Atom"),
pub fn reset_bang(&self, new: &MalVal) -> MalRet {
match self {
Atom(a) => {
*a.borrow_mut() = new.clone();
Ok(new.clone())
}
_ => error("attempt to reset! a non-Atom"),
}
}
}
pub fn swap_bang(&self, args: &MalArgs) -> MalRet {
match self {
Atom(a) => {
let f = &args[0];
let mut fargs = args[1..].to_vec();
fargs.insert(0, a.borrow().clone());
*a.borrow_mut() = f.apply(fargs)?;
Ok(a.borrow().clone())
},
_ => error("attempt to swap! a non-Atom"),
pub fn swap_bang(&self, args: &MalArgs) -> MalRet {
match self {
Atom(a) => {
let f = &args[0];
let mut fargs = args[1..].to_vec();
fargs.insert(0, a.borrow().clone());
*a.borrow_mut() = f.apply(fargs)?;
Ok(a.borrow().clone())
}
_ => error("attempt to swap! a non-Atom"),
}
}
}
pub fn get_meta(&self) -> MalRet {
match self {
List(_,meta) | Vector(_,meta) | Hash(_,meta) => Ok((&**meta).clone()),
Func(_,meta) => Ok((&**meta).clone()),
MalFunc{meta,..} => Ok((&**meta).clone()),
_ => error("meta not supported by type"),
pub fn get_meta(&self) -> MalRet {
match self {
List(_, meta) | Vector(_, meta) | Hash(_, meta) => Ok((&**meta).clone()),
Func(_, meta) => Ok((&**meta).clone()),
MalFunc { meta, .. } => Ok((&**meta).clone()),
_ => error("meta not supported by type"),
}
}
}
pub fn with_meta(&mut self, new_meta: &MalVal) -> MalRet {
match self {
List(_, ref mut meta) |
Vector(_, ref mut meta) |
Hash(_, ref mut meta) |
Func(_,ref mut meta) |
MalFunc{ref mut meta, ..} => {
*meta = Rc::new((&*new_meta).clone());
},
_ => return error("with-meta not supported by type"),
};
Ok(self.clone())
}
pub fn with_meta(&mut self, new_meta: &MalVal) -> MalRet {
match self {
List(_, ref mut meta)
| Vector(_, ref mut meta)
| Hash(_, ref mut meta)
| Func(_, ref mut meta)
| MalFunc { ref mut meta, .. } => {
*meta = Rc::new((&*new_meta).clone());
}
_ => return error("with-meta not supported by type"),
};
Ok(self.clone())
}
}
impl PartialEq for MalVal {
fn eq(&self, other: &MalVal) -> bool {
match (self, other) {
(Nil,Nil) => true,
(Bool(ref a),Bool(ref b)) => a == b,
(Int(ref a),Int(ref b)) => a == b,
(Str(ref a),Str(ref b)) => a == b,
(Sym(ref a),Sym(ref b)) => a == b,
(List(ref a,_),List(ref b,_)) |
(Vector(ref a,_),Vector(ref b,_)) |
(List(ref a,_),Vector(ref b,_)) |
(Vector(ref a,_),List(ref b,_)) => a == b,
(Hash(ref a,_),Hash(ref b,_)) => a == b,
(MalFunc{..}, MalFunc{..}) => false,
_ => false,
fn eq(&self, other: &MalVal) -> bool {
match (self, other) {
(Nil, Nil) => true,
(Bool(ref a), Bool(ref b)) => a == b,
(Int(ref a), Int(ref b)) => a == b,
(Str(ref a), Str(ref b)) => a == b,
(Sym(ref a), Sym(ref b)) => a == b,
(List(ref a, _), List(ref b, _))
| (Vector(ref a, _), Vector(ref b, _))
| (List(ref a, _), Vector(ref b, _))
| (Vector(ref a, _), List(ref b, _)) => a == b,
(Hash(ref a, _), Hash(ref b, _)) => a == b,
(MalFunc { .. }, MalFunc { .. }) => false,
_ => false,
}
}
}
}
pub fn func(f: fn(MalArgs) -> MalRet) -> MalVal {
Func(f, Rc::new(Nil))
Func(f, Rc::new(Nil))
}
pub fn _assoc(mut hm: FnvHashMap<String,MalVal>, kvs: MalArgs) -> MalRet {
if kvs.len() % 2 != 0 {
return error("odd number of elements")
}
for (k, v) in kvs.iter().tuples() {
match k {
Str(s) => { hm.insert(s.to_string(), v.clone()); },
_ => { return error("key is not string") },
pub fn _assoc(mut hm: FnvHashMap<String, MalVal>, kvs: MalArgs) -> MalRet {
if kvs.len() % 2 != 0 {
return error("odd number of elements");
}
}
Ok(Hash(Rc::new(hm),Rc::new(Nil)))
for (k, v) in kvs.iter().tuples() {
match k {
Str(s) => {
hm.insert(s.to_string(), v.clone());
}
_ => return error("key is not string"),
}
}
Ok(Hash(Rc::new(hm), Rc::new(Nil)))
}
pub fn _dissoc(mut hm: FnvHashMap<String,MalVal>, ks: MalArgs) -> MalRet {
for k in ks.iter() {
match k {
Str(ref s) => { hm.remove(s); },
_ => { return error("key is not string") },
pub fn _dissoc(mut hm: FnvHashMap<String, MalVal>, ks: MalArgs) -> MalRet {
for k in ks.iter() {
match k {
Str(ref s) => {
hm.remove(s);
}
_ => return error("key is not string"),
}
}
}
Ok(Hash(Rc::new(hm),Rc::new(Nil)))
Ok(Hash(Rc::new(hm), Rc::new(Nil)))
}
pub fn hash_map(kvs: MalArgs) -> MalRet {
let hm: FnvHashMap<String,MalVal> = FnvHashMap::default();
_assoc(hm, kvs)
let hm: FnvHashMap<String, MalVal> = FnvHashMap::default();
_assoc(hm, kvs)
}
// vim: ts=2:sw=2:expandtab