use std::fmt::Debug;
use log::trace;
use crate::runtime::error::state_error;
use crate::runtime::error::OrNumberError;
use crate::runtime::internals::concatenation_len;
use crate::runtime::list::index_concatenation_for;
use crate::runtime::utilities::{get_range, next_two_raw_ref, push_boolean};
use garnish_lang_traits::{GarnishDataType, GarnishData, GarnishNumber, RuntimeError, TypeConstants};
pub(crate) fn equal<Data: GarnishData>(this: &mut Data) -> Result<Option<Data::Size>, RuntimeError<Data::Error>> {
let equal = perform_equality_check(this)?;
push_boolean(this, equal)?;
Ok(None)
}
pub fn not_equal<Data: GarnishData>(this: &mut Data) -> Result<Option<Data::Size>, RuntimeError<Data::Error>> {
let equal = perform_equality_check(this)?;
push_boolean(this, !equal)?;
Ok(None)
}
pub fn type_equal<Data: GarnishData>(this: &mut Data) -> Result<Option<Data::Size>, RuntimeError<Data::Error>> {
let (right, left) = next_two_raw_ref(this)?;
let left_type = this.get_data_type(left)?;
let right_type = this.get_data_type(right.clone())?;
let right_type = if right_type == GarnishDataType::Type {
this.get_type(right)?
} else {
right_type
};
let equal = left_type == right_type;
push_boolean(this, equal)?;
Ok(None)
}
fn perform_equality_check<Data: GarnishData>(this: &mut Data) -> Result<bool, RuntimeError<Data::Error>> {
let two = Data::Size::one() + Data::Size::one();
if this.get_register_len() < two {
state_error(format!("Not enough registers to perform comparison."))?;
}
let start = this.get_register_len() - two;
while this.get_register_len() > start {
let (right, left) = next_two_raw_ref(this)?;
if !data_equal(this, left, right)? {
while this.get_register_len() > start {
this.pop_register();
}
return Ok(false);
}
}
Ok(true)
}
fn data_equal<Data: GarnishData>(
this: &mut Data,
left_addr: Data::Size,
right_addr: Data::Size,
) -> Result<bool, RuntimeError<Data::Error>> {
let (left_type, right_type) = (this.get_data_type(left_addr.clone())?, this.get_data_type(right_addr.clone())?);
let equal = match (left_type, right_type) {
(GarnishDataType::Unit, GarnishDataType::Unit)
| (GarnishDataType::True, GarnishDataType::True)
| (GarnishDataType::False, GarnishDataType::False) => true,
(GarnishDataType::Type, GarnishDataType::Type) => this.get_type(left_addr)? == this.get_type(right_addr)?,
(GarnishDataType::Expression, GarnishDataType::Expression) => compare(this, left_addr, right_addr, Data::get_expression)?,
(GarnishDataType::External, GarnishDataType::External) => compare(this, left_addr, right_addr, Data::get_external)?,
(GarnishDataType::Symbol, GarnishDataType::Symbol) => compare(this, left_addr, right_addr, Data::get_symbol)?,
(GarnishDataType::Char, GarnishDataType::Char) => compare(this, left_addr, right_addr, Data::get_char)?,
(GarnishDataType::Byte, GarnishDataType::Byte) => compare(this, left_addr, right_addr, Data::get_byte)?,
(GarnishDataType::Number, GarnishDataType::Number) => compare(this, left_addr, right_addr, Data::get_number)?,
(GarnishDataType::Char, GarnishDataType::CharList) => {
if this.get_char_list_len(right_addr.clone())? == Data::Size::one() {
let c1 = this.get_char(left_addr)?;
let c2 = this.get_char_list_item(right_addr, Data::Number::zero())?;
c1 == c2
} else {
false
}
}
(GarnishDataType::CharList, GarnishDataType::Char) => {
if this.get_char_list_len(left_addr.clone())? == Data::Size::one() {
let c1 = this.get_char_list_item(left_addr, Data::Number::zero())?;
let c2 = this.get_char(right_addr)?;
c1 == c2
} else {
false
}
}
(GarnishDataType::Byte, GarnishDataType::ByteList) => {
if this.get_byte_list_len(right_addr.clone())? == Data::Size::one() {
let c1 = this.get_byte(left_addr)?;
let c2 = this.get_byte_list_item(right_addr, Data::Number::zero())?;
c1 == c2
} else {
false
}
}
(GarnishDataType::ByteList, GarnishDataType::Byte) => {
if this.get_byte_list_len(left_addr.clone())? == Data::Size::one() {
let c1 = this.get_byte_list_item(left_addr, Data::Number::zero())?;
let c2 = this.get_byte(right_addr)?;
c1 == c2
} else {
false
}
}
(GarnishDataType::CharList, GarnishDataType::CharList) => {
let len1 = this.get_char_list_len(left_addr.clone())?;
let len2 = this.get_char_list_len(right_addr.clone())?;
if len1 != len2 {
false
} else {
let mut count = Data::Size::one();
let mut equal = true;
while count < len1 {
let i = Data::size_to_number(count.clone());
let c1 = this.get_char_list_item(left_addr.clone(), i.clone())?;
let c2 = this.get_char_list_item(right_addr.clone(), i)?;
if c1 != c2 {
equal = false;
}
count += Data::Size::one();
}
equal
}
}
(GarnishDataType::ByteList, GarnishDataType::ByteList) => {
let len1 = this.get_byte_list_len(left_addr.clone())?;
let len2 = this.get_byte_list_len(right_addr.clone())?;
if len1 != len2 {
false
} else {
let mut count = Data::Size::one();
let mut equal = true;
while count < len1 {
let i = Data::size_to_number(count.clone());
let c1 = this.get_byte_list_item(left_addr.clone(), i.clone())?;
let c2 = this.get_byte_list_item(right_addr.clone(), i)?;
if c1 != c2 {
equal = false;
}
count += Data::Size::one();
}
equal
}
}
(GarnishDataType::Range, GarnishDataType::Range) => {
let (start1, end1) = this.get_range(left_addr)?;
let (start2, end2) = this.get_range(right_addr)?;
let start_equal = match (this.get_data_type(start1.clone())?, this.get_data_type(start2.clone())?) {
(GarnishDataType::Unit, GarnishDataType::Unit) => true,
(GarnishDataType::Number, GarnishDataType::Number) => this.get_number(start1)? == this.get_number(start2)?,
_ => false,
};
let end_equal = match (this.get_data_type(end1.clone())?, this.get_data_type(end2.clone())?) {
(GarnishDataType::Unit, GarnishDataType::Unit) => true,
(GarnishDataType::Number, GarnishDataType::Number) => this.get_number(end1)? == this.get_number(end2)?,
_ => false,
};
start_equal && end_equal
}
(GarnishDataType::Pair, GarnishDataType::Pair) => {
let (left1, right1) = this.get_pair(left_addr)?;
let (left2, right2) = this.get_pair(right_addr)?;
this.push_register(left1)?;
this.push_register(left2)?;
this.push_register(right1)?;
this.push_register(right2)?;
true
}
(GarnishDataType::Concatenation, GarnishDataType::Concatenation) => {
let len1 = concatenation_len(this, left_addr.clone())?;
let len2 = concatenation_len(this, right_addr.clone())?;
if len1 != len2 {
false
} else {
let mut count = Data::Number::zero();
let len = Data::size_to_number(len1);
while count < len {
match (
index_concatenation_for(this, left_addr.clone(), count.clone())?,
index_concatenation_for(this, right_addr.clone(), count.clone())?,
) {
(Some(left), Some(right)) => {
this.push_register(left)?;
this.push_register(right)?;
}
_ => {
return Ok(false);
}
}
count = count.increment().or_num_err()?;
}
true
}
}
(GarnishDataType::List, GarnishDataType::Concatenation) => {
let len1 = this.get_list_len(left_addr.clone())?;
let len2 = concatenation_len(this, right_addr.clone())?;
if len1 != len2 {
false
} else {
let mut count = Data::Number::zero();
let len = Data::size_to_number(len1);
while count < len {
match (this.get_list_item(left_addr.clone(), count.clone())?, index_concatenation_for(this, right_addr.clone(), count.clone())?) {
(left, Some(right)) => {
this.push_register(left)?;
this.push_register(right)?;
}
_ => {
return Ok(false);
}
}
count = count.increment().or_num_err()?;
}
true
}
}
(GarnishDataType::Concatenation, GarnishDataType::List) => {
let len1 = concatenation_len(this, left_addr.clone())?;
let len2 = this.get_list_len(right_addr.clone())?;
if len1 != len2 {
false
} else {
let mut count = Data::Number::zero();
let len = Data::size_to_number(len1);
while count < len {
match (index_concatenation_for(this, left_addr.clone(), count.clone())?, this.get_list_item(right_addr.clone(), count.clone())?) {
(Some(left), right) => {
this.push_register(left)?;
this.push_register(right)?;
}
_ => {
return Ok(false);
}
}
count = count.increment().or_num_err()?;
}
true
}
}
(GarnishDataType::Slice, GarnishDataType::Slice) => {
let (value1, range1) = this.get_slice(left_addr)?;
let (value2, range2) = this.get_slice(right_addr)?;
let (start1, _, len1) = get_range(this, range1)?;
let (start2, _, len2) = get_range(this, range2)?;
if len1 != len2 {
false
} else {
match (this.get_data_type(value1.clone())?, this.get_data_type(value2.clone())?) {
(GarnishDataType::CharList, GarnishDataType::CharList) => {
let mut index1 = start1;
let mut index2 = start2;
let mut count = Data::Number::zero();
let list_len1 = Data::size_to_number(this.get_char_list_len(value1.clone())?);
let list_len2 = Data::size_to_number(this.get_char_list_len(value2.clone())?);
while count < len1 {
let item1 = if index1 < list_len1 {
this.get_char_list_item(value1.clone(), index1.clone())?
} else {
return Ok(false);
};
let item2 = if index2 < list_len2 {
this.get_char_list_item(value2.clone(), index2.clone())?
} else {
return Ok(false);
};
if item1 != item2 {
return Ok(false);
}
index1 = index1.increment().or_num_err()?;
index2 = index2.increment().or_num_err()?;
count = count.increment().or_num_err()?;
}
true
}
(GarnishDataType::ByteList, GarnishDataType::ByteList) => {
let mut index1 = start1;
let mut index2 = start2;
let mut count = Data::Number::zero();
let list_len1 = Data::size_to_number(this.get_byte_list_len(value1.clone())?);
let list_len2 = Data::size_to_number(this.get_byte_list_len(value2.clone())?);
while count < len1 {
let item1 = if index1 < list_len1 {
this.get_byte_list_item(value1.clone(), index1.clone())?
} else {
return Ok(false);
};
let item2 = if index2 < list_len2 {
this.get_byte_list_item(value2.clone(), index2.clone())?
} else {
return Ok(false);
};
if item1 != item2 {
return Ok(false);
}
index1 = index1.increment().or_num_err()?;
index2 = index2.increment().or_num_err()?;
count = count.increment().or_num_err()?;
}
true
}
(GarnishDataType::List, GarnishDataType::List) => {
let mut index1 = start1;
let mut index2 = start2;
let mut count = Data::Number::zero();
let list_len1 = Data::size_to_number(this.get_list_len(value1.clone())?);
let list_len2 = Data::size_to_number(this.get_list_len(value2.clone())?);
while count < len1 {
let item1 = if index1 < list_len1 {
this.get_list_item(value1.clone(), index1.clone())?
} else {
this.add_unit()?
};
let item2 = if index2 < list_len2 {
this.get_list_item(value2.clone(), index2.clone())?
} else {
this.add_unit()?
};
this.push_register(item1)?;
this.push_register(item2)?;
index1 = index1.increment().or_num_err()?;
index2 = index2.increment().or_num_err()?;
count = count.increment().or_num_err()?;
}
true
}
_ => false,
}
}
}
(GarnishDataType::List, GarnishDataType::List) => {
let association_len1 = this.get_list_associations_len(left_addr.clone())?;
let associations_len2 = this.get_list_associations_len(right_addr.clone())?;
let len1 = this.get_list_len(left_addr.clone())?;
let len2 = this.get_list_len(right_addr.clone())?;
if association_len1 != associations_len2 || len1 != len2 {
false
} else {
let mut count = Data::Size::zero();
while count < len1 {
let i = Data::size_to_number(count.clone());
let left_item = this.get_list_item(left_addr.clone(), i.clone())?;
let right_item = this.get_list_item(right_addr.clone(), i)?;
let (left_is_associative, pair_sym, pair_item) = match this.get_data_type(left_item.clone().clone())? {
GarnishDataType::Pair => {
let (left, right) = this.get_pair(left_item.clone())?;
match this.get_data_type(left.clone())? {
GarnishDataType::Symbol => (true, this.get_symbol(left)?, right),
_ => (false, Data::Symbol::zero(), Data::Size::zero()),
}
}
_ => (false, Data::Symbol::zero(), Data::Size::zero()),
};
if left_is_associative {
match this.get_list_item_with_symbol(right_addr.clone(), pair_sym)? {
Some(right_item) => {
this.push_register(pair_item)?;
this.push_register(right_item)?;
}
None => {
return Ok(false);
}
}
} else {
this.push_register(left_item)?;
this.push_register(right_item)?;
}
count += Data::Size::one();
}
true
}
}
_ => false,
};
Ok(equal)
}
fn compare<Data: GarnishData, F, V: PartialOrd + Debug>(
this: &Data,
left_addr: Data::Size,
right_addr: Data::Size,
get_func: F,
) -> Result<bool, Data::Error>
where
F: Fn(&Data, Data::Size) -> Result<V, Data::Error>,
{
let left = get_func(this, left_addr)?;
let right = get_func(this, right_addr)?;
trace!("Comparing {:?} == {:?}", left, right);
Ok(left == right)
}