go/mal
java/target/
java/dependency-reduced-pom.xml
-rust/step0_repl
-rust/step1_read_print
-rust/step2_eval
-rust/step3_env
-rust/step4_if_fn_do
-rust/step5_tco
-rust/step6_file
-rust/step7_quote
-rust/step8_macros
-rust/step9_try
-rust/stepA_interop
+rust/target/
+rust/Cargo.lock
+rust/.cargo
# Settings
#
-IMPLS = bash c clojure cs go java js make mal perl php ps python ruby
+IMPLS = bash c clojure cs go java js make mal perl php ps python ruby rust
step0 = step0_repl
step1 = step1_read_print
ps_STEP_TO_PROG = ps/$($(1)).ps
python_STEP_TO_PROG = python/$($(1)).py
ruby_STEP_TO_PROG = ruby/$($(1)).rb
+rust_STEP_TO_PROG = rust/target/$($(1))
bash_RUNSTEP = bash ../$(2) $(3)
ps_RUNSTEP = $(4)gs -q -I./ -dNODISPLAY -- ../$(2) $(3)$(4)
python_RUNSTEP = $(PYTHON) ../$(2) $(3)
ruby_RUNSTEP = ruby ../$(2) $(3)
+rust_RUNSTEP = ../$(2) $(3)
# Extra options to pass to runtest.py
cs_TEST_OPTS = --redirect
## Description
Mal is an interpreter for a subset of the Clojure programming
-language. Mal is implemented from scratch in 14 different languages:
+language. Mal is implemented from scratch in 15 different languages:
* Bash shell
* C
* Postscript
* Python
* Ruby
+* Rust
Mal is also a learning tool. Each implentation of mal is separated
ruby stepX_YYY.rb
```
+### Rust (0.13)
+
+The rust implementation of mal requires the rust compiler and build
+tool (cargo) to build.
+
+```
+cd rust
+cargo build
+./target/stepX_YYY
+```
+
## Running tests
The are nearly 400 generic Mal tests (for all implementations) in the
Future Implementations:
- Rust:
+ - http://doc.rust-lang.org/index.html
+ - http://doc.rust-lang.org/intro.html
+ - http://doc.rust-lang.org/guide.html
+ - http://rustbyexample.com/index.html
- http://www.rustforrubyists.com/book/index.html
- - http://static.rust-lang.org/doc/0.9/complement-cheatsheet.html
- http://pzol.github.io/getting_rusty/
- - release notes:
- - https://github.com/mozilla/rust/wiki/Doc-detailed-release-notes
- - this week in rust:
- - http://cmr.github.io/
- - readline:
- - http://redbrain.co.uk/2013/11/09/rust-and-readline-c-ffi/
- - http://www.reddit.com/r/rust/comments/1q9pqc/rust_cffi_and_readline/
- - https://github.com/dbp/rustrepl
- - hash-map:
- - http://static.rust-lang.org/doc/master/std/hashmap/index.html
- - http://static.rust-lang.org/doc/master/std/hashmap/struct.HashMap.html
- - vector/list:
- - http://static.rust-lang.org/doc/master/std/vec/index.html
- - example code:
- - https://github.com/dradtke/rust-dominion/blob/master/dominion/mod.rs
+
+ - http://blog.thiago.me/notes-about-rust-modules/
+ - http://doc.rust-lang.org/std/io/io
+ - https://github.com/shaleh/rust-readline/blob/master/src/lib.rs
+ - http://stackoverflow.com/questions/23942627/does-rust-0-10-have-a-rl-package
+ - http://blog.skylight.io/rust-means-never-having-to-close-a-socket/
- Redmonk languages from Jan 2014:
http://sogrady-media.redmonk.com/sogrady/files/2014/01/lang-rank-114-wm.png
--- /dev/null
+[package]
+
+name = "Mal"
+version = "0.0.1"
+authors = [ "Your name <you@example.com>" ]
+
+
+[dependencies.cadencemarseille-pcre]
+
+git = "https://github.com/kanaka/rust-pcre"
+
+[[bin]]
+
+name = "exp"
+
+[[bin]]
+
+name = "step0_repl"
+
+[[bin]]
+
+name = "step1_read_print"
--- /dev/null
+//#![feature(phase)]
+//#[phase(plugin)]
+//extern crate regex_macros;
+//extern crate regex;
+
+extern crate pcre;
+
+use std::rc::Rc;
+use types::{MalVal,Nil,True,False,Int,Strn,Sym,List};
+use self::pcre::Pcre;
+use super::printer::unescape_str;
+
+#[deriving(Show, Clone)]
+struct Reader {
+ tokens : Vec<String>,
+ position : uint,
+}
+
+impl Reader {
+ fn next(&mut self) -> Option<String> {
+ if self.position < self.tokens.len() {
+ self.position += 1;
+ Some(self.tokens[self.position-1].to_string())
+ } else {
+ None
+ }
+ }
+ fn peek(&self) -> Option<String> {
+ if self.position < self.tokens.len() {
+ Some(self.tokens[self.position].to_string())
+ } else {
+ None
+ }
+ }
+}
+
+fn tokenize(str :String) -> Vec<String> {
+ let mut results = vec![];
+
+ let re = match Pcre::compile(r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)"###) {
+ Err(_) => { fail!("failed to compile regex") },
+ Ok(re) => re
+ };
+
+ let mut it = re.matches(str.as_slice());
+ loop {
+ let opt_m = it.next();
+ if opt_m.is_none() { break; }
+ let m = opt_m.unwrap();
+ if m.group(1) == "" { break; }
+
+ results.push((*m.group(1)).to_string());
+ }
+ results
+}
+
+fn read_atom(rdr : &mut Reader) -> Result<MalVal,String> {
+ let otoken = rdr.next();
+ //println!("read_atom: {}", otoken);
+ if otoken.is_none() { return Err("read_atom underflow".to_string()); }
+ let stoken = otoken.unwrap();
+ let token = stoken.as_slice();
+ if regex!(r"^-?[0-9]+$").is_match(token) {
+ let num : Option<int> = from_str(token);
+ Ok(Rc::new(Int(num.unwrap())))
+ } else if regex!(r#"^".*"$"#).is_match(token) {
+ let new_str = token.slice(1,token.len()-1);
+ Ok(Rc::new(Strn(unescape_str(new_str))))
+ } else if token == "nil" {
+ Ok(Rc::new(Nil))
+ } else if token == "true" {
+ Ok(Rc::new(True))
+ } else if token == "false" {
+ Ok(Rc::new(False))
+ } else {
+ Ok(Rc::new(Sym(String::from_str(token))))
+ }
+}
+
+fn read_list(rdr : &mut Reader) -> Result<MalVal,String> {
+ let otoken = rdr.next();
+ if otoken.is_none() { return Err("read_atom underflow".to_string()); }
+ let stoken = otoken.unwrap();
+ let token = stoken.as_slice();
+ if token != "(" { return Err("expected '('".to_string()); }
+
+ let mut ast_vec : Vec<MalVal> = vec![];
+ loop {
+ let otoken = rdr.peek();
+ if otoken.is_none() { return Err("expected ')', got EOF".to_string()); }
+ let stoken = otoken.unwrap();
+ let token = stoken.as_slice();
+ if token == ")" { break; }
+
+ match read_form(rdr) {
+ Ok(mv) => ast_vec.push(mv),
+ Err(e) => return Err(e),
+ }
+ }
+ rdr.next();
+
+ //ast_vec.push(Rc::new(Nil));
+ Ok(Rc::new(List(ast_vec)))
+}
+
+fn read_form(rdr : &mut Reader) -> Result<MalVal,String> {
+ let otoken = rdr.peek();
+ //println!("read_form: {}", otoken);
+ let stoken = otoken.unwrap();
+ let token = stoken.as_slice();
+ match token {
+ ")" => Err("unexected ')'".to_string()),
+ "(" => read_list(rdr),
+ _ => read_atom(rdr)
+ }
+}
+
+pub fn read_str(str :String) -> Result<MalVal,String> {
+ let tokens = tokenize(str);
+ if tokens.len() == 0 {
+ return Err("<empty line>".to_string());
+ }
+ //println!("tokens: {}", tokens);
+ let rdr = &mut Reader{tokens: tokens, position: 0};
+ read_form(rdr)
+}
--- /dev/null
+// Based on: https://github.com/shaleh/rust-readline (MIT)
+extern crate libc;
+
+use std::c_str;
+
+use std::io::{File, Append, Write};
+use std::io::BufferedReader;
+
+mod ext_readline {
+ extern crate libc;
+ use self::libc::c_char;
+ #[link(name = "readline")]
+ extern {
+ pub fn add_history(line: *const c_char);
+ pub fn readline(p: *const c_char) -> *const c_char;
+ }
+}
+
+pub fn add_history(line: &str) {
+ unsafe {
+ ext_readline::add_history(line.to_c_str().as_ptr());
+ }
+}
+
+pub fn readline(prompt: &str) -> Option<String> {
+ let cprmt = prompt.to_c_str();
+ unsafe {
+ let ret = ext_readline::readline(cprmt.as_ptr());
+ if ret.is_null() { // user pressed Ctrl-D
+ None
+ }
+ else {
+ c_str::CString::new(ret, true).as_str().map(|ret| ret.to_string())
+ }
+ }
+}
+
+// --------------------------------------------
+
+static mut history_loaded : bool = false;
+static HISTORY_FILE : &'static str = "/home/joelm/.mal-history";
+
+fn load_history() {
+ unsafe {
+ if history_loaded { return; }
+ history_loaded = true;
+ }
+
+ let path = Path::new(HISTORY_FILE);
+ let mut file = BufferedReader::new(File::open(&path));
+ for line in file.lines() {
+ let rt: &[_] = &['\r', '\n'];
+ let line2 = line.unwrap();
+ let line3 = line2.as_slice().trim_right_chars(rt);
+ add_history(line3);
+ }
+}
+
+fn append_to_history(line: &str) {
+ let path = Path::new("/home/joelm/.mal-history");
+ let mut file = File::open_mode(&path, Append, Write);
+ let _ = file.write_line(line);
+}
+
+pub fn mal_readline (prompt: &str) -> Option<String> {
+ load_history();
+ let line = readline(prompt);
+ match line {
+ None => None,
+ _ => {
+ add_history(line.clone().unwrap().as_slice());
+ append_to_history(line.clone().unwrap().as_slice());
+ line
+ }
+ }
+}
--- /dev/null
+use readline::mal_readline;
+mod readline;
+
+// read
+fn read(str: String) -> String {
+ str
+}
+
+// eval
+fn eval(ast: String) -> String {
+ ast
+}
+
+// print
+fn print(exp: String) -> String {
+ exp
+}
+
+fn main() {
+ loop {
+ let line = mal_readline("user> ");
+ match line { None => break, _ => () }
+ println!("{}", print(eval(read(line.unwrap()))));
+ }
+}
--- /dev/null
+// support precompiled regexes in reader.rs
+#![feature(phase)]
+#[phase(plugin)]
+extern crate regex_macros;
+extern crate regex;
+
+use std::rc::Rc;
+use types::{MalVal,List,Vector,Int,Nil};
+mod readline;
+mod types;
+mod reader;
+mod printer;
+
+// read
+fn read(str: String) -> Result<MalVal,String> {
+ reader::read_str(str)
+}
+
+// eval
+fn eval(ast: MalVal) -> Result<MalVal,String> {
+ Ok(ast)
+}
+
+// print
+fn print(exp: MalVal) -> String {
+ exp.pr_str(true)
+}
+
+fn rep(str: String) -> Result<String,String> {
+ match read(str) {
+ Err(e) => Err(e),
+ Ok(ast) => {
+ //println!("read: {}", ast);
+ match eval(ast) {
+ Err(e) => Err(e),
+ Ok(exp) => Ok(print(exp)),
+ }
+ }
+ }
+}
+
+fn main() {
+ loop {
+ let line = readline::mal_readline("user> ");
+ match line { None => break, _ => () }
+ match rep(line.unwrap()) {
+ Ok(str) => println!("{}", str),
+ Err(str) => println!("Error: {}", str),
+ }
+ }
+}
--- /dev/null
+use std::rc::Rc;
+use std::collections;
+use std::fmt;
+use super::printer::escape_str;
+
+#[deriving(Clone)]
+pub enum MalType {
+ Nil,
+ True,
+ False,
+ Int(int),
+ Strn(String),
+ Sym(String),
+ List(Vec<MalVal>),
+ Vector(Vec<MalVal>),
+ HashMap(collections::HashMap<String, MalVal>),
+}
+
+pub type MalVal = Rc<MalType>;
+
+impl MalType {
+ pub fn pr_str(&self, print_readably: bool) -> String {
+ let _r = print_readably;
+ let mut res = String::new();
+ match *self {
+ Nil => res.push_str("nil"),
+ True => res.push_str("true"),
+ False => res.push_str("false"),
+ Int(v) => res.push_str(v.to_string().as_slice()),
+ Sym(ref v) => res.push_str((*v).as_slice()),
+ Strn(ref v) => {
+ if print_readably {
+ res.push_str(escape_str((*v).as_slice()).as_slice())
+ } else {
+ res.push_str(v.as_slice())
+ }
+ }
+ List(ref v) => {
+ let mut first = true;
+ res.push_str("(");
+ for item in v.iter() {
+ if first { first = false; } else { res.push_str(" "); }
+ res.push_str(item.pr_str(_r).as_slice());
+ }
+ res.push_str(")")
+ }
+/*
+*/
+ /*
+ 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
+ }
+}
+
+impl fmt::Show for MalType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.pr_str(true))
+ }
+}
(** 1 2)
;=>(** 1 2)
+;; Test commas as whitespace
+(1 2, 3,,,,),,
+;=>(1 2 3)
+
;; Testing read of vectors
[+ 1 2]
;=>[+ 1 2]
{ "a" {"b" { "cde" 3 } }}
;=>{"a" {"b" {"cde" 3}}}
-;; Test commas as whitespace
-(1 2, 3,,,,),,
-;=>(1 2 3)
-
;;
;; Testing reader errors
;;; TODO: fix these so they fail correctly