Moving String to MalStr (Rc<str>)

Because someone, somewhere, said that's better

Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
teo3300
2024-01-26 01:20:34 +09:00
parent 7448091fdb
commit d8f431b739
8 changed files with 109 additions and 85 deletions

View File

@ -35,7 +35,7 @@ use crate::parse_tools::read_file;
use crate::printer::pr_str;
use crate::reader::{read_str, Reader};
use crate::types::MalType::{Fun, Int, List, Nil, Str};
use crate::types::{mal_assert, mal_equals, MalArgs, MalErr};
use crate::types::{mal_assert, mal_equals, MalErr};
pub fn ns_init() -> Env {
env_init!(None,
@ -51,11 +51,11 @@ pub fn ns_init() -> Env {
">" => Fun(|a| comparison_op( |a, b| a > b, a), "Returns true if the first argument is strictly greater than the second one, nil otherwise"),
"<=" => Fun(|a| comparison_op( |a, b| a <= b, a), "Returns true if the first argument is smaller than or equal to the second one, nil otherwise"),
">=" => Fun(|a| comparison_op( |a, b| a >= b, a), "Returns true if the first argument is greater than or equal to the second one, nil otherwise"),
"pr-str" => Fun(|a| Ok(Str(a.iter().map(|i| pr_str(i, true)).collect::<Vec<String>>().join(" "))), "Print readably all arguments"),
"str" => Fun(|a| Ok(Str(a.iter().map(|i| pr_str(i, false)).collect::<Vec<String>>().join(""))), "Print non readably all arguments"),
"pr-str" => Fun(|a| Ok(Str(a.iter().map(|i| pr_str(i, true)).collect::<Vec<String>>().join(" ").into())), "Print readably all arguments"),
"str" => Fun(|a| Ok(Str(a.iter().map(|i| pr_str(i, false)).collect::<Vec<String>>().join("").into())), "Print non readably all arguments"),
"prn" => Fun(|a| {a.iter().for_each(|a| print!("{} ", pr_str(a, false))); Ok(Nil) }, "Print readably all the arguments"),
"println" => Fun(|a| {a.iter().for_each(|a| print!("{} ", pr_str(a, false))); println!(); Ok(Nil) }, "Print readably all the arguments"),
"list" => Fun(|a| Ok(List(MalArgs::new(a.to_vec()))), "Return the arguments as a list"),
"list" => Fun(|a| Ok(List(a.into())), "Return the arguments as a list"),
"type" => Fun(|a| Ok(car(a)?.label_type()), "Returns a label indicating the type of it's argument"),
"count" => Fun(|a| Ok(Int(car(a)?.if_list()?.len() as isize)), "Return the number of elements in the first argument"),
"=" => Fun(mal_equals, "Return true if the first two parameters are the same type and content, in case of lists propagate to all elements (NOT IMPLEMENTED for 'Map', 'Fun' and 'MalFun')"),
@ -63,7 +63,7 @@ pub fn ns_init() -> Env {
"read-string" => Fun(|a| read_str(Reader::new().push(car(a)?.if_string()?)).map_err(MalErr::severe), "Tokenize and read the first argument"),
"slurp" => Fun(|a| Ok(Str(read_file(car(a)?.if_string()?)?)), "Read a file and return the content as a string"),
"env" => Fun(|a| match env::var(car(a)?.if_string()?) {
Ok(s) => Ok(Str(s)),
Ok(s) => Ok(Str(s.into())),
_ => Ok(Nil),
}, "Retrieve the specified environment variable, returns NIL if that variable does not exist")
)

View File

@ -34,7 +34,7 @@ pub fn env_new(outer: Option<Env>) -> Env {
}
pub fn env_set(env: &Env, sym: &str, val: &MalType) {
env.data.borrow_mut().insert(sym.to_string(), val.clone());
env.data.borrow_mut().insert(sym.into(), val.clone());
}
pub fn env_get(env: &Env, sym: &str) -> MalRet {

View File

@ -4,6 +4,7 @@ use crate::env::{first_last, Env};
use crate::printer::prt;
use crate::types::MalType as M;
use crate::types::{MalArgs, MalErr, MalMap, MalRet, MalType};
use std::borrow::Borrow;
use std::rc::Rc;
/// Resolve the first element of the list as the function name and call it
@ -98,7 +99,7 @@ fn fn_star_form(list: &[MalType], env: Env) -> MalRet {
Ok(M::MalFun {
// eval: eval_ast,
params: Rc::new(binds.clone()),
ast: Rc::new(M::List(MalArgs::new(exprs.to_vec()))),
ast: Rc::new(M::List(exprs.into())),
env,
})
}
@ -134,6 +135,21 @@ pub fn outermost(env: &Env) -> Env {
env.clone()
}
macro_rules! apply {
($ast:expr, $env:expr) => {{
let apply_list = &eval_ast(&$ast, $env.clone())?;
let eval_ret = eval_func(apply_list)?;
match eval_ret {
CallFunc::Builtin(ret) => return Ok(ret),
CallFunc::MalFun(fun_ast, fun_env) => {
$ast = fun_ast;
$env = fun_env;
}
}
}};
}
/// Intermediate function to discern special forms from defined symbols
pub fn eval(ast: &MalType, env: Env) -> MalRet {
let mut ast = ast.clone();
@ -143,46 +159,39 @@ pub fn eval(ast: &MalType, env: Env) -> MalRet {
M::List(list) if list.is_empty() => return Ok(ast.clone()),
M::List(list) => {
let (symbol, args) = car_cdr(list)?;
match symbol {
M::Sym(sym) if sym == "def!" => return def_bang_form(args, env.clone()), // Set for env
M::Sym(sym) if sym == "let*" => (ast, env) = let_star_form(args, env.clone())?,
M::Sym(sym) if sym == "do" => ast = do_form(args, env.clone())?,
M::Sym(sym) if sym == "if" => ast = if_form(args, env.clone())?,
M::Sym(sym) if sym == "fn*" || sym == "λ" /* :) */ => {
return fn_star_form(args, env.clone())
}
M::Sym(sym) if sym == "help" => return help_form(args, env.clone()),
M::Sym(sym) if sym == "find" => return find_form(args, env.clone()),
// Oh God, what have I done
M::Sym(sym) if sym == "quote" => return Ok(car(args)?.clone()),
M::Sym(sym) if sym == "ok?" => {
return match eval(car(args)?, env.clone()) {
Err(_) => Ok(M::Nil),
_ => Ok(M::Bool(true)),
if let M::Sym(sym) = symbol {
match sym.borrow() {
// I don't like to borrow tho
"def!" => return def_bang_form(args, env.clone()), // Set for env
"let*" => {(ast, env) = let_star_form(args, env.clone())?; continue;},
"do" => {ast = do_form(args, env.clone())?; continue;},
"if" => {ast = if_form(args, env.clone())?; continue;},
"fn*" | "λ" /* :) */ => {
return fn_star_form(args, env.clone())
}
}
// Special form, sad
// Bruh, is basically double eval
M::Sym(sym) if sym == "eval" => {
ast = eval(env::car(args)?, env.clone())?;
// Climb to the outermost environment (The repl env)
env = outermost(&env);
}
// Filter out special forms
// "apply"/invoke
_ => {
let apply_list = &eval_ast(&ast, env.clone())?;
let eval_ret = eval_func(apply_list)?;
match eval_ret {
CallFunc::Builtin(ret) => return Ok(ret),
CallFunc::MalFun(fun_ast, fun_env) => {
ast = fun_ast;
env = fun_env;
"help" => return help_form(args, env.clone()),
"find" => return find_form(args, env.clone()),
// Oh God, what have I done
"quote" => return Ok(car(args)?.clone()),
"ok?" => {
return match eval(car(args)?, env.clone()) {
Err(_) => Ok(M::Nil),
_ => Ok(M::Bool(true)),
}
}
// Special form, sad
// Bruh, is basically double eval
"eval" => {
ast = eval(env::car(args)?, env.clone())?;
// Climb to the outermost environment (The repl env)
env = outermost(&env);
continue;
}
_ => {}
}
};
}
// "apply"/invoke
apply!(ast, env)
}
_ => return eval_ast(&ast, env),
}
@ -195,14 +204,14 @@ fn eval_collection(list: &MalArgs, env: Env) -> Result<MalArgs, MalErr> {
for el in list.as_ref() {
ret.push(eval(el, env.clone())?);
}
Ok(MalArgs::new(ret))
Ok(ret.into())
}
/// Evaluate the values of a map
fn eval_map(map: &MalMap, env: Env) -> MalRet {
let mut ret = MalMap::new();
for (k, v) in map {
ret.insert(k.to_string(), eval(v, env.clone())?);
ret.insert(k.clone(), eval(v, env.clone())?);
}
Ok(M::Map(ret))
}

View File

@ -2,7 +2,7 @@ use crate::env::Env;
use crate::eval::eval;
use crate::reader::{read_str, Reader};
use crate::step6_file::rep;
use crate::types::{MalErr, MalRet};
use crate::types::{MalErr, MalRet, MalStr};
use std::fs::File;
use std::io::Read;
use std::path::Path;
@ -39,7 +39,7 @@ pub fn load_home_file(filename: &str, env: &Env, warn: bool) {
}
}
pub fn read_file(filename: &str) -> Result<String, MalErr> {
pub fn read_file(filename: &str) -> Result<MalStr, MalErr> {
let mut file = File::open(filename).map_err(|_| {
MalErr::unrecoverable(format!("Failed to open file '{}'", filename).as_str())
})?;
@ -49,7 +49,7 @@ pub fn read_file(filename: &str) -> Result<String, MalErr> {
MalErr::unrecoverable(format!("Failed to read content of '{}'", filename).as_str())
})?;
Ok(content)
Ok(content.into())
}
pub fn load_file(filename: &str, env: &Env) -> MalRet {

View File

@ -5,9 +5,9 @@ use crate::types::{escape_str, MalType};
fn key_str(val: &str) -> MalType {
if val.starts_with('ʞ') {
M::Key(val.to_string())
M::Key(val.into())
} else {
M::Str(val.to_string())
M::Str(val.into())
}
}

View File

@ -79,9 +79,9 @@ impl Reader {
self.next()?;
match terminator {
")" => Ok(List(MalArgs::new(vector))),
"]" => Ok(Vector(MalArgs::new(vector))),
"}" => make_map(MalArgs::new(vector)),
")" => Ok(List(vector.into())),
"]" => Ok(Vector(vector.into())),
"}" => make_map(vector.into()),
t => Err(MalErr::unrecoverable(
format!("Unknown collection terminator: {}", t).as_str(),
)),
@ -100,15 +100,15 @@ impl Reader {
return Ok(Int(tk.parse::<isize>().unwrap()));
} else if tk.starts_with('\"') {
if tk.len() > 1 && tk.ends_with('\"') {
return Ok(Str(unescape_str(tk)));
return Ok(Str(unescape_str(tk).into()));
}
return Err(MalErr::unrecoverable(
"End of line reached without closing string",
));
} else if tk.starts_with(':') {
return Ok(Key(format!("ʞ{}", tk)));
return Ok(Key(format!("ʞ{}", tk).into()));
}
Ok(Sym(tk.to_string()))
Ok(Sym(tk.into()))
}
}
}
@ -128,8 +128,8 @@ impl Reader {
// Ugly quote transformation for quote expansion
"'" => {
self.next()?;
Ok(List(Rc::new(vec![
MalType::Sym("quote".to_string()),
Ok(List(Rc::new([
MalType::Sym("quote".into()),
self.read_form()?,
])))
}
@ -168,6 +168,8 @@ fn tokenize(input: &str) -> Tokens {
#[cfg(test)]
mod tests {
use std::borrow::Borrow;
use crate::{
reader::read_str,
types::{MalMap, MalType as M},
@ -262,9 +264,15 @@ mod tests {
assert!(matches!(r.read_atom(), Ok(x) if matches!(x, M::Nil)));
assert!(matches!(r.read_atom(), Ok(x) if matches!(x, M::Int(1))));
assert!(matches!(r.read_atom(), Ok(x) if matches!(x, M::Bool(true))));
assert!(matches!(r.read_atom(), Ok(x) if matches!(x.clone(), M::Sym(v) if v == "a")));
assert!(matches!(r.read_atom(), Ok(x) if matches!(x.clone(), M::Str(v) if v == "s")));
assert!(matches!(r.read_atom(), Ok(x) if matches!(x.clone(), M::Key(v) if v == "ʞ:a")));
assert!(
matches!(r.read_atom(), Ok(x) if matches!(x.clone(), M::Sym(v) if matches!(v.borrow(), "a")))
);
assert!(
matches!(r.read_atom(), Ok(x) if matches!(x.clone(), M::Str(v) if matches!(v.borrow(), "s")))
);
assert!(
matches!(r.read_atom(), Ok(x) if matches!(x.clone(), M::Key(v) if matches!(v.borrow(), "ʞ:a")))
);
assert!(matches!(r.read_atom(), Err(e) if !e.is_recoverable()));
assert!(matches!(r.read_atom(), Err(e) if !e.is_recoverable()));
assert!(matches!(r.read_atom(), Err(e) if !e.is_recoverable()));
@ -314,7 +322,11 @@ mod tests {
assert!(matches!(t.get("n"), Some(x) if matches!(&x, M::Nil)));
assert!(matches!(t.get("t"), Some(x) if matches!(&x, M::Bool(v) if *v)));
assert!(matches!(t.get("i"), Some(x) if matches!(&x, M::Int(v) if *v == 1)));
assert!(matches!(t.get("s"), Some(x) if matches!(&x, M::Str(v) if v == "str")));
assert!(matches!(t.get("ʞ:s"), Some(x) if matches!(&x, M::Key(v) if v == "ʞ:sym")));
assert!(
matches!(t.get("s"), Some(x) if matches!(&x, M::Str(v) if matches!(v.borrow(), "str")))
);
assert!(
matches!(t.get("ʞ:s"), Some(x) if matches!(&x, M::Key(v) if matches!(v.borrow(), "ʞ:sym")))
);
}
}

View File

@ -1,6 +1,11 @@
use crate::env::{car_cdr, Env};
use std::{collections::HashMap, rc::Rc};
pub type MalStr = Rc<str>;
pub type MalArgs = Rc<[MalType]>;
pub type MalMap = HashMap<MalStr, MalType>;
pub type MalRet = Result<MalType, MalErr>;
// All Mal types should inherit from this
#[derive(Clone)]
pub enum MalType {
@ -15,9 +20,9 @@ pub enum 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),
Sym(MalStr),
Key(MalStr),
Str(MalStr),
Int(isize),
Bool(bool),
Nil,
@ -70,20 +75,19 @@ impl MalType {
}
pub fn label_type(&self) -> MalType {
let mut labl = "ʞ:".to_string();
labl.push_str(match self {
M::Nil => "nil",
M::Bool(_) => "bool",
M::Int(_) => "int",
M::Fun(_, _) | M::MalFun { .. } => "lambda",
M::Key(_) => "key",
M::Str(_) => "string",
M::Sym(_) => "symbol",
M::List(_) => "list",
M::Vector(_) => "vector",
M::Map(_) => "map",
});
M::Key(labl)
Key(match self {
M::Nil => "ʞ:nil",
M::Bool(_) => "ʞ:bool",
M::Int(_) => "ʞ:int",
M::Fun(_, _) | M::MalFun { .. } => "ʞ:lambda",
M::Key(_) => "ʞ:key",
M::Str(_) => "ʞ:string",
M::Sym(_) => "ʞ:symbol",
M::List(_) => "ʞ:ist",
M::Vector(_) => "ʞ:vector",
M::Map(_) => "ʞ:map",
}
.into())
}
}
@ -163,10 +167,6 @@ impl MalErr {
}
}
pub type MalArgs = Rc<Vec<MalType>>;
pub type MalMap = HashMap<String, MalType>;
pub type MalRet = Result<MalType, MalErr>;
use crate::printer::prt;
use MalType::{Key, Map, Str};
@ -181,7 +181,7 @@ pub fn make_map(list: MalArgs) -> MalRet {
match &list[i] {
Key(k) | Str(k) => {
let v = &list[i + 1];
map.insert(k.to_string(), v.clone());
map.insert(k.clone(), v.clone());
}
_ => {
return Err(MalErr::unrecoverable(