1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-19 17:47:53 +03:00

rust: add step4_if_fn_do

This commit is contained in:
Joel Martin 2014-10-25 14:02:42 -05:00
parent 8f5b0f1040
commit 0a4d62f2f8
9 changed files with 521 additions and 38 deletions

View File

@ -9,22 +9,27 @@ authors = [ "Your name <you@example.com>" ]
git = "https://github.com/kanaka/rust-pcre"
#[profile.dev]
#
#debug = true
[[bin]]
name = "exp"
[[bin]]
name = "step0_repl"
[[bin]]
name = "step1_read_print"
[[bin]]
name = "step2_eval"
[[bin]]
name = "step3_env"
[[bin]]
name = "step4_if_fn_do"

147
rust/src/core.rs Normal file
View File

@ -0,0 +1,147 @@
#![allow(dead_code)]
use std::rc::Rc;
use std::collections::HashMap;
use types::{MalVal,MalRet,Func,True,False,Int,Strn,List,Nil};
use printer;
// General functions
fn equal_q(a:Vec<MalVal>) -> MalRet {
if a.len() != 2 {
return Err("Wrong arity to equal? call".to_string());
}
match a[0] == a[1] {
true => Ok(Rc::new(True)),
false => Ok(Rc::new(False)),
}
}
// String routines
fn pr_str(a:Vec<MalVal>) -> MalRet {
Ok(Rc::new(Strn(printer::pr_list(&a, true, "", "", " "))))
}
fn str(a:Vec<MalVal>) -> MalRet {
Ok(Rc::new(Strn(printer::pr_list(&a, false, "", "", ""))))
}
fn prn(a:Vec<MalVal>) -> MalRet {
println!("{}", printer::pr_list(&a, true, "", "", " "))
Ok(Rc::new(Nil))
}
fn println(a:Vec<MalVal>) -> MalRet {
println!("{}", printer::pr_list(&a, false, "", "", " "))
Ok(Rc::new(Nil))
}
// Numeric functions
fn int_op(f: |i:int,j:int|-> int, a:Vec<MalVal>) -> MalRet {
match *a[0] {
Int(a0) => match *a[1] {
Int(a1) => Ok(Rc::new(Int(f(a0,a1)))),
_ => Err("second arg must be an int".to_string()),
},
_ => Err("first arg must be an int".to_string()),
}
}
fn bool_op(f: |i:int,j:int|-> bool, a:Vec<MalVal>) -> MalRet {
match *a[0] {
Int(a0) => match *a[1] {
Int(a1) => {
match f(a0,a1) {
true => Ok(Rc::new(True)),
false => Ok(Rc::new(False)),
}
//Ok(Rc::new(Int(f(a0,a1)))),
},
_ => Err("second arg must be an int".to_string()),
},
_ => Err("first arg must be an int".to_string()),
}
}
pub fn add(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i+j }, a) }
pub fn sub(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i-j }, a) }
pub fn mul(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i*j }, a) }
pub fn div(a:Vec<MalVal>) -> MalRet { int_op(|i,j| { i/j }, a) }
pub fn lt (a:Vec<MalVal>) -> MalRet { bool_op(|i,j| { i<j }, a) }
pub fn lte(a:Vec<MalVal>) -> MalRet { bool_op(|i,j| { i<=j }, a) }
pub fn gt (a:Vec<MalVal>) -> MalRet { bool_op(|i,j| { i>j }, a) }
pub fn gte(a:Vec<MalVal>) -> MalRet { bool_op(|i,j| { i>=j }, a) }
// Sequence functions
pub fn list(a:Vec<MalVal>) -> MalRet {
Ok(Rc::new(List(a)))
}
pub fn list_q(a:Vec<MalVal>) -> MalRet {
if a.len() != 1 {
return Err("Wrong arity to list? call".to_string());
}
match *a[0].clone() {
List(_) => Ok(Rc::new(True)),
_ => Ok(Rc::new(False)),
}
}
pub fn count(a:Vec<MalVal>) -> MalRet {
if a.len() != 1 {
return Err("Wrong arity to count call".to_string());
}
match *a[0].clone() {
List(ref lst) => {
Ok(Rc::new(Int(lst.len().to_int().unwrap())))
},
_ => Err("count called on non-sequence".to_string()),
}
}
pub fn empty_q(a:Vec<MalVal>) -> MalRet {
if a.len() != 1 {
return Err("Wrong arity to empty? call".to_string());
}
match *a[0].clone() {
List(ref lst) => {
match lst.len() {
0 => Ok(Rc::new(True)),
_ => Ok(Rc::new(False)),
}
},
_ => Err("empty? called on non-sequence".to_string()),
}
}
pub fn ns() -> HashMap<String,MalVal> {
let mut ns: HashMap<String,MalVal> = HashMap::new();;
ns.insert("=".to_string(), Rc::new(Func(equal_q)));
ns.insert("pr-str".to_string(), Rc::new(Func(pr_str)));
ns.insert("str".to_string(), Rc::new(Func(str)));
ns.insert("prn".to_string(), Rc::new(Func(prn)));
ns.insert("println".to_string(), Rc::new(Func(println)));
ns.insert("<".to_string(), Rc::new(Func(lt)));
ns.insert("<=".to_string(), Rc::new(Func(lte)));
ns.insert(">".to_string(), Rc::new(Func(gt)));
ns.insert(">=".to_string(), Rc::new(Func(gte)));
ns.insert("+".to_string(), Rc::new(Func(add)));
ns.insert("-".to_string(), Rc::new(Func(sub)));
ns.insert("*".to_string(), Rc::new(Func(mul)));
ns.insert("/".to_string(), Rc::new(Func(div)));
ns.insert("list".to_string(), Rc::new(Func(list)));
ns.insert("list?".to_string(), Rc::new(Func(list_q)));
ns.insert("empty?".to_string(), Rc::new(Func(empty_q)));
ns.insert("count".to_string(), Rc::new(Func(count)));
return ns;
}

View File

@ -1,9 +1,11 @@
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use types::{MalVal,MalRet,Nil};
use types::{MalVal,MalRet,Nil,Sym,List};
#[allow(dead_code)]
struct EnvType {
data: HashMap<String,MalVal>,
outer: Option<Env>,
@ -11,10 +13,54 @@ struct EnvType {
pub type Env = Rc<RefCell<EnvType>>;
#[allow(dead_code)]
pub fn env_new(outer: Option<Env>) -> Env {
Rc::new(RefCell::new(EnvType{data: HashMap::new(), outer: outer}))
}
#[allow(dead_code)]
pub fn env_bind(env: &Env,
mbinds: MalVal,
mexprs: MalVal) -> Result<Env,String> {
let mut variadic = false;
match *mbinds {
List(ref binds) => {
match *mexprs {
List(ref exprs) => {
let mut it = binds.iter().enumerate();
for (i, b) in it {
match **b {
Sym(ref strn) => {
if *strn == "&".to_string() {
variadic = true;
break;
} else {
env_set(env, strn.clone(), exprs[i].clone());
}
}
_ => return Err("non-symbol bind".to_string()),
}
}
if variadic {
let (i, sym) = it.next().unwrap();
match **sym {
Sym(ref s) => {
let rest = exprs.slice(i-1,exprs.len()).to_vec();
env_set(env, s.clone(), Rc::new(List(rest)));
}
_ => return Err("& bind to non-symbol".to_string()),
}
}
Ok(env.clone())
},
_ => Err("exprs must be a list".to_string()),
}
},
_ => Err("binds must be a list".to_string()),
}
}
#[allow(dead_code)]
pub fn env_find(env: Env, key: String) -> Option<Env> {
if env.borrow().data.contains_key(&key) {
Some(env)
@ -26,10 +72,12 @@ pub fn env_find(env: Env, key: String) -> Option<Env> {
}
}
#[allow(dead_code)]
pub fn env_set(env: &Env, key: String, val: MalVal) {
env.borrow_mut().data.insert(key, val.clone());
}
#[allow(dead_code)]
pub fn env_get(env: Env, key: String) -> MalRet {
match env_find(env, key.clone()) {
Some(e) => {
@ -42,3 +90,11 @@ pub fn env_get(env: Env, key: String) -> MalRet {
}
}
impl fmt::Show for EnvType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.outer {
Some(ref o) => write!(f, "[{}/outer:{}]", self.data, o.borrow()),
_ => write!(f, "{}", self.data)
}
}
}

View File

@ -1,3 +1,5 @@
use types::MalVal;
pub fn escape_str(s: &str) -> String {
let mut escaped = String::new();
escaped.push('"');
@ -24,3 +26,20 @@ pub fn unescape_str(s: &str) -> String {
let re2 = regex!(r#"\n"#);
re2.replace_all(re1.replace_all(s.as_slice(), "\"").as_slice(), "\n")
}
pub fn pr_list(lst: &Vec<MalVal>, pr: bool,
start: &str , end: &str, join: &str) -> String {
let mut first = true;
let mut res = String::new();
res.push_str(start);
for mv in lst.iter() {
if first {
first = false;
} else {
res.push_str(join);
}
res.push_str(mv.pr_str(pr).as_slice());
}
res.push_str(end);
res
}

View File

@ -7,6 +7,7 @@ extern crate regex;
use types::{MalVal,MalRet};
mod readline;
mod types;
mod env;
mod reader;
mod printer;

View File

@ -10,6 +10,7 @@ use std::collections::HashMap;
use types::{MalVal,MalRet,Nil,Int,Sym,List,Func};
mod readline;
mod types;
mod env;
mod reader;
mod printer;

View File

@ -42,9 +42,6 @@ fn eval_ast(ast: MalVal, env: Env) -> MalRet {
Ok(ast)
}
}
/*
Ok(ast)
*/
}
fn eval(ast: MalVal, env: Env) -> MalRet {

236
rust/src/step4_if_fn_do.rs Normal file
View File

@ -0,0 +1,236 @@
// support precompiled regexes in reader.rs
#![feature(phase)]
#[phase(plugin)]
extern crate regex_macros;
extern crate regex;
use std::rc::Rc;
use types::{MalVal,MalRet,MalFunc,MalFuncData,
Nil,False,Sym,List,Vector,Func};
use env::{Env,env_new,env_bind,env_set,env_get};
mod readline;
mod types;
mod reader;
mod printer;
mod env;
mod core;
// read
fn read(str: String) -> MalRet {
reader::read_str(str)
}
// eval
fn eval_ast(ast: MalVal, env: Env) -> MalRet {
let ast2 = ast.clone();
match *ast2 {
//match *ast {
Sym(ref sym) => {
env_get(env.clone(), sym.clone())
},
List(ref a) => {
let mut ast_vec : Vec<MalVal> = vec![];
for mv in a.iter() {
let mv2 = mv.clone();
match eval(mv2, env.clone()) {
Ok(mv) => { ast_vec.push(mv); },
Err(e) => { return Err(e); },
}
}
Ok(Rc::new(List(ast_vec)))
},
_ => {
Ok(ast)
}
}
}
fn eval(ast: MalVal, env: Env) -> MalRet {
//println!("eval: {}, {}", ast, env.borrow());
//println!("eval: {}", ast);
let ast2 = ast.clone();
match *ast2 {
List(_) => (), // continue
_ => return eval_ast(ast2, env),
}
// apply list
match *ast2 {
List(ref args) => {
if args.len() == 0 {
return Ok(ast);
}
let ref a0 = *args[0];
match *a0 {
Sym(ref a0sym) => {
match a0sym.as_slice() {
"def!" => {
let a1 = (*args)[1].clone();
let a2 = (*args)[2].clone();
let res = eval(a2, env.clone());
match res {
Ok(r) => {
match *a1 {
Sym(ref s) => {
env_set(&env.clone(), s.clone(), r.clone());
return Ok(r);
},
_ => {
return Err("def! of non-symbol".to_string())
}
}
},
Err(e) => return Err(e),
}
},
"let*" => {
let let_env = env_new(Some(env.clone()));
let a1 = (*args)[1].clone();
let a2 = (*args)[2].clone();
match *a1 {
List(ref binds) | Vector(ref binds) => {
let mut it = binds.iter();
while it.len() >= 2 {
let b = it.next().unwrap();
let exp = it.next().unwrap();
match **b {
Sym(ref bstr) => {
match eval(exp.clone(), let_env.clone()) {
Ok(r) => {
env_set(&let_env, bstr.clone(), r);
},
Err(e) => {
return Err(e);
},
}
},
_ => {
return Err("let* with non-symbol binding".to_string());
},
}
}
},
_ => return Err("let* with non-list bindings".to_string()),
}
return eval(a2, let_env.clone());
},
"do" => {
let el = Rc::new(List(args.slice(1,args.len()).to_vec()));
match eval_ast(el, env.clone()) {
Err(e) => return Err(e),
Ok(el) => {
match *el {
List(ref lst) => {
let ref last = lst[lst.len()-1];
return Ok(last.clone());
}
_ => (),
}
},
}
},
"if" => {
let a1 = (*args)[1].clone();
let cond = eval(a1, env.clone());
if cond.is_err() { return cond; }
match *cond.unwrap() {
False | Nil => {
if args.len() >= 4 {
let a3 = (*args)[3].clone();
return eval(a3, env.clone());
} else {
return Ok(Rc::new(Nil));
}
},
_ => {
let a2 = (*args)[2].clone();
return eval(a2, env.clone());
},
}
},
"fn*" => {
let a1 = (*args)[1].clone();
let a2 = (*args)[2].clone();
return Ok(Rc::new(MalFunc(MalFuncData{
exp: a2,
env: env.clone(),
params: a1})));
},
_ => ()
}
}
_ => (),
}
// function call
match eval_ast(ast, env.clone()) {
Err(e) => Err(e),
Ok(el) => {
match *el {
List(ref args) => {
// TODO: make this work
//match args.as_slice() {
// [&Func(f), rest..] => {
// (*f)(rest.to_vec())
// },
// _ => Err("attempt to call non-function".to_string()),
//}
let args2 = args.clone();
match *args2[0] {
Func(f) => f(args.slice(1,args.len()).to_vec()),
MalFunc(ref mf) => {
let mfc = mf.clone();
let alst = List(args.slice(1,args.len()).to_vec());
let new_env = env_new(Some(mfc.env.clone()));
match env_bind(&new_env, mfc.params,
Rc::new(alst)) {
Ok(_) => return eval(mfc.exp, new_env),
Err(e) => return Err(e),
}
},
_ => Err("attempt to call non-function".to_string()),
}
}
_ => Err("Invalid apply".to_string()),
}
}
}
}
_ => Err("Expected list".to_string()),
}
}
// print
fn print(exp: MalVal) -> String {
exp.pr_str(true)
}
fn rep(str: String, env: Env) -> Result<String,String> {
match read(str) {
Err(e) => Err(e),
Ok(ast) => {
//println!("read: {}", ast);
match eval(ast, env) {
Err(e) => Err(e),
Ok(exp) => Ok(print(exp)),
}
}
}
}
fn main() {
let repl_env = env_new(None);
for (k, v) in core::ns().into_iter() { env_set(&repl_env, k, v); }
let _ = rep("(def! not (fn* (a) (if a false true)))".to_string(),
repl_env.clone());
loop {
let line = readline::mal_readline("user> ");
match line { None => break, _ => () }
match rep(line.unwrap(), repl_env.clone()) {
Ok(str) => println!("{}", str),
Err(str) => println!("Error: {}", str),
}
}
}

View File

@ -1,7 +1,8 @@
use std::rc::Rc;
use std::collections;
use std::fmt;
use super::printer::escape_str;
use super::printer::{escape_str,pr_list};
use super::env::Env;
#[deriving(Clone)]
pub enum MalType {
@ -17,12 +18,20 @@ pub enum MalType {
Func(fn(Vec<MalVal>) -> MalRet),
//Func(fn(&[MalVal]) -> MalRet),
//Func(|Vec<MalVal>|:'a -> MalRet),
MalFunc(MalFuncData),
}
pub type MalVal = Rc<MalType>;
pub type MalRet = Result<MalVal,String>;
#[deriving(Clone)]
pub struct MalFuncData {
pub exp: MalVal,
pub env: Env,
pub params: MalVal,
}
impl MalType {
pub fn pr_str(&self, print_readably: bool) -> String {
@ -40,49 +49,61 @@ impl MalType {
} else {
res.push_str(v.as_slice())
}
}
},
List(ref v) => {
res = pr_list(v, _r, "(", ")", " ")
},
Vector(ref v) => {
res = pr_list(v, _r, "[", "]", " ")
},
HashMap(ref v) => {
let mut first = true;
res.push_str("(");
for item in v.iter() {
res.push_str("{");
for (key, value) in v.iter() {
if first { first = false; } else { res.push_str(" "); }
res.push_str(item.pr_str(_r).as_slice());
res.push_str(key.as_slice());
res.push_str(" ");
res.push_str(value.pr_str(_r).as_slice());
}
res.push_str(")")
}
// TODO: better function representation
res.push_str("}")
},
// TODO: better native function representation
//Func(ref v) => {
Func(_) => {
res.push_str(format!("#<function ...>").as_slice())
}
},
MalFunc(ref mf) => {
res.push_str(format!("(fn* {} {})", mf.params, mf.exp).as_slice())
},
/*
Vector(ref v) => {
let mut first = true;
write!(f, "[");
for item in v.iter() {
if first { first = false; } else { write!(f, " ") }
item.fmt(f);
}
write!(f, "]");
}
Hash_Map(ref v) => {
let mut first = true;
write!(f, "{}", "{");
for (key, value) in v.iter() {
if first { first = false; } else { write!(f, " ") }
write!(f, "\"{}\"", *key);
write!(f, " ");
value.fmt(f);
}
write!(f, "{}", "}");
}
// Atom(ref v) => v.fmt(f),
*/
_ => { res.push_str("#<unknown type>") }
//_ => { res.push_str("#<unknown type>") },
};
res
}
}
impl PartialEq for MalType {
fn eq(&self, other: &MalType) -> bool {
match (self, other) {
(&Nil, &Nil) |
(&True, &True) |
(&False, &False) => true,
(&Int(ref a), &Int(ref b)) => a == b,
(&Strn(ref a), &Strn(ref b)) => a == b,
(&Sym(ref a), &Sym(ref b)) => a == b,
(&List(ref a), &List(ref b)) |
(&Vector(ref a), &Vector(ref b)) => a == b,
(&HashMap(ref a), &HashMap(ref b)) => a == b,
// TODO: fix this
(&Func(_), &Func(_)) => false,
(&MalFunc(_), &MalFunc(_)) => false,
_ => return false,
}
}
}
impl fmt::Show for MalType {