Using Rc for everything

- avoid eccessive cloning

Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
teo3300
2023-11-15 10:50:29 +09:00
parent f240d31f27
commit 695c42010c
7 changed files with 46 additions and 43 deletions

View File

@ -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),
}
}
}

View File

@ -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),

View File

@ -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();

View File

@ -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());

View File

@ -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::<Vec<String>>()
.join(" ")
),
Fun(_, desc) => format!("{}", desc),
Fun(..) => "#<builtin>".to_string(),
MalFun { .. } => "#<function>".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<MalType>, ast: Rc<MalType>) {
print!("; {} {}:\n", sym, prt(&params));
match ast {
match ast.as_ref() {
List(list) => {
for el in list {
println!("; {}", prt(&el))

View File

@ -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::<Vec<String>>();
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::<Vec<String>>();
tokens
}

View File

@ -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<MalType>,
ast: Box<MalType>,
params: Rc<MalType>,
ast: Rc<MalType>,
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("\\\"", "\"")
}
}