mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 01:05:32 +01:00
feat(types.rs): Implemented fractional numbers
Fracional numbers as extension of integernumbers, with Euclid's algorithm for fractional simplification, added functions "floor" "num" "den", added syntax [+/-]<num>/<den> to define fractional numbers BREAKING CHANGE:
This commit is contained in:
@ -36,8 +36,8 @@
|
|||||||
(>= b a)))
|
(>= b a)))
|
||||||
|
|
||||||
; Other functions in core.rs
|
; Other functions in core.rs
|
||||||
(def! int? (fn* [a]
|
(def! num? (fn* [a]
|
||||||
(= (type a) :int)))
|
(= (type a) :number)))
|
||||||
|
|
||||||
(def! sym? (fn* [a]
|
(def! sym? (fn* [a]
|
||||||
(= (type a) :symbol)))
|
(= (type a) :symbol)))
|
||||||
|
|||||||
11
src/core.rs
11
src/core.rs
@ -38,8 +38,11 @@ macro_rules! env_init {
|
|||||||
use crate::parse_tools::read_file;
|
use crate::parse_tools::read_file;
|
||||||
use crate::printer::{pr_str, prt};
|
use crate::printer::{pr_str, prt};
|
||||||
use crate::reader::{read_str, Reader};
|
use crate::reader::{read_str, Reader};
|
||||||
use crate::types::MalType::{Atom, Fun, Int, List, Nil, Str};
|
|
||||||
use crate::types::{mal_equals, reset_bang, MalErr};
|
use crate::types::{mal_equals, reset_bang, MalErr};
|
||||||
|
use crate::types::{
|
||||||
|
Frac,
|
||||||
|
MalType::{Atom, Fun, List, Nil, Num, Str},
|
||||||
|
};
|
||||||
|
|
||||||
macro_rules! if_atom {
|
macro_rules! if_atom {
|
||||||
($val:expr) => {{
|
($val:expr) => {{
|
||||||
@ -72,10 +75,14 @@ pub fn ns_init() -> Env {
|
|||||||
"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"),
|
||||||
"type" => Fun(|a| Ok(car(a)?.label_type()), "Returns a label indicating the type of it's argument"),
|
"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"),
|
"count" => Fun(|a| Ok(Num(Frac::num(car(a)?.if_list()?.len() as isize))), "Return the number of elements in the first argument"),
|
||||||
"=" => 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')"),
|
"=" => 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')"),
|
||||||
"car" => Fun(|a| mal_car(car(a)?), "Returns the first element of the list, NIL if its empty"),
|
"car" => Fun(|a| mal_car(car(a)?), "Returns the first element of the list, NIL if its empty"),
|
||||||
"cdr" => Fun(|a| mal_cdr(car(a)?), "Returns all the list but the first element"),
|
"cdr" => Fun(|a| mal_cdr(car(a)?), "Returns all the list but the first element"),
|
||||||
|
// Number functions, still to decide how to handle
|
||||||
|
"num" => Fun(|a| Ok(Num(Frac::num(car(a)?.if_number()?.get_num()))), "Get numerator of the number"),
|
||||||
|
"den" => Fun(|a| Ok(Num(Frac::num(car(a)?.if_number()?.get_den() as isize))), "Get denominator of the number"),
|
||||||
|
"floor" => Fun(|a| Ok(Num(Frac::num(car(a)?.if_number()?.int()))), "Approximate the number to the closest smaller integer"),
|
||||||
// A tribute to PHP's explode (PHP, a language I never used)
|
// A tribute to PHP's explode (PHP, a language I never used)
|
||||||
"boom" => Fun(mal_boom, "Split a string into a list of characters\n; BE CAREFUL WHEN USING"),
|
"boom" => Fun(mal_boom, "Split a string into a list of characters\n; BE CAREFUL WHEN USING"),
|
||||||
"read-string" => Fun(|a| read_str(Reader::new().push(car(a)?.if_string()?)).map_err(MalErr::severe), "Tokenize and read the first argument"),
|
"read-string" => Fun(|a| read_str(Reader::new().push(car(a)?.if_string()?)).map_err(MalErr::severe), "Tokenize and read the first argument"),
|
||||||
|
|||||||
37
src/env.rs
37
src/env.rs
@ -1,8 +1,9 @@
|
|||||||
use crate::eval::eval;
|
use crate::eval::eval;
|
||||||
use crate::types::MalErr;
|
use crate::types::MalErr;
|
||||||
use crate::types::{MalMap, MalRet, MalType};
|
use crate::types::{Frac, MalMap, MalRet, MalType};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::usize;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct EnvType {
|
pub struct EnvType {
|
||||||
@ -132,9 +133,9 @@ pub fn call_func(func: &MalType, args: &[MalType]) -> CallRet {
|
|||||||
return Err(MalErr::unrecoverable("No key provided to Vector construct"));
|
return Err(MalErr::unrecoverable("No key provided to Vector construct"));
|
||||||
}
|
}
|
||||||
match &args[0] {
|
match &args[0] {
|
||||||
M::Int(i) => {
|
M::Num(i) => {
|
||||||
if { 0..v.len() as isize }.contains(i) {
|
if { 0..v.len() as isize }.contains(&i.int()) {
|
||||||
Ok(CallFunc::Builtin(v[*i as usize].clone()))
|
Ok(CallFunc::Builtin(v[i.int() as usize].clone()))
|
||||||
} else {
|
} else {
|
||||||
Ok(CallFunc::Builtin(M::Nil))
|
Ok(CallFunc::Builtin(M::Nil))
|
||||||
}
|
}
|
||||||
@ -149,29 +150,41 @@ 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.iter().any(|x| matches!(x, M::Int(0))) {
|
if list
|
||||||
|
.iter()
|
||||||
|
.any(|x| matches!(x, M::Num(v) if v.exact_zero()))
|
||||||
|
{
|
||||||
return Err(MalErr::unrecoverable("Attempting division by 0"));
|
return Err(MalErr::unrecoverable("Attempting division by 0"));
|
||||||
}
|
}
|
||||||
Ok(list)
|
Ok(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arithmetic_op(set: isize, f: fn(isize, isize) -> isize, args: &[MalType]) -> MalRet {
|
pub fn arithmetic_op(set: isize, f: fn(Frac, Frac) -> Frac, args: &[MalType]) -> MalRet {
|
||||||
Ok(M::Int(match args.len() {
|
Ok(M::Num(match args.len() {
|
||||||
0 => set,
|
0 => Frac::num(set),
|
||||||
1 => f(set, args[0].if_number()?),
|
1 => f(Frac::num(set), args[0].if_number()?),
|
||||||
_ => {
|
_ => {
|
||||||
// TODO: Maybe an accumulator
|
// TODO: Maybe an accumulator
|
||||||
let mut left = args[0].if_number()?;
|
let mut left = args[0].if_number()?;
|
||||||
for el in &args[1..] {
|
for el in &args[1..] {
|
||||||
left = f(left, el.if_number()?);
|
left = f(left, el.if_number()?);
|
||||||
|
|
||||||
|
const SIM_TRIG: usize = usize::isqrt(std::isize::MAX as usize);
|
||||||
|
|
||||||
|
// TODO: consider if simplifying at every operation or every N
|
||||||
|
// or just at the end, no idea on how it scale
|
||||||
|
if std::cmp::max(left.get_num().abs() as usize, left.get_den()) > SIM_TRIG {
|
||||||
|
left = left.simplify();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
left
|
// Always simplify at the end
|
||||||
|
left.simplify()
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
use MalType::{Nil, T};
|
use MalType::{Nil, T};
|
||||||
pub fn comparison_op(f: fn(isize, isize) -> bool, args: &[MalType]) -> MalRet {
|
pub fn comparison_op(f: fn(Frac, Frac) -> bool, args: &[MalType]) -> MalRet {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return Ok(Nil);
|
return Ok(Nil);
|
||||||
}
|
}
|
||||||
@ -259,7 +272,7 @@ use std::process::exit;
|
|||||||
|
|
||||||
pub fn mal_exit(list: &[MalType]) -> MalRet {
|
pub fn mal_exit(list: &[MalType]) -> MalRet {
|
||||||
match car(list)? {
|
match car(list)? {
|
||||||
MalType::Int(val) => exit(*val as i32),
|
MalType::Num(val) => exit(val.int() as i32),
|
||||||
_ => exit(-1),
|
_ => exit(-1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ pub fn pr_str(ast: &MalType, print_readably: bool) -> String {
|
|||||||
M::T => "t".to_string(),
|
M::T => "t".to_string(),
|
||||||
M::Sym(sym) => sym.to_string(),
|
M::Sym(sym) => sym.to_string(),
|
||||||
M::Key(sym) => sym[2..].to_string(),
|
M::Key(sym) => sym[2..].to_string(),
|
||||||
M::Int(val) => val.to_string(),
|
M::Num(val) => val.to_string(),
|
||||||
M::Str(str) => {
|
M::Str(str) => {
|
||||||
if print_readably {
|
if print_readably {
|
||||||
escape_str(str)
|
escape_str(str)
|
||||||
|
|||||||
@ -95,8 +95,11 @@ impl Reader {
|
|||||||
"t" => Ok(T),
|
"t" => Ok(T),
|
||||||
"nil" => Ok(Nil),
|
"nil" => Ok(Nil),
|
||||||
tk => {
|
tk => {
|
||||||
if Regex::new(r"^-?[0-9]+$").unwrap().is_match(tk) {
|
if Regex::new(r"^[-\+]?[0-9]+(/[0-9]+)?$")
|
||||||
return Ok(Int(tk.parse::<isize>().unwrap()));
|
.unwrap()
|
||||||
|
.is_match(tk)
|
||||||
|
{
|
||||||
|
return Ok(Num(Frac::from_str(&tk)));
|
||||||
}
|
}
|
||||||
if tk.starts_with('\"') {
|
if tk.starts_with('\"') {
|
||||||
if tk.len() > 1 && tk.ends_with('\"') {
|
if tk.len() > 1 && tk.ends_with('\"') {
|
||||||
@ -180,7 +183,7 @@ mod tests {
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
reader::read_str,
|
reader::read_str,
|
||||||
types::{MalMap, MalType as M},
|
types::{Frac, MalMap, MalType as M},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{tokenize, Reader};
|
use super::{tokenize, Reader};
|
||||||
@ -270,7 +273,9 @@ mod tests {
|
|||||||
let r = Reader::new();
|
let r = Reader::new();
|
||||||
r.push("nil 1 t a \"s\" :a ) ] }");
|
r.push("nil 1 t a \"s\" :a ) ] }");
|
||||||
assert!(matches!(r.read_atom(), Ok(x) if matches!(x, M::Nil)));
|
assert!(matches!(r.read_atom(), Ok(x) if matches!(x, M::Nil)));
|
||||||
assert!(matches!(r.read_atom(), Ok(x) if matches!(x, M::Int(1))));
|
assert!(
|
||||||
|
matches!(r.read_atom(), Ok(x) if matches!(x.clone(), M::Num(v) if v == Frac::num(1)))
|
||||||
|
);
|
||||||
assert!(matches!(r.read_atom(), Ok(x) if matches!(x, M::T)));
|
assert!(matches!(r.read_atom(), Ok(x) if matches!(x, M::T)));
|
||||||
assert!(
|
assert!(
|
||||||
matches!(r.read_atom(), Ok(x) if matches!(x.clone(), M::Sym(v) if matches!(v.borrow(), "a")))
|
matches!(r.read_atom(), Ok(x) if matches!(x.clone(), M::Sym(v) if matches!(v.borrow(), "a")))
|
||||||
@ -298,7 +303,7 @@ mod tests {
|
|||||||
if matches!(x.clone(), M::List(list)
|
if matches!(x.clone(), M::List(list)
|
||||||
if list.len() == expected.len()
|
if list.len() == expected.len()
|
||||||
&& list.iter().zip(expected)
|
&& list.iter().zip(expected)
|
||||||
.all(|(x, y)| matches!(x, M::Int(v) if (*v as isize) == y)))));
|
.all(|(x, y)| matches!(x, M::Num(v) if v.int() == y)))));
|
||||||
r.clear();
|
r.clear();
|
||||||
|
|
||||||
// Test vector
|
// Test vector
|
||||||
@ -309,7 +314,7 @@ mod tests {
|
|||||||
if matches!(x.clone(), M::Vector(list)
|
if matches!(x.clone(), M::Vector(list)
|
||||||
if list.len() == exp.len()
|
if list.len() == exp.len()
|
||||||
&& list.iter().zip(exp)
|
&& list.iter().zip(exp)
|
||||||
.all(|(x, y)| matches!(x, M::Int(v) if (*v as isize) == y)))));
|
.all(|(x, y)| matches!(x, M::Num(v) if v.int() == y)))));
|
||||||
r.clear();
|
r.clear();
|
||||||
|
|
||||||
// Test map
|
// Test map
|
||||||
@ -329,7 +334,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
assert!(matches!(t.get("n"), Some(x) if matches!(&x, M::Nil)));
|
assert!(matches!(t.get("n"), Some(x) if matches!(&x, M::Nil)));
|
||||||
assert!(matches!(t.get("t"), Some(x) if matches!(&x, M::T)));
|
assert!(matches!(t.get("t"), Some(x) if matches!(&x, M::T)));
|
||||||
assert!(matches!(t.get("i"), Some(x) if matches!(&x, M::Int(v) if *v == 1)));
|
assert!(matches!(t.get("i"), Some(x) if matches!(&x, M::Num(v) if v.int() == 1)));
|
||||||
assert!(
|
assert!(
|
||||||
matches!(t.get("s"), Some(x) if matches!(&x, M::Str(v) if matches!(v.borrow(), "str")))
|
matches!(t.get("s"), Some(x) if matches!(&x, M::Str(v) if matches!(v.borrow(), "str")))
|
||||||
);
|
);
|
||||||
|
|||||||
146
src/types.rs
146
src/types.rs
@ -1,11 +1,145 @@
|
|||||||
use crate::env::{car_cdr, Env};
|
use crate::env::{car_cdr, Env};
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
cmp::Ordering,
|
||||||
|
collections::HashMap,
|
||||||
|
ops::{Add, Div, Mul, Sub},
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
pub type MalStr = Rc<str>;
|
pub type MalStr = Rc<str>;
|
||||||
pub type MalArgs = Rc<[MalType]>;
|
pub type MalArgs = Rc<[MalType]>;
|
||||||
pub type MalMap = HashMap<MalStr, MalType>;
|
pub type MalMap = HashMap<MalStr, MalType>;
|
||||||
pub type MalRet = Result<MalType, MalErr>;
|
pub type MalRet = Result<MalType, MalErr>;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Frac {
|
||||||
|
num: isize,
|
||||||
|
den: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Frac {
|
||||||
|
type Output = Self;
|
||||||
|
fn add(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
num: self.num * other.den as isize + other.num * self.den as isize,
|
||||||
|
den: self.den * other.den,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Frac {
|
||||||
|
type Output = Self;
|
||||||
|
fn sub(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
num: self.num * other.den as isize - other.num * self.den as isize,
|
||||||
|
den: self.den * other.den,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul for Frac {
|
||||||
|
type Output = Self;
|
||||||
|
fn mul(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
num: self.num * other.num,
|
||||||
|
den: self.den * other.den,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div for Frac {
|
||||||
|
type Output = Self;
|
||||||
|
fn div(self, other: Frac) -> Self {
|
||||||
|
let other_sign = other.num.signum();
|
||||||
|
Self {
|
||||||
|
num: self.num * other.den as isize * other_sign,
|
||||||
|
den: self.den * other.num.abs() as usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Frac {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some((self.num * other.den as isize).cmp(&(other.num * self.den as isize)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Frac {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
(!((self.num < 0) ^ (other.num < 0)))
|
||||||
|
&& self.num.abs() as usize * other.den == other.num.abs() as usize * self.den
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Frac {
|
||||||
|
pub fn frac(num: isize, den: usize) -> Self {
|
||||||
|
Self { num, den }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn num(num: isize) -> Self {
|
||||||
|
Self { num, den: 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exact_zero(&self) -> bool {
|
||||||
|
self.num == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_num(&self) -> isize {
|
||||||
|
self.num
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_den(&self) -> usize {
|
||||||
|
self.den
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _gcd(&self) -> usize {
|
||||||
|
let mut t: usize;
|
||||||
|
let mut a = self.num.abs() as usize;
|
||||||
|
let mut b = self.den;
|
||||||
|
while b > 0 {
|
||||||
|
t = b;
|
||||||
|
b = a % b;
|
||||||
|
a = t;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn simplify(&self) -> Frac {
|
||||||
|
// Euclid's algorithm to reduce fraction
|
||||||
|
// TODO: (decide if implementing this automathically once fraction
|
||||||
|
// numbers become bigger than specified)
|
||||||
|
let gcd = self._gcd();
|
||||||
|
return Frac {
|
||||||
|
num: self.num / gcd as isize,
|
||||||
|
den: self.den / gcd,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int(&self) -> isize {
|
||||||
|
self.num / self.den as isize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
let mut tmp = self.num.to_string();
|
||||||
|
if self.den != 1 {
|
||||||
|
tmp = tmp + "/" + &self.den.to_string()
|
||||||
|
}
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
// return Ok(Num(Frac::num(tk.parse::<isize>().unwrap())));
|
||||||
|
|
||||||
|
pub fn from_str(tk: &str) -> Self {
|
||||||
|
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()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// All Mal types should inherit from this
|
// All Mal types should inherit from this
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum MalType {
|
pub enum MalType {
|
||||||
@ -24,7 +158,7 @@ pub enum MalType {
|
|||||||
Key(MalStr),
|
Key(MalStr),
|
||||||
Str(MalStr),
|
Str(MalStr),
|
||||||
Ch(char),
|
Ch(char),
|
||||||
Int(isize),
|
Num(Frac),
|
||||||
Atom(Rc<RefCell<MalType>>),
|
Atom(Rc<RefCell<MalType>>),
|
||||||
Nil,
|
Nil,
|
||||||
T,
|
T,
|
||||||
@ -37,9 +171,9 @@ impl Default for &MalType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MalType {
|
impl MalType {
|
||||||
pub fn if_number(&self) -> Result<isize, MalErr> {
|
pub fn if_number(&self) -> Result<Frac, MalErr> {
|
||||||
match self {
|
match self {
|
||||||
Self::Int(val) => Ok(*val),
|
Self::Num(val) => Ok(val.clone()),
|
||||||
_ => Err(MalErr::unrecoverable(
|
_ => Err(MalErr::unrecoverable(
|
||||||
format!("{:?} is not a number", prt(self)).as_str(),
|
format!("{:?} is not a number", prt(self)).as_str(),
|
||||||
)),
|
)),
|
||||||
@ -87,7 +221,7 @@ impl MalType {
|
|||||||
+ match self {
|
+ match self {
|
||||||
M::Nil => "nil",
|
M::Nil => "nil",
|
||||||
M::T => "t",
|
M::T => "t",
|
||||||
M::Int(_) => "int",
|
M::Num(_) => "number",
|
||||||
M::Fun(_, _) | M::MalFun { .. } => "lambda",
|
M::Fun(_, _) | M::MalFun { .. } => "lambda",
|
||||||
M::Key(_) => "key",
|
M::Key(_) => "key",
|
||||||
M::Str(_) => "string",
|
M::Str(_) => "string",
|
||||||
@ -109,7 +243,7 @@ fn mal_compare(args: (&MalType, &MalType)) -> bool {
|
|||||||
match (args.0, args.1) {
|
match (args.0, args.1) {
|
||||||
(M::Nil, M::Nil) => true,
|
(M::Nil, M::Nil) => true,
|
||||||
(M::T, M::T) => true,
|
(M::T, M::T) => true,
|
||||||
(M::Int(a), M::Int(b)) => a == b,
|
(M::Num(a), M::Num(b)) => a == b,
|
||||||
(M::Ch(a), M::Ch(b)) => a == b,
|
(M::Ch(a), M::Ch(b)) => a == b,
|
||||||
(M::Key(a), M::Key(b)) | (M::Str(a), M::Str(b)) | (M::Sym(a), M::Sym(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)) => {
|
(M::List(a), M::List(b)) | (M::Vector(a), M::Vector(b)) => {
|
||||||
|
|||||||
@ -1,23 +1,28 @@
|
|||||||
; +
|
; +
|
||||||
(assert (= (+ 1 -4) -3))
|
(assert-eq (+ 1 -4) -3)
|
||||||
(assert (= (+ 1 1) 2))
|
(assert-eq (+ 1 1) 2)
|
||||||
|
|
||||||
; -
|
; -
|
||||||
(assert (= (- 2 1) 1))
|
(assert-eq (- 2 1) 1)
|
||||||
(assert (= (- 1 2) -1))
|
(assert-eq (- 1 2) -1)
|
||||||
|
|
||||||
; *
|
; *
|
||||||
(assert (= (* 2 3) 6))
|
(assert-eq (* 2 3) 6)
|
||||||
(assert (= (* -2 3) -6))
|
(assert-eq (* -2 3) -6)
|
||||||
(assert (= (* -2 -3) 6))
|
(assert-eq (* -2 -3) 6)
|
||||||
|
|
||||||
; /
|
; /
|
||||||
(assert (= (/ 3 2) 1))
|
(assert-eq (/ 3 2) 3/2)
|
||||||
(assert (= (/ 2 3) 0))
|
(assert-eq (floor (/ 3 2)) 1)
|
||||||
(assert (= (/ -10 2) -5))
|
(assert-eq (/ 2 3) 2/3)
|
||||||
(assert (= (/ -10 -2) 5))
|
(assert-eq (floor (/ 2 3)) 0)
|
||||||
|
(assert-eq (/ -10 2) -5)
|
||||||
|
(assert-eq (/ -10 -2) 5)
|
||||||
(assert (not (ok? (/ 12 0))))
|
(assert (not (ok? (/ 12 0))))
|
||||||
|
|
||||||
|
; frac rart
|
||||||
|
(assert-eq (num 3/2) 3)
|
||||||
|
(assert-eq (den 3/2) 2)
|
||||||
|
|
||||||
; >
|
; >
|
||||||
(assert (> 3 2))
|
(assert (> 3 2))
|
||||||
(assert (not (> 1 2)))
|
(assert (not (> 1 2)))
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
; filter with builtin function
|
; filter with builtin function
|
||||||
(assert-eq (list '(1) '(2) '(3)) (filter car (list '(1) '() '(2) '() '(3))))
|
(assert-eq (list '(1) '(2) '(3)) (filter car (list '(1) '() '(2) '() '(3))))
|
||||||
; filter with lambda function
|
; filter with lambda function
|
||||||
(assert-eq (list 1 2 3) (filter int? (list 1 "string" 2 'symbol 3 :label)))
|
(assert-eq (list 1 2 3) (filter num? (list 1 "string" 2 'symbol 3 :label)))
|
||||||
|
|||||||
Reference in New Issue
Block a user