Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# ClojuRS

Clojure + Rust
* This is a personal implementation of Clojure as a hobby, if you want a more complete project try [ClojrueRS](https://github.com/clojure-rs/ClojureRS)

<img width="173" alt="Captura de Tela 2021-10-11 às 14 38 05" src="https://user-images.githubusercontent.com/14813660/136832806-de181d66-7dde-4f67-bddd-5170b2839556.png">
24 changes: 17 additions & 7 deletions src/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use num_bigint::BigInt;
use num_traits::{ToPrimitive, Zero};
use ordered_float::OrderedFloat;

use crate::{error::Error, funtions::eval_list};
use crate::{error::Error, funtions::eval_list, helper};

#[derive(Debug, Clone, Eq)]
pub enum DefinitionTypes {
Expand Down Expand Up @@ -76,17 +76,13 @@ impl Display for DefinitionTypes {

impl Ord for DefinitionTypes {
fn cmp(&self, other: &Self) -> Ordering {
let s = self.to_string();
let o = other.to_string();
s.cmp(&o)
helper::cmp(self, other)
}
}

impl PartialOrd for DefinitionTypes {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let s = self.to_string();
let o = other.to_string();
Some(s.cmp(&o))
helper::partial_cmp(self, other)
}
}

Expand Down Expand Up @@ -129,6 +125,20 @@ impl PartialEq for DefinitionTypes {
}

impl DefinitionTypes {
pub fn to_usize(&self) -> Option<usize> {
match self {
DefinitionTypes::Int(bi) => bi.to_usize(),
DefinitionTypes::Rational(num, den) => {
if num % den == BigInt::from(0) {
(num / den).to_usize()
} else {
None
}
}
_ => None,
}
}

pub fn print(&self) -> Result<String, Error> {
let res = match self.clone() {
DefinitionTypes::Symbol(el) => el,
Expand Down
323 changes: 323 additions & 0 deletions src/funtions/collections.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
use std::collections::{BTreeMap, HashMap};

use num_traits::ToPrimitive;

use crate::{definitions::DefinitionTypes as T, error::Error, helper::arity_exception, DATA};

pub fn get(info: &[T]) -> Result<T, Error> {
if let Some(value) = arity_exception("get", 3, info.len()) {
return value;
}

match (info.get(0), info.get(1), info.get(2)) {
(None, _, _) => Err(Error::Reason(String::from(
"Collection is required for get",
))),
(_, None, _) => Err(Error::Reason(String::from(
"Access index is required for get",
))),
(Some(collection), Some(index), not_found) => match collection {
T::String(s) => match (index.to_usize(), not_found) {
(None, Some(nf)) => Ok(nf.clone()),
(None, None) => Err(Error::IntParseError),
(Some(idx), None) => {
Ok(T::Char(s.chars().nth(idx).ok_or_else(|| {
Error::Reason("Index out of bounds".to_string())
})?))
}
(Some(idx), Some(nf)) => {
if s.len() > idx {
Ok(T::Char(s.chars().nth(idx).ok_or_else(|| {
Error::Reason("Index out of bounds".to_string())
})?))
} else {
Ok(nf.clone())
}
}
},
T::HashSet(hs) => {
if hs.contains(index) {
Ok(index.clone())
} else if not_found.is_some() {
Ok(not_found.unwrap().clone())
} else {
Ok(T::Nil)
}
}
T::OrderedSet(os) => {
if os.contains(index) {
Ok(index.clone())
} else if not_found.is_some() {
Ok(not_found.unwrap().clone())
} else {
Ok(T::Nil)
}
}
T::HashMap(hm) => {
if hm.contains_key(index) {
Ok(hm.get(index).unwrap().clone())
} else if not_found.is_some() {
Ok(not_found.unwrap().clone())
} else {
Ok(T::Nil)
}
}
T::OrderedMap(om) => {
if om.contains_key(index) {
Ok(om.get(index).unwrap().clone())
} else if not_found.is_some() {
Ok(not_found.unwrap().clone())
} else {
Ok(T::Nil)
}
}
T::List(_) => {
let l = collection.to_owned().eval()?;
if let Some(nf) = not_found {
get(&[l, index.to_owned(), nf.to_owned()])
} else {
get(&[l, index.to_owned()])
}
}
T::Vector(v) => match index {
T::Symbol(sym) => {
Ok(DATA
.lock()
.map_or(Err(Error::CantEval(Some(sym.clone()))), |e| {
e.get(sym)
.map(|inner_e| inner_e.to_owned())
.ok_or_else(|| Error::CantEval(Some(sym.clone())))
})?)
}
T::Int(idx) => {
if let Some(idx) = idx.to_usize() {
if v.len() > idx {
Ok(v[idx].clone())
} else if not_found.is_some() {
Ok(not_found.unwrap().clone())
} else {
Err(Error::Reason(String::from("Index out of bounds")))
}
} else {
Err(Error::CantEval(Some(format!(
"Can't eval {} at index {}",
collection.print()?,
&idx
))))
}
}
_ => Err(Error::Reason(String::from("Index out of bounds"))),
},
_ => Err(Error::Reason(String::from(
"First argument must be a collection for get",
))),
},
}
}

pub fn to_vector(list: &[T]) -> Result<T, Error> {
Ok(T::Vector(list.iter().map(|e| e.to_owned()).collect()))
}

pub fn to_hashset(list: &[T]) -> Result<T, Error> {
Ok(T::HashSet(
list.iter().filter_map(|e| e.clone().eval().ok()).collect(),
))
}

pub fn to_orderedset(list: &[T]) -> Result<T, Error> {
Ok(T::OrderedSet(
list.iter().filter_map(|e| e.clone().eval().ok()).collect(),
))
}

pub fn to_hashmap(list: &[T]) -> Result<T, Error> {
if list.len() % 2 == 0 {
Ok(T::HashMap(
list.chunks(2)
.map(|e| Ok((e[0].clone().eval()?, e[1].clone().eval()?)))
.collect::<Result<HashMap<T, T>, Error>>()?,
))
} else {
Err(Error::Reason(String::from(
"Hash map must be formed by pairs",
)))
}
}

pub fn to_orderedmap(list: &[T]) -> Result<T, Error> {
if list.len() % 2 == 0 {
Ok(T::OrderedMap(
list.chunks(2)
.map(|e| Ok((e[0].clone().eval()?, e[1].clone().eval()?)))
.collect::<Result<BTreeMap<T, T>, Error>>()?,
))
} else {
Err(Error::Reason(String::from(
"Sorted map must be formed by pairs",
)))
}
}

pub fn assoc(info: &[T]) -> Result<T, Error> {
if let Some(value) = arity_exception("assoc", 3, info.len()) {
return value;
}
match (info.get(0), info.get(1), info.get(2)) {
(None, _, _) => Err(Error::Reason(String::from(
"Collection is required for assoc",
))),
(_, None, _) => Err(Error::Reason(String::from(
"Access index/key is required for assoc",
))),
(Some(collection), Some(key), Some(value)) => match collection.clone().eval()? {
T::Vector(v) => {
if let T::Int(index) = key {
let idx = index.to_usize();
match idx {
Some(i) if i > v.len() => Err(Error::Reason(
"Index must be inside vector's bound + 1".to_owned(),
)),
Some(i) if i == v.len() => {
let mut v = v.clone();
v.push(value.clone());
Ok(T::Vector(v))
}
_ => {
let mut v = v.clone();
v[idx.ok_or(Error::IntParseError)?] = value.clone();
Ok(T::Vector(v))
}
}
} else {
Err(Error::Reason(String::from("Index must be of type int")))
}
}
T::HashMap(hm) => {
let mut hm = hm.clone();
hm.entry(key.clone())
.and_modify(|e| *e = value.clone())
.or_insert_with(|| value.clone());
Ok(T::HashMap(hm))
}
T::OrderedMap(om) => {
let mut om = om.clone();
om.entry(key.clone())
.and_modify(|e| *e = value.clone())
.or_insert_with(|| value.clone());
Ok(T::OrderedMap(om))
}
_ => Err(Error::Reason("Assoc not available for type".to_owned())),
},
(Some(_), Some(_), None) => Err(Error::ArityException(
3,
format!("`assoc` has arity of 3 but received {}", 2),
)),
}
}

pub fn dissoc(info: &[T]) -> Result<T, Error> {
if let Some(value) = arity_exception("dissoc", 2, info.len()) {
return value;
}
match (info.get(0), info.get(1)) {
(None, _) => Err(Error::Reason(String::from(
"Collection is required for dissoc",
))),
(_, None) => Err(Error::Reason(String::from(
"Access index/key is required for dissoc",
))),
(Some(collection), Some(key)) => match collection.clone().eval()? {
T::HashMap(hm) => {
let mut hm = hm.clone();
if hm.contains_key(key) {
hm.remove(key);
}
Ok(T::HashMap(hm))
}
T::OrderedMap(om) => {
let mut om = om.clone();
if om.contains_key(key) {
om.remove(key);
}
Ok(T::OrderedMap(om))
}
_ => Err(Error::Reason("Dissoc not available for type".to_owned())),
},
}
}

pub fn contains(info: &[T]) -> Result<T, Error> {
if let Some(value) = arity_exception("contains?", 2, info.len()) {
return value;
}

match (info.get(0), info.get(1)) {
(None, _) => Err(Error::Reason(String::from(
"Collection is required for contains?",
))),
(_, None) => Err(Error::Reason(String::from(
"Access index/key is required for contains?",
))),
(Some(collection), Some(index)) => match collection {
T::String(s) => {
let evaluated_index = match index {
T::String(s) => s.to_owned(),
T::Char(c) => String::from(c.to_owned()),
_ => index.print()?,
};

Ok(T::Bool(s.contains(&evaluated_index)))
}
T::HashSet(hs) => {
if hs.contains(index) {
Ok(T::Bool(true))
} else {
Ok(T::Bool(false))
}
}
T::OrderedSet(os) => {
if os.contains(index) {
Ok(T::Bool(true))
} else {
Ok(T::Bool(false))
}
}
T::HashMap(hm) => {
if hm.contains_key(index) {
Ok(T::Bool(true))
} else {
Ok(T::Bool(false))
}
}
T::OrderedMap(om) => {
if om.contains_key(index) {
Ok(T::Bool(true))
} else {
Ok(T::Bool(false))
}
}
T::List(_) => {
let l = collection.to_owned().eval()?;
contains(&[l, index.clone()])
}
T::Vector(v) => match index {
T::Int(idx) => {
if let Some(idx) = idx.to_usize() {
if v.len() > idx {
Ok(T::Bool(true))
} else {
Ok(T::Bool(false))
}
} else {
Err(Error::IntParseError)
}
}
_ => Err(Error::Reason(String::from("Index must be an integer"))),
},
_ => Err(Error::Reason(String::from(
"First argument must be a collection or a string for contains?",
))),
},
}
}
2 changes: 1 addition & 1 deletion src/funtions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub mod logic;
pub mod math;
pub mod std;
// pub mod adapter_consumers Issue 14
// pub mod collections Issue 15
pub mod collections;

pub type Func = fn(&[T]) -> Result<T, Error>;

Expand Down
Loading