From 54d196582fd004c3ba23e3bd3113dd7edf34d547 Mon Sep 17 00:00:00 2001 From: teo3300 Date: Wed, 7 Jun 2023 14:41:12 +0200 Subject: [PATCH] Oneline multi expression Multiple expressions can be evaluated on a single line Signed-off-by: teo3300 --- src/main.rs | 29 ++++++++++++++--------------- src/printer.rs | 8 ++++---- src/reader.rs | 40 ++++++++++++++++++++-------------------- src/step1_read_print.rs | 17 ++++++++++------- src/types.rs | 34 +++++++++++++++++++++++++++------- 5 files changed, 75 insertions(+), 53 deletions(-) diff --git a/src/main.rs b/src/main.rs index d3571ac..2fe8b1d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,34 +10,33 @@ use step1_read_print::rep; fn main() -> io::Result<()> { loop { - print!("user> "); - // Flush the prompt to appear before command - let _ = io::stdout().flush(); - let mut input = String::new(); loop { + print!("user> "); + // Flush the prompt to appear before command + let _ = io::stdout().flush(); + // Read line to compose program inpug let mut line = String::new(); io::stdin().read_line(&mut line)?; - // Append line to input - input.push_str(&line); - - // If there is nothing to evaluate skip rep - if input == "\n" { - continue; + if line == "\n" { + break; } + input.push_str(&line); + // Perform rep on whole available input match rep(&input) { - Ok(output) => println!("{}", output), - Err((err, depth)) => { + Ok(output) => { + for el in output { + println!("{}", el); + } + } + Err(err) => { if line == "\n" { println!("ERROR: {}", err); } else { - print!("user> {}", " ".repeat(depth)); - // Flush the prompt to appear before command - let _ = io::stdout().flush(); continue; } } diff --git a/src/printer.rs b/src/printer.rs index e813d0f..13f7c98 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -5,14 +5,14 @@ use crate::types::MalType::*; pub fn pr_str(ast: &MalType, print_readably: bool) -> String { match ast { Nil => "nil".to_string(), - Symbol(sym) | Keyword(sym) => sym.to_string(), + Sym(sym) | Key(sym) => sym.val.to_string(), Int(val) => val.to_string(), Bool(val) => val.to_string(), Str(str) => { if print_readably { - escape_str(str) + escape_str(&str.val) } else { - str.to_string() + str.val.to_string() } } List(el) => format!( @@ -33,7 +33,7 @@ pub fn pr_str(ast: &MalType, print_readably: bool) -> String { Map(el) => format!( "{{{}}}", el.iter() - .map(|sub| vec![sub.0.to_string(), pr_str(sub.1, print_readably)].join(" ")) + .map(|sub| vec![sub.0.val.to_string(), pr_str(sub.1, print_readably)].join(" ")) .collect::>() .join(" ") ), diff --git a/src/reader.rs b/src/reader.rs index da499b3..dd5ef32 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -1,6 +1,7 @@ // Specyfy components in "types" use crate::types::*; // By specifying enum variants it's possible to omit namespace +use crate::types::KeyVar; use crate::types::MalType::*; use regex::Regex; @@ -8,7 +9,6 @@ use regex::Regex; pub struct Reader { tokens: Vec, ptr: usize, - depth: usize, } // TODO: instead of panic on missing ")" try implementing a multi line parsing @@ -16,11 +16,11 @@ pub struct Reader { // (append to the "last" list) while traversing impl Reader { fn new(tokens: Vec) -> Reader { - Reader { - tokens, - ptr: 0, - depth: 0, - } + Reader { tokens, ptr: 0 } + } + + fn avail(&self) -> bool { + self.ptr < self.tokens.len() } /// Returns the token at the current position @@ -47,8 +47,6 @@ impl Reader { /// NOTE: `read_list` calls `read_form` -> enable recursion /// (lists can contains other lists) fn read_list(&mut self, terminator: &str) -> MalRet { - self.depth += 1; - self.next()?; let mut vector = Vec::new(); @@ -60,14 +58,12 @@ impl Reader { vector.push(self.read_form()?) } self.next()?; - let ret = match terminator { + match terminator { ")" => Ok(List(vector)), "]" => Ok(Vector(vector)), "}" => make_map(vector), _ => Err(format!("Unknown collection terminator: {}", terminator)), - }; - self.depth -= 1; - ret + } } /// Read atomic token and return appropriate scalar () @@ -84,14 +80,14 @@ impl Reader { Ok(Int(token.parse::().unwrap())) } else if token.starts_with('\"') { if token.ends_with('\"') { - Ok(Str(unescape_str(&token))) + Ok(Str(map_key(KeyVar::Str, &unescape_str(&token)))) } else { Err("Unterminated string, expected \"".to_string()) } } else if token.starts_with(':') { - Ok(Keyword(token)) + Ok(Key(map_key(KeyVar::Key, &token))) } else { - Ok(Symbol(token)) + Ok(Sym(map_key(KeyVar::Sym, &token))) } } } @@ -119,16 +115,20 @@ impl Reader { /// Create anew Reader with the tokens /// Call read_from with the reader instance /// TODO: catch errors -pub fn read_str(input: &str) -> Result { +pub fn read_str(input: &str) -> Result, MalErr> { let tokens = tokenize(input); match tokens.len() { - 0 => Ok(Nil), + 0 => Ok(vec![Nil]), _ => { let mut reader = Reader::new(tokens); - match reader.read_form() { - Err(err) => Err((err, reader.depth)), - Ok(any) => Ok(any), + let mut res = Vec::new(); + while reader.avail() { + match reader.read_form() { + Err(err) => return Err(err), + Ok(any) => res.push(any), + }; } + Ok(res) } } } diff --git a/src/step1_read_print.rs b/src/step1_read_print.rs index c718e5e..9feac11 100644 --- a/src/step1_read_print.rs +++ b/src/step1_read_print.rs @@ -10,27 +10,30 @@ use crate::types::{MalErr, MalType}; #[allow(non_snake_case)] /// Read input and generate an ast -fn READ(input: &str) -> Result { +fn READ(input: &str) -> Result, MalErr> { match read_str(input) { Ok(ast) => Ok(ast), - Err((err, depth)) => Err((format!("Unexpected error during READ: {}", err), depth)), + Err(err) => Err(format!("Unexpected error during READ: {}", err)), } } #[allow(non_snake_case)] /// Evaluate the generated ast -fn EVAL(ast: MalType) -> MalType { - println!("{:#?}", ast); +fn EVAL(ast: Vec) -> Vec { ast } #[allow(non_snake_case)] /// Print out the result of the evaluation -fn PRINT(input: MalType) -> String { - pr_str(&input, true) +fn PRINT(input: Vec) -> Vec { + let mut ret = Vec::new(); + for expr in input { + ret.push(pr_str(&expr, true)) + } + ret } -pub fn rep(input: &str) -> Result { +pub fn rep(input: &str) -> Result, MalErr> { let ast = READ(input)?; let out = EVAL(ast); Ok(PRINT(out)) diff --git a/src/types.rs b/src/types.rs index bf587c7..1b67cd8 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,15 +2,35 @@ use std::collections::HashMap; +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub enum KeyVar { + Key, + Sym, + Str, +} + +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct KeyType { + pub val: String, + var: KeyVar, +} + +pub fn map_key(var: KeyVar, val: &str) -> KeyType { + KeyType { + val: val.to_string(), + var, + } +} + // All Mal types should inherit from this #[derive(Debug, Clone)] pub enum MalType { List(Vec), Vector(Vec), - Map(HashMap), - Symbol(String), - Keyword(String), - Str(String), + Map(HashMap), + Sym(KeyType), + Key(KeyType), + Str(KeyType), Int(isize), Bool(bool), Nil, @@ -30,7 +50,7 @@ pub type MalErr = String; pub type MalArgs = Vec; pub type MalRet = Result; -use MalType::{Map, Symbol}; +use MalType::{Key, Map, Str, Sym}; pub fn make_map(list: MalArgs) -> MalRet { if list.len() % 2 != 0 { @@ -41,9 +61,9 @@ pub fn make_map(list: MalArgs) -> MalRet { for i in (0..list.len()).step_by(2) { match &list[i] { - Symbol(k) => { + Sym(k) | Key(k) | Str(k) => { let v = list[i + 1].clone(); - map.insert(k.to_string(), v); + map.insert(k.clone(), v); } _ => return Err(format!("Map key not valid: {:?}", list[i])), }