Adding helper functions

- improved 'help'
- added 'find'
- using clojure syntax for function arguments

Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
teo3300
2023-12-29 11:01:12 +09:00
parent 672c73cb4f
commit e6924d42b4
6 changed files with 47 additions and 24 deletions

View File

@ -10,7 +10,7 @@ pub struct EnvType {
}
impl EnvType {
pub fn keys(&self) -> String {
pub fn keys(&self) -> Vec<String> {
let mut keys = self
.data
.borrow()
@ -18,7 +18,7 @@ impl EnvType {
.map(|(k, _)| k.clone())
.collect::<Vec<String>>();
keys.sort_unstable();
keys.join(" ")
keys
}
}
@ -51,7 +51,7 @@ pub fn env_get(env: &Env, sym: &str) -> MalRet {
pub fn env_binds(outer: Env, binds: &MalType, exprs: &[MalType]) -> Result<Env, MalErr> {
let env = env_new(Some(outer));
let binds = binds.if_list()?;
let binds = binds.if_vec()?;
if binds.len() != exprs.len() {
return Err(MalErr::unrecoverable(
format!("Expected {} args, got {}", binds.len(), exprs.len()).as_str(),

View File

@ -53,7 +53,7 @@ fn let_star_form(list: &[MalType], env: Env) -> Result<(MalType, Env), MalErr> {
let inner_env = env_new(Some(env.clone()));
// change the inner environment
let (car, cdr) = car_cdr(list)?;
let list = car.if_list()?;
let list = car.if_vec()?;
if list.len() % 2 != 0 {
return Err(MalErr::unrecoverable(
"let* form, number of arguments must be even",
@ -92,7 +92,7 @@ fn if_form(list: &[MalType], env: Env) -> MalRet {
fn fn_star_form(list: &[MalType], env: Env) -> MalRet {
let (binds, exprs) = car_cdr(list)?;
binds.if_list()?;
binds.if_vec()?;
Ok(M::MalFun {
// eval: eval_ast,
params: Rc::new(binds.clone()),
@ -104,18 +104,24 @@ fn fn_star_form(list: &[MalType], env: Env) -> MalRet {
use crate::printer::print_malfun;
pub fn help_form(list: &[MalType], env: Env) -> MalRet {
if list.is_empty() {
eprintln!("\t[defined symbols]:\n{}", env.keys())
} else {
let (sym, _) = car_cdr(list)?;
let sym_str = sym.if_symbol()?;
match eval(sym, env.clone())? {
M::Fun(_, desc) => println!("{}\t[builtin]: {}", sym_str, desc),
M::MalFun { params, ast, .. } => print_malfun(sym_str, params, ast),
_ => eprintln!("{}\t[symbol]: {}", sym_str, prt(&env_get(&env, sym_str)?)),
}
let (sym, _) = car_cdr(list)?;
let sym_str = sym.if_symbol()?;
match eval(sym, env.clone())? {
M::Fun(_, desc) => println!("{}\t[builtin]: {}\n", sym_str, desc),
M::MalFun { params, ast, .. } => print_malfun(sym_str, params, ast),
_ => eprintln!("{}\t[symbol]: {}\n", sym_str, prt(&env_get(&env, sym_str)?)),
}
Ok(M::Bool(true))
Ok(M::Nil)
}
pub fn find_form(list: &[MalType], env: Env) -> MalRet {
let mut filtered = env.keys();
for mat in list {
let mat = mat.if_symbol()?;
filtered.retain(|x| x.contains(mat));
}
eprintln!("\t[matches]:\n{}\n", filtered.join(" "));
Ok(M::Nil)
}
pub fn outermost(env: &Env) -> Env {
@ -142,6 +148,7 @@ pub fn eval(ast: &MalType, env: Env) -> MalRet {
M::Sym(sym) if sym == "if" => ast = if_form(cdr, env.clone())?,
M::Sym(sym) if sym == "fn*" => return fn_star_form(cdr, env.clone()),
M::Sym(sym) if sym == "help" => return help_form(cdr, env.clone()),
M::Sym(sym) if sym == "find" => return find_form(cdr, env.clone()),
// Special form, sad
// Bruh, is basically double eval
M::Sym(sym) if sym == "eval" => {
@ -324,7 +331,7 @@ mod tests {
matches!(let_star_form(load!("(let* 1)"), env.clone()), Err(e) if !e.is_recoverable())
);
assert!(
matches!(let_star_form(load!("(let* (a))"), env.clone()), Err(e) if !e.is_recoverable())
matches!(let_star_form(load!("(let* [a])"), env.clone()), Err(e) if !e.is_recoverable())
); /*
assert!(matches!(
let_star_form(load!("(let* ())"), env.clone()),
@ -400,9 +407,9 @@ mod tests {
fn fn_star() {
let env = _env_empty();
assert!(matches!(
fn_star_form(load!("(fn* (a b) 1 2)"), env.clone()),
fn_star_form(load!("(fn* [a b] 1 2)"), env.clone()),
Ok(MalType::MalFun {params, ast, .. })
if matches!((*params).clone(), MalType::List(v)
if matches!((*params).clone(), MalType::Vector(v)
if matches!(&v[0], MalType::Sym(v) if v == "a")
&& matches!(&v[1], MalType::Sym(v) if v == "b")
&& matches!((*ast).clone(), MalType::List(v)

View File

@ -11,9 +11,14 @@ use std::path::Path;
use std::process::exit;
pub fn load_core(env: &Env) {
eval_str("(def! not (fn* (x) (if x nil true)))", env).unwrap();
eval_str("(def! not (fn* [x] (if x nil true)))", env).unwrap();
eval_str(
"(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))",
"(def! load-file (fn* [f] (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))",
env,
)
.unwrap();
eval_str(
"(def! conf-reload (fn* [] (load-file (str MAL_HOME \"/\" \"config.mal\"))))",
env,
)
.unwrap();
@ -29,6 +34,8 @@ pub fn load_conf(work_env: &Env) {
Ok(s) => s,
Err(_) => env::var("HOME").unwrap() + "/.config/mal",
};
// Add config path to mal
eval_str(format!("(def! MAL_HOME \"{home}\")").as_str(), work_env).unwrap();
let config = home + "/" + CONFIG;
if Path::new(&config).exists() {

View File

@ -66,5 +66,5 @@ pub fn print_malfun(sym: &str, params: Rc<MalType>, ast: Rc<MalType>) {
.if_list()
.unwrap_or(&[])
.iter()
.for_each(|el| println!("; {}", pr_str(el, true)));
.for_each(|el| println!("; {}\n", pr_str(el, true)));
}

View File

@ -42,6 +42,15 @@ impl MalType {
}
}
pub fn if_vec(&self) -> Result<&[MalType], MalErr> {
match self {
Self::Vector(list) => Ok(list),
_ => Err(MalErr::unrecoverable(
format!("{:?} is not a vector", prt(self)).as_str(),
)),
}
}
pub fn if_symbol(&self) -> Result<&str, MalErr> {
match self {
Self::Sym(sym) => Ok(sym),