Compare commits

...

4 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
7 changed files with 64 additions and 29 deletions

View File

@ -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))))

View File

@ -23,16 +23,18 @@
(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-h (fn* [string delimiter]
(def! split (fn* [string delimiter]
"Split the string at every occurrence of substring delimiter"
"An empty delimiter is splitting every character"
(if (= (strlen delimiter) 1)
(_split-ch string (char delimiter))
(do (def! delimiter (boom delimiter))
@ -48,20 +50,13 @@
(split-r string delimiter "" (str chunk matches curr) chunks)))))))
(reverse (split-r (boom string) delimiter "" "" '()))))))
(def! split (fn* [string delimiter]
"Split the string at every occurrence of substring delimiter"
"An empty delimiter is splitting every character"
(if (= (strlen delimiter) 0)
(cdr (split-h string delimiter))
(split-h string delimiter))))
(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]

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> {
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
.iter()
.any(|x| matches!(x, M::Num(v) if v.exact_zero()))
{
return Err(MalErr::unrecoverable("Attempting division by 0"));
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()))
{
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 {

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()));
// 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()),

View File

@ -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) {

View File

@ -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(),
)),
}
}

1
test.mal Normal file
View File

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