use std::collections::HashMap;
use std::fmt::{Display, Formatter};
use std::sync::{Arc, RwLock, mpsc::Sender, Mutex};
use std::sync::mpsc::Receiver;
use std::thread::JoinHandle;
use bitvec::prelude::*;
use malachite::{Natural, Rational};
use malachite::base::num::basic::traits::{Zero, One};
use regex::{Regex, RegexBuilder};
use crate::{ExecResult, IOStreams, LogLevel};
pub use crate::errors::Utf8Error;
#[derive(Debug)]
pub enum Value {
B(BitVec<u8, Lsb0>),
N(Rational),
S(String),
A(Vec<Value>)
}
impl Default for Value {
fn default() -> Self {
Self::A(Vec::new())
}
}
impl Drop for Value {
fn drop(&mut self) {
use Value::*;
use std::mem::take;
if let A(a) = self {
let mut q: Vec<Vec<Value>> = vec![take(a)]; while let Some(mut a) = q.pop() {
for v in &mut a { if let A(aa) = v { q.push(take(aa)) }
}
}
}
}
}
impl Clone for Value {
fn clone(&self) -> Self {
use Value::*;
match self {
B(b) => B(b.clone()),
N(n) => N(n.clone()),
S(s) => S(s.clone()),
A(_) => unsafe { crate::fns::exec1(|v, _| Ok(v.clone()), self, false).unwrap_unchecked() } }
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
use Value::*;
match (self, other) {
(A(aa), A(ab)) => { if aa.len() != ab.len() {return false;}
let mut stk: Vec<(std::slice::Iter<Value>, std::slice::Iter<Value>)> = vec![(aa.iter(), ab.iter())]; while let Some((ia, ib)) = stk.last_mut() { if let (Some(va), Some(vb)) = (ia.next(), ib.next()) { match (va, vb) {
(A(aa), A(ab)) => { if aa.len() != ab.len() {return false;}
stk.push((aa.iter(), ab.iter())); },
(A(_), _) | (_, A(_)) => {return false;},
(B(ba), B(bb)) => if ba != bb {return false;},
(S(sa), S(sb)) => if sa != sb {return false;},
(N(na), N(nb)) => if na != nb {return false;},
_ => {return false;}
}
}
else { stk.pop(); }
}
true
},
(A(_), _) | (_, A(_)) => false, (B(ba), B(bb)) => ba == bb,
(S(sa), S(sb)) => sa == sb,
(N(na), N(nb)) => na == nb,
_ => false
}
}
}
impl Eq for Value {}
impl Value {
unsafe fn display_scalar(&self, k: usize, o: &Natural, nm: NumOutMode, sb: bool) -> String {
use Value::*;
match self {
B(b) => {
b.iter().by_vals().map(|b| if b {'T'} else {'F'}).collect()
},
N(n) => {
use crate::num::*;
use NumOutMode::*;
match nm {
Auto => {
nauto(n, k, o)
},
Norm => {
let (neg, ipart, fpart, rpart) = digits(n, k, o);
nnorm(neg, &ipart, &fpart, &rpart, o)
},
Sci => {
let (neg, ipart, fpart, rpart) = digits(n, k, o);
nsci(neg, &ipart, &fpart, &rpart, o)
},
Frac => {
nfrac(n, k, o)
},
}
},
S(s) => {
if sb {
String::from("[") + s + "]"
}
else {
s.to_owned()
}
},
A(_) => {
unsafe { std::hint::unreachable_unchecked() }
}
}
}
pub fn display(&self, k: usize, o: &Natural, nm: NumOutMode, sb: bool) -> String {
use Value::*;
match self {
A(a) => { let mut stk: Vec<std::slice::Iter<Value>> = vec![a.iter()];
let mut res = String::from('(');
while let Some(i) = stk.last_mut() { if let Some(val) = i.next() { match val {
A(aa) => { if res.ends_with(' ') {res.pop();}
res.push('(');
stk.push(aa.iter()); },
_ => { let s = unsafe { val.display_scalar(k, o, nm, sb) };
res += &s; res.push(' ');
}
}
}
else { if res.ends_with(' ') {
res.pop(); }
res.push(')');
stk.pop(); }
}
res
},
_ => { unsafe { self.display_scalar(k, o, nm, sb) }
}
}
}
}
impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.display(DEFAULT_PARAMS.0, &DEFAULT_PARAMS.2, DEFAULT_PARAMS.3, true))
}
}
pub fn reg_index_nice(ri: &Rational) -> String {
use malachite::base::num::conversion::traits::PowerOf2Digits;
Natural::try_from(ri).ok().and_then(|n| { let bytes: Vec<u8> = n.to_power_of_2_digits_desc(8); str::from_utf8(&bytes).ok().map(|s| String::from("[") + s + "]") })
.unwrap_or_else(|| {
crate::num::nauto(ri, DEFAULT_PARAMS.0, &DEFAULT_PARAMS.2) })
}
pub type ThreadResult = (Vec<Arc<Value>>, std::io::Result<ExecResult>);
#[derive(Default, Debug)]
pub struct Register {
pub v: Vec<Arc<Value>>,
pub th: Option<(JoinHandle<ThreadResult>, Sender<()>, Sender<()>)>
}
impl Register {
pub fn end_thread(&mut self, kill: bool) -> Option<String> {
use crate::ExecResult::*;
let mut res = None;
if let Some((jh, ktx, jtx)) = self.th.take() {
if kill {
ktx.send(()).unwrap_or_else(|_| panic!("Thread panicked, terminating!"));
}
jtx.send(()).unwrap_or_else(|_| panic!("Thread panicked, terminating!"));
match jh.join() {
Ok(mut tr) => {
match tr.1 {
Err(e) => {
res = Some(format!("IO error in thread: {}", e));
},
Ok(SoftQuit(c)) if c != 0 => {
res = Some(format!("Thread quit with code {}", c));
},
Ok(HardQuit(c)) if c != 0 => {
res = Some(format!("Thread hard-quit with code {}", c));
},
Ok(Killed) => {
res = Some("Thread was killed".into());
},
_ => {} }
self.v.append(&mut tr.0);
},
Err(e) => {
std::panic::resume_unwind(e);
}
}
}
res
}
}
impl Clone for Register {
fn clone(&self) -> Self {
Self {
v: self.v.clone(),
th: None
}
}
}
impl Drop for Register {
fn drop(&mut self) {
if let Some(s) = self.end_thread(true) {
eprintln!("{s}");
}
}
}
#[derive(Clone, Debug)]
pub struct RegStore {
pub low: [Register; 128],
pub high: HashMap<Rational, Register>
}
impl RegStore {
pub fn try_get(&self, index: &Rational) -> Option<&Register> {
usize::try_from(index).ok()
.and_then(|u| self.low.get(u))
.or_else(|| self.high.get(index))
}
pub fn try_get_mut(&mut self, index: &Rational) -> Option<&mut Register> {
usize::try_from(index).ok()
.and_then(|u| self.low.get_mut(u))
.or_else(|| self.high.get_mut(index))
}
pub fn get_mut(&mut self, index: &Rational) -> &mut Register {
usize::try_from(index).ok()
.and_then(|u| self.low.get_mut(u))
.unwrap_or_else(|| self.high.entry(index.clone()).or_default())
}
pub fn trim(&mut self) {
for reg in &mut self.low {
reg.v.shrink_to_fit();
}
self.high.retain(|_, reg| {
if reg.v.is_empty() {
reg.th.is_some()
}
else {
reg.v.shrink_to_fit();
true
}
});
self.high.shrink_to_fit();
}
pub fn clear_vals(&mut self) {
for reg in &mut self.low {
reg.v = Vec::new();
}
self.high.retain(|_, reg| {
reg.v = Vec::new();
reg.th.is_some()
});
self.high.shrink_to_fit();
}
pub fn replace_vals(&mut self, mut other: Self) {
for (reg, oreg) in self.low.iter_mut().zip(other.low.iter_mut()) {
std::mem::swap(&mut reg.v, &mut oreg.v); if reg.th.is_none() {
reg.th = oreg.th.take();
}
}
self.high.retain(|ri, reg| {
if let Some(mut oreg) = other.high.remove(ri) { std::mem::swap(&mut reg.v, &mut oreg.v);
if reg.th.is_none() {
reg.th = oreg.th.take();
}
!reg.v.is_empty() || reg.th.is_some()
}
else { reg.v = Vec::new();
reg.th.is_some()
}
});
for (ori, oreg) in other.high.drain().filter(|(_, oreg)| !oreg.v.is_empty()) { self.high.insert(ori, oreg);
}
}
pub fn end_threads(&mut self, kill: bool) -> Vec<String> {
use crate::ExecResult::*;
let mut res = Vec::new();
for (ri_nice, reg) in
self.low.iter_mut().enumerate().map(|(ri, reg)| (reg_index_nice(&Rational::from(ri)), reg))
.chain(self.high.iter_mut().map(|(ri, reg)| (reg_index_nice(ri), reg)))
{
if let Some((jh, ktx, jtx)) = reg.th.take() {
if kill {
ktx.send(()).unwrap_or_else(|_| panic!("Thread {} panicked, terminating!", ri_nice));
}
jtx.send(()).unwrap_or_else(|_| panic!("Thread {} panicked, terminating!", ri_nice));
match jh.join() {
Ok(mut tr) => {
match tr.1 {
Err(e) => {
res.push(format!("IO error in thread {}: {}", ri_nice, e));
},
Ok(SoftQuit(c)) if c != 0 => {
res.push(format!("Thread {} quit with code {}", ri_nice, c));
},
Ok(HardQuit(c)) if c != 0 => {
res.push(format!("Thread {} hard-quit with code {}", ri_nice, c));
},
Ok(Killed) => {
res.push(format!("Thread {} was killed", ri_nice));
},
_ => {} }
reg.v.append(&mut tr.0);
},
Err(e) => {
std::panic::resume_unwind(e);
}
}
}
}
res
}
}
impl Default for RegStore {
fn default() -> Self {
Self {
low: std::array::from_fn(|_| Register::default()),
high: HashMap::default()
}
}
}
impl Drop for RegStore {
fn drop(&mut self) {
for s in self.end_threads(true) {
eprintln!("{s}");
}
}
}
#[derive(Default, Debug)]
#[repr(transparent)] pub(crate) struct RegexCache(pub(crate) RwLock<HashMap<String, Regex>>);
impl RegexCache {
pub(crate) fn get(&self, s: &String) -> Result<Regex, String> {
if let Some(re) = self.0.read().unwrap().get(s) {
Ok(re.clone())
}
else {
match RegexBuilder::new(s)
.size_limit(usize::MAX)
.dfa_size_limit(usize::MAX)
.build() {
Ok(re) => {
self.0.write().unwrap().insert(s.clone(), re.clone());
Ok(re)
},
Err(e) => Err(format!("Can't compile regex: {e}"))
}
}
}
pub(crate) fn clear(&self) {
*self.0.write().unwrap() = HashMap::new();
}
}
#[derive(Clone, Debug)]
pub enum Utf8Iter<'a> {
Owned {
bytes: Vec<u8>,
pos: usize
},
Borrowed {
bytes: &'a [u8],
pos: usize
}
}
impl Default for Utf8Iter<'_> {
fn default() -> Self {
Self::Owned { bytes: Vec::new(), pos: 0 }
}
}
impl From<Vec<u8>> for Utf8Iter<'_> {
fn from(bytes: Vec<u8>) -> Self {
Self::Owned { bytes, pos: 0 }
}
}
impl<'a> From<&'a [u8]> for Utf8Iter<'a> {
fn from(bytes: &'a [u8]) -> Self {
Self::Borrowed { bytes, pos: 0 }
}
}
impl From<String> for Utf8Iter<'_> {
fn from(s: String) -> Self {
Self::Owned { bytes: s.into_bytes(), pos: 0 }
}
}
impl<'a> From<&'a str> for Utf8Iter<'a> {
fn from(s: &'a str) -> Self {
Self::Borrowed { bytes: s.as_bytes(), pos: 0 }
}
}
impl TryFrom<Utf8Iter<'_>> for String {
type Error = String;
fn try_from(mut value: Utf8Iter) -> Result<String, String> {
let mut res = String::new();
while !value.is_finished() {
res.push(value.try_next_char().map_err(|e| format!("At byte {}: {e}", {
match value {
Utf8Iter::Borrowed {pos, ..} => pos,
Utf8Iter::Owned {pos, ..} => pos
}
}))?);
}
Ok(res)
}
}
impl Iterator for Utf8Iter<'_> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
let (bytes, pos): (&[u8], &mut usize) = match self {
Self::Borrowed {bytes, pos} => (bytes, pos),
Self::Owned {bytes, pos} => (bytes, pos)
};
bytes.get(*pos).copied().inspect(|_| {*pos+=1;})
}
}
impl Utf8Iter<'_> {
pub fn try_next_char(&mut self) -> Result<char, Utf8Error> {
use Utf8Error::*;
let (bytes, pos): (&[u8], &mut usize) = match self {
Self::Borrowed {bytes, pos} => (bytes, pos),
Self::Owned {bytes, pos} => (bytes, pos)
};
if let Some(b0) = bytes.get(*pos).copied() {
let c;
match b0 {
0x00..=0x7F => {
*pos += 1;
Ok(b0 as char)
}
0x80..=0xBF => {
Err(ContAtStart(b0))
}
0xC2..=0xDF => {
if let Some(b1) = bytes.get(*pos+1).copied() {
if !(0x80..=0xBF).contains(&b1) {
return Err(NonCont2(b0, b1));
}
c = ((b0 & 0x1F) as u32) << 6
| (b1 & 0x3F) as u32;
*pos += 2;
Ok(unsafe{char::from_u32_unchecked(c)})
}
else {
Err(Missing1(b0))
}
}
0xE0..=0xEF => {
if let Some(b1) = bytes.get(*pos+1).copied() {
if let Some(b2) = bytes.get(*pos+2).copied() {
if !(0x80..=0xBF).contains(&b1) {
return Err(NonCont3(b0, b1, b2));
}
if !(0x80..=0xBF).contains(&b2) {
return Err(NonCont3(b0, b1, b2));
}
c = ((b0 & 0x0F) as u32) << 12
| ((b1 & 0x3F) as u32) << 6
| (b2 & 0x3F) as u32;
if c < 0x0800 {
return Err(Overlong3(c, b0, b1, b2));
}
if (0xD800u32..=0xDFFFu32).contains(&c) {
return Err(Utf16Surr(c, b0, b1, b2));
}
*pos += 3;
Ok(unsafe{char::from_u32_unchecked(c)})
}
else {
Err(Missing2(b0, b1))
}
}
else {
Err(Missing1(b0))
}
}
0xF0..=0xF4 => {
if let Some(b1) = bytes.get(*pos+1).copied() {
if let Some(b2) = bytes.get(*pos+2).copied() {
if let Some(b3) = bytes.get(*pos+3).copied() {
if !(0x80..=0xBF).contains(&b1) {
return Err(NonCont4(b0, b1, b2, b3));
}
if !(0x80..=0xBF).contains(&b2) {
return Err(NonCont4(b0, b1, b2, b3));
}
if !(0x80..=0xBF).contains(&b3) {
return Err(NonCont4(b0, b1, b2, b3));
}
c = ((b0 & 0x07) as u32) << 18
| ((b1 & 0x3F) as u32) << 12
| ((b2 & 0x3F) as u32) << 6
| (b3 & 0x3F) as u32;
if c < 0x10000 {
return Err(Overlong4(c, b0, b1, b2, b3));
}
if c > 0x10FFFF {
return Err(TooLarge(c, b0, b1, b2, b3));
}
*pos += 4;
Ok(unsafe{char::from_u32_unchecked(c)})
}
else {
Err(Missing3(b0, b1, b2))
}
}
else {
Err(Missing2(b0, b1))
}
}
else {
Err(Missing1(b0))
}
}
0xC0 | 0xC1 | 0xF5..=0xFF => Err(Impossible(b0)),
}
}
else {
Err(OutOfBounds(*pos, bytes.len()))
}
}
pub fn try_macros(top: &Value, sec: Option<&Value>) -> Result<(Vec<(Self, Natural)>, bool), String> {
use Value::*;
use crate::errors::TypeLabel;
use crate::conv::Promote2;
if let Some(sec) = sec { if let Ok(res) = Self::try_macros(top, None) { return Ok(res); } match (sec, top) {
(A(_), A(_)) | (A(_), _) | (_, A(_)) => { let mut stk = vec![Promote2::try_from((sec, top)).map_err(|e| e.to_string())?];
let mut res = Vec::new();
while let Some(it) = stk.last_mut() { if let Some((va, vb)) = it.next() { match (va, vb) {
(A(_), A(_)) | (A(_), _) | (_, A(_)) => { let nit = Promote2::try_from((va, vb)).map_err(|e| e.to_string())?;
stk.push(nit); },
(S(sa), B(bb)) => {
let nb = Natural::from(bb.count_ones());
if nb != Natural::ZERO {
res.push((sa.to_owned().into(), nb));
}
},
(S(sa), N(rb)) => {
if let Ok(nb) = Natural::try_from(rb) {
if nb != Natural::ZERO {
res.push((sa.to_owned().into(), nb));
}
}
else {
return Err(format!("Can't possibly repeat a macro {} times", crate::num::nauto(rb, DEFAULT_PARAMS.0, &DEFAULT_PARAMS.2)));
}
},
(_, S(_)) => { return Err("Unexpected string in top array".into());
},
_ => {
return Err(format!("Expected only pairs of macro strings and non-strings, found {} and {} in arrays", TypeLabel::from(sec), TypeLabel::from(top)));
}
}
}
else { stk.pop(); }
}
Ok((res, false))
},
(S(sa), B(bb)) => {
let nb = Natural::from(bb.count_ones());
if nb != Natural::ZERO {
Ok((vec![(sa.to_owned().into(), nb)], false))
}
else {Ok((vec![], false))}
},
(S(sa), N(rb)) => {
if let Ok(nb) = Natural::try_from(rb) {
if nb != Natural::ZERO {
Ok((vec![(sa.to_owned().into(), nb)], false))
}
else {Ok((vec![], false))}
}
else {
Err(format!("Can't possibly repeat a macro {} times", crate::num::nauto(rb, DEFAULT_PARAMS.0, &DEFAULT_PARAMS.2)))
}
},
(_, S(_)) => { unsafe { std::hint::unreachable_unchecked() }
},
_ => {
Err(format!("Expected a macro string and a non-string, {} and {} given", TypeLabel::from(sec), TypeLabel::from(top)))
}
}
}
else { match top {
A(a) => { let mut stk: Vec<std::slice::Iter<Value>> = vec![a.iter()];
let mut res = Vec::new();
while let Some(i) = stk.last_mut() { if let Some(val) = i.next() { match val {
A(na) => { stk.push(na.iter()); },
S(s) => { res.push((s.to_owned().into(), Natural::ONE));
},
_ => {
return Err(format!("Expected only macro strings, found {} in array", TypeLabel::from(val)));
}
}
}
else { stk.pop(); }
}
Ok((res, true))
},
S(s) => {
Ok((vec![(s.to_owned().into(), Natural::ONE)], true))
},
_ => {
Err(format!("Expected a macro string, {} given", TypeLabel::from(top)))
}
}
}
}
pub(crate) fn is_finished(&self) -> bool {
let (bytes, pos): (&[u8], &usize) = match self {
Self::Borrowed {bytes, pos} => (bytes, pos),
Self::Owned {bytes, pos} => (bytes, pos)
};
bytes.len() <= *pos
}
pub(crate) const fn back(&mut self) {
let pos = match self {
Self::Borrowed {pos, ..} => pos,
Self::Owned {pos, ..} => pos
};
*pos -= 1;
}
pub(crate) const fn rewind(&mut self) {
let pos = match self {
Self::Borrowed {pos, ..} => pos,
Self::Owned {pos, ..} => pos
};
*pos = 0;
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)] pub enum NumOutMode { #[default] Auto=0, Norm=1, Sci=2, Frac=3 }
pub type Params = (usize, Natural, Natural, NumOutMode);
pub const DEFAULT_PARAMS: Params = {
(0, Natural::const_from(10), Natural::const_from(10), NumOutMode::Auto)
};
#[derive(Clone, Debug)]
#[repr(transparent)] pub struct ParamStk(Vec<Params>);
impl ParamStk {
pub fn create(&mut self) {
self.0.push(DEFAULT_PARAMS)
}
pub fn destroy(&mut self) {
self.0.pop();
if self.0.is_empty() {self.create();}
}
pub fn clear(&mut self) {
*self = Self::default();
}
pub const fn inner(&self) -> &Vec<Params> {
&self.0
}
pub const unsafe fn inner_mut(&mut self) -> &mut Vec<Params> {
&mut self.0
}
pub fn try_set_k(&mut self, r: &Rational) -> Result<(), &'static str> {
if let Ok(u) = r.try_into() {
unsafe { self.0.last_mut().unwrap_unchecked().0 = u; }
Ok(())
}
else {Err(const_format::concatcp!("Output precision must be a natural number <={}", usize::MAX))}
}
pub fn try_set_i(&mut self, r: &Rational) -> Result<(), &'static str> {
if let Ok(n) = r.try_into() && n>=2u8 {
unsafe { self.0.last_mut().unwrap_unchecked().1 = n; }
Ok(())
}
else {Err("Input base must be a natural number >=2")}
}
pub fn try_set_o(&mut self, r: &Rational) -> Result<(), &'static str> {
if let Ok(n) = r.try_into() && n>=2u8 {
unsafe { self.0.last_mut().unwrap_unchecked().2 = n; }
Ok(())
}
else {Err("Output base must be a natural number >=2")}
}
pub fn try_set_m(&mut self, r: &Rational) -> Result<(), &'static str> {
if let Ok(u) = u8::try_from(r) && u<=3 {
unsafe { self.0.last_mut().unwrap_unchecked().3 = std::mem::transmute::<u8, NumOutMode>(u); }
Ok(())
}
else {Err("Output mode must be 0|1|2|3")}
}
pub fn set_k(&mut self, u: usize) {
unsafe { self.0.last_mut().unwrap_unchecked().0 = u; }
}
pub fn set_m(&mut self, m: NumOutMode) {
unsafe { self.0.last_mut().unwrap_unchecked().3 = m; }
}
pub fn get_k(&self) -> usize {
unsafe { self.0.last().unwrap_unchecked().0 }
}
pub fn get_i(&self) -> &Natural {
unsafe { &self.0.last().unwrap_unchecked().1 }
}
pub fn get_o(&self) -> &Natural {
unsafe { &self.0.last().unwrap_unchecked().2 }
}
pub fn get_m(&self) -> NumOutMode {
unsafe { self.0.last().unwrap_unchecked().3 }
}
}
impl Default for ParamStk {
fn default() -> Self {
let mut p = Self(Vec::new());
p.create();
p
}
}
impl TryFrom<Vec<Params>> for ParamStk {
type Error = ();
fn try_from(value: Vec<Params>) -> Result<Self, Self::Error> {
(!value.is_empty()).then_some(Self(value)).ok_or(())
}
}
impl From<ParamStk> for Vec<Params> {
fn from(value: ParamStk) -> Self {
value.0
}
}
#[derive(Default, Debug, Clone)]
pub struct State {
pub mstk: Vec<Arc<Value>>,
pub regs: RegStore,
pub params: ParamStk
}
impl Display for State {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", String::from_utf8_lossy(&crate::STATE_FILE_HEADER))?;
for (lri, lreg) in self.regs.low.iter().enumerate().filter(|(_, lreg)| !lreg.v.is_empty()) {
for val in &lreg.v {
writeln!(f, "{val}")?;
}
writeln!(f, "{lri}:ff")?;
}
for (hri, hreg) in self.regs.high.iter().filter(|(_, hreg)| !hreg.v.is_empty()) {
for val in &hreg.v {
writeln!(f, "{val}")?;
}
writeln!(f, "{}:ff", crate::num::nauto(hri, DEFAULT_PARAMS.0, &DEFAULT_PARAMS.2))?;
}
for val in &self.mstk {
writeln!(f, "{val}")?;
}
write!(f, "{}", {
let v: Vec<String> = self.params.inner().iter().map(|par| {
let mut ps = String::new();
if par.0 != DEFAULT_PARAMS.0 {ps += &format!("{}k", par.0);}
if par.2 != DEFAULT_PARAMS.2 {ps += &format!("{}o", par.2);}
if par.3 != DEFAULT_PARAMS.3 {ps += &format!("{}m", par.3 as u8);}
if par.1 != DEFAULT_PARAMS.1 {ps += &format!("{}i", par.1);} ps
}).collect();
v.join("{")
})?;
Ok(())
}
}
impl State {
pub fn trim(&mut self) {
self.mstk.shrink_to_fit();
self.regs.trim();
self.params.0.shrink_to_fit();
}
pub fn clear_vals(&mut self) {
self.mstk = Vec::new();
self.regs.clear_vals();
self.params = ParamStk::default();
}
pub fn replace_vals(&mut self, other: Self) {
self.mstk = other.mstk;
self.regs.replace_vals(other.regs);
self.params = other.params;
}
pub fn dedup(&mut self) {
let mut regs: Vec<&mut [Arc<Value>]> = vec![&mut self.mstk]; regs.extend(self.regs.low.iter_mut().filter_map(|reg| (!reg.v.is_empty()).then(|| &mut reg.v[..]))); regs.extend(self.regs.high.values_mut().filter_map(|reg| (!reg.v.is_empty()).then(|| &mut reg.v[..]))); while let Some(reg) = regs.pop() {
for i in 1..=reg.len() { let (done, rest) = unsafe { reg.split_at_mut_unchecked(i) }; let sample = unsafe { done.last().unwrap_unchecked() }; for other in rest.iter_mut().chain(regs.iter_mut().flat_map(|reg| reg.iter_mut())) { if !Arc::ptr_eq(sample, other) && (**sample == **other) { *other = Arc::clone(sample) } }
}
}
}
#[expect(clippy::missing_safety_doc)]
pub unsafe fn interpreter(
&mut self,
start: Utf8Iter,
io: Arc<Mutex<IOStreams>>,
ll: LogLevel,
kill: Option<&Receiver<()>>,
restrict: bool
) -> std::io::Result<ExecResult>
{
unsafe { crate::interpreter(self, start, io, ll, kill, restrict) }
}
#[expect(clippy::missing_safety_doc)]
pub unsafe fn interpreter_no_io(
&mut self,
start: Utf8Iter,
kill: Option<&Receiver<()>>,
restrict: bool
) -> std::io::Result<ExecResult>
{
unsafe { crate::interpreter_no_io(self, start, kill, restrict) }
}
pub fn interpreter_no_os(
&mut self,
start: Utf8Iter,
io: Arc<Mutex<IOStreams>>,
ll: LogLevel,
kill: Option<&Receiver<()>>,
) -> std::io::Result<ExecResult>
{
crate::interpreter_no_os(self, start, io, ll, kill)
}
pub fn interpreter_simple(
&mut self,
start: Utf8Iter,
kill: Option<&Receiver<()>>
) -> std::io::Result<ExecResult>
{
crate::interpreter_simple(self, start, kill)
}
}