pub mod structs;
use structs::*;
pub(crate) mod fns;
pub(crate) mod cmds;
pub(crate) mod errors;
pub(crate) mod conv;
pub(crate) mod num;
#[cfg(not(feature = "no_os"))]
mod os;
use std::io::{Write, BufRead, ErrorKind};
use std::ops::Neg;
use std::panic::catch_unwind;
use std::ptr::NonNull;
use std::str::FromStr;
use std::sync::{Arc, Mutex, mpsc::{Receiver, TryRecvError, RecvTimeoutError}};
use linefeed::{DefaultTerminal, Interface};
use bitvec::prelude::*;
use malachite::{Natural, Integer, Rational};
use malachite::base::num::arithmetic::traits::{NegAssign, Pow};
use malachite::base::num::basic::traits::{NegativeOne, Zero, One};
use malachite::base::num::conversion::traits::{ConvertibleFrom, PowerOf2DigitIterable, RoundingFrom, WrappingFrom};
use malachite::base::num::random::RandomPrimitiveInts;
use malachite::base::rational_sequences::RationalSequence;
use malachite::base::rounding_modes::RoundingMode;
use crate::errors::TypeLabel;
pub const STATE_FILE_HEADER: [u8;20] = *b"# ADC state file v1\n";
struct LineEditor(Interface<DefaultTerminal>);
pub trait ReadLine {
fn read_line(&mut self) -> std::io::Result<String>;
fn clear_history(&mut self);
}
impl<T: BufRead> ReadLine for T {
fn read_line(&mut self) -> std::io::Result<String> {
let mut buf = String::new();
self.read_line(&mut buf)?;
Ok(buf)
}
fn clear_history(&mut self) {
}
}
impl ReadLine for LineEditor {
fn read_line(&mut self) -> std::io::Result<String> {
use linefeed::{ReadResult, Signal};
match self.0.read_line() {
Ok(ReadResult::Input(s)) => {
self.0.add_history_unique(s.clone());
Ok(s)
},
Ok(ReadResult::Eof) => {Err(ErrorKind::UnexpectedEof.into())},
Ok(ReadResult::Signal(sig)) => {
self.0.cancel_read_line()?;
match sig {
Signal::Break | Signal::Interrupt | Signal::Quit => {Err(ErrorKind::Interrupted.into())},
Signal::Continue => {Err(std::io::Error::other("Unhandled SIGCONT"))},
Signal::Suspend => {Err(std::io::Error::other("Unhandled SIGTSTP"))},
Signal::Resize => {Err(std::io::Error::other("Unhandled window resize"))},
}
},
Err(e) => {Err(e)}
}
}
fn clear_history(&mut self) {
self.0.clear_history();
}
}
fn input_stream() -> Box<dyn ReadLine + Send> {
use linefeed::Signal::*;
match Interface::new("") { Ok(iface) => {
iface.set_report_signal(Break, true);
iface.set_report_signal(Interrupt, true);
iface.set_report_signal(Quit, true);
Box::new(LineEditor(iface))
},
Err(_) => { Box::new(std::io::BufReader::new(std::io::stdin()))
}
}
}
pub struct IOStreams (
pub Box<dyn ReadLine + Send>,
pub Box<dyn Write + Send>,
pub Box<dyn Write + Send>
);
impl IOStreams {
pub fn empty() -> Self {
Self (
Box::new(std::io::empty()),
Box::new(std::io::empty()),
Box::new(std::io::empty())
)
}
pub fn process() -> Self {
Self (
input_stream(),
Box::new(std::io::stdout()),
Box::new(std::io::stderr())
)
}
}
lazy_static::lazy_static! {
pub(crate) static ref RE_CACHE: RegexCache = RegexCache::default();
}
fn rng_preset(bytes: [u8; 32]) -> RandomPrimitiveInts<u64> {
malachite::base::num::random::random_primitive_ints(malachite::base::random::Seed::from_bytes(bytes))
}
fn rng_os() -> RandomPrimitiveInts<u64> {
let mut bytes = [0u8; 32];
getrandom::fill(&mut bytes).unwrap();
rng_preset(bytes)
}
#[derive(Default, Debug, Clone, Copy, Hash)]
enum Command {
Fn1(fns::Mon),
Fn2(fns::Dya),
Fn3(fns::Tri),
Cmd(cmds::Cmd),
Exec,
ExecR,
Lit,
Space,
#[default] Wrong
}
const CMDS: [Command; 128] = {
use Command::*;
use fns::*;
use cmds::*;
[
Space, Wrong, Wrong, Wrong, Wrong, Wrong, Wrong, Wrong, Wrong, Space, Space, Space, Space, Space, Wrong, Wrong,
Wrong, Wrong, Wrong, Wrong, Wrong, Wrong, Wrong, Wrong, Wrong, Wrong, Wrong, Wrong, Wrong, Wrong, Wrong, Wrong,
Space, Fn1(neg), Exec, Space, Wrong, Fn2(modu), Wrong, Lit, Exec, Exec, Fn2(mul), Fn2(add), Wrong, Fn2(sub), Lit, Fn2(div),
Lit, Lit, Lit, Lit, Lit, Lit, Lit, Lit, Lit, Lit, Exec, Wrong, Fn2(lt), Fn2(eq), Fn2(gt), Exec,
Lit, Wrong, Wrong, Cmd(cln), Exec, Wrong, Lit, Fn2(logb), Wrong, Cmd(gi), ExecR, Cmd(gk), ExecR, Cmd(gm), Exec, Cmd(go),
Exec, Exec, Exec, ExecR, Lit, Wrong, Fn2(root), Exec, ExecR, Wrong, ExecR, Lit, Wrong, Wrong, Fn2(pow), Exec,
Exec, Exec, Wrong, Cmd(cls), Exec, Wrong, Exec, Fn1(log), Wrong, Cmd(si), ExecR, Cmd(sk), ExecR, Cmd(sm), Fn1(fac), Cmd(so),
Exec, Exec, Cmd(rev), ExecR, Fn2(trig), Wrong, Fn1(sqrt), Exec, Exec, Wrong, Fn1(disc), Cmd(cbo), Fn3(bar), Cmd(cbc), Fn2(euc), Wrong
]
};
fn byte_cmd(b: u8) -> Command {
CMDS.get(b as usize).copied().unwrap_or_default()
}
fn string_or_bytes(v: &[u8]) -> String {
str::from_utf8(v).map(|s| s.to_owned()).unwrap_or_else(|_| {
let mut res = String::from("(not UTF-8: [");
for b in v {
res += &format!("\\{b:02X}");
}
res += "])";
res
})
}
const fn upper_hex_to_nibble(b: u8) -> Option<u8> {
match b {
b'0'..=b'9' => Some(unsafe{b.unchecked_sub(0x30)}), b'A'..=b'F' => Some(unsafe{b.unchecked_sub(0x37)}),
_ => None
}
}
const fn mixed_ascii_to_digit(b: u8) -> Option<u8> {
match b {
b'0'..=b'9' => Some(unsafe{b.unchecked_sub(0x30)}), b'A'..=b'Z' => Some(unsafe{b.unchecked_sub(0x37)}),
b'a'..=b'z' => Some(unsafe{b.unchecked_sub(0x57)}),
_ => None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LogLevel {
Normal,
Debug,
Quiet
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[must_use] pub enum ExecResult {
Finished,
SoftQuit(u8),
HardQuit(u8),
Killed
}
pub fn interpreter_simple(
st: &mut State,
start: Utf8Iter,
kill: Option<&Receiver<()>>
) -> std::io::Result<ExecResult>
{
let no_io = Arc::new(Mutex::new(IOStreams::empty()));
interpreter_no_os(st, start, no_io, LogLevel::Quiet, kill)
}
pub fn interpreter_no_os(
st: &mut State,
start: Utf8Iter,
io: Arc<Mutex<IOStreams>>,
ll: LogLevel,
kill: Option<&Receiver<()>>,
) -> std::io::Result<ExecResult>
{
unsafe { interpreter(st, start, io, ll, kill, true) }
}
pub unsafe fn interpreter_no_io(
st: &mut State,
start: Utf8Iter,
kill: Option<&Receiver<()>>,
restrict: bool
) -> std::io::Result<ExecResult>
{
let no_io = Arc::new(Mutex::new(IOStreams::empty()));
unsafe { interpreter(st, start, no_io, LogLevel::Quiet, kill, restrict) }
}
pub unsafe fn interpreter(
st: &mut State,
start: Utf8Iter,
io: Arc<Mutex<IOStreams>>,
mut ll: LogLevel,
kill: Option<&Receiver<()>>,
#[cfg_attr(feature = "no_os", expect(unused_variables))]
mut restrict: bool
) -> std::io::Result<ExecResult>
{
use ExecResult::*;
let th_name = if kill.is_some() { std::thread::current().name().unwrap().to_owned()
}
else {
String::new()
};
let mut pbuf: Option<String> = None;
let mut elatch: Option<(Natural, char, String)> = None;
macro_rules! synerr {
($c:expr, $s:expr) => {
if ll != LogLevel::Quiet {
let err = &mut io.lock().unwrap().2;
writeln!(err, "! {th_name}{}: {}", $c, $s)?;
err.flush()?;
}
elatch = Some((Natural::ZERO, $c, $s.into()));
};
($c:expr, $f:literal, $($s:expr),*) => {
let s = format!($f, $($s),*);
if ll != LogLevel::Quiet {
let err = &mut io.lock().unwrap().2;
writeln!(err, "! {th_name}{}: {}", $c, s)?;
err.flush()?;
}
elatch = Some((Natural::ZERO, $c, s));
};
}
macro_rules! valerr {
($c:expr, $s:expr) => {
if ll != LogLevel::Quiet {
let err = &mut io.lock().unwrap().2;
writeln!(err, "? {th_name}{}: {}", $c, $s)?;
err.flush()?;
}
elatch = Some((Natural::ZERO, $c, $s.into()));
};
($c:expr, $f:literal, $($s:expr),*) => {
let s = format!($f, $($s),*);
if ll != LogLevel::Quiet {
let err = &mut io.lock().unwrap().2;
writeln!(err, "? {th_name}{}: {}", $c, s)?;
err.flush()?;
}
elatch = Some((Natural::ZERO, $c, s));
};
}
macro_rules! debug {
($s:expr) => {
if ll == LogLevel::Debug {
let err = &mut io.lock().unwrap().2;
writeln!(err, "\tDEBUG: {th_name}{}", $s)?;
err.flush()?;
}
};
($f:literal, $($s:expr),*) => {
if ll == LogLevel::Debug {
let err = &mut io.lock().unwrap().2;
writeln!(err, "\tDEBUG: {th_name}{}", format!($f, $($s),*))?;
err.flush()?;
}
};
}
let mut rptr: Option<Rational> = None;
let mut rng: Option<RandomPrimitiveInts<u64>> = None;
let mut call: Vec<(Utf8Iter, Natural)> = vec![(start, Natural::const_from(1))];
'mac: while let Some((mac, count)) = call.last_mut() { let mut alt = false;
let mut abuf: Vec<Value> = Vec::new(); let mut dest: Vec<NonNull<Vec<Value>>> = Vec::new(); macro_rules! push {
($v:expr) => {
if let Some(p) = dest.last_mut() {
unsafe {
p.as_mut().push($v); }
}
else {
st.mstk.push(Arc::new($v));
}
};
}
macro_rules! append {
($v:expr) => {
if let Some(p) = dest.last_mut() {
unsafe {
p.as_mut().append(&mut $v); }
}
else {
for val in $v {
st.mstk.push(Arc::new(val));
}
}
};
}
'cmd: while let Some(b) = mac.next() { if let Some(rx) = kill { match rx.try_recv() {
Ok(()) => { #[expect(unused_assignments)]
for s in st.regs.end_threads(true) { valerr!('j', s);
}
return Ok(Killed);
},
Err(TryRecvError::Empty) => { },
Err(TryRecvError::Disconnected) => { unreachable!()
}
}
}
if let Some(e) = &mut elatch { e.0 += Natural::ONE;
}
use Command::*;
match byte_cmd(b) {
Fn1(mon) => {
if let Some(va) = st.mstk.pop() {
debug!("Monadic {}{} with {}", if alt {"alt-"} else {""}, b as char, TypeLabel::from(&*va));
match catch_unwind(|| fns::exec1(mon, &va, alt)) {
Ok(Ok(vz)) => {
push!(vz);
},
Ok(Err(e)) => {
st.mstk.push(va);
valerr!(b as char, e.to_string());
},
Err(cause) => {
st.mstk.push(va);
valerr!(b as char, "Caught function panic: {:?}", cause);
}
}
}
else {
synerr!(b as char, "Expected 1 argument, 0 given");
}
},
Fn2(dya) => {
if let Some(vb) = st.mstk.pop() {
if let Some(va) = st.mstk.pop() {
debug!("Dyadic {}{} with ({}, {})", if alt {"alt-"} else {""}, b as char, TypeLabel::from(&*va), TypeLabel::from(&*vb));
match catch_unwind(|| fns::exec2(dya, &va, &vb, alt)) {
Ok(Ok(vz)) => {
push!(vz);
},
Ok(Err(e)) => {
st.mstk.push(va);
st.mstk.push(vb);
valerr!(b as char, e.to_string());
},
Err(cause) => {
st.mstk.push(va);
st.mstk.push(vb);
valerr!(b as char, "Caught function panic: {:?}", cause);
}
}
}
else {
st.mstk.push(vb);
synerr!(b as char, "Expected 2 arguments, 1 given");
}
}
else {
synerr!(b as char, "Expected 2 arguments, 0 given");
}
},
Fn3(tri) => {
if let Some(vc) = st.mstk.pop() {
if let Some(vb) = st.mstk.pop() {
if let Some(va) = st.mstk.pop() {
debug!("Triadic {}{} with ({}, {}, {})", if alt {"alt-"} else {""}, b as char, TypeLabel::from(&*va), TypeLabel::from(&*vb), TypeLabel::from(&*vc));
match catch_unwind(|| fns::exec3(tri, &va, &vb, &vc, alt)) {
Ok(Ok(vz)) => {
push!(vz);
},
Ok(Err(e)) => {
st.mstk.push(va);
st.mstk.push(vb);
st.mstk.push(vc);
valerr!(b as char, e.to_string());
},
Err(cause) => {
st.mstk.push(va);
st.mstk.push(vb);
st.mstk.push(vc);
valerr!(b as char, "Caught function panic: {:?}", cause);
}
}
}
else {
st.mstk.push(vb);
st.mstk.push(vc);
synerr!(b as char, "Expected 3 arguments, 2 given");
}
}
else {
st.mstk.push(vc);
synerr!(b as char, "Expected 3 arguments, 1 given");
}
}
else {
synerr!(b as char, "Expected 3 arguments, 0 given");
}
},
Cmd(cmd) => {
debug!("Impure command {}", b as char);
match cmd(st) {
Ok(mut v) => {
append!(v);
}
Err(e) => {
if let Some(se) = e.strip_suffix('!') {
synerr!(b as char, se);
}
else {
valerr!(b as char, e);
}
}
}
},
Exec => {
debug!("Command {}{}", if alt {"alt-"} else {""}, b as char);
match b {
b'`' => { alt = true;
continue 'cmd; },
b':' if !alt => { if let Some(va) = st.mstk.pop() {
if let Value::N(r) = &*va {
rptr = Some(r.clone());
}
else {
let ta = TypeLabel::from(&*va);
st.mstk.push(va);
synerr!(':', "Expected a number, {} given", ta);
}
}
else {
synerr!(':', "Expected 1 argument, 0 given");
}
},
b':' if alt => { push!(
if let Some(ri) = rptr.take() {
Value::N(ri)
}
else {
Value::A(vec![])
}
);
},
b'd' => {
if let Some(v) = st.mstk.last() {
if let Some(p) = dest.last_mut() { unsafe {
p.as_mut().push((**v).clone()); }
}
else {
st.mstk.push(Arc::clone(v));
}
}
else {
synerr!('d', "Stack is empty");
}
},
b'D' => {
if let Some(va) = st.mstk.pop() {
if let Value::N(r) = &*va {
match usize::try_from(r) {
Ok(0) => {}, Ok(u) => {
if let Some(from) = st.mstk.len().checked_sub(u) {
if let Some(p) = dest.last_mut() { for v in &st.mstk[from..] {
unsafe {
p.as_mut().push((**v).clone()); }
}
}
else {
st.mstk.extend_from_within(from..);
}
}
else {
st.mstk.push(va);
valerr!('D', "Can't duplicate {} values, stack depth is {}", u, st.mstk.len() - 1);
}
}
Err(_) => {
let vs = va.to_string();
st.mstk.push(va);
valerr!('D', "Can't possibly duplicate {} values", vs);
}
}
}
else {
let ta = TypeLabel::from(&*va);
st.mstk.push(va);
synerr!('D', "Expected a number, {} given", ta);
}
}
else {
synerr!('D', "Expected 1 argument, 0 given");
}
},
b'R' => { if let Some(va) = st.mstk.pop() {
if let Value::N(r) = &*va {
match usize::try_from(r) {
Ok(0) => {}, Ok(u) => {
if let Some(from) = st.mstk.len().checked_sub(u) {
if alt {st.mstk[from..].rotate_left(1);}
else {st.mstk[from..].rotate_right(1);}
}
else {
st.mstk.push(va);
valerr!('R', "Can't rotate {} values, stack depth is {}", u, st.mstk.len() - 1);
}
}
Err(_) => {
let vs = va.to_string();
st.mstk.push(va);
valerr!('R', "Can't possibly rotate {} values", vs);
}
}
}
else {
let ta = TypeLabel::from(&*va);
st.mstk.push(va);
synerr!('R', "Expected a number, {} given", ta);
}
}
else {
synerr!('R', "Expected 1 argument, 0 given");
}
},
b'?' => { let res = {
let inp = &mut io.lock().unwrap().0;
inp.read_line()
};
match res {
Ok(s) => {
push!(Value::S(s));
},
Err(e) => {
match e.kind() {
ErrorKind::Interrupted => {
valerr!('?', "Interrupted");
},
ErrorKind::UnexpectedEof => {
push!(Value::S(String::new()));
},
_ => {
return Err(e);
}
}
}
}
},
b'p' => { if let Some(va) = st.mstk.pop() {
let vs = va.display(st.params.get_k(), st.params.get_o(), st.params.get_m(), alt);
if let Some(s) = &mut pbuf {
s.push_str(&vs);
s.push('\n');
}
else {
let out = &mut io.lock().unwrap().1;
writeln!(out, "{}", vs)?;
out.flush()?;
}
}
else {
synerr!('p', "Expected 1 argument, 0 given");
}
},
b'P' => { if let Some(va) = st.mstk.pop() {
let vs = va.display(st.params.get_k(), st.params.get_o(), st.params.get_m(), alt);
if let Some(s) = &mut pbuf {
s.push_str(&vs);
}
else {
let out = &mut io.lock().unwrap().1;
write!(out, "{}", vs)?;
out.flush()?;
}
}
else {
synerr!('P', "Expected 1 argument, 0 given");
}
},
b'"' => { if let Some(s) = pbuf.take() { push!(Value::S(s));
}
else { pbuf = Some(String::new());
}
},
b'(' => { let nn = if let Some(p) = dest.last_mut() { unsafe { p.as_mut().push(Value::A(Vec::new())); let Value::A(new) = &mut p.as_mut().last_mut().unwrap_unchecked() else { std::hint::unreachable_unchecked() }; NonNull::from(new)
}}
else {
NonNull::from(&mut abuf) };
dest.push(nn);
},
b')' => { if dest.pop().is_some() {
if dest.is_empty() { st.mstk.push(Arc::new(Value::A(std::mem::take(&mut abuf)))); }
}
else {
synerr!(')', "Mismatched closing ')'");
}
},
b'x' => {
if let Some(top) = st.mstk.pop() {
let sec = st.mstk.pop();
match Utf8Iter::try_macros(&top, sec.as_deref()) {
Ok((stk, ret)) => {
if let Some(sec) = sec && ret { st.mstk.push(sec);
}
if mac.is_finished() && *count == Natural::ZERO { call.pop();
}
call.extend(stk.into_iter().rev());
continue 'mac;
},
Err(e) => {
if let Some(sec) = sec {st.mstk.push(sec);}
st.mstk.push(top);
synerr!('x', "{}", e);
}
}
}
else {
synerr!('x', "Expected 1 or 2 arguments, 0 given");
}
},
b'q' => {
let u = u8::wrapping_from(&Integer::rounding_from(rptr.unwrap_or_default(), RoundingMode::Down).0);
return if alt {
Ok(HardQuit(u))
}
else {
Ok(SoftQuit(u))
};
},
b'Q' => {
if let Some(va) = st.mstk.pop() {
match &*va {
Value::N(r) => {
if let Ok(u) = usize::try_from(r) {
call.truncate(call.len().saturating_sub(u));
if !dest.is_empty() { st.mstk.push(Arc::new(Value::A(std::mem::take(&mut abuf))));
dest.clear(); }
continue 'mac;
}
else {
let vs = va.to_string();
st.mstk.push(va);
valerr!('Q', "Cannot possibly break {} macros", vs);
}
},
_ => {
let ta = TypeLabel::from(&*va);
st.mstk.push(va);
synerr!('Q', "Expected a number, {} given", ta);
}
}
}
else {
synerr!('Q', "Expected 1 argument, 0 given");
}
},
b'a' => { match mac.next() {
Some(b) if !matches!(byte_cmd(b), Space) => {
match b {
b'a' => {
todo!()
},
_ => {
synerr!('a', "Invalid array command 'a{}'", b as char);
}
}
},
Some(_) | None => {
synerr!('a', "Incomplete array command 'a'");
}
}
},
b'f' => { match mac.next() {
Some(b) if !matches!(byte_cmd(b), Space) => {
match b {
b'z' => { push!(Value::N(st.mstk.len().into()));
},
b'r' => { st.mstk.reverse();
},
b'R' => { if let Some(va) = st.mstk.pop() {
if let Value::N(r) = &*va {
match usize::try_from(r) {
Ok(0) => {}, Ok(u) => {
if let Some(from) = st.mstk.len().checked_sub(u) {
st.mstk[from..].reverse();
}
else {
st.mstk.push(va);
valerr!('f', "Can't reverse {} values, stack depth is {}", u, st.mstk.len() - 1);
}
}
Err(_) => {
let vs = va.to_string();
st.mstk.push(va);
valerr!('f', "Can't possibly reverse {} values", vs);
}
}
}
else {
let ta = TypeLabel::from(&*va);
st.mstk.push(va);
synerr!('f', "Expected a number, {} given", ta);
}
}
else {
synerr!('f', "Expected 1 argument, 0 given");
}
},
b'f' => { let ri = if let Some(r) = rptr.take() {r}
else {
if matches!(mac.next().map(|b| {mac.back(); byte_cmd(b)}), None | Some(Space)) {
synerr!('f', "No register index");
alt = false;
continue 'cmd;
}
Rational::from(
match mac.try_next_char() {
Ok(c) => {c as u32},
Err(e) => {
*count = Natural::ZERO;
synerr!('\0', "Aborting invalid macro: {}", e);
break 'cmd;
}
}
)
};
let reg = st.regs.get_mut(&ri);
std::mem::swap(&mut st.mstk, &mut reg.v);
},
b'p' => { for v in &st.mstk {
let vs = v.display(st.params.get_k(), st.params.get_o(), st.params.get_m(), alt);
if let Some(s) = &mut pbuf {
s.push_str(&vs);
s.push('\n');
}
else {
let out = &mut io.lock().unwrap().1;
writeln!(out, "{}", vs)?;
out.flush()?;
}
}
},
_ => {
synerr!('f', "Invalid stack command 'f{}'", b as char);
}
}
},
Some(_) | None => {
synerr!('f', "Incomplete stack command 'f'");
}
}
},
b'N' => {
match (st.mstk.pop(), alt) {
(Some(va), false) => { match &*va {
Value::N(r) => {
match Natural::try_from(r) {
Ok(n) => {
push!(Value::N(Rational::from(
malachite::natural::random::get_random_natural_less_than(rng.get_or_insert_with(rng_os), &n)
)));
},
_ => {
st.mstk.push(va);
valerr!('N', "Limit must be a natural number");
}
}
},
_ => {
let ta = TypeLabel::from(&*va);
st.mstk.push(va);
synerr!('N', "Expected a number, {} given", ta);
}
}
}
(Some(va), true) => { match &*va {
Value::N(r) => {
match Integer::try_from(r) {
Ok(Integer::NEGATIVE_ONE) => { rng = None;
},
Ok(i) if Natural::convertible_from(&i) => { let n= unsafe { Natural::try_from(i).unwrap_unchecked() }; let mut bytes: Vec<u8> = PowerOf2DigitIterable::<u8>::power_of_2_digits(&n, 8).take(32).collect();
bytes.resize(32, 0);
rng = Some(rng_preset(
unsafe { <[u8; 32]>::try_from(bytes).unwrap_unchecked() }
));
},
_ => {
st.mstk.push(va);
valerr!('N', "Seed must be a natural number or `1");
}
}
},
_ => {
let ta = TypeLabel::from(&*va);
st.mstk.push(va);
synerr!('N', "Expected a number, {} given", ta);
}
}
}
(None, _) => {
synerr!('N', "Expected 1 argument, 0 given");
}
}
},
b'w' => { if let Some(va) = st.mstk.pop() {
if let Value::N(r) = &*va {
if let Some(dur) = u128::try_from(r).ok().and_then(|ns| {
(ns <= std::time::Duration::MAX.as_nanos()).then(|| std::time::Duration::from_nanos_u128(ns))
}) {
if let Some(rx) = kill {
match rx.recv_timeout(dur) {
Ok(()) => { #[expect(unused_assignments)]
for s in st.regs.end_threads(true) { valerr!('j', s);
}
return Ok(Killed);
},
Err(RecvTimeoutError::Timeout) => { },
Err(RecvTimeoutError::Disconnected) => { unreachable!()
}
}
}
else {
std::thread::sleep(dur); }
}
else {
let vs = va.to_string();
st.mstk.push(va);
valerr!('w', "Can't possibly wait {} ns", vs);
}
}
else {
let ta = TypeLabel::from(&*va);
st.mstk.push(va);
synerr!('w', "Expected a number, {} given", ta);
}
}
else {
synerr!('w', "Expected 1 argument, 0 given");
}
},
b'W' => {
push!(Value::N(
match std::time::SystemTime::UNIX_EPOCH.elapsed() {
Ok(dur) => {
Rational::from(dur.as_nanos())
},
Err(e) => {
Rational::from(e.duration().as_nanos()).neg()
}
}
));
},
b'_' => { let mut word = Vec::new();
while let Some(b) = mac.next() {
if matches!(byte_cmd(b), Space) {
mac.back();
break;
}
else {
word.push(b);
}
}
match &word[..] { b"restrict" => {
#[cfg_attr(feature = "no_os", expect(unused_assignments))] { restrict = true; }
},
b"quiet" => {
ll = LogLevel::Quiet;
},
b"error" => {
ll = LogLevel::Normal;
},
b"debug" => {
ll = LogLevel::Debug;
},
b"err" => {
push!(Value::A(
if let Some((n, c, s)) = elatch.take() {
vec![
Value::N(n.into()),
Value::S(c.into()),
Value::S(s)
]
}
else { vec![] }
));
},
b"th" => {
push!(Value::S(th_name.clone()));
},
b"joinall" => {
for s in st.regs.end_threads(false) {
valerr!('j', s);
}
},
b"killall" => {
for s in st.regs.end_threads(true) {
valerr!('j', s);
}
},
b"trim" => {
st.trim();
RE_CACHE.clear();
},
b"dedup" => {
st.dedup();
},
b"clhist" => {
io.lock().unwrap().0.clear_history();
},
b"clpar" => {
st.params = ParamStk::default();
},
b"clall" => {
st.clear_vals();
RE_CACHE.clear();
},
_ => {
#[cfg(feature = "no_os")]
{
synerr!('_', "Invalid word command '{}'", string_or_bytes(&word));
}
#[cfg(not(feature = "no_os"))]
{
match os::OS_CMDS.lock() {
Ok(guard) => {
match (restrict, guard.get(&word).copied()) {
(false, Some(oscmd)) => {
let res = oscmd(st);
drop(guard); match res {
Ok(mut v) => {
append!(v);
},
Err(e) => {
if let Some(se) = e.strip_suffix('!') {
synerr!('_', "OS command '{}': {}", string_or_bytes(&word), se);
}
else {
valerr!('_', "OS command '{}': {}", string_or_bytes(&word), e);
}
}
}
},
(true, Some(_)) => {
synerr!('_', "OS command '{}' is disabled (restricted mode)", string_or_bytes(&word));
},
_ => {
synerr!('_', "Invalid word command '{}'", string_or_bytes(&word));
}
}
},
Err(_) => {
panic!("A child thread panicked, terminating!");
}
}
}
}
}
},
_ => unreachable!()
}
},
ExecR => {
let ri = if let Some(r) = rptr.take() {r}
else {
if matches!(mac.next().map(|b| {mac.back(); byte_cmd(b)}), None | Some(Space)) {
synerr!(b as char, "No register index");
alt = false;
continue 'cmd;
}
Rational::from(
match mac.try_next_char() {
Ok(c) => {c as u32},
Err(e) => {
*count = Natural::ZERO;
synerr!('\0', "Aborting invalid macro: {}", e);
break 'cmd;
}
}
)
};
debug!("Register command {}{}", if alt {"alt-"} else {""}, b as char);
match b {
b'Z' => { push!(Value::N(
st.regs.try_get(&ri).map(|reg| reg.v.len().into()).unwrap_or_default()
));
},
b's' => {
if let Some(va) = st.mstk.pop() {
let reg = st.regs.get_mut(&ri);
if let Some(rv) = reg.v.last_mut() {
*rv = va;
}
else {
reg.v.push(va);
}
}
else {
synerr!('s', "Stack is empty");
}
},
b'S' => {
if let Some(va) = st.mstk.pop() {
st.regs.get_mut(&ri).v.push(va);
}
else {
synerr!('S', "Stack is empty");
}
},
b'l' => {
if let Some(rv) = st.regs.try_get(&ri).and_then(|reg| reg.v.last()) {
if let Some(p) = dest.last_mut() { unsafe {
p.as_mut().push((**rv).clone()); }
}
else {
st.mstk.push(Arc::clone(rv));
}
}
else {
synerr!('l', "Register {} is empty", reg_index_nice(&ri));
}
},
b'L' => {
if let Some(rv) = st.regs.try_get_mut(&ri).and_then(|reg| reg.v.pop()) {
if let Some(p) = dest.last_mut() { unsafe {
p.as_mut().push((*rv).clone()); }
}
else {
st.mstk.push(Arc::clone(&rv));
}
}
else {
synerr!('L', "Register {} is empty", reg_index_nice(&ri));
}
},
b'X' => {
if let Some(true) = st.regs.try_get(&ri).map(|reg| reg.th.is_some()) {
valerr!('X', "Register {} is already running a thread", reg_index_nice(&ri));
}
else if let Some(va) = st.mstk.pop() {
if let Value::S(sa) = &*va {
let th_start = sa.to_owned().into();
let (ktx, krx) = std::sync::mpsc::channel::<()>();
let (jtx, jrx) = std::sync::mpsc::channel::<()>();
let tb = std::thread::Builder::new().name(format!("{th_name}{}: ", reg_index_nice(&ri)));
let mut th_st = if alt { st.clone() } else { State::default() };
let th_io = Arc::clone(&io);
match tb.spawn(move || {
let th_res = unsafe { interpreter(&mut th_st, th_start, th_io, ll, Some(&krx), restrict) };
let _ = jrx.recv(); th_st.regs.end_threads( th_res.is_err() ||
match krx.try_recv() { Ok(()) => { true },
Err(TryRecvError::Empty) => { false },
Err(TryRecvError::Disconnected) => { unreachable!() } }
);
(th_st.mstk, th_res)
}) {
Ok(jh) => {
st.regs.get_mut(&ri).th = Some((jh, ktx, jtx));
},
Err(e) => {
st.mstk.push(va);
valerr!('X', "Can't spawn child thread: {}", e);
}
}
}
else {
let ta = TypeLabel::from(&*va);
st.mstk.push(va);
synerr!('X', "Expected a macro string, {} given", ta);
}
}
else {
synerr!('X', "Expected 1 argument, 0 given");
}
},
b'j' => {
let ri_nice = reg_index_nice(&ri);
if let Some(reg) = st.regs.try_get_mut(&ri) && let Some((jh, ktx, jtx)) = reg.th.take() {
if alt {
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) => {
let s = format!("IO error in thread {}: {}", ri_nice, e);
eprintln!("? {th_name}j: {}", s); elatch = Some((Natural::ZERO, 'j', s));
},
Ok(SoftQuit(c)) if c != 0 => {
valerr!('j', "Thread {} quit with code {}", ri_nice, c);
},
Ok(HardQuit(c)) if c != 0 => {
valerr!('j', "Thread {} hard-quit with code {}", ri_nice, c);
},
Ok(Killed) => {
valerr!('j', "Thread {} was killed", ri_nice);
},
_ => {} }
reg.v.append(&mut tr.0);
},
Err(e) => {
std::panic::resume_unwind(e);
}
}
}
else {
valerr!('j', "Register {} is not running a thread", ri_nice);
}
},
b'J' => {
if let Some(Some((jh, _, _))) = st.regs.try_get(&ri).map(|reg| ®.th) {
let mut bz = BitVec::new();
bz.push(jh.is_finished());
push!(Value::B(bz));
}
else {
valerr!('J', "Register {} is not running a thread", reg_index_nice(&ri));
}
},
_ => unreachable!()
}
},
Lit => {
match b {
b'T' | b'F' => { debug!("Boolean literal");
let mut bits = BitVec::new();
bits.push(b == b'T');
while let Some(b) = mac.next() {
match b {
b'T' => {bits.push(true);},
b'F' => {bits.push(false);},
_ => {
mac.back();
break;
}
}
}
push!(Value::B(bits));
},
b'\'' | b'0'..=b'9' | b'.' | b'@' => { debug!("Number literal");
let mut ipart = Vec::new();
let mut fpart = Vec::new();
let mut rpart = Vec::new();
let mut get_epart = true;
let mut exp = None;
let mut discard = false;
let mut ibase = st.params.get_i().clone();
match (b == b'\'', ibase > Natural::const_from(36)) {
(false, high_base) => { mac.back();
ibase = if high_base {Natural::const_from(10)} else {ibase}; while let Some(ib) = mac.next() { let id = ib.wrapping_sub(0x30);
match id {
0..=9 if id < ibase => {ipart.push(Natural::from(id));},
0..=9 => {
synerr!('\'', "Digit {} is too high for base {}", id, ibase);
discard = true;
},
_ => {
mac.back();
break;
}
}
}
match mac.next() {
Some(b'.') => { let mut recur = false; while let Some(fb) = mac.next() {
let fd = fb.wrapping_sub(0x30);
match fd {
0x30 if !recur => {recur = true;}, 0..=9 if !recur && fd < ibase => {fpart.push(Natural::from(fd));},
0..=9 if recur && fd < ibase => {rpart.push(Natural::from(fd));},
0..=9 => {
synerr!('\'', "Digit {} is too high for base {}", fd, ibase);
discard = true;
},
_ => {
mac.back();
break;
}
}
}
},
Some(_) => {mac.back();},
None => {}
}
}
(true, false) => { while let Some(ib) = mac.next() { if let Some(id) = mixed_ascii_to_digit(ib) {
if id < ibase {ipart.push(Natural::from(id));}
else {
synerr!('\'', "Digit {} is too high for base {}", id, ibase);
discard = true;
}
}
else {
mac.back();
break;
}
}
match mac.next() {
Some(b'.') => { let mut recur = false; while let Some(fb) = mac.next() {
if let Some(fd) = mixed_ascii_to_digit(fb) {
if fd < ibase {
if !recur {fpart.push(Natural::from(fd));}
else {rpart.push(Natural::from(fd));}
}
else {
synerr!('\'', "Digit {} is too high for base {}", fd, ibase);
discard = true;
}
}
else if !recur && fb == b'`' {recur = true;}
else {
mac.back();
break;
}
}
},
Some(_) => {mac.back();},
None => {}
}
},
(true, true) => { get_epart = false;
let ns= mac.by_ref().take_while(|b| *b != b'\'').collect::<Vec<u8>>();
if ns.is_empty() {
synerr!('\'', "Empty any-base number");
alt = false;
continue 'cmd;
}
for nc in ns.iter() {
match nc {
b' ' | b'.' | b'0'..=b'9' | b'@' | b'`' => {} wrong => {
synerr!('\'', "Invalid character in any-base number: {}", string_or_bytes(&[*wrong]));
alt = false;
continue 'cmd;
}
}
}
let mut ms = Vec::new();
match ns.split(|b| *b == b'@').collect::<Vec<&[u8]>>()[..] {
[mpart] => { ms = mpart.to_vec();
},
[mpart, epart] => { ms = mpart.to_vec();
let mut es = epart.to_vec();
if let Some(first) = es.first_mut() && *first == b'`' { *first = b'-'; }
match String::from_utf8(es) {
Ok(es) => {
match es.parse::<i64>() {
Ok(i) => { exp = Some(i); },
Err(e) => {
use std::num::IntErrorKind::*;
match e.kind() {
Empty => { exp = Some(0); },
InvalidDigit => {
valerr!('\'', "Invalid exponent: {}", es);
alt = false;
continue 'cmd;
},
PosOverflow | NegOverflow => {
valerr!('\'', "Exponent {} is unrepresentable", es);
alt = false;
continue 'cmd;
},
_ => { unreachable!() }
}
}
}
},
_ => { unreachable!() }
}
},
ref v => {
synerr!('\'', "{} exponent signs (@) in any-base number", v.len() - 1);
drop(ms);
alt = false;
continue 'cmd;
}
}
let mut is = Vec::new();
let mut frs = Vec::new();
match ms.split(|b| *b == b'.').collect::<Vec<&[u8]>>()[..] {
[ipart] => {
is = ipart.to_vec();
},
[ipart, fpart] => {
is = ipart.to_vec();
frs = fpart.to_vec();
},
ref v => {
synerr!('\'', "{} fractional points (.) in any-base number", v.len() - 1);
drop(is);
alt = false;
continue 'cmd;
}
}
if is.contains(&b'`') {
synerr!('\'', "Unexpected negative sign (`) in any-base number");
alt = false;
continue 'cmd;
}
let mut fs = Vec::new();
let mut rs = Vec::new();
match frs.split(|b| *b == b'`').collect::<Vec<&[u8]>>()[..] {
[fpart] => {
fs = fpart.to_vec();
},
[fpart, rpart] => {
fs = fpart.to_vec();
rs = rpart.to_vec();
},
ref v => {
synerr!('\'', "{} recurring marks (`) in any-base number", v.len() - 1);
drop(fs);
alt = false;
continue 'cmd;
}
}
if !is.is_empty() { for id in is.split(|b| *b == b' ') {
let id = str::from_utf8(id).unwrap();
ipart.push(Natural::from_str(id).unwrap());
}}
if !fs.is_empty() { for fd in fs.split(|b| *b == b' ') {
let fd = str::from_utf8(fd).unwrap();
fpart.push(Natural::from_str(fd).unwrap());
}}
if !rs.is_empty() { for rd in rs.split(|b| *b == b' ') {
let rd = str::from_utf8(rd).unwrap();
rpart.push(Natural::from_str(rd).unwrap());
}}
for d in ipart.iter().chain(fpart.iter()).chain(rpart.iter()) {
if *d >= ibase {
synerr!('\'', "Digit {} is too high for base {}", d, ibase);
alt = false;
continue 'cmd;
}
}
}
}
let m_empty = ipart.is_empty() && fpart.is_empty() && rpart.is_empty(); let mut r;
if m_empty {
r = Rational::ZERO;
}
else {
ipart.reverse(); r = Rational::from_digits(&ibase, ipart, RationalSequence::from_vecs(fpart, rpart));
if alt {r.neg_assign();} }
if get_epart {
match mac.next() {
Some(b'@') => { let mut es = String::new();
let mut eneg = false; while let Some(eb) = mac.next() {
match eb {
b'`' if !eneg => { es.push('-'); }
b'0'..=b'9' => { es.push(eb as char); }
_ => {
mac.back();
break;
}
}
eneg = true; }
if es.is_empty() { es.push('0'); }
if m_empty { r = Rational::ONE; } if let Ok(i) = es.parse::<i64>() {
r *= Rational::from(st.params.get_i()).pow(i); }
else {
valerr!('\'', "Exponent {} is unrepresentable", es);
discard = true;
}
}
Some(_) => { mac.back(); }
None => {}
}
}
else if let Some(i) = exp {
if m_empty { r = Rational::ONE; } r *= Rational::from(ibase).pow(i); }
if !discard {
push!(Value::N(r));
}
},
b'[' => { debug!("String literal");
let mut bytes = Vec::new();
let mut discard = false;
let mut nest = 1usize;
while let Some(b) = mac.next() {
match b {
b'[' => {
nest = unsafe { nest.unchecked_add(1) };
bytes.push(b'[');
},
b']' => {
nest = unsafe { nest.unchecked_sub(1) };
if nest == 0 {
break;
}
else {
bytes.push(b']');
}
},
b'\\' => { match mac.next() {
Some(b'a') => {bytes.push(0x07);}, Some(b'b') => {bytes.push(0x08);}, Some(b't') => {bytes.push(0x09);}, Some(b'n') => {bytes.push(0x0A);}, Some(b'v') => {bytes.push(0x0B);}, Some(b'f') => {bytes.push(0x0C);}, Some(b'r') => {bytes.push(0x0D);}, Some(b'e') => {bytes.push(0x1B);}, Some(b'[') => {bytes.push(b'[');}, Some(b']') => {bytes.push(b']');}, Some(b'\\') => {bytes.push(b'\\');}, Some(b0) => { if let Some(high) = upper_hex_to_nibble(b0) { if let Some(b1) = mac.next() {
if let Some(low) = upper_hex_to_nibble(b1) {
bytes.push(high << 4 | low);
}
else {
synerr!('[', "Invalid byte escape: \\{}{}", b0 as char, b1 as char);
discard = true;
}
}
else {
synerr!('[', "Incomplete byte escape: \\{}", b0 as char);
discard = true;
}
}
else { mac.back();
match mac.try_next_char() {
Ok(c) => {
synerr!('[', "Invalid character escape: \\{} (U+{:04X})", c, c as u32);
discard = true;
},
Err(e) => {
*count = Natural::ZERO;
synerr!('\0', "Aborting invalid macro: {}", e);
break 'cmd;
}
}
}
},
None => {
synerr!('[', "Incomplete character escape: \\");
discard = true;
}
}
},
_ => {
bytes.push(b);
}
}
}
if !discard {
match String::try_from(Utf8Iter::from(bytes)) {
Ok(s) => {
push!(Value::S(s));
},
Err(e) => {
synerr!('[', "Invalid string: {}", e);
}
}
}
},
_ => unreachable!()
}
},
Space if b == b'#' => { debug!("Line comment");
mac.find(|b| *b == b'\n'); },
Space => {
},
Wrong => {
mac.back();
match mac.try_next_char() {
Ok(c) => {
synerr!(c, "Invalid command: {} (U+{:04X})", c, c as u32);
},
Err(e) => {
*count = Natural::ZERO;
synerr!('\0', "Aborting invalid macro: {}", e);
break 'cmd;
}
}
}
}
alt = false; }
if !dest.is_empty() { st.mstk.push(Arc::new(Value::A(std::mem::take(&mut abuf))));
dest.clear(); }
*count -= Natural::ONE;
if *count == Natural::ZERO { call.pop();
}
else { mac.rewind();
}
}
Ok(Finished)
}