mirror of
https://github.com/kanaka/mal.git
synced 2024-09-11 13:55:55 +03:00
commit
26e29fe36e
479
rust/core.rs
479
rust/core.rs
@ -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
|
||||
|
||||
|
105
rust/env.rs
105
rust/env.rs
@ -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
|
||||
|
@ -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
|
||||
|
233
rust/reader.rs
233
rust/reader.rs
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
276
rust/types.rs
276
rust/types.rs
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user