Cleaned code

- removed dereferences
- help function only provide info for the first symbol

Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
teo3300
2023-11-15 17:42:23 +09:00
parent 695c42010c
commit 3c5b03a2a8
9 changed files with 79 additions and 82 deletions

View File

@ -19,6 +19,7 @@ pub fn call_func(func: &MalType, args: &[MalType]) -> MalRet {
params,
ast,
env,
..
} => {
let inner_env = env_binds(env.clone(), params, args)?;
// It's fine to clone the environment here
@ -38,7 +39,7 @@ fn if_number(val: &MalType) -> Result<isize, MalErr> {
match val {
Int(val) => Ok(*val),
_ => Err(MalErr::unrecoverable(
format!("{:?} is not a number", prt(&val)).as_str(),
format!("{:?} is not a number", prt(val)).as_str(),
)),
}
}
@ -62,41 +63,37 @@ pub fn arithmetic_op(set: isize, f: fn(isize, isize) -> isize, args: &[MalType])
use MalType::{Bool, Nil};
pub fn comparison_op(f: fn(isize, isize) -> bool, args: &[MalType]) -> MalRet {
match args.len() {
0 => Err(MalErr::unrecoverable(
"Comparison requires at least 1 argument",
)),
_ => {
let (left, rights) = car_cdr(args);
let mut left = if_number(&left)?;
for right in rights {
let right = if_number(right)?;
if !f(left, right) {
return Ok(Nil);
}
left = right;
}
Ok(Bool(true))
let (left, rights) = car_cdr(args)?;
let mut left = if_number(left)?;
for right in rights {
let right = if_number(right)?;
if !f(left, right) {
return Ok(Nil);
}
left = right;
}
Ok(Bool(true))
}
/// Extract the car and cdr from a list
pub fn car_cdr(list: &[MalType]) -> (&MalType, &[MalType]) {
(
&list[0],
if list.len() > 1 {
&list[1..]
} else {
&list[0..0]
},
)
pub fn car_cdr(list: &[MalType]) -> Result<(&MalType, &[MalType]), MalErr> {
match list.len() {
0 => Err(MalErr::unrecoverable("Expected at least one argument")),
_ => Ok((
&list[0],
if list.len() > 1 {
&list[1..]
} else {
&list[0..0]
},
)),
}
}
use std::process::exit;
pub fn core_exit(list: &[MalType]) -> MalRet {
match car_cdr(list).0 {
match car_cdr(list)?.0 {
Int(val) => exit(*val as i32),
_ => exit(-1),
}

View File

@ -49,8 +49,9 @@ pub fn env_new(outer: Option<Env>) -> Env {
})
}
pub fn env_set(env: &Env, sym: &str, val: &MalType) {
pub fn env_set(env: &Env, sym: &str, val: &MalType) -> MalType {
env.data.borrow_mut().insert(sym.to_string(), val.clone());
val.clone()
}
pub fn env_get(env: &Env, sym: &String) -> MalRet {
@ -76,7 +77,9 @@ pub fn env_binds(outer: Env, binds: &MalType, exprs: &[MalType]) -> Result<Env,
} // TODO: May be possible to leave this be and not set additional elements at all
for (bind, expr) in binds.iter().zip(exprs.iter()) {
match bind {
Sym(sym) => env_set(&env, sym, expr),
Sym(sym) => {
env_set(&env, sym, expr);
}
_ => {
return Err(MalErr::unrecoverable(
format!("Initializing environment: {:?} is not a symbol", prt(bind))

View File

@ -1,6 +1,6 @@
use std::rc::Rc;
use crate::core::{call_func, car_cdr};
use crate::core::{call_func, car_cdr, scream};
use crate::env::Env;
use crate::env::{env_get, env_new, env_set};
use crate::printer::prt;
@ -12,10 +12,10 @@ use crate::types::{MalArgs, MalErr, MalMap, MalRet, MalType};
fn eval_func(list: &MalType) -> MalRet {
match list {
List(list) => {
let (func, args) = car_cdr(list);
call_func(&func, args)
let (func, args) = car_cdr(list)?;
call_func(func, args)
}
_ => todo!("This should never happen, if you see this message I probably broke the code"),
_ => scream(),
}
}
@ -32,23 +32,19 @@ fn eval_func(list: &MalType) -> MalRet {
/// def! special form:
/// Evaluate the second expression and assign it to the first symbol
fn def_bang_form(list: &[MalType], env: Env) -> MalRet {
match list.len() {
2 => match &list[0] {
Sym(sym) => {
let cdr = eval(&list[1], env.clone())?;
env_set(&env, sym.as_str(), &cdr);
Ok(cdr)
}
_ => Err(MalErr::unrecoverable(
format!(
"def! Assigning {:?} to {:?}, which is not a symbol",
prt(&list[1]),
prt(&list[0])
)
.as_str(),
)),
},
_ => Err(MalErr::unrecoverable("def! form: needs 2 arguments")),
if list.len() != 2 {
return Err(MalErr::unrecoverable("def! form: needs 2 arguments"));
}
match &list[0] {
Sym(sym) => Ok(env_set(&env, sym.as_str(), &eval(&list[1], env.clone())?)),
_ => Err(MalErr::unrecoverable(
format!(
"def! Assigning {:?} to {:?}, which is not a symbol",
prt(&list[1]),
prt(&list[0])
)
.as_str(),
)),
}
}
@ -59,12 +55,12 @@ fn let_star_form(list: &[MalType], env: Env) -> MalRet {
// Create the inner environment
let inner_env = env_new(Some(env.clone()));
// change the inner environment
let (car, cdr) = car_cdr(list);
let (car, cdr) = car_cdr(list)?;
match car {
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.clone())?;
def_bang_form(&list[i..=i + 1], inner_env.clone())?;
}
if cdr.is_empty() {
// TODO: check if it exists a better way to do this
@ -98,8 +94,8 @@ fn if_form(list: &[MalType], env: Env) -> MalRet {
"if form: number of arguments is wrong",
));
}
let (cond, branches) = car_cdr(list);
match eval(&cond, env.clone())? {
let (cond, branches) = car_cdr(list)?;
match eval(cond, env.clone())? {
Nil | Bool(false) => match branches.len() {
1 => Ok(Nil),
_ => eval(&branches[1], env),
@ -109,37 +105,38 @@ fn if_form(list: &[MalType], env: Env) -> MalRet {
}
fn fn_star_form(list: &[MalType], env: Env) -> MalRet {
if list.is_empty() {
return Err(MalErr::unrecoverable("fn* form: specify lambda arguments"));
}
let (binds, exprs) = car_cdr(list);
let (binds, exprs) = car_cdr(list)?;
Ok(MalFun {
eval: eval_ast,
params: Rc::new(binds.clone()),
ast: Rc::new(List(exprs.to_vec())),
env: env,
env,
})
}
use crate::printer::print_malfun;
pub fn help_form(list: &[MalType], env: Env) -> MalRet {
for sym in list {
match sym {
Sym(sym_str) => match eval(sym, env.clone())? {
let (sym, _) = car_cdr(list)?;
match car_cdr(list)?.0 {
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),
_ => println!("{:?} is not defined as a function", sym_str),
},
_ => println!("{:?} is not a symbol", prt(sym)),
_ => println!("{}\t[symbol]: {}", sym_str, prt(&env_get(&env, sym_str)?)),
}
Ok(Bool(true))
}
_ => {
println!("{} is not a symbol", prt(sym));
Ok(Nil)
}
}
return Ok(Bool(true));
}
/// Intermediate function to discern special forms from defined symbols
fn apply(list: &MalArgs, env: Env) -> MalRet {
let (car, cdr) = car_cdr(list);
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

View File

@ -11,7 +11,7 @@ pub fn load_file(filename: &str, env: &Env) -> io::Result<()> {
let reader = BufReader::new(file);
let mut last: Result<Vec<String>, MalErr> = Ok(Vec::new());
let comment_line = Regex::new(r#"^[\s]*;.*"#).unwrap();
let comment_line = Regex::new(r"^[\s]*;.*").unwrap();
let parser = Reader::new();
for line in reader.lines() {
@ -19,7 +19,7 @@ pub fn load_file(filename: &str, env: &Env) -> io::Result<()> {
Ok(line) => {
// Read line to compose program inpu
if line == "" || comment_line.is_match(&line) {
if line.is_empty() || comment_line.is_match(&line) {
continue; // Don't even add it
} else {
parser.push(&line);
@ -39,13 +39,12 @@ pub fn load_file(filename: &str, env: &Env) -> io::Result<()> {
Err(err) => eprintln!("Error reading line: {}", err),
}
}
match last {
Err(error) => println!(
if let Err(error) = last {
println!(
"; ERROR parsing: '{}'\n; {}\n; the environment is in an unknown state",
filename,
error.message()
),
_ => {}
)
}
Ok(())
}

View File

@ -36,7 +36,7 @@ pub fn pr_str(ast: &MalType, print_readably: bool) -> String {
Map(el) => format!(
"{{{}}}",
el.iter()
.map(|sub| vec![sub.0.to_string(), pr_str(sub.1, print_readably)].join(" "))
.map(|sub| [sub.0.to_string(), pr_str(sub.1, print_readably)].join(" "))
.collect::<Vec<String>>()
.join(" ")
),
@ -46,15 +46,15 @@ pub fn pr_str(ast: &MalType, print_readably: bool) -> String {
}
pub fn prt(ast: &MalType) -> String {
return pr_str(ast, true);
pr_str(ast, true)
}
pub fn print_malfun(sym: &String, params: Rc<MalType>, ast: Rc<MalType>) {
print!("; {} {}:\n", sym, prt(&params));
println!("{}\t[function]: {}", sym, prt(&params));
match ast.as_ref() {
List(list) => {
for el in list {
println!("; {}", prt(&el))
println!("; {}", prt(el))
}
}
_ => panic!("Function body is not a list"),

View File

@ -134,7 +134,7 @@ pub fn read_str(reader: &Reader) -> MalRet {
// Add error handling for strings that are not terminated
fn tokenize(input: &str) -> Tokens {
let tokens =
Regex::new(r###"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]*)"###)
Regex::new(r#"[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"?|;.*|[^\s\[\]{}('"`,;)]*)"#)
.unwrap()
.captures_iter(input)
.map(|e| e[1].to_string())

View File

@ -31,7 +31,7 @@ fn PRINT(output: MalType) -> String {
pub fn rep(reader: &Reader, env: &Env) -> Result<Vec<String>, MalErr> {
let mut ret_str = Vec::new();
loop {
let ast = READ(&reader)?;
let ast = READ(reader)?;
let out = EVAL(ast, env.clone())?;
ret_str.push(PRINT(out));
if reader.ended() {

View File

@ -36,10 +36,7 @@ pub struct MalErr {
impl MalErr {
pub fn new(message: String, severity: Severity) -> Self {
Self {
message: message,
severity,
}
Self { message, severity }
}
pub fn message(&self) -> String {

View File

@ -1,6 +1,8 @@
(def! func ; ole
(fn* (a b c) ; this is all a bunch
"This function does nothing useful"
; of useless comments
(def! d (+ a b)) ; to check if the parser
(- c d))) ; is able to ignore them
@ -8,5 +10,7 @@
(test 1 2 14) (help +)
(help -)(func 1 2 3)
(help test)
(help func)
(help 1)
; I even implemented return statement
(exit (func 1 2 3))