diff options
| author | Joel Martin <github@martintribe.org> | 2014-10-25 11:42:07 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2015-01-06 21:58:35 -0600 |
| commit | abdd56ebc0e01cd92f694ef2bcafcc394453d055 (patch) | |
| tree | d1ace96ac90e5d888e4d4d05dd4ca0c0445a856e /rust/src | |
| parent | f41866dbe99080f0916512261f0412c5bc65f190 (diff) | |
| download | mal-abdd56ebc0e01cd92f694ef2bcafcc394453d055.tar.gz mal-abdd56ebc0e01cd92f694ef2bcafcc394453d055.zip | |
Rust: step0_repl and step1_read_print
Diffstat (limited to 'rust/src')
| -rw-r--r-- | rust/src/reader.rs | 126 | ||||
| -rw-r--r-- | rust/src/readline.rs | 76 | ||||
| -rw-r--r-- | rust/src/step0_repl.rs | 25 | ||||
| -rw-r--r-- | rust/src/step1_read_print.rs | 51 | ||||
| -rw-r--r-- | rust/src/types.rs | 83 |
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)) + } +} |
