use crate::runtime::error::state_error;
use crate::runtime::range::range_len;
use crate::runtime::utilities::{next_ref, push_number, push_unit};
use garnish_lang_traits::helpers::iterate_concatenation_mut;
use garnish_lang_traits::Instruction;
use garnish_lang_traits::{GarnishDataType, GarnishContext, GarnishData, RuntimeError, TypeConstants};
pub(crate) fn access_left_internal<Data: GarnishData, Context: GarnishContext<Data>>(
this: &mut Data,
context: Option<&mut Context>,
) -> Result<Option<Data::Size>, RuntimeError<Data::Error>> {
let r = next_ref(this)?;
match this.get_data_type(r.clone())? {
GarnishDataType::Pair => {
let (left, _) = this.get_pair(r)?;
this.push_register(left)?;
}
GarnishDataType::Range => {
let (start, _) = this.get_range(r)?;
match this.get_data_type(start.clone())? {
GarnishDataType::Number => {
this.push_register(start)?;
}
_ => push_unit(this)?,
}
}
GarnishDataType::Slice => {
let (value, _) = this.get_slice(r)?;
this.push_register(value)?;
}
GarnishDataType::Concatenation => {
let (left, _) = this.get_concatenation(r)?;
this.push_register(left)?;
}
t => match context {
None => push_unit(this)?,
Some(c) => {
if !c.defer_op(
this,
Instruction::AccessLeftInternal,
(t, r),
(GarnishDataType::Unit, Data::Size::zero()),
)? {
push_unit(this)?
}
}
},
}
Ok(None)
}
pub(crate) fn access_right_internal<Data: GarnishData, Context: GarnishContext<Data>>(
this: &mut Data,
context: Option<&mut Context>,
) -> Result<Option<Data::Size>, RuntimeError<Data::Error>> {
let r = next_ref(this)?;
match this.get_data_type(r.clone())? {
GarnishDataType::Pair => {
let (_, right) = this.get_pair(r)?;
this.push_register(right)?;
}
GarnishDataType::Range => {
let (_, end) = this.get_range(r)?;
match this.get_data_type(end.clone())? {
GarnishDataType::Number => {
this.push_register(end)?;
}
_ => push_unit(this)?,
}
}
GarnishDataType::Slice => {
let (_, range) = this.get_slice(r)?;
this.push_register(range)?;
}
GarnishDataType::Concatenation => {
let (_, right) = this.get_concatenation(r)?;
this.push_register(right)?;
}
t => match context {
None => push_unit(this)?,
Some(c) => {
if !c.defer_op(
this,
Instruction::AccessRightInternal,
(t, r),
(GarnishDataType::Unit, Data::Size::zero()),
)? {
push_unit(this)?
}
}
},
}
Ok(None)
}
pub(crate) fn access_length_internal<Data: GarnishData, Context: GarnishContext<Data>>(
this: &mut Data,
context: Option<&mut Context>,
) -> Result<Option<Data::Size>, RuntimeError<Data::Error>> {
let r = next_ref(this)?;
match this.get_data_type(r.clone())? {
GarnishDataType::List => {
let len = Data::size_to_number(this.get_list_len(r)?);
push_number(this, len)?;
}
GarnishDataType::CharList => {
let len = Data::size_to_number(this.get_char_list_len(r)?);
push_number(this, len)?;
}
GarnishDataType::ByteList => {
let len = Data::size_to_number(this.get_byte_list_len(r)?);
push_number(this, len)?;
}
GarnishDataType::Range => {
let (start, end) = this.get_range(r)?;
match (this.get_data_type(end.clone())?, this.get_data_type(start.clone())?) {
(GarnishDataType::Number, GarnishDataType::Number) => {
let start_int = this.get_number(start)?;
let end_int = this.get_number(end)?;
let result = range_len::<Data>(start_int, end_int)?;
let addr = this.add_number(result)?;
this.push_register(addr)?;
}
_ => push_unit(this)?,
}
}
GarnishDataType::Slice => {
let (_, range_addr) = this.get_slice(r)?;
let (start, end) = this.get_range(range_addr)?;
match (this.get_data_type(start.clone())?, this.get_data_type(end.clone())?) {
(GarnishDataType::Number, GarnishDataType::Number) => {
let start = this.get_number(start)?;
let end = this.get_number(end)?;
let addr = this.add_number(range_len::<Data>(start, end)?)?;
this.push_register(addr)?;
}
(s, e) => state_error(format!("Non integer values used for range {:?} {:?}", s, e))?,
}
}
GarnishDataType::Concatenation => {
let count = concatenation_len(this, r)?;
let addr = this.add_number(Data::size_to_number(count))?;
this.push_register(addr)?;
}
t => match context {
None => push_unit(this)?,
Some(c) => {
if !c.defer_op(
this,
Instruction::AccessLengthInternal,
(t, r),
(GarnishDataType::Unit, Data::Size::zero()),
)? {
push_unit(this)?
}
}
},
}
Ok(None)
}
pub(crate) fn concatenation_len<Data: GarnishData>(this: &mut Data, addr: Data::Size) -> Result<Data::Size, RuntimeError<Data::Error>> {
Ok(iterate_concatenation_mut(this, addr, |_, _, _| Ok(None))?.1)
}