diff --git a/src/main.rs b/src/main.rs index c8aa90f..9daa0d1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,10 +29,8 @@ fn main() -> io::Result<()> { // Perform rep on whole available input match rep(&input) { Ok(output) => { - for el in output { - num += 1; - println!("[{}]> {}", num, el); - } + num += 1; + println!("[{}]> {}", num, output); } Err(err) => { if line == "\n" { diff --git a/src/reader.rs b/src/reader.rs index dd5ef32..488d3f8 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -19,26 +19,23 @@ impl Reader { Reader { tokens, ptr: 0 } } - fn avail(&self) -> bool { - self.ptr < self.tokens.len() + // May be improved + fn get_token(&self, i: usize) -> Result { + match self.tokens.get(i) { + Some(token) => Ok(token.to_string()), + None => Err("Unexpected EOF".to_string()), + } } /// Returns the token at the current position fn peek(&self) -> Result { - match self.tokens.get(self.ptr) { - Some(token) => Ok(token.to_string()), - None => Err("Unexpected EOF, Missing parenthesis?".to_string()), - } + self.get_token(self.ptr) } /// Returns the token at current position and increment current position - // TODO: PLEASE USE THE PEEK FUNCTION fn next(&mut self) -> Result { self.ptr += 1; - match self.tokens.get(self.ptr - 1) { - Some(token) => Ok(token.to_string()), - None => Err("Unexpected EOF, Missing parenthesis?".to_string()), - } + self.get_token(self.ptr - 1) } /// Repeatedly calls `read_form` of the reader object until it finds a ")" token @@ -50,44 +47,36 @@ impl Reader { self.next()?; let mut vector = Vec::new(); - loop { - let token = self.peek()?; - if token == terminator { - break; - } + + while self.peek()? != terminator { vector.push(self.read_form()?) } self.next()?; + match terminator { ")" => Ok(List(vector)), "]" => Ok(Vector(vector)), "}" => make_map(vector), - _ => Err(format!("Unknown collection terminator: {}", terminator)), + _ => Err("Unknown collection terminator".to_string()), } } /// Read atomic token and return appropriate scalar () fn read_atom(&mut self) -> MalRet { - let token = self.next()?; - let re_digits = Regex::new(r"^-?[0-9]+$").unwrap(); - match token.as_str() { - ")" | "]" | "}" => Err(format!("Lone parenthesis {}", token)), + match &self.next()?[..] { + ")" | "]" | "}" => Err("Missing open parenthesis".to_string()), "false" => Ok(Bool(false)), "true" => Ok(Bool(true)), "nil" => Ok(Nil), - _ => { - if re_digits.is_match(&token) { - Ok(Int(token.parse::().unwrap())) - } else if token.starts_with('\"') { - if token.ends_with('\"') { - Ok(Str(map_key(KeyVar::Str, &unescape_str(&token)))) - } else { - Err("Unterminated string, expected \"".to_string()) - } - } else if token.starts_with(':') { - Ok(Key(map_key(KeyVar::Key, &token))) + tk => { + if Regex::new(r"^-?[0-9]+$").unwrap().is_match(tk) { + Ok(Int(tk.parse::().unwrap())) + } else if tk.starts_with('\"') { + Ok(Str(map_key(KeyVar::Str, &unescape_str(tk)))) + } else if tk.starts_with(':') { + Ok(Key(map_key(KeyVar::Key, tk))) } else { - Ok(Sym(map_key(KeyVar::Sym, &token))) + Ok(Sym(map_key(KeyVar::Sym, tk))) } } } @@ -99,9 +88,8 @@ impl Reader { /// "(" -> call `read_list` /// otherwise -> call `read_atom` fn read_form(&mut self) -> MalRet { - let token = self.peek()?; // String slice containing the whole string - match &token[..] { + match &self.peek()?[..] { // Consume "(" and parse list "(" => self.read_list(")"), "[" => self.read_list("]"), @@ -114,43 +102,17 @@ impl Reader { /// Call `tokenize` on a string /// Create anew Reader with the tokens /// Call read_from with the reader instance -/// TODO: catch errors -pub fn read_str(input: &str) -> Result, MalErr> { - let tokens = tokenize(input); - match tokens.len() { - 0 => Ok(vec![Nil]), - _ => { - let mut reader = Reader::new(tokens); - let mut res = Vec::new(); - while reader.avail() { - match reader.read_form() { - Err(err) => return Err(err), - Ok(any) => res.push(any), - }; - } - Ok(res) - } - } +pub fn read_str(input: &str) -> MalRet { + Reader::new(tokenize(input)).read_form() } /// Read a string and return a list of tokens in it (following regex in README) // Add error handling for strings that are not terminated fn tokenize(input: &str) -> Vec { - let mut tokens = Vec::new(); - - let re = Regex::new( - r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*\n|[^\s\[\]{}('"`,;)]*)"###, - ) - .unwrap(); - for match_str in re.captures_iter(input) { - if !match_str[1].is_empty() { - // Drop comments - if match_str[1].starts_with(';') { - continue; - } - tokens.push(match_str[1].to_string()); - } - } - - tokens + Regex::new(r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*\n|[^\s\[\]{}('"`,;)]*)"###) + .unwrap() + .captures_iter(input) + .map(|e| e[1].to_string()) + .filter(|e| !e.is_empty() || !e.starts_with(';')) + .collect::>() } diff --git a/src/step1_read_print.rs b/src/step1_read_print.rs index bf12e30..8287d59 100644 --- a/src/step1_read_print.rs +++ b/src/step1_read_print.rs @@ -6,11 +6,11 @@ use crate::printer::pr_str; use crate::reader::read_str; -use crate::types::{MalErr, MalType}; +use crate::types::{MalRet, MalType}; #[allow(non_snake_case)] /// Read input and generate an ast -fn READ(input: &str) -> Result, MalErr> { +fn READ(input: &str) -> MalRet { match read_str(input) { Ok(ast) => Ok(ast), Err(err) => Err(format!("@ READ: {}", err)), @@ -19,22 +19,18 @@ fn READ(input: &str) -> Result, MalErr> { #[allow(non_snake_case)] /// Evaluate the generated ast -fn EVAL(ast: Vec) -> Vec { - ast +fn EVAL(ast: MalType) -> MalRet { + Ok(ast) } #[allow(non_snake_case)] /// Print out the result of the evaluation -fn PRINT(input: Vec) -> Vec { - let mut ret = Vec::new(); - for expr in input { - ret.push(pr_str(&expr, true)) - } - ret +fn PRINT(output: MalType) -> String { + pr_str(&output, true) } -pub fn rep(input: &str) -> Result, MalErr> { +pub fn rep(input: &str) -> Result { let ast = READ(input)?; - let out = EVAL(ast); + let out = EVAL(ast)?; Ok(PRINT(out)) }