From 695c42010cd64f3c1711990d9297899eb05c9f26 Mon Sep 17 00:00:00 2001 From: teo3300 Date: Wed, 15 Nov 2023 10:50:29 +0900 Subject: [PATCH] Using Rc for everything - avoid eccessive cloning Signed-off-by: teo3300 --- src/core.rs | 10 +++++----- src/eval.rs | 29 +++++++++++++++++------------ src/main.rs | 4 ++-- src/parse_tools.rs | 7 +++---- src/printer.rs | 8 +++++--- src/reader.rs | 22 +++++++++------------- src/types.rs | 9 +++++---- 7 files changed, 46 insertions(+), 43 deletions(-) diff --git a/src/core.rs b/src/core.rs index 68d2c5b..9a8a150 100644 --- a/src/core.rs +++ b/src/core.rs @@ -2,8 +2,8 @@ use crate::env::env_binds; use crate::printer::prt; -use crate::types::{MalType, MalErr, MalRet}; -use crate::types::MalType::{Fun, MalFun, List}; +use crate::types::MalType::{Fun, List, MalFun}; +use crate::types::{MalErr, MalRet, MalType}; use MalType::Int; @@ -68,7 +68,7 @@ pub fn comparison_op(f: fn(isize, isize) -> bool, args: &[MalType]) -> MalRet { )), _ => { let (left, rights) = car_cdr(args); - let mut left = if_number(left)?; + let mut left = if_number(&left)?; for right in rights { let right = if_number(right)?; if !f(left, right) { @@ -98,6 +98,6 @@ use std::process::exit; pub fn core_exit(list: &[MalType]) -> MalRet { match car_cdr(list).0 { Int(val) => exit(*val as i32), - _ => exit(-1) + _ => exit(-1), } -} \ No newline at end of file +} diff --git a/src/eval.rs b/src/eval.rs index 736b0ba..db80a2c 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1,9 +1,11 @@ -use crate::env::{env_get, env_new, env_set}; +use std::rc::Rc; + +use crate::core::{call_func, car_cdr}; use crate::env::Env; +use crate::env::{env_get, env_new, env_set}; use crate::printer::prt; use crate::types::MalType::*; -use crate::core::{car_cdr, call_func}; -use crate::types::{MalArgs, MalMap, MalRet, MalErr, MalType}; +use crate::types::{MalArgs, MalErr, MalMap, MalRet, MalType}; /// Resolve the first element of the list as the function name and call it /// with the other elements as arguments @@ -11,7 +13,7 @@ fn eval_func(list: &MalType) -> MalRet { match list { List(list) => { let (func, args) = car_cdr(list); - call_func(func, args) + call_func(&func, args) } _ => todo!("This should never happen, if you see this message I probably broke the code"), } @@ -20,13 +22,16 @@ fn eval_func(list: &MalType) -> MalRet { // When evaluating an expression it's possible // (actually the only option I'm aware until now) // to clone the environment "env.clone()", performances aside +// [env.clone()] +// NOTE: Ok, so cloning an Env now is not bad, since it's an Rc +// (Just pointing this out because I know I will try to change this later) // // It's not possible, however, to clone the outer when defining // a new environment that will be used later (such as when using fn*) /// def! special form: /// Evaluate the second expression and assign it to the first symbol -fn def_bang_form(list: &[MalType], env: &Env) -> MalRet { +fn def_bang_form(list: &[MalType], env: Env) -> MalRet { match list.len() { 2 => match &list[0] { Sym(sym) => { @@ -59,7 +64,7 @@ fn let_star_form(list: &[MalType], env: Env) -> MalRet { List(list) if list.len() % 2 == 0 => { // TODO: Find a way to avoid index looping that is ugly for i in (0..list.len()).step_by(2) { - def_bang_form(&list[i..i + 2], &inner_env)?; + def_bang_form(&list[i..i + 2], inner_env.clone())?; } if cdr.is_empty() { // TODO: check if it exists a better way to do this @@ -94,7 +99,7 @@ fn if_form(list: &[MalType], env: Env) -> MalRet { )); } let (cond, branches) = car_cdr(list); - match eval(cond, env.clone())? { + match eval(&cond, env.clone())? { Nil | Bool(false) => match branches.len() { 1 => Ok(Nil), _ => eval(&branches[1], env), @@ -110,8 +115,8 @@ fn fn_star_form(list: &[MalType], env: Env) -> MalRet { let (binds, exprs) = car_cdr(list); Ok(MalFun { eval: eval_ast, - params: Box::new(binds.clone()), - ast: Box::new(List(exprs.to_vec())), + params: Rc::new(binds.clone()), + ast: Rc::new(List(exprs.to_vec())), env: env, }) } @@ -123,7 +128,7 @@ pub fn help_form(list: &[MalType], env: Env) -> MalRet { match sym { Sym(sym_str) => match eval(sym, env.clone())? { Fun(_, desc) => println!("{}\t[builtin]: {}", sym_str, desc), - MalFun { params, ast, .. } => print_malfun(sym_str, *params, *ast), + MalFun { params, ast, .. } => print_malfun(sym_str, params, ast), _ => println!("{:?} is not defined as a function", sym_str), }, _ => println!("{:?} is not a symbol", prt(sym)), @@ -136,8 +141,8 @@ pub fn help_form(list: &[MalType], env: Env) -> MalRet { fn apply(list: &MalArgs, env: Env) -> MalRet { let (car, cdr) = car_cdr(list); match car { - Sym(sym) if sym == "def!" => def_bang_form(cdr, &env), // Set for env - Sym(sym) if sym == "let*" => let_star_form(cdr, env), // Clone the env + Sym(sym) if sym == "def!" => def_bang_form(cdr, env), // Set for env + Sym(sym) if sym == "let*" => let_star_form(cdr, env), // Clone the env Sym(sym) if sym == "do" => do_form(cdr, env), Sym(sym) if sym == "if" => if_form(cdr, env), Sym(sym) if sym == "fn*" => fn_star_form(cdr, env), diff --git a/src/main.rs b/src/main.rs index 34fa482..813feb2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ // io lib to read input and print output use std::env::args; +mod core; mod env; mod eval; mod parse_tools; @@ -8,10 +9,9 @@ mod printer; mod reader; mod step4_if_fn_do; mod types; -mod core; use env::env_init; -use parse_tools::{load_file, interactive}; +use parse_tools::{interactive, load_file}; fn main() { let reply_env = env_init(); diff --git a/src/parse_tools.rs b/src/parse_tools.rs index 00573c1..56eee63 100644 --- a/src/parse_tools.rs +++ b/src/parse_tools.rs @@ -26,9 +26,7 @@ pub fn load_file(filename: &str, env: &Env) -> io::Result<()> { } last = match rep(&parser, env) { - Err(error) if error.is_recoverable() => { - Err(error) - } + Err(error) if error.is_recoverable() => Err(error), tmp => { parser.clear(); tmp.map_err(|error| { @@ -74,7 +72,8 @@ pub fn interactive(env: Env) { match rep(&parser, &env) { Ok(output) => output.iter().for_each(|el| println!("[{}]> {}", num, el)), Err(error) => { - if error.is_recoverable() {// && line != "\n" { + if error.is_recoverable() { + // && line != "\n" { continue; } println!("; [{}]> Error @ {}", num, error.message()); diff --git a/src/printer.rs b/src/printer.rs index 9d2ecec..3062a1c 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use crate::types::escape_str; use crate::types::MalType; use crate::types::MalType::*; @@ -38,7 +40,7 @@ pub fn pr_str(ast: &MalType, print_readably: bool) -> String { .collect::>() .join(" ") ), - Fun(_, desc) => format!("{}", desc), + Fun(..) => "#".to_string(), MalFun { .. } => "#".to_string(), } } @@ -47,9 +49,9 @@ pub fn prt(ast: &MalType) -> String { return pr_str(ast, true); } -pub fn print_malfun(sym: &String, params: MalType, ast: MalType) { +pub fn print_malfun(sym: &String, params: Rc, ast: Rc) { print!("; {} {}:\n", sym, prt(¶ms)); - match ast { + match ast.as_ref() { List(list) => { for el in list { println!("; {}", prt(&el)) diff --git a/src/reader.rs b/src/reader.rs index e792fda..5bd6d23 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -28,15 +28,12 @@ impl Reader { pub fn push(&self, input: &str) { self.ptr.set(0); // reset the state of the parser and push the additional strings - self.tokens - .borrow_mut() - .append(&mut tokenize(input)) + self.tokens.borrow_mut().append(&mut tokenize(input)) } pub fn clear(&self) { self.ptr.set(0); - *self.tokens - .borrow_mut() = Vec::new(); + *self.tokens.borrow_mut() = Vec::new(); } // May be improved @@ -136,13 +133,12 @@ pub fn read_str(reader: &Reader) -> MalRet { /// 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) -> Tokens { - let tokens = Regex::new( - r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]*)"###, - ) - .unwrap() - .captures_iter(input) - .map(|e| e[1].to_string()) - .filter(|e| !(e.is_empty() || e.starts_with(';'))) - .collect::>(); + let tokens = + Regex::new(r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]*)"###) + .unwrap() + .captures_iter(input) + .map(|e| e[1].to_string()) + .filter(|e| !(e.is_empty() || e.starts_with(';'))) + .collect::>(); tokens } diff --git a/src/types.rs b/src/types.rs index 6b2bc8e..8033d64 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,5 @@ use crate::env::Env; -use std::collections::HashMap; +use std::{collections::HashMap, rc::Rc}; // All Mal types should inherit from this #[derive(Clone, Debug)] @@ -10,10 +10,11 @@ pub enum MalType { Fun(fn(&[MalType]) -> MalRet, &'static str), // Used for base functions, implemented using the underlying language (rust) MalFun { eval: fn(ast: &MalType, env: Env) -> MalRet, - params: Box, - ast: Box, + params: Rc, + ast: Rc, env: Env, }, // Used for functions defined within mal + // Use Rc so I can now clone like there's no tomorrow Sym(String), Key(String), Str(String), @@ -107,4 +108,4 @@ pub fn unescape_str(s: &str) -> String { .replace("\\\\", "\\") .replace("\\n", "\n") .replace("\\\"", "\"") -} \ No newline at end of file +}