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} || (\
|
@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"
|
||||||
|
|||||||
@ -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))))
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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"),
|
||||||
|
|||||||
26
src/env.rs
26
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> {
|
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 {
|
||||||
|
|||||||
@ -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()),
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
18
src/types.rs
18
src/types.rs
@ -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(),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user