aboutsummaryrefslogtreecommitdiff
path: root/rust/src
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-10-25 11:42:07 -0500
committerJoel Martin <github@martintribe.org>2015-01-06 21:58:35 -0600
commitabdd56ebc0e01cd92f694ef2bcafcc394453d055 (patch)
treed1ace96ac90e5d888e4d4d05dd4ca0c0445a856e /rust/src
parentf41866dbe99080f0916512261f0412c5bc65f190 (diff)
downloadmal-abdd56ebc0e01cd92f694ef2bcafcc394453d055.tar.gz
mal-abdd56ebc0e01cd92f694ef2bcafcc394453d055.zip
Rust: step0_repl and step1_read_print
Diffstat (limited to 'rust/src')
-rw-r--r--rust/src/reader.rs126
-rw-r--r--rust/src/readline.rs76
-rw-r--r--rust/src/step0_repl.rs25
-rw-r--r--rust/src/step1_read_print.rs51
-rw-r--r--rust/src/types.rs83
5 files changed, 361 insertions, 0 deletions
diff --git a/rust/src/reader.rs b/rust/src/reader.rs
new file mode 100644
index 0000000..9ad129b
--- /dev/null
+++ b/rust/src/reader.rs
@@ -0,0 +1,126 @@
+//#![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)
+}
diff --git a/rust/src/readline.rs b/rust/src/readline.rs
new file mode 100644
index 0000000..17d1ed9
--- /dev/null
+++ b/rust/src/readline.rs
@@ -0,0 +1,76 @@
+// 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
+ }
+ }
+}
diff --git a/rust/src/step0_repl.rs b/rust/src/step0_repl.rs
new file mode 100644
index 0000000..ac9cf24
--- /dev/null
+++ b/rust/src/step0_repl.rs
@@ -0,0 +1,25 @@
+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()))));
+ }
+}
diff --git a/rust/src/step1_read_print.rs b/rust/src/step1_read_print.rs
new file mode 100644
index 0000000..c6f87d0
--- /dev/null
+++ b/rust/src/step1_read_print.rs
@@ -0,0 +1,51 @@
+// 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),
+ }
+ }
+}
diff --git a/rust/src/types.rs b/rust/src/types.rs
new file mode 100644
index 0000000..3e0e027
--- /dev/null
+++ b/rust/src/types.rs
@@ -0,0 +1,83 @@
+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))
+ }
+}