mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 09:15:32 +01:00
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:
49
src/core.rs
49
src/core.rs
@ -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),
|
||||
}
|
||||
|
||||
@ -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))
|
||||
|
||||
73
src/eval.rs
73
src/eval.rs
@ -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
|
||||
|
||||
@ -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(())
|
||||
}
|
||||
|
||||
@ -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(¶ms));
|
||||
println!("{}\t[function]: {}", sym, prt(¶ms));
|
||||
match ast.as_ref() {
|
||||
List(list) => {
|
||||
for el in list {
|
||||
println!("; {}", prt(&el))
|
||||
println!("; {}", prt(el))
|
||||
}
|
||||
}
|
||||
_ => panic!("Function body is not a list"),
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user