use garnish_lang_traits::helpers::iterate_concatenation_mut;
use garnish_lang_traits::Instruction;
use crate::runtime::internals::concatenation_len;
use crate::runtime::list::{is_value_association};
use crate::{
get_range, next_ref, next_two_raw_ref, push_unit, ExpressionDataType, GarnishLangRuntimeContext, GarnishLangRuntimeData, GarnishNumber,
OrNumberError, RuntimeError, TypeConstants,
};
pub(crate) fn type_of<Data: GarnishLangRuntimeData>(this: &mut Data) -> Result<Option<Data::Size>, RuntimeError<Data::Error>> {
let a = next_ref(this)?;
let t = this.get_data_type(a)?;
this.add_type(t).and_then(|r| this.push_register(r))?;
Ok(None)
}
pub(crate) fn type_cast<Data: GarnishLangRuntimeData, Context: GarnishLangRuntimeContext<Data>>(
this: &mut Data,
context: Option<&mut Context>,
) -> Result<Option<Data::Size>, RuntimeError<Data::Error>> {
let (right, left) = next_two_raw_ref(this)?;
let (left_type, mut right_type) = (this.get_data_type(left)?, this.get_data_type(right)?);
if right_type == ExpressionDataType::Type {
right_type = this.get_type(right)?;
}
match (left_type, right_type) {
(l, r) if l == r => this.push_register(left)?,
(ExpressionDataType::CharList, ExpressionDataType::Byte) => {
this.add_byte_from(left).and_then(|r| this.push_register(r))?;
}
(ExpressionDataType::CharList, ExpressionDataType::Number) => {
this.add_number_from(left).and_then(|r| this.push_register(r))?;
}
(_, ExpressionDataType::CharList) => {
this.add_char_list_from(left).and_then(|r| this.push_register(r))?;
}
(_, ExpressionDataType::ByteList) => {
this.add_byte_list_from(left).and_then(|r| this.push_register(r))?;
}
(_, ExpressionDataType::Symbol) => {
this.add_symbol_from(left).and_then(|r| this.push_register(r))?;
}
(ExpressionDataType::Number, ExpressionDataType::Char) => {
primitive_cast(this, left, Data::get_number, Data::number_to_char, Data::add_char)?;
}
(ExpressionDataType::Number, ExpressionDataType::Byte) => {
primitive_cast(this, left, Data::get_number, Data::number_to_byte, Data::add_byte)?;
}
(ExpressionDataType::Char, ExpressionDataType::Number) => {
primitive_cast(this, left, Data::get_char, Data::char_to_number, Data::add_number)?;
}
(ExpressionDataType::Char, ExpressionDataType::Byte) => {
primitive_cast(this, left, Data::get_char, Data::char_to_byte, Data::add_byte)?;
}
(ExpressionDataType::Byte, ExpressionDataType::Number) => {
primitive_cast(this, left, Data::get_byte, Data::byte_to_number, Data::add_number)?;
}
(ExpressionDataType::Byte, ExpressionDataType::Char) => {
primitive_cast(this, left, Data::get_byte, Data::byte_to_char, Data::add_char)?;
}
(ExpressionDataType::CharList, ExpressionDataType::Char) => {
let len = this.get_char_list_len(left)?;
if len == Data::Size::one() {
this.get_char_list_item(left, Data::Number::zero())
.and_then(|c| this.add_char(c))
.and_then(|r| this.push_register(r))?;
} else {
push_unit(this)?;
}
}
(ExpressionDataType::Range, ExpressionDataType::List) => {
let (start, end) = this.get_range(left)?;
let len = end - start + Data::Size::one();
let (start, end, _) = get_range(this, left)?;
let mut count = start;
this.start_list(len)?;
while count <= end {
let addr = this.add_number(count)?;
this.add_to_list(addr, false)?;
count = count.increment().or_num_err()?;
}
this.end_list().and_then(|r| this.push_register(r))?
}
(ExpressionDataType::CharList, ExpressionDataType::List) => {
let len = this.get_char_list_len(left)?;
list_from_char_list(this, left, Data::Number::zero(), Data::size_to_number(len))?;
}
(ExpressionDataType::ByteList, ExpressionDataType::List) => {
let len = this.get_byte_list_len(left)?;
list_from_byte_list(this, left, Data::Number::zero(), Data::size_to_number(len))?;
}
(ExpressionDataType::Concatenation, ExpressionDataType::List) => {
let len = concatenation_len(this, left)?;
this.start_list(len)?;
iterate_concatenation_mut(
this,
left,
|this, _, addr| {
let is_associative = is_value_association(this, addr)?;
this.add_to_list(addr, is_associative)?;
Ok(None)
},
)?;
let addr = this.end_list()?;
this.push_register(addr)?;
}
(ExpressionDataType::Slice, ExpressionDataType::List) => {
let (value, range) = this.get_slice(left)?;
let (start, end, len) = get_range(this, range)?;
match this.get_data_type(value)? {
ExpressionDataType::List => {
let len = this.get_list_len(value)?;
this.start_list(len)?;
let mut i = start;
while i <= end {
let addr = this.get_list_item(value, i)?;
let is_associative = match this.get_data_type(addr)? {
ExpressionDataType::Pair => {
let (left, _) = this.get_pair(addr)?;
match this.get_data_type(left)? {
ExpressionDataType::Symbol => true,
_ => false,
}
}
_ => false,
};
this.add_to_list(addr, is_associative)?;
i = i.increment().or_num_err()?;
}
this.end_list().and_then(|r| this.push_register(r))?
}
ExpressionDataType::CharList => {
list_from_char_list(this, value, start, end.increment().or_num_err()?)?;
}
ExpressionDataType::ByteList => {
list_from_byte_list(this, value, start, end.increment().or_num_err()?)?;
}
ExpressionDataType::Concatenation => {
this.start_list(Data::number_to_size(len).or_num_err()?)?;
iterate_concatenation_mut(
this,
value,
|this, current_index, addr| {
if current_index < start {
return Ok(None);
}
if current_index > end {
return Ok(Some(addr));
}
let is_associative = is_value_association(this, addr)?;
this.add_to_list(addr, is_associative)?;
Ok(None)
},
)?;
let addr = this.end_list()?;
this.push_register(addr)?;
}
_ => push_unit(this)?,
}
}
(ExpressionDataType::Unit, ExpressionDataType::True) | (ExpressionDataType::False, ExpressionDataType::True) => {
this.add_false().and_then(|r| this.push_register(r))?;
}
(ExpressionDataType::Unit, ExpressionDataType::False) => this.add_true().and_then(|r| this.push_register(r))?,
(ExpressionDataType::Unit, _) => push_unit(this)?,
(_, ExpressionDataType::False) => this.add_false().and_then(|r| this.push_register(r))?,
(_, ExpressionDataType::True) => this.add_true().and_then(|r| this.push_register(r))?,
(l, r) => match context {
None => push_unit(this)?,
Some(c) => {
if !c.defer_op(this, Instruction::ApplyType, (l, left), (r, right))? {
push_unit(this)?
}
}
},
}
Ok(None)
}
pub(crate) fn list_from_char_list<Data: GarnishLangRuntimeData>(
this: &mut Data,
byte_list_addr: Data::Size,
start: Data::Number,
end: Data::Number,
) -> Result<Option<Data::Size>, RuntimeError<Data::Error>> {
let len = this.get_char_list_len(byte_list_addr)?;
let mut count = start;
this.start_list(len)?;
while count < end {
let c = this.get_char_list_item(byte_list_addr, count)?;
let addr = this.add_char(c)?;
this.add_to_list(addr, false)?;
count = count.increment().or_num_err()?;
}
this.end_list().and_then(|r| this.push_register(r))?;
Ok(None)
}
pub(crate) fn list_from_byte_list<Data: GarnishLangRuntimeData>(
this: &mut Data,
byte_list_addr: Data::Size,
start: Data::Number,
end: Data::Number,
) -> Result<Option<Data::Size>, RuntimeError<Data::Error>> {
let len = this.get_byte_list_len(byte_list_addr)?;
let mut count = start;
this.start_list(len)?;
while count < end {
let c = this.get_byte_list_item(byte_list_addr, count)?;
let addr = this.add_byte(c)?;
this.add_to_list(addr, false)?;
count = count.increment().or_num_err()?;
}
this.end_list().and_then(|r| this.push_register(r))?;
Ok(None)
}
pub(crate) fn primitive_cast<Data: GarnishLangRuntimeData, From, To, GetFunc, CastFunc, AddFunc>(
this: &mut Data,
addr: Data::Size,
get: GetFunc,
cast: CastFunc,
add: AddFunc,
) -> Result<Option<Data::Size>, RuntimeError<Data::Error>>
where
GetFunc: Fn(&Data, Data::Size) -> Result<From, Data::Error>,
CastFunc: Fn(From) -> Option<To>,
AddFunc: FnOnce(&mut Data, To) -> Result<Data::Size, Data::Error>,
{
let i = get(this, addr)?;
match cast(i) {
Some(i) => {
let r = add(this, i)?;
this.push_register(r)?;
}
None => push_unit(this)?,
}
Ok(None)
}