mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 01:05:32 +01:00
Using Rc for everything
- avoid eccessive cloning Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
10
src/core.rs
10
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
src/eval.rs
29
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),
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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(¶ms));
|
||||
match ast {
|
||||
match ast.as_ref() {
|
||||
List(list) => {
|
||||
for el in list {
|
||||
println!("; {}", prt(&el))
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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("\\\"", "\"")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user