mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 09:15:32 +01:00
Compare commits
9 Commits
dev
...
f14b2daaf3
| Author | SHA1 | Date | |
|---|---|---|---|
| f14b2daaf3 | |||
| a1692214f6 | |||
| 75057723ff | |||
| 4f528b832a | |||
| 92be9e4483 | |||
| ce9e50f2ed | |||
| da617713c3 | |||
| de43cdfefb | |||
| 57c2590a84 |
6
Makefile
6
Makefile
@ -23,6 +23,10 @@ conf: test
|
||||
@test -s ${CONFIG_FILE} || (\
|
||||
echo ";; Write here your mal config" >> ${MAL_HOME}/config.mal\
|
||||
&& echo "; (def! BANNER \"\") ; Hide banner at startup" >> ${MAL_HOME}/config.mal\
|
||||
&& echo '' >> ${MAL_HOME}/config.mal\
|
||||
&& echo '(def! BANNER (str BANNER' >> ${MAL_HOME}/config.mal\
|
||||
&& echo '";\n; **** To remove this banner and config mal, edit: ****\n"' >> ${MAL_HOME}/config.mal\
|
||||
&& echo '"; /Users/rogora/.config/mal/config.mal\n"))' >> ${MAL_HOME}/config.mal\
|
||||
)
|
||||
|
||||
install: build-release test conf
|
||||
@ -41,4 +45,4 @@ install: build-release test conf
|
||||
@echo "To start mal run:"
|
||||
@printf "\tmal [path/to/script [args ...]]\n\n"
|
||||
@echo "To config mal edit:"
|
||||
@printf "\t${MAL_HOME}/config.mal"
|
||||
@printf "\t${MAL_HOME}/config.mal\n"
|
||||
|
||||
@ -51,6 +51,12 @@
|
||||
(def! empty? (fn* [l]
|
||||
(= (count l) 0)))
|
||||
|
||||
(def! ceil (fn* [n]
|
||||
(if (= (num n) 1)
|
||||
n
|
||||
(+ (floor n) 1))))
|
||||
|
||||
; Other functions
|
||||
(def! assert-msg (fn* [e m]
|
||||
(if (not e)
|
||||
(raise m))))
|
||||
|
||||
@ -23,13 +23,13 @@
|
||||
(def! _split-ch (fn* [s c]
|
||||
"Split the string at every occurrence of character sc"
|
||||
(def! s (boom s))
|
||||
(def! split-r (fn* [l t r]
|
||||
(def! split-r (fn* [l tt r]
|
||||
(if (empty? l)
|
||||
(cons t r)
|
||||
(cons tt r)
|
||||
(do (def! cc (car l))
|
||||
(if (= cc c)
|
||||
(split-r (cdr l) "" (cons t r))
|
||||
(split-r (cdr l) (str t cc) r))))))
|
||||
(split-r (cdr l) "" (cons tt r))
|
||||
(split-r (cdr l) (str tt cc) r))))))
|
||||
(reverse (split-r s "" '()))))
|
||||
|
||||
(def! split (fn* [string delimiter]
|
||||
@ -53,10 +53,10 @@
|
||||
(def! join (fn* [l s]
|
||||
"Join element of list l to a stiring, using s as separator"
|
||||
(def! s (or s ""))
|
||||
(def! join-r (fn* [l t]
|
||||
(def! join-r (fn* [l tt]
|
||||
(if (empty? l)
|
||||
t
|
||||
(join-r (cdr l) (str t s (car l))))))
|
||||
tt
|
||||
(join-r (cdr l) (str tt s (car l))))))
|
||||
(join-r (cdr l) (car l))))
|
||||
|
||||
(def! chsub (fn* [s c1 c2]
|
||||
|
||||
@ -70,7 +70,7 @@ pub fn ns_init() -> Env {
|
||||
"<=" => 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("").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"),
|
||||
"str" => Fun(|a| Ok(Str(a.iter().map(|i| pr_str(i, false)).collect::<Vec<String>>().join("").into())), "Concatenate all arguments as a string"),
|
||||
"prn" => Fun(|a| {a.iter().for_each(|a| print!("{}", pr_str(a, false))); let _ = io::stdout().flush(); 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(a.into())), "Return the arguments as a list"),
|
||||
|
||||
20
src/env.rs
20
src/env.rs
@ -57,7 +57,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_vec()?;
|
||||
let binds = binds.if_list()?;
|
||||
let binl = binds.len();
|
||||
let expl = exprs.len();
|
||||
if binl < expl {
|
||||
@ -150,13 +150,25 @@ pub fn call_func(func: &MalType, args: &[MalType]) -> CallRet {
|
||||
}
|
||||
|
||||
pub fn any_zero(list: &[MalType]) -> Result<&[MalType], MalErr> {
|
||||
if list
|
||||
match list.len() {
|
||||
1 => {
|
||||
if list[0].if_number()?.get_num() == 0 {
|
||||
Err(MalErr::unrecoverable("Attempting division by 0"))
|
||||
} else {
|
||||
Ok(list)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if list[1..list.len()]
|
||||
.iter()
|
||||
.any(|x| matches!(x, M::Num(v) if v.exact_zero()))
|
||||
{
|
||||
return Err(MalErr::unrecoverable("Attempting division by 0"));
|
||||
}
|
||||
Err(MalErr::unrecoverable("Attempting division by 0"))
|
||||
} else {
|
||||
Ok(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arithmetic_op(set: isize, f: fn(Frac, Frac) -> Frac, args: &[MalType]) -> MalRet {
|
||||
|
||||
@ -75,7 +75,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_vec()?;
|
||||
let list = car.if_list()?;
|
||||
if list.len() % 2 != 0 {
|
||||
return Err(MalErr::unrecoverable(
|
||||
"let* form, number of arguments must be even",
|
||||
@ -114,7 +114,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_vec()?;
|
||||
binds.if_list()?;
|
||||
Ok(M::MalFun {
|
||||
// eval: eval_ast,
|
||||
params: Rc::new(binds.clone()),
|
||||
|
||||
@ -71,7 +71,18 @@ use rustyline::error::ReadlineError;
|
||||
use rustyline::DefaultEditor;
|
||||
|
||||
pub fn pre_load(argv: &Vec<String>, env: &Env) {
|
||||
eval_str(format!("(def! *ARGV* '({}))", argv[1..].iter().map(|x| x.to_string() + " ").collect::<String>()).as_str(), &env).unwrap();
|
||||
eval_str(
|
||||
format!(
|
||||
"(def! *ARGV* '({}))",
|
||||
argv[1..]
|
||||
.iter()
|
||||
.map(|x| "\"".to_string() + x + "\" ")
|
||||
.collect::<String>()
|
||||
)
|
||||
.as_str(),
|
||||
&env,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn interactive(env: Env) {
|
||||
|
||||
18
src/types.rs
18
src/types.rs
@ -105,6 +105,10 @@ impl Frac {
|
||||
return a;
|
||||
}
|
||||
|
||||
// Ideally builtin functions ( + - * / ) can operate with the values in any
|
||||
// form without problems, occasional simplification is done for numeric
|
||||
// stability, ensure to simplify after insert and before using any funcition
|
||||
// (floor, ceil, etc.)
|
||||
pub fn simplify(&self) -> Frac {
|
||||
// Euclid's algorithm to reduce fraction
|
||||
// TODO: (decide if implementing this automathically once fraction
|
||||
@ -130,13 +134,19 @@ impl Frac {
|
||||
// return Ok(Num(Frac::num(tk.parse::<isize>().unwrap())));
|
||||
|
||||
pub fn from_str(tk: &str) -> Self {
|
||||
match tk.find("/") {
|
||||
let frac = match tk.find("/") {
|
||||
Some(v) => Self {
|
||||
num: tk[0..v].parse::<isize>().unwrap(),
|
||||
den: tk[v + 1..tk.len()].parse::<usize>().unwrap(),
|
||||
},
|
||||
None => Frac::num(tk.parse::<isize>().unwrap()),
|
||||
}
|
||||
};
|
||||
// Ensure that value is simplified before being inserted
|
||||
// otherwise
|
||||
// (/ 4 4) results in 1/1
|
||||
// 4/4 results in 4/4
|
||||
// this breaks some functions (like ceil) and doesn't make much sense
|
||||
frac.simplify()
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,9 +192,9 @@ impl MalType {
|
||||
|
||||
pub fn if_list(&self) -> Result<&[MalType], MalErr> {
|
||||
match self {
|
||||
Self::List(list) => Ok(list),
|
||||
Self::List(list) | Self::Vector(list) => Ok(list),
|
||||
_ => Err(MalErr::unrecoverable(
|
||||
format!("{:?} is not a list", prt(self)).as_str(),
|
||||
format!("{:?} is not an iterable", prt(self)).as_str(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user