Compare commits

...

9 Commits

Author SHA1 Message Date
f14b2daaf3 Fixing division
Previously was returning error on
- (/ 0 n) with error: attempting division by zero
2025-07-23 10:21:16 +09:00
a1692214f6 Fixed *ARGV*
Arguments are now srings (were symbols previously)
2025-07-23 09:55:21 +09:00
75057723ff Fixed fractional insertion 2025-07-23 09:35:11 +09:00
4f528b832a Fixed core and libs
- Added ceil function for fractionals
- Fixed symbols in string library
2025-07-23 09:26:52 +09:00
92be9e4483 Merge branch 'dev' 2025-07-22 11:49:34 +09:00
ce9e50f2ed Merge branch 'dev' 2025-06-13 18:34:30 +09:00
da617713c3 Fixe documentation 2025-05-10 13:05:02 +09:00
de43cdfefb Renaming libraries 2025-04-15 17:52:32 +09:00
57c2590a84 style(Makefile): whoops 2025-04-15 13:46:39 +09:00
11 changed files with 67 additions and 23 deletions

View File

@ -23,6 +23,10 @@ conf: test
@test -s ${CONFIG_FILE} || (\ @test -s ${CONFIG_FILE} || (\
echo ";; Write here your mal config" >> ${MAL_HOME}/config.mal\ echo ";; Write here your mal config" >> ${MAL_HOME}/config.mal\
&& echo "; (def! BANNER \"\") ; Hide banner at startup" >> ${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 install: build-release test conf
@ -41,4 +45,4 @@ install: build-release test conf
@echo "To start mal run:" @echo "To start mal run:"
@printf "\tmal [path/to/script [args ...]]\n\n" @printf "\tmal [path/to/script [args ...]]\n\n"
@echo "To config mal edit:" @echo "To config mal edit:"
@printf "\t${MAL_HOME}/config.mal" @printf "\t${MAL_HOME}/config.mal\n"

View File

@ -51,6 +51,12 @@
(def! empty? (fn* [l] (def! empty? (fn* [l]
(= (count l) 0))) (= (count l) 0)))
(def! ceil (fn* [n]
(if (= (num n) 1)
n
(+ (floor n) 1))))
; Other functions
(def! assert-msg (fn* [e m] (def! assert-msg (fn* [e m]
(if (not e) (if (not e)
(raise m)))) (raise m))))

View File

@ -23,13 +23,13 @@
(def! _split-ch (fn* [s c] (def! _split-ch (fn* [s c]
"Split the string at every occurrence of character sc" "Split the string at every occurrence of character sc"
(def! s (boom s)) (def! s (boom s))
(def! split-r (fn* [l t r] (def! split-r (fn* [l tt r]
(if (empty? l) (if (empty? l)
(cons t r) (cons tt r)
(do (def! cc (car l)) (do (def! cc (car l))
(if (= cc c) (if (= cc c)
(split-r (cdr l) "" (cons t r)) (split-r (cdr l) "" (cons tt r))
(split-r (cdr l) (str t cc) r)))))) (split-r (cdr l) (str tt cc) r))))))
(reverse (split-r s "" '())))) (reverse (split-r s "" '()))))
(def! split (fn* [string delimiter] (def! split (fn* [string delimiter]
@ -53,10 +53,10 @@
(def! join (fn* [l s] (def! join (fn* [l s]
"Join element of list l to a stiring, using s as separator" "Join element of list l to a stiring, using s as separator"
(def! s (or s "")) (def! s (or s ""))
(def! join-r (fn* [l t] (def! join-r (fn* [l tt]
(if (empty? l) (if (empty? l)
t tt
(join-r (cdr l) (str t s (car l)))))) (join-r (cdr l) (str tt s (car l))))))
(join-r (cdr l) (car l)))) (join-r (cdr l) (car l))))
(def! chsub (fn* [s c1 c2] (def! chsub (fn* [s c1 c2]

View File

@ -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 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"), ">=" => 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"), "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"), "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"), "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"), "list" => Fun(|a| Ok(List(a.into())), "Return the arguments as a list"),

View File

@ -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> { pub fn env_binds(outer: Env, binds: &MalType, exprs: &[MalType]) -> Result<Env, MalErr> {
let env = env_new(Some(outer)); let env = env_new(Some(outer));
let binds = binds.if_vec()?; let binds = binds.if_list()?;
let binl = binds.len(); let binl = binds.len();
let expl = exprs.len(); let expl = exprs.len();
if binl < expl { 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> { pub fn any_zero(list: &[MalType]) -> Result<&[MalType], MalErr> {
if list match list.len() {
.iter() 1 => {
.any(|x| matches!(x, M::Num(v) if v.exact_zero())) if list[0].if_number()?.get_num() == 0 {
{ Err(MalErr::unrecoverable("Attempting division by 0"))
return 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()))
{
Err(MalErr::unrecoverable("Attempting division by 0"))
} else {
Ok(list)
}
}
} }
Ok(list)
} }
pub fn arithmetic_op(set: isize, f: fn(Frac, Frac) -> Frac, args: &[MalType]) -> MalRet { pub fn arithmetic_op(set: isize, f: fn(Frac, Frac) -> Frac, args: &[MalType]) -> MalRet {

View File

@ -75,7 +75,7 @@ fn let_star_form(list: &[MalType], env: Env) -> Result<(MalType, Env), MalErr> {
let inner_env = env_new(Some(env.clone())); let inner_env = env_new(Some(env.clone()));
// change the inner environment // change the inner environment
let (car, cdr) = car_cdr(list)?; let (car, cdr) = car_cdr(list)?;
let list = car.if_vec()?; let list = car.if_list()?;
if list.len() % 2 != 0 { if list.len() % 2 != 0 {
return Err(MalErr::unrecoverable( return Err(MalErr::unrecoverable(
"let* form, number of arguments must be even", "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 { fn fn_star_form(list: &[MalType], env: Env) -> MalRet {
let (binds, exprs) = car_cdr(list)?; let (binds, exprs) = car_cdr(list)?;
binds.if_vec()?; binds.if_list()?;
Ok(M::MalFun { Ok(M::MalFun {
// eval: eval_ast, // eval: eval_ast,
params: Rc::new(binds.clone()), params: Rc::new(binds.clone()),

View File

@ -71,7 +71,18 @@ use rustyline::error::ReadlineError;
use rustyline::DefaultEditor; use rustyline::DefaultEditor;
pub fn pre_load(argv: &Vec<String>, env: &Env) { 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) { pub fn interactive(env: Env) {

View File

@ -105,6 +105,10 @@ impl Frac {
return a; 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 { pub fn simplify(&self) -> Frac {
// Euclid's algorithm to reduce fraction // Euclid's algorithm to reduce fraction
// TODO: (decide if implementing this automathically once fraction // TODO: (decide if implementing this automathically once fraction
@ -130,13 +134,19 @@ impl Frac {
// return Ok(Num(Frac::num(tk.parse::<isize>().unwrap()))); // return Ok(Num(Frac::num(tk.parse::<isize>().unwrap())));
pub fn from_str(tk: &str) -> Self { pub fn from_str(tk: &str) -> Self {
match tk.find("/") { let frac = match tk.find("/") {
Some(v) => Self { Some(v) => Self {
num: tk[0..v].parse::<isize>().unwrap(), num: tk[0..v].parse::<isize>().unwrap(),
den: tk[v + 1..tk.len()].parse::<usize>().unwrap(), den: tk[v + 1..tk.len()].parse::<usize>().unwrap(),
}, },
None => Frac::num(tk.parse::<isize>().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> { pub fn if_list(&self) -> Result<&[MalType], MalErr> {
match self { match self {
Self::List(list) => Ok(list), Self::List(list) | Self::Vector(list) => Ok(list),
_ => Err(MalErr::unrecoverable( _ => Err(MalErr::unrecoverable(
format!("{:?} is not a list", prt(self)).as_str(), format!("{:?} is not an iterable", prt(self)).as_str(),
)), )),
} }
} }

1
test.mal Normal file
View File

@ -0,0 +1 @@
(println *ARGV*)