mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 09:15:32 +01:00
Type retrival, moving back some math to core.rs
Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
@ -35,24 +35,26 @@ use crate::parse_tools::read_file;
|
||||
use crate::printer::pr_str;
|
||||
use crate::reader::{read_str, Reader};
|
||||
use crate::types::MalType::{Bool, Fun, Int, List, Nil, Str};
|
||||
use crate::types::{mal_assert, mal_comp, MalArgs, MalErr};
|
||||
use crate::types::{mal_assert, mal_equals, MalArgs, MalErr};
|
||||
|
||||
pub fn ns_init() -> Env {
|
||||
env_init!(None,
|
||||
// That's it, you are all going to be simpler functions
|
||||
"exit" => Fun(mal_exit, "Quits the program with specified status"),
|
||||
"raise" => Fun(|a| Err(MalErr::unrecoverable(car(a)?.if_string()?)), "Raise an unrecoverable error with the specified message"),
|
||||
// Ok, keep * and / here because computing basic math operators recursively is fun but not too convenient
|
||||
"-" => Fun(|a| num_op(|a, b| Int(a - b), a), "Returns the difference of the arguments"),
|
||||
"*" => Fun(|a| num_op(|a, b| Int(a * b), a), "Returns the product of the arguments"),
|
||||
"/-unsafe" => Fun(|a| num_op(|a, b| Int(a / b), a), "Returns the quotient of the arguments (not checking for division by 0)"),
|
||||
"<" => Fun(|a| num_op(|a, b| Bool(a < b), a), "Returns true if the first argument is strictly smallerthan 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"),
|
||||
"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(Bool(a.iter().all(|el| matches!(el, List(_))))), "Return true if the first argument is a list, false otherwise"),
|
||||
"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_comp, "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')"),
|
||||
"=" => 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')"),
|
||||
"assert" => Fun(mal_assert, "Return an error if assertion fails"),
|
||||
"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"),
|
||||
|
||||
@ -146,7 +146,9 @@ pub fn eval(ast: &MalType, env: Env) -> MalRet {
|
||||
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*" => return fn_star_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
|
||||
|
||||
@ -63,7 +63,6 @@ pub fn load_file(filename: &str, env: &Env) -> MalRet {
|
||||
)
|
||||
} // WTF this is becoming ever less like rust and more like lisp, did I really completely skip the file reading?
|
||||
|
||||
extern crate rustyline;
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::DefaultEditor;
|
||||
|
||||
|
||||
25
src/types.rs
25
src/types.rs
@ -68,29 +68,46 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
use crate::types::MalType as M;
|
||||
|
||||
// That's a quite chonky function
|
||||
fn mal_eq(args: (&MalType, &MalType)) -> bool {
|
||||
fn mal_compare(args: (&MalType, &MalType)) -> bool {
|
||||
match (args.0, args.1) {
|
||||
(M::Nil, M::Nil) => true,
|
||||
(M::Bool(a), M::Bool(b)) => a == b,
|
||||
(M::Int(a), M::Int(b)) => a == b,
|
||||
(M::Key(a), M::Key(b)) | (M::Str(a), M::Str(b)) | (M::Sym(a), M::Sym(b)) => a == b,
|
||||
(M::List(a), M::List(b)) | (M::Vector(a), M::Vector(b)) => {
|
||||
a.len() == b.len() && a.iter().zip(b.iter()).all(mal_eq)
|
||||
a.len() == b.len() && a.iter().zip(b.iter()).all(mal_compare)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mal_comp(args: &[MalType]) -> MalRet {
|
||||
pub fn mal_equals(args: &[MalType]) -> MalRet {
|
||||
if args.len() != 2 {
|
||||
return Err(MalErr::unrecoverable("Expected 2 arguments"));
|
||||
}
|
||||
Ok(M::Bool(mal_eq(bin_unpack(args)?)))
|
||||
Ok(M::Bool(mal_compare(bin_unpack(args)?)))
|
||||
}
|
||||
|
||||
pub fn mal_assert(args: &[MalType]) -> MalRet {
|
||||
|
||||
Reference in New Issue
Block a user