adc_lang/
lib.rs

1//! ADC interpreter API. Many safe items are public for manual access.
2
3pub mod structs;
4use structs::*;
5
6pub(crate) mod fns;
7
8pub(crate) mod cmds;
9
10pub(crate) mod errors;
11
12pub(crate) mod conv;
13
14pub(crate) mod num;
15
16#[cfg(not(feature = "no_os"))]
17mod os;
18
19use std::io::{Write, BufRead, ErrorKind};
20use std::ops::Neg;
21use std::panic::catch_unwind;
22use std::ptr::NonNull;
23use std::str::FromStr;
24use std::sync::{Arc, Mutex, mpsc::{Receiver, TryRecvError, RecvTimeoutError}};
25use linefeed::{DefaultTerminal, Interface};
26use bitvec::prelude::*;
27use malachite::{Natural, Integer, Rational};
28use malachite::base::num::arithmetic::traits::{DivRem, NegAssign, Pow};
29use malachite::base::num::basic::traits::{NegativeOne, Zero, One};
30use malachite::base::num::conversion::traits::{ConvertibleFrom, PowerOf2DigitIterable, RoundingFrom, WrappingFrom};
31use malachite::base::num::random::RandomPrimitiveInts;
32use malachite::base::rational_sequences::RationalSequence;
33use malachite::base::rounding_modes::RoundingMode;
34use crate::errors::TypeLabel;
35
36
37/// Added at the start of saved state files
38pub const STATE_FILE_HEADER: [u8;20] = *b"# ADC state file v1\n";
39
40struct LineEditor(Interface<DefaultTerminal>);
41impl Default for LineEditor {
42	fn default() -> Self {
43		use linefeed::Signal::*;
44		let iface = Interface::new("").unwrap();
45		//these do not take &mut self, shrug
46		iface.set_report_signal(Break, true);
47		iface.set_report_signal(Interrupt, true);
48		iface.set_report_signal(Quit, true);
49		Self(iface)
50	}
51}
52
53/// Generic input stream adapter trait, used for adding a proper line editor.
54///
55/// Comes with a generic implementation for all [`BufRead`] types.
56pub trait ReadLine {
57	/// This is called once by every execution of the `?` command within ADC.
58	///
59	/// [`ErrorKind::Interrupted`] causes `?` to error, [`ErrorKind::UnexpectedEof`] makes an empty string, other [`ErrorKind`]s are returned early from the interpreter.
60	fn read_line(&mut self) -> std::io::Result<String>;
61
62	/// If [`Self`] implements a history, clear it.
63	fn clear_history(&mut self);
64}
65impl<T: BufRead> ReadLine for T {
66	fn read_line(&mut self) -> std::io::Result<String> {
67		let mut buf = String::new();
68		self.read_line(&mut buf)?;
69		Ok(buf)
70	}
71
72	fn clear_history(&mut self) {
73		// no history, do nothing
74	}
75}
76impl ReadLine for LineEditor {
77	fn read_line(&mut self) -> std::io::Result<String> {
78		use linefeed::{ReadResult, Signal};
79		match self.0.read_line() {
80			Ok(ReadResult::Input(s)) => {
81				self.0.add_history_unique(s.clone());
82				Ok(s)
83			},
84			Ok(ReadResult::Eof) => {Err(ErrorKind::UnexpectedEof.into())},
85			Ok(ReadResult::Signal(sig)) => {
86				self.0.cancel_read_line()?;
87				match sig {
88					Signal::Break | Signal::Interrupt | Signal::Quit => {Err(ErrorKind::Interrupted.into())},
89					Signal::Continue => {Err(std::io::Error::other("Unhandled SIGCONT"))},
90					Signal::Suspend => {Err(std::io::Error::other("Unhandled SIGTSTP"))},
91					Signal::Resize => {Err(std::io::Error::other("Unhandled window resize"))},
92				}
93			},
94			Err(e) => {Err(e)}
95		}
96	}
97
98	fn clear_history(&mut self) {
99		self.0.clear_history();
100	}
101}
102
103/// Bundle of standard IO streams, generic interface to support custom IO wrappers
104pub struct IOStreams (
105	/// Input
106	pub Box<dyn ReadLine + Send>,
107	/// Output
108	pub Box<dyn Write + Send>,
109	/// Error
110	pub Box<dyn Write + Send>
111);
112impl IOStreams {
113	/// Use dummy IO streams, these do nothing
114	pub fn empty() -> Self {
115		Self (
116			Box::new(std::io::empty()),
117			Box::new(std::io::empty()),
118			Box::new(std::io::empty())
119		)
120	}
121
122	/// Use IO streams of the process (stdin, stdout, stderr), with extra [line editor](linefeed) on stdin
123	pub fn process() -> Self {
124		Self (
125			Box::new(LineEditor::default()),
126			Box::new(std::io::stdout()),
127			Box::new(std::io::stderr())
128		)
129	}
130}
131
132lazy_static::lazy_static! {
133	pub(crate) static ref RE_CACHE: RegexCache = RegexCache::default();
134}
135
136fn rng_preset(bytes: [u8; 32]) -> RandomPrimitiveInts<u64> {
137	malachite::base::num::random::random_primitive_ints(malachite::base::random::Seed::from_bytes(bytes))
138}
139
140fn rng_os() -> RandomPrimitiveInts<u64> {
141	let mut bytes = [0u8; 32];
142	getrandom::fill(&mut bytes).unwrap();
143	rng_preset(bytes)
144}
145
146#[derive(Default, Clone, Copy)]
147enum Command {
148	///monadic pure function
149	Fn1(fns::Mon),
150
151	///dyadic pure function
152	Fn2(fns::Dya),
153	
154	///triadic pure function
155	Fn3(fns::Tri),
156
157	///impure command
158	Cmd(cmds::Cmd),
159
160	///really impure (macros, IO, OS...)
161	Exec,
162
163	///impure with register access
164	ExecR,
165
166	///begin value literal
167	Lit,
168
169	///no command
170	Space,
171
172	///invalid command
173	#[default] Wrong,
174}
175
176/// Dense map of bytes to commands. SAFETY: Length must be exactly 256.
177const CMDS: [Command; 256] = {
178	use Command::*;
179	use fns::*;
180	use cmds::*;
181	[
182		//NUL		SOH			STX			ETX			EOT			ENQ			ACK			BEL			BS			HT			LF			VT			FF			CR			SO			SI
183		Space,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Space,		Space,		Space,		Space,		Space,		Wrong,		Wrong,
184
185		//DLE		DC1			DC2			DC3			DC4			NAK			SYN			ETB			CAN			EM			SUB			ESC			FS			GS			RS			US
186		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,		Wrong,
187
188		//SP		!			"			#			$			%			&			'			(			)			*			+			,			-			.			/
189		Space,		Fn1(neg),	Exec,		Space,		Wrong,		Fn2(modu),	Wrong,		Lit,		Exec,		Exec,		Fn2(mul),	Fn2(add),	Wrong,		Fn2(sub),	Lit,		Fn2(div),
190
191		//0			1			2			3			4			5			6			7			8			9			:			;			<			=			>			?
192		Lit,		Lit,		Lit,		Lit,		Lit,		Lit,		Lit,		Lit,		Lit,		Lit,		Exec,		Wrong,		Fn2(lt),	Fn2(eq),	Fn2(gt),	Exec,
193
194		//@			A			B			C			D			E			F			G			H			I			J			K			L			M			N			O
195		Lit,		Wrong,		Wrong,		Cmd(cln),	Exec,		Wrong,		Lit,		Fn2(logb),	Wrong,		Cmd(gi),	ExecR,		Cmd(gk),	ExecR,		Cmd(gm),	Exec,		Cmd(go),
196
197		//P			Q			R			S			T			U			V			W			X			Y			Z			[			\			]			^			_
198		Exec,		Exec,		Exec,		ExecR,		Lit,		Wrong,		Fn2(root),	Exec,		ExecR,		Wrong,		ExecR,		Lit,		Wrong,		Wrong,		Fn2(pow),	Exec,
199
200		//`			a			b			c			d			e			f			g			h			i			j			k			l			m			n			o
201		Exec,		Exec,		Wrong,		Cmd(cls),	Exec,		Wrong,		Exec,		Fn1(log),	Wrong,		Cmd(si),	ExecR,		Cmd(sk),	ExecR,		Cmd(sm),	Fn1(fac),	Cmd(so),
202
203		//p			q			r			s			t			u			v			w			x			y			z			{			|			}			~			DEL
204		Exec,		Exec,		Cmd(rev),	ExecR,		Fn2(trig),	Wrong,		Fn1(sqrt),	Exec,		Exec,		Wrong,		Fn1(disc),	Cmd(cbo),	Fn3(bar),	Cmd(cbc),	Fn2(euc),	Wrong,
205
206		//~~description of what i'm doing:~~ non-ASCII:
207		Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,
208		Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,
209		Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,
210		Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong,Wrong
211	]
212};
213
214fn byte_cmd(b: u8) -> Command {
215	unsafe {	//SAFETY: length is 256
216		*CMDS.get_unchecked(b as usize)
217	}
218}
219
220fn string_or_bytes(v: &[u8]) -> String {
221	str::from_utf8(v).map(|s| s.to_owned()).unwrap_or_else(|_| {
222		let mut res = String::from("(not UTF-8: [");
223		for b in v {
224			res += &format!("\\{b:02X}");
225		}
226		res += "])";
227		res
228	})
229}
230
231fn upper_hex_to_nibble(b: u8) -> Option<u8> {
232	match b {
233		b'0'..=b'9' => Some(unsafe{b.unchecked_sub(0x30)}),	//SAFETY: underflow is impossible
234		b'A'..=b'F' => Some(unsafe{b.unchecked_sub(0x37)}),
235		_ => None
236	}
237}
238
239fn mixed_ascii_to_digit(b: u8) -> Option<u8> {
240	match b {
241		b'0'..=b'9' => Some(unsafe{b.unchecked_sub(0x30)}),	//SAFETY: underflow is impossible
242		b'A'..=b'Z' => Some(unsafe{b.unchecked_sub(0x37)}),
243		b'a'..=b'z' => Some(unsafe{b.unchecked_sub(0x57)}),
244		_ => None
245	}
246}
247
248/// How much information to output on stderr
249#[derive(Debug, PartialEq, Eq, Clone, Copy)]
250pub enum LogLevel {
251	/// Only output error messages
252	Normal,
253	/// Report every command (without values)
254	Debug,
255	/// No error messages, stderr disabled
256	Quiet
257}
258
259/// Results of running [`interpreter`], wrappers should handle these differently
260#[derive(Clone, Copy, Debug, PartialEq)]
261#[must_use] pub enum ExecResult {
262	/// Commands ran to completion, request further input
263	Finished,
264
265	/// Exit of current instance requested (q), end context
266	SoftQuit(u8),
267
268	/// Complete exit requested (`q), terminate
269	HardQuit(u8),
270
271	/// Received kill signal
272	Killed
273}
274
275/// Safe wrapper around [`interpreter`], see its documentation.
276pub fn interpreter_no_os(
277	st: &mut State,
278	start: Utf8Iter,
279	io: Arc<Mutex<IOStreams>>,
280	ll: LogLevel,
281	kill: Option<&Receiver<()>>,
282) -> std::io::Result<ExecResult>
283{
284	//SAFETY: restrict = true
285	unsafe { interpreter(st, start, io, ll, kill, true) }
286}
287
288/// Interpreter entry point, executes ADC commands to modify state.
289///
290/// # Arguments
291/// - `st`: State struct to work on, modified in-place
292/// - `start`: Initial commands to run
293/// - `io`: Bundle of IO stream handles
294///   - `io.0`: Input, read by ? one line at a time
295///   - `io.1`: Output, written to by printing commands
296///   - `io.2`: Error messages, one per line
297/// - `ll`: Level of verbosity for `io.2`
298/// - `kill`: Kill signal, receiving terminates the interpreter. Polled as often as possible, effect is practically immediate.
299///   - `Some(_)` will also enable `restrict`, as it's unsafe or impossible to kill the thread while performing some OS interactions.
300/// - `restrict`: Restricted mode switch, enable for untrusted input. If `false`, the interpreter may read/write files and execute OS commands, subject to any OS-level permissions.
301///
302/// # Errors
303/// Any IO errors (except those specified [here](ReadLine::read_line)) that arise when accessing the [`IOStreams`] are returned early, aborting the interpreter.
304///
305/// This will result in an incomplete, although internally consistent, [`State`]. Keep that possibility to a minimum when preparing custom [`IOStreams`].
306///
307/// # Safety
308/// If built without the `no_os` feature (default), passing `kill = None` and `restrict = false` enables OS interactions that are fundamentally unsound in multithreaded contexts.
309///
310/// Simultaneously/asynchronously executing multiple instances of this function with said arguments is *Undefined Behavior*. Do not rely on known values of `st` and `start` for safety.
311///
312/// Alternatively, [`interpreter_no_os`] provides a safe wrapper.
313pub unsafe fn interpreter(
314	st: &mut State,
315	start: Utf8Iter,
316	io: Arc<Mutex<IOStreams>>,
317	mut ll: LogLevel,
318	kill: Option<&Receiver<()>>,
319	#[cfg_attr(feature = "no_os", expect(unused_variables))]
320	mut restrict: bool
321) -> std::io::Result<ExecResult>
322{
323	use ExecResult::*;
324
325	let th_name = if kill.is_some() {	//if running in a child thread
326		#[cfg_attr(feature = "no_os", expect(unused_assignments))] { restrict = true; }	//extra safety just in case
327		std::thread::current().name().unwrap().to_owned()
328	}
329	else {
330		String::new()
331	};
332
333	let mut pbuf: Option<String> = None;	//print-to-string buffer
334
335	let mut elatch: Option<(Natural, char, String)> = None;
336
337	macro_rules! synerr {
338		($c:expr, $s:expr) => {
339			if ll != LogLevel::Quiet {
340				let err = &mut io.lock().unwrap().2;
341				writeln!(err, "! {th_name}{}: {}", $c, $s)?;
342				err.flush()?;
343				//drop lock
344			}
345			elatch = Some((Natural::ZERO, $c, $s.into()));
346		};
347    	($c:expr, $f:literal, $($s:expr),*) => {
348			let s = format!($f, $($s),*);
349			if ll != LogLevel::Quiet {
350				let err = &mut io.lock().unwrap().2;
351				writeln!(err, "! {th_name}{}: {}", $c, s)?;
352				err.flush()?;
353				//drop lock
354			}
355			elatch = Some((Natural::ZERO, $c, s));
356		};
357	}
358
359	macro_rules! valerr {
360    	($c:expr, $s:expr) => {
361			if ll != LogLevel::Quiet {
362				let err = &mut io.lock().unwrap().2;
363				writeln!(err, "? {th_name}{}: {}", $c, $s)?;
364				err.flush()?;
365				//drop lock
366			}
367			elatch = Some((Natural::ZERO, $c, $s.into()));
368		};
369		($c:expr, $f:literal, $($s:expr),*) => {
370			let s = format!($f, $($s),*);
371			if ll != LogLevel::Quiet {
372				let err = &mut io.lock().unwrap().2;
373				writeln!(err, "? {th_name}{}: {}", $c, s)?;
374				err.flush()?;
375				//drop lock
376			}
377			elatch = Some((Natural::ZERO, $c, s));
378		};
379	}
380
381	macro_rules! debug {
382    	($s:expr) => {
383			if ll == LogLevel::Debug {
384				let err = &mut io.lock().unwrap().2;
385				writeln!(err, "\tDEBUG: {th_name}{}", $s)?;
386				err.flush()?;
387				//drop lock
388			}
389		};
390		($f:literal, $($s:expr),*) => {
391			if ll == LogLevel::Debug {
392				let err = &mut io.lock().unwrap().2;
393				writeln!(err, "\tDEBUG: {th_name}{}", format!($f, $($s),*))?;
394				err.flush()?;
395				//drop lock
396			}
397		};
398	}
399
400	let mut rptr: Option<Rational> = None;	//allow setting by a macro
401
402	let mut rng: Option<RandomPrimitiveInts<u64>> = None;
403
404	let mut call: Vec<(Utf8Iter, Natural)> = vec![(start, Natural::const_from(1))];
405
406	'mac: while let Some((mac, count)) = call.last_mut() {	//while call stack has contents, macro scope:
407		*count -= Natural::ONE;
408		let mut alt = false;
409
410		let mut abuf: Vec<Value> = Vec::new();	//array input buffer
411		let mut dest: Vec<NonNull<Vec<Value>>> = Vec::new();	//stack of destinations for new values, shadows mstk with the following macros
412		/// push one value to main stack or array buffer
413		macro_rules! push {
414    		($v:expr) => {
415				if let Some(p) = dest.last_mut() {
416					unsafe {
417						p.as_mut().push($v);	//SAFETY: `NonNull`s point to nested arrays in abuf, only one is accessed at a time
418					}
419				}
420				else {
421					st.mstk.push(Arc::new($v));
422				}
423			};
424		}
425		/// append values to main stack or array buffer
426		macro_rules! append {
427    		($v:expr) => {
428				if let Some(p) = dest.last_mut() {
429					unsafe {
430						p.as_mut().append(&mut $v);	//SAFETY: `NonNull`s point to nested arrays in abuf, only one is accessed at a time
431					}
432				}
433				else {
434					for val in $v {
435						st.mstk.push(Arc::new(val));
436					}
437				}
438			};
439		}
440
441		'cmd: while let Some(b) = mac.next() {	//main parsing loop, single command scope:
442			if let Some(rx) = kill {	//check kill signal
443				match rx.try_recv() {
444					Ok(()) => {	//killed by parent
445						#[expect(unused_assignments)]
446						for s in st.regs.end_threads(true) {	//propagate to children
447							valerr!('j', s);
448						}
449						return Ok(Killed);
450					},
451					Err(TryRecvError::Empty) => {	//not killed
452						//do nothing
453					},
454					Err(TryRecvError::Disconnected) => {	//parent should never disconnect
455						unreachable!()
456					}
457				}
458			}
459			
460			if let Some(e) = &mut elatch {	//advance error latch counter
461				e.0 += Natural::ONE;
462			}
463
464			use Command::*;
465			match byte_cmd(b) {
466				Fn1(mon) => {
467					if let Some(va) = st.mstk.pop() {
468						debug!("Monadic {}{} with {}", if alt {"alt-"} else {""}, b as char, TypeLabel::from(&*va));
469						match catch_unwind(|| fns::exec1(mon, &va, alt)) {
470							Ok(Ok(vz)) => {
471								push!(vz);
472							},
473							Ok(Err(e)) => {
474								st.mstk.push(va);
475								valerr!(b as char, e.to_string());
476							},
477							Err(cause) => {
478								st.mstk.push(va);
479								valerr!(b as char, "Caught function panic: {:?}", cause);
480							}
481						}
482					}
483					else {
484						synerr!(b as char, "Expected 1 argument, 0 given");
485					}
486				},
487				Fn2(dya) => {
488					if let Some(vb) = st.mstk.pop() {
489						if let Some(va) = st.mstk.pop() {
490							debug!("Dyadic {}{} with ({}, {})", if alt {"alt-"} else {""}, b as char, TypeLabel::from(&*va), TypeLabel::from(&*vb));
491							match catch_unwind(|| fns::exec2(dya, &va, &vb, alt)) {
492								Ok(Ok(vz)) => {
493									push!(vz);
494								},
495								Ok(Err(e)) => {
496									st.mstk.push(va);
497									st.mstk.push(vb);
498									valerr!(b as char, e.to_string());
499								},
500								Err(cause) => {
501									st.mstk.push(va);
502									st.mstk.push(vb);
503									valerr!(b as char, "Caught function panic: {:?}", cause);
504								}
505							}
506						}
507						else {
508							st.mstk.push(vb);
509							synerr!(b as char, "Expected 2 arguments, 1 given");
510						}
511					}
512					else {
513						synerr!(b as char, "Expected 2 arguments, 0 given");
514					}
515				},
516				Fn3(tri) => {
517					if let Some(vc) = st.mstk.pop() {
518						if let Some(vb) = st.mstk.pop() {
519							if let Some(va) = st.mstk.pop() {
520								debug!("Triadic {}{} with ({}, {}, {})", if alt {"alt-"} else {""}, b as char, TypeLabel::from(&*va), TypeLabel::from(&*vb), TypeLabel::from(&*vc));
521								match catch_unwind(|| fns::exec3(tri, &va, &vb, &vc, alt)) {
522									Ok(Ok(vz)) => {
523										push!(vz);
524									},
525									Ok(Err(e)) => {
526										st.mstk.push(va);
527										st.mstk.push(vb);
528										st.mstk.push(vc);
529										valerr!(b as char, e.to_string());
530									},
531									Err(cause) => {
532										st.mstk.push(va);
533										st.mstk.push(vb);
534										st.mstk.push(vc);
535										valerr!(b as char, "Caught function panic: {:?}", cause);
536									}
537								}
538							}
539							else {
540								st.mstk.push(vb);
541								st.mstk.push(vc);
542								synerr!(b as char, "Expected 3 arguments, 2 given");
543							}
544						}
545						else {
546							st.mstk.push(vc);
547							synerr!(b as char, "Expected 3 arguments, 1 given");
548						}
549					}
550					else {
551						synerr!(b as char, "Expected 3 arguments, 0 given");
552					}
553				},
554				Cmd(cmd) => {
555					debug!("Impure command {}", b as char);
556					match cmd(st) {
557						Ok(mut v) => {
558							append!(v);
559						}
560						Err(e) => {
561							if let Some(se) = e.strip_suffix('!') {
562								synerr!(b as char, se);
563							}
564							else {
565								valerr!(b as char, e);
566							}
567						}
568					}
569				},
570				Exec => {
571					debug!("Command {}{}", if alt {"alt-"} else {""}, b as char);
572					match b {
573						b'`' => {	//alt prefix
574							alt = true;
575							continue 'cmd;	//force digraph
576						},
577						b':' if !alt => {	//set register pointer
578							if let Some(va) = st.mstk.pop() {
579								if let Value::N(r) = &*va {
580									rptr = Some(r.clone());
581								}
582								else {
583									let ta = TypeLabel::from(&*va);
584									st.mstk.push(va);
585									synerr!(':', "Expected a number, {} given", ta);
586								}
587							}
588							else {
589								synerr!(':', "Expected 1 argument, 0 given");
590							}
591						},
592						b':' if alt => {	//get register pointer
593							push!(
594								if let Some(ri) = rptr.take() {
595									Value::N(ri)
596								}
597								else {
598									Value::A(vec![])
599								}
600							);
601						},
602						b'd' => {
603							if let Some(v) = st.mstk.last() {
604								if let Some(p) = dest.last_mut() {	//manual shadowed push
605									unsafe {
606										p.as_mut().push((**v).clone());	//SAFETY: `NonNull`s point to nested arrays in abuf, only one is accessed at a time
607									}
608								}
609								else {
610									st.mstk.push(Arc::clone(v));
611								}
612							}
613							else {
614								synerr!('d', "Stack is empty");
615							}
616						},
617						b'D' => {
618							if let Some(va) = st.mstk.pop() {
619								if let Value::N(r) = &*va {
620									match usize::try_from(r) {
621										Ok(0) => {},	//no-op
622										Ok(u) => {
623											if let Some(from) = st.mstk.len().checked_sub(u) {
624												if let Some(p) = dest.last_mut() {	//manual shadowed append
625													for v in &st.mstk[from..] {
626														unsafe {
627															p.as_mut().push((**v).clone());	//SAFETY: `NonNull`s point to nested arrays in abuf, only one is accessed at a time
628														}
629													}
630												}
631												else {
632													st.mstk.extend_from_within(from..);
633												}
634											}
635											else {
636												st.mstk.push(va);
637												valerr!('D', "Can't duplicate {} values, stack depth is {}", u, st.mstk.len() - 1);
638											}
639										}
640										Err(_) => {
641											let vs = va.to_string();
642											st.mstk.push(va);
643											valerr!('D', "Can't possibly duplicate {} values", vs);
644										}
645									}
646								}
647								else {
648									let ta = TypeLabel::from(&*va);
649									st.mstk.push(va);
650									synerr!('D', "Expected a number, {} given", ta);
651								}
652							}
653							else {
654								synerr!('D', "Expected 1 argument, 0 given");
655							}
656						},
657						b'R' => {	//rotate
658							if let Some(va) = st.mstk.pop() {
659								if let Value::N(r) = &*va {
660									match usize::try_from(r) {
661										Ok(0) => {},	//no-op
662										Ok(u) => {
663											if let Some(from) = st.mstk.len().checked_sub(u) {
664												if alt {st.mstk[from..].rotate_left(1);}
665												else {st.mstk[from..].rotate_right(1);}
666											}
667											else {
668												st.mstk.push(va);
669												valerr!('R', "Can't rotate {} values, stack depth is {}", u, st.mstk.len() - 1);
670											}
671										}
672										Err(_) => {
673											let vs = va.to_string();
674											st.mstk.push(va);
675											valerr!('R', "Can't possibly rotate {} values", vs);
676										}
677									}
678								}
679								else {
680									let ta = TypeLabel::from(&*va);
681									st.mstk.push(va);
682									synerr!('R', "Expected a number, {} given", ta);
683								}
684							}
685							else {
686								synerr!('R', "Expected 1 argument, 0 given");
687							}
688						},
689						b'?' => {	//read line
690							let res = {
691								let inp = &mut io.lock().unwrap().0;
692								inp.read_line()
693								//drop lock
694							};
695							match res {
696								Ok(s) => {
697									push!(Value::S(s));
698								},
699								Err(e) => {
700									match e.kind() {
701										ErrorKind::Interrupted => {
702											valerr!('?', "Interrupted");
703										},
704										ErrorKind::UnexpectedEof => {
705											push!(Value::S(String::new()));
706										},
707										_ => {
708											return Err(e);
709										}
710									}
711								}
712							}
713						},
714						b'p' => {	//println top
715							if let Some(va) = st.mstk.pop() {
716								let vs = va.display(st.params.get_k(), st.params.get_o(), st.params.get_m(), alt);
717								if let Some(s) = &mut pbuf {
718									s.push_str(&vs);
719									s.push('\n');
720								}
721								else {
722									let out = &mut io.lock().unwrap().1;
723									writeln!(out, "{}", vs)?;
724									out.flush()?;
725									//drop lock
726								}
727							}
728							else {
729								synerr!('p', "Expected 1 argument, 0 given");
730							}
731						},
732						b'P' => {	//print top
733							if let Some(va) = st.mstk.pop() {
734								let vs = va.display(st.params.get_k(), st.params.get_o(), st.params.get_m(), alt);
735								if let Some(s) = &mut pbuf {
736									s.push_str(&vs);
737								}
738								else {
739									let out = &mut io.lock().unwrap().1;
740									write!(out, "{}", vs)?;
741									out.flush()?;
742									//drop lock
743								}
744							}
745							else {
746								synerr!('P', "Expected 1 argument, 0 given");
747							}
748						},
749						b'"' => {	//toggle pbuf
750							if let Some(s) = pbuf.take() {	//close
751								push!(Value::S(s));
752							}
753							else {	//open
754								pbuf = Some(String::new());
755							}
756						},
757						b'(' => {	//array input: open
758							let nn = if let Some(p) = dest.last_mut() { unsafe {	//SAFETY: dest is only mutated here, logic is sound
759								p.as_mut().push(Value::A(Vec::new()));	//create nested A
760								let Value::A(new) = &mut p.as_mut().last_mut().unwrap_unchecked() else { std::hint::unreachable_unchecked() };	//and get reference to it
761								NonNull::from(new)
762							}}
763							else {
764								NonNull::from(&mut abuf)	//abuf itself as first layer
765							};
766							dest.push(nn);
767						},
768						b')' => {	//array input: close
769							if dest.pop().is_some() {
770								if dest.is_empty() {	//completed
771									st.mstk.push(Arc::new(Value::A(std::mem::take(&mut abuf))));	//commit to stack
772								}
773							}
774							else {
775								synerr!(')', "Mismatched closing ')'");
776							}
777						},
778						b'x' => {
779							if let Some(top) = st.mstk.pop() {
780								let sec = st.mstk.pop();
781								match Utf8Iter::from_vals(&top, sec.as_deref()) {
782									Ok((mut stk, ret)) => {
783										if let Some(sec) = sec && ret {	//sec was not used, return
784											st.mstk.push(sec);
785										}
786
787										if mac.is_finished() && *count == Natural::ZERO {	//tail call optimization: discard current macro if finished
788											call.pop();
789										}
790
791										call.append(&mut stk);
792										continue 'mac;
793									},
794									Err(e) => {
795										if let Some(sec) = sec {st.mstk.push(sec);}
796										st.mstk.push(top);
797										synerr!('x', "{}", e);
798									}
799								}
800							}
801							else {
802								synerr!('x', "Expected 1 or 2 arguments, 0 given");
803							}
804						},
805						b'q' => {
806							let u = u8::wrapping_from(&Integer::rounding_from(rptr.unwrap_or_default(), RoundingMode::Down).0);
807							return if alt {
808								Ok(HardQuit(u))
809							}
810							else {
811								Ok(SoftQuit(u))
812							};
813						},
814						b'Q' => {
815							if let Some(va) = st.mstk.pop() {
816								match &*va {
817									Value::N(r) => {
818										if let Ok(u) = usize::try_from(r) {
819											call.truncate(call.len().saturating_sub(u));
820											if !dest.is_empty() {	//flush array buffer if not completed
821												st.mstk.push(Arc::new(Value::A(std::mem::take(&mut abuf))));
822												dest.clear();	//SAFETY: remove leftover dangling references
823											}
824											continue 'mac;
825										}
826										else {
827											let vs = va.to_string();
828											st.mstk.push(va);
829											valerr!('Q', "Cannot possibly break {} macros", vs);
830										}
831									},
832									_ => {
833										let ta = TypeLabel::from(&*va);
834										st.mstk.push(va);
835										synerr!('Q', "Expected a number, {} given", ta);
836									}
837								}
838							}
839							else {
840								synerr!('Q', "Expected 1 argument, 0 given");
841							}
842						},
843						b'a' => {	//array commands
844							match mac.next() {
845								Some(b) if !matches!(byte_cmd(b), Space) => {
846									match b {
847										b'a' => {
848											todo!()
849										},
850										_ => {
851											synerr!('a', "Invalid array command 'a{}'", b as char);
852										}
853									}
854								},
855								Some(_) | None => {
856									synerr!('a', "Incomplete array command 'a'");
857								}
858							}
859						},
860						b'f' => {	//stack commands
861							match mac.next() {
862								Some(b) if !matches!(byte_cmd(b), Space) => {
863									match b {
864										b'z' => {	//stack depth
865											push!(Value::N(st.mstk.len().into()));
866										},
867										b'r' => {	//reverse stack
868											st.mstk.reverse();
869										},
870										b'R' => {	//reverse part of stack
871											if let Some(va) = st.mstk.pop() {
872												if let Value::N(r) = &*va {
873													match usize::try_from(r) {
874														Ok(0) => {},	//no-op
875														Ok(u) => {
876															if let Some(from) = st.mstk.len().checked_sub(u) {
877																st.mstk[from..].reverse();
878															}
879															else {
880																st.mstk.push(va);
881																valerr!('f', "Can't reverse {} values, stack depth is {}", u, st.mstk.len() - 1);
882															}
883														}
884														Err(_) => {
885															let vs = va.to_string();
886															st.mstk.push(va);
887															valerr!('f', "Can't possibly reverse {} values", vs);
888														}
889													}
890												}
891												else {
892													let ta = TypeLabel::from(&*va);
893													st.mstk.push(va);
894													synerr!('f', "Expected a number, {} given", ta);
895												}
896											}
897											else {
898												synerr!('f', "Expected 1 argument, 0 given");
899											}
900										},
901										b'f' => {	//swap with reg
902											let ri = if let Some(r) = rptr.take() {r}
903											else {
904												if matches!(mac.next().map(|b| {mac.back(); byte_cmd(b)}), None | Some(Space)) {
905													synerr!('f', "No register index");
906													alt = false;
907													continue 'cmd;
908												}
909												Rational::from(
910													match mac.try_next_char() {
911														Ok(c) => {c as u32},
912														Err(e) => {
913															*count = Natural::ZERO;
914															synerr!('\0', "Aborting invalid macro: {}", e);
915															break 'cmd;
916														}
917													}
918												)
919											};
920											let reg = st.regs.get_mut(&ri);
921											std::mem::swap(&mut st.mstk, &mut reg.v);
922										},
923										b'p' => {	//print stack
924											for v in &st.mstk {
925												let vs = v.display(st.params.get_k(), st.params.get_o(), st.params.get_m(), alt);
926												if let Some(s) = &mut pbuf {
927													s.push_str(&vs);
928													s.push('\n');
929												}
930												else {
931													let out = &mut io.lock().unwrap().1;
932													writeln!(out, "{}", vs)?;
933													out.flush()?;
934												}
935											}
936										},
937										_ => {
938											synerr!('f', "Invalid stack command 'f{}'", b as char);
939										}
940									}
941								},
942								Some(_) | None => {
943									synerr!('f', "Incomplete stack command 'f'");
944								}
945							}
946						},
947						b'N' => {
948							match (st.mstk.pop(), alt) {
949								(Some(va), false) => {	//get random natural
950									match &*va {
951										Value::N(r) => {
952											match Natural::try_from(r) {
953												Ok(n) => {
954													push!(Value::N(Rational::from(
955														malachite::natural::random::get_random_natural_less_than(rng.get_or_insert_with(rng_os), &n)
956													)));
957												},
958												_ => {
959													st.mstk.push(va);
960													valerr!('N', "Limit must be a natural number");
961												}
962											}
963										},
964										_ => {
965											let ta = TypeLabel::from(&*va);
966											st.mstk.push(va);
967											synerr!('N', "Expected a number, {} given", ta);
968										}
969									}
970								}
971								(Some(va), true) => {	//seed rng
972									match &*va {
973										Value::N(r) => {
974											match Integer::try_from(r) {
975												Ok(Integer::NEGATIVE_ONE) => {	//return to os seed (lazy init)
976													rng = None;
977												},
978												Ok(i) if Natural::convertible_from(&i) => {	//custom seed
979													let n= unsafe { Natural::try_from(i).unwrap_unchecked() };	//SAFETY: just checked
980													let mut bytes: Vec<u8> = PowerOf2DigitIterable::<u8>::power_of_2_digits(&n, 8).take(32).collect();
981													bytes.resize(32, 0);
982													rng = Some(rng_preset( unsafe { <[u8; 32]>::try_from(bytes).unwrap_unchecked() } ));
983												},
984												_ => {
985													st.mstk.push(va);
986													valerr!('N', "Seed must be a natural number or `1");
987												}
988											}
989										},
990										_ => {
991											let ta = TypeLabel::from(&*va);
992											st.mstk.push(va);
993											synerr!('N', "Expected a number, {} given", ta);
994										}
995									}
996								}
997								(None, _) => {
998									synerr!('N', "Expected 1 argument, 0 given");
999								}
1000							}
1001						},
1002						b'w' => {	//wait
1003							if let Some(va) = st.mstk.pop() {
1004								if let Value::N(r) = &*va {
1005									if let Some(dur) = Natural::try_from(r).ok().and_then(|n| {
1006										let (s, ns) = n.div_rem(Natural::const_from(1_000_000_000));
1007										u64::try_from(&s).ok().map(|s| {
1008											let ns = unsafe { u32::try_from(&ns).unwrap_unchecked() };	//SAFETY: remainder always fits
1009											std::time::Duration::new(s, ns)
1010										})
1011									}) {
1012										if let Some(rx) = kill {
1013											match rx.recv_timeout(dur) {
1014												Ok(()) => {	//killed by parent
1015													#[expect(unused_assignments)]
1016													for s in st.regs.end_threads(true) {	//propagate to children
1017														valerr!('j', s);
1018													}
1019													return Ok(Killed);
1020												},
1021												Err(RecvTimeoutError::Timeout) => {	//wait completed, not killed
1022													//do nothing
1023												},
1024												Err(RecvTimeoutError::Disconnected) => {	//parent should never disconnect
1025													unreachable!()
1026												}
1027											}
1028										}
1029										else {
1030											std::thread::sleep(dur);	//no kill receiver, just sleep
1031										}
1032									}
1033									else {
1034										let vs = va.to_string();
1035										st.mstk.push(va);
1036										valerr!('w', "Can't possibly wait {} ns", vs);
1037									}
1038								}
1039								else {
1040									let ta = TypeLabel::from(&*va);
1041									st.mstk.push(va);
1042									synerr!('w', "Expected a number, {} given", ta);
1043								}
1044							}
1045							else {
1046								synerr!('w', "Expected 1 argument, 0 given");
1047							}
1048						},
1049						b'W' => {
1050							push!(Value::N(
1051								match std::time::SystemTime::UNIX_EPOCH.elapsed() {
1052									Ok(dur) => {
1053										Rational::from(dur.as_nanos())
1054									},
1055									Err(e) => {
1056										Rational::from(e.duration().as_nanos()).neg()
1057									}
1058								}
1059							));
1060						},
1061						b'_' => {	//word commands
1062							let mut word = Vec::new();
1063							while let Some(b) = mac.next() {
1064								if matches!(byte_cmd(b), Space) {
1065									mac.back();
1066									break;
1067								}
1068								else {
1069									word.push(b);
1070								}
1071							}
1072
1073							match &word[..] {	//word commands:
1074								b"restrict" => {
1075									#[cfg_attr(feature = "no_os", expect(unused_assignments))] { restrict = true; }
1076								},
1077								b"quiet" => {
1078									ll = LogLevel::Quiet;
1079								},
1080								b"error" => {
1081									ll = LogLevel::Normal;
1082								},
1083								b"debug" => {
1084									ll = LogLevel::Debug;
1085								},
1086								b"err" => {
1087									push!(Value::A(
1088										if let Some((n, c, s)) = elatch.take() {
1089											vec![
1090												Value::N(n.into()),
1091												Value::S(c.into()),
1092												Value::S(s)
1093											]
1094										}
1095										else { vec![] }
1096									));
1097								},
1098								b"th" => {
1099									push!(Value::S(th_name.clone()));
1100								},
1101								b"joinall" => {
1102									for s in st.regs.end_threads(false) {
1103										valerr!('j', s);
1104									}
1105								},
1106								b"killall" => {
1107									for s in st.regs.end_threads(true) {
1108										valerr!('j', s);
1109									}
1110								},
1111								b"trim" => {
1112									st.trim();
1113									RE_CACHE.clear();
1114								},
1115								b"clhist" => {
1116									io.lock().unwrap().0.clear_history();
1117								},
1118								b"clpar" => {
1119									st.params = ParamStk::default();
1120								},
1121								b"clall" => {
1122									st.clear_vals();
1123									RE_CACHE.clear();
1124								},
1125								_ => {
1126									#[cfg(feature = "no_os")]
1127									{
1128										synerr!('_', "Invalid word command '{}'", string_or_bytes(&word));
1129									}
1130									#[cfg(not(feature = "no_os"))]
1131									{
1132										match (restrict, os::OS_CMDS.get(&word).copied()) {
1133											(false, Some(oscmd)) => {
1134												match unsafe { oscmd(st) } {	//SAFETY: only reachable in the main thread, according to interpreter contract
1135													Ok(mut v) => {
1136														append!(v);
1137													}
1138													Err(e) => {
1139														if let Some(se) = e.strip_suffix('!') {
1140															synerr!('_', "OS command '{}': {}", string_or_bytes(&word), se);
1141														}
1142														else {
1143															valerr!('_', "OS command '{}': {}", string_or_bytes(&word), e);
1144														}
1145													}
1146												}
1147											}
1148											(true, Some(_)) => {
1149												synerr!('_', "OS command '{}' is disabled (restricted mode)", string_or_bytes(&word));
1150											},
1151											_ => {
1152												synerr!('_', "Invalid word command '{}'", string_or_bytes(&word));
1153											}
1154										}
1155									}
1156								}
1157							}
1158						},
1159						_ => unreachable!()
1160					}
1161				},
1162				ExecR => {
1163					let ri = if let Some(r) = rptr.take() {r}
1164					else {
1165						if matches!(mac.next().map(|b| {mac.back(); byte_cmd(b)}), None | Some(Space)) {
1166							synerr!(b as char, "No register index");
1167							alt = false;
1168							continue 'cmd;
1169						}
1170						Rational::from(
1171							match mac.try_next_char() {
1172								Ok(c) => {c as u32},
1173								Err(e) => {
1174									*count = Natural::ZERO;
1175									synerr!('\0', "Aborting invalid macro: {}", e);
1176									break 'cmd;
1177								}
1178							}
1179						)
1180					};
1181					debug!("Register command {}{}", if alt {"alt-"} else {""}, b as char);
1182					match b {
1183						b'Z' => {	//stack depth
1184							push!(Value::N(
1185								st.regs.try_get(&ri).map(|reg| reg.v.len().into()).unwrap_or_default()
1186							));
1187						},
1188						b's' => {
1189							if let Some(va) = st.mstk.pop() {
1190								let reg = st.regs.get_mut(&ri);
1191								if let Some(rv) = reg.v.last_mut() {
1192									*rv = va;
1193								}
1194								else {
1195									reg.v.push(va);
1196								}
1197							}
1198							else {
1199								synerr!('s', "Stack is empty");
1200							}
1201						},
1202						b'S' => {
1203							if let Some(va) = st.mstk.pop() {
1204								st.regs.get_mut(&ri).v.push(va);
1205							}
1206							else {
1207								synerr!('S', "Stack is empty");
1208							}
1209						},
1210						b'l' => {
1211							if let Some(rv) = st.regs.try_get(&ri).and_then(|reg| reg.v.last()) {
1212								if let Some(p) = dest.last_mut() {	//manual shadowed push
1213									unsafe {
1214										p.as_mut().push((**rv).clone());	//SAFETY: `NonNull`s point to nested arrays in abuf, only one is accessed at a time
1215									}
1216								}
1217								else {
1218									st.mstk.push(Arc::clone(rv));
1219								}
1220							}
1221							else {
1222								synerr!('l', "Register {} is empty", reg_index_nice(&ri));
1223							}
1224						},
1225						b'L' => {
1226							if let Some(rv) = st.regs.try_get_mut(&ri).and_then(|reg| reg.v.pop()) {
1227								if let Some(p) = dest.last_mut() {	//manual shadowed push
1228									unsafe {
1229										p.as_mut().push((*rv).clone());	//SAFETY: `NonNull`s point to nested arrays in abuf, only one is accessed at a time
1230									}
1231								}
1232								else {
1233									st.mstk.push(Arc::clone(&rv));
1234								}
1235							}
1236							else {
1237								synerr!('L', "Register {} is empty", reg_index_nice(&ri));
1238							}
1239						},
1240						b'X' => {
1241							if let Some(true) = st.regs.try_get(&ri).map(|reg| reg.th.is_some()) {
1242								valerr!('X', "Register {} is already running a thread", reg_index_nice(&ri));
1243							}
1244							else if let Some(va) = st.mstk.pop() {
1245								if let Value::S(sa) = &*va {
1246									let th_start = sa.to_owned().into();
1247									let (ktx, krx) = std::sync::mpsc::channel::<()>();
1248									let (jtx, jrx) = std::sync::mpsc::channel::<()>();
1249									let tb = std::thread::Builder::new().name(format!("{th_name}{}: ", reg_index_nice(&ri)));
1250									let mut th_st = if alt { st.clone() } else { State::default() };
1251									let th_io = Arc::clone(&io);
1252
1253									match tb.spawn(move || {
1254										let th_res = interpreter_no_os(&mut th_st, th_start, th_io, ll, Some(&krx));
1255
1256										let _ = jrx.recv();	//wait for join signal
1257										th_st.regs.end_threads(	//join children
1258											th_res.is_err()	//kill on io error
1259											||
1260											match krx.try_recv() {	//check for unprocessed kill signal
1261												Ok(()) => { true },
1262												Err(TryRecvError::Empty) => { false },
1263												Err(TryRecvError::Disconnected) => { unreachable!() }	//parent should never disconnect
1264											}
1265										);
1266
1267										(th_st.mstk, th_res)
1268									}) {
1269										Ok(jh) => {
1270											st.regs.get_mut(&ri).th = Some((jh, ktx, jtx));
1271										},
1272										Err(e) => {
1273											st.mstk.push(va);
1274											valerr!('X', "Can't spawn child thread: {}", e);
1275										}
1276									}
1277								}
1278								else {
1279									let ta = TypeLabel::from(&*va);
1280									st.mstk.push(va);
1281									synerr!('X', "Expected a string, {} given", ta);
1282								}
1283							}
1284							else {
1285								synerr!('X', "Expected 1 argument, 0 given");
1286							}
1287						},
1288						b'j' => {
1289							let ri_nice = reg_index_nice(&ri);
1290							if let Some(reg) = st.regs.try_get_mut(&ri) && let Some((jh, ktx, jtx)) = reg.th.take() {
1291								if alt {
1292									ktx.send(()).unwrap_or_else(|_| panic!("Thread {} panicked, terminating!", ri_nice));
1293								}
1294								jtx.send(()).unwrap_or_else(|_| panic!("Thread {} panicked, terminating!", ri_nice));
1295								match jh.join() {
1296									Ok(mut tr) => {
1297										match tr.1 {
1298											Err(e) => {
1299												let s = format!("IO error in thread {}: {}", ri_nice, e);
1300												eprintln!("? {th_name}j: {}", s);	//io.2 might be unusable, fall back to fresh stderr
1301												elatch = Some((Natural::ZERO, 'j', s));
1302											},
1303											Ok(SoftQuit(c)) if c != 0 => {
1304												valerr!('j', "Thread {} quit with code {}", ri_nice, c);
1305											},
1306											Ok(HardQuit(c)) if c != 0 => {
1307												valerr!('j', "Thread {} hard-quit with code {}", ri_nice, c);
1308											},
1309											Ok(Killed) => {
1310												valerr!('j', "Thread {} was killed", ri_nice);
1311											},
1312											_ => {}
1313										}
1314
1315										reg.v.append(&mut tr.0);
1316									},
1317									Err(e) => {
1318										std::panic::resume_unwind(e);
1319									}
1320								}
1321							}
1322							else {
1323								valerr!('j', "Register {} is not running a thread", ri_nice);
1324							}
1325						},
1326						b'J' => {
1327							if let Some(Some((jh, _, _))) = st.regs.try_get(&ri).map(|reg| &reg.th) {
1328								let mut bz = BitVec::new();
1329								bz.push(jh.is_finished());
1330								push!(Value::B(bz));
1331							}
1332							else {
1333								valerr!('J', "Register {} is not running a thread", reg_index_nice(&ri));
1334							}
1335						},
1336						_ => unreachable!()
1337					}
1338				},
1339				Lit => {
1340					match b {
1341						b'T' | b'F' => {	//booleans
1342							debug!("Boolean literal");
1343							let mut bits = BitVec::new();
1344							bits.push(b == b'T');
1345							while let Some(b) = mac.next() {
1346								match b {
1347									b'T' => {bits.push(true);},
1348									b'F' => {bits.push(false);},
1349									_ => {
1350										mac.back();
1351										break;
1352									}
1353								}
1354							}
1355							push!(Value::B(bits));
1356						},
1357						b'\'' | b'0'..=b'9' | b'.' | b'@' => {	//numbers
1358							debug!("Number literal");
1359							let mut ipart = Vec::new();
1360							let mut fpart = Vec::new();
1361							let mut rpart = Vec::new();
1362							let mut get_epart = true;
1363							let mut exp = None;
1364							let mut discard = false;
1365							let mut ibase = st.params.get_i().clone();
1366
1367							match (b == b'\'', ibase > Natural::const_from(36)) {
1368								(false, high_base) => {	//plain
1369									mac.back();
1370									ibase = if high_base {Natural::const_from(10)} else {ibase};	//if plain but I>36, interpret mantissa with I=10
1371									while let Some(ib) = mac.next() {	//integer part
1372										let id = ib.wrapping_sub(0x30);
1373										match id {
1374											0..=9 if id < ibase => {ipart.push(Natural::from(id));},
1375											0..=9 => {
1376												synerr!('\'', "Digit {} is too high for base {}", id, ibase);
1377												discard = true;
1378											},
1379											_ => {
1380												mac.back();
1381												break;
1382											}
1383										}
1384									}
1385									match mac.next() {
1386										Some(b'.') => {	//fractional part
1387											let mut recur = false;	//recurring digits started
1388											while let Some(fb) = mac.next() {
1389												let fd = fb.wrapping_sub(0x30);
1390												match fd {
1391													0x30 if !recur => {recur = true;},	//b'`' == 0x60
1392													0..=9 if !recur && fd < ibase => {fpart.push(Natural::from(fd));},
1393													0..=9 if recur && fd < ibase => {rpart.push(Natural::from(fd));},
1394													0..=9 => {
1395														synerr!('\'', "Digit {} is too high for base {}", fd, ibase);
1396														discard = true;
1397													},
1398													_ => {
1399														mac.back();
1400														break;
1401													}
1402												}
1403											}
1404										},
1405										Some(_) => {mac.back();},
1406										None => {}
1407									}
1408								}
1409								(true, false) => {	//prefixed
1410									while let Some(ib) = mac.next() {	//integer part
1411										if let Some(id) = mixed_ascii_to_digit(ib) {
1412											if id < ibase {ipart.push(Natural::from(id));}
1413											else {
1414												synerr!('\'', "Digit {} is too high for base {}", id, ibase);
1415												discard = true;
1416											}
1417										}
1418										else {
1419											mac.back();
1420											break;
1421										}
1422									}
1423									match mac.next() {
1424										Some(b'.') => {	//fractional part
1425											let mut recur = false;	//recurring digits started
1426											while let Some(fb) = mac.next() {
1427												if let Some(fd) = mixed_ascii_to_digit(fb) {
1428													if fd < ibase {
1429														if !recur {fpart.push(Natural::from(fd));}
1430														else {rpart.push(Natural::from(fd));}
1431													}
1432													else {
1433														synerr!('\'', "Digit {} is too high for base {}", fd, ibase);
1434														discard = true;
1435													}
1436												}
1437												else if !recur && fb == b'`' {recur = true;}
1438												else {
1439													mac.back();
1440													break;
1441												}
1442											}
1443										},
1444										Some(_) => {mac.back();},
1445										None => {}
1446									}
1447								},
1448								(true, true) => {	//enclosed
1449									get_epart = false;
1450									let ns= mac.by_ref().take_while(|b| *b != b'\'').collect::<Vec<u8>>();
1451									if ns.is_empty() {
1452										synerr!('\'', "Empty any-base number");
1453										alt = false;
1454										continue 'cmd;
1455									}
1456									for nc in ns.iter() {
1457										match nc {
1458											b' ' | b'.' | b'0'..=b'9' | b'@' | b'`' => {}	//fine
1459											wrong => {
1460												synerr!('\'', "Invalid character in any-base number: {}", string_or_bytes(&[*wrong]));
1461												alt = false;
1462												continue 'cmd;
1463											}
1464										}
1465									}
1466									let mut ms = Vec::new();
1467									match ns.split(|b| *b == b'@').collect::<Vec<&[u8]>>()[..] {
1468										[mpart] => {	//only mantissa
1469											ms = mpart.to_vec();
1470										},
1471										[mpart, epart] => {	//mantissa and exponent
1472											ms = mpart.to_vec();
1473											let mut es = epart.to_vec();
1474											if let Some(first) = es.first_mut() && *first == b'`' { *first = b'-'; }
1475											match String::from_utf8(es) {
1476												Ok(es) => {
1477													match es.parse::<i64>() {
1478														Ok(i) => { exp = Some(i); },
1479														Err(e) => {
1480															use std::num::IntErrorKind::*;
1481															match e.kind() {
1482																Empty => { exp = Some(0); },
1483																InvalidDigit => {
1484																	valerr!('\'', "Invalid exponent: {}", es);
1485																	alt = false;
1486																	continue 'cmd;
1487																},
1488																PosOverflow | NegOverflow => {
1489																	valerr!('\'', "Exponent {} is unrepresentable", es);
1490																	alt = false;
1491																	continue 'cmd;
1492																},
1493																_ => { unreachable!() }
1494															}
1495														}
1496													}
1497												},
1498												_ => { unreachable!() }
1499											}
1500										},
1501										ref v => {
1502											synerr!('\'', "{} exponent signs (@) in any-base number", v.len() - 1);
1503											drop(ms);
1504											alt = false;
1505											continue 'cmd;
1506										}
1507									}
1508									let mut is = Vec::new();
1509									let mut frs = Vec::new();
1510									match ms.split(|b| *b == b'.').collect::<Vec<&[u8]>>()[..] {
1511										[ipart] => {
1512											is = ipart.to_vec();
1513										},
1514										[ipart, fpart] => {
1515											is = ipart.to_vec();
1516											frs = fpart.to_vec();
1517										},
1518										ref v => {
1519											synerr!('\'', "{} fractional points (.) in any-base number", v.len() - 1);
1520											drop(is);
1521											alt = false;
1522											continue 'cmd;
1523										}
1524									}
1525									if is.contains(&b'`') {
1526										synerr!('\'', "Unexpected negative sign (`) in any-base number");
1527										alt = false;
1528										continue 'cmd;
1529									}
1530									let mut fs = Vec::new();
1531									let mut rs = Vec::new();
1532									match frs.split(|b| *b == b'`').collect::<Vec<&[u8]>>()[..] {
1533										[fpart] => {
1534											fs = fpart.to_vec();
1535										},
1536										[fpart, rpart] => {
1537											fs = fpart.to_vec();
1538											rs = rpart.to_vec();
1539										},
1540										ref v => {
1541											synerr!('\'', "{} recurring marks (`) in any-base number", v.len() - 1);
1542											drop(fs);
1543											alt = false;
1544											continue 'cmd;
1545										}
1546									}
1547									if !is.is_empty() { for id in is.split(|b| *b == b' ') {
1548										let id = str::from_utf8(id).unwrap();
1549										ipart.push(Natural::from_str(id).unwrap());
1550									}}
1551									if !fs.is_empty() { for fd in fs.split(|b| *b == b' ') {
1552										let fd = str::from_utf8(fd).unwrap();
1553										fpart.push(Natural::from_str(fd).unwrap());
1554									}}
1555									if !rs.is_empty() { for rd in rs.split(|b| *b == b' ') {
1556										let rd = str::from_utf8(rd).unwrap();
1557										rpart.push(Natural::from_str(rd).unwrap());
1558									}}
1559									for d in ipart.iter().chain(fpart.iter()).chain(rpart.iter()) {
1560										if *d >= ibase {
1561											synerr!('\'', "Digit {} is too high for base {}", d, ibase);
1562											alt = false;
1563											continue 'cmd;
1564										}
1565									}
1566								}
1567							}
1568
1569							let m_empty = ipart.is_empty() && fpart.is_empty() && rpart.is_empty();	//simpler const zero, also keep track of emptiness
1570							let mut r;
1571							if m_empty {
1572								r = Rational::ZERO;
1573							}
1574							else {
1575								ipart.reverse();	//malachite needs reverse order
1576								r = Rational::from_digits(&ibase, ipart, RationalSequence::from_vecs(fpart, rpart));
1577								if alt {r.neg_assign();}	//negative sign was set using alt
1578							}
1579
1580							if get_epart {
1581								match mac.next() {
1582									Some(b'@') => {    //exponent part
1583										let mut es = String::new();
1584										let mut eneg = false;    //negative sign occurred
1585										while let Some(eb) = mac.next() {
1586											match eb {
1587												b'`' if !eneg => { es.push('-'); }
1588												b'0'..=b'9' => { es.push(eb as char); }
1589												_ => {
1590													mac.back();
1591													break;
1592												}
1593											}
1594											eneg = true;    //only allow on first char
1595										}
1596										if es.is_empty() { es.push('0'); }
1597										if m_empty { r = Rational::ONE; }    //only exponent part present
1598										if let Ok(i) = es.parse::<i64>() {
1599											r *= Rational::from(st.params.get_i()).pow(i);    //apply exponent, get original ibase
1600										}
1601										else {
1602											valerr!('\'', "Exponent {} is unrepresentable", es);
1603											discard = true;
1604										}
1605									}
1606									Some(_) => { mac.back(); }
1607									None => {}
1608								}
1609							}
1610							else if let Some(i) = exp {
1611       							if m_empty { r = Rational::ONE; }    //only exponent part present
1612       							r *= Rational::from(ibase).pow(i);    //apply exponent
1613							}
1614
1615							if !discard {
1616								push!(Value::N(r));
1617							}
1618						},
1619						b'[' => {	//strings
1620							debug!("String literal");
1621							let mut bytes = Vec::new();
1622							let mut discard = false;
1623							let mut nest = 1usize;
1624							while let Some(b) = mac.next() {
1625								match b {
1626									b'[' => {
1627										nest = unsafe { nest.unchecked_add(1) };
1628										bytes.push(b'[');
1629									},
1630									b']' => {
1631										nest = unsafe { nest.unchecked_sub(1) };
1632										if nest == 0 {
1633											break;
1634										}
1635										else {
1636											bytes.push(b']');
1637										}
1638									},
1639									b'\\' => {	//character escapes
1640										match mac.next() {
1641											Some(b'a') => {bytes.push(0x07);},		//bell
1642											Some(b'b') => {bytes.push(0x08);},		//backspace
1643											Some(b't') => {bytes.push(0x09);},		//horizontal tab
1644											Some(b'n') => {bytes.push(0x0A);},		//line feed
1645											Some(b'v') => {bytes.push(0x0B);},		//vertical tab
1646											Some(b'f') => {bytes.push(0x0C);},		//form feed
1647											Some(b'r') => {bytes.push(0x0D);},		//carriage return
1648											Some(b'e') => {bytes.push(0x1B);},		//escape
1649											Some(b'[') => {bytes.push(b'[');},		//literal opening bracket
1650											Some(b']') => {bytes.push(b']');},		//literal closing bracket
1651											Some(b'\\') => {bytes.push(b'\\');},	//literal backslash
1652											Some(b0) => {							//other escapes
1653												if let Some(high) = upper_hex_to_nibble(b0) {	//byte literal
1654													if let Some(b1) = mac.next() {
1655														if let Some(low) = upper_hex_to_nibble(b1) {
1656															bytes.push(high << 4 | low);
1657														}
1658														else {
1659															synerr!('[', "Invalid byte escape: \\{}{}", b0 as char, b1 as char);
1660															discard = true;
1661														}
1662													}
1663													else {
1664														synerr!('[', "Incomplete byte escape: \\{}", b0 as char);
1665														discard = true;
1666													}
1667												}
1668												else {	//wrong escape
1669													mac.back();
1670													match mac.try_next_char() {
1671														Ok(c) => {
1672															synerr!('[', "Invalid character escape: \\{} (U+{:04X})", c, c as u32);
1673															discard = true;
1674														},
1675														Err(e) => {
1676															*count = Natural::ZERO;
1677															synerr!('\0', "Aborting invalid macro: {}", e);
1678															break 'cmd;
1679														}
1680													}
1681												}
1682											},
1683											None => {
1684												synerr!('[', "Incomplete character escape: \\");
1685												discard = true;
1686											}
1687										}
1688									},
1689									_ => {
1690										bytes.push(b);
1691									}
1692								}
1693							}
1694							if !discard {
1695								match String::try_from(Utf8Iter::from(bytes)) {
1696									Ok(s) => {
1697										push!(Value::S(s));
1698									},
1699									Err(e) => {
1700										synerr!('[', "Invalid string: {}", e);
1701									}
1702								}
1703							}
1704						},
1705						_ => unreachable!()
1706					}
1707				},
1708				Space if b == b'#' => {	//line comment
1709					debug!("Line comment");
1710					mac.find(|b| *b == b'\n');
1711				},
1712				Space => {
1713					//do nothing
1714				},
1715				Wrong => {
1716					mac.back();
1717					match mac.try_next_char() {
1718						Ok(c) => {
1719							synerr!(c, "Invalid command: {} (U+{:04X})", c, c as u32);
1720						},
1721						Err(e) => {
1722							*count = Natural::ZERO;
1723							synerr!('\0', "Aborting invalid macro: {}", e);
1724							break 'cmd;
1725						}
1726					}
1727				}
1728			}
1729			
1730			alt = false;	//reset after digraph
1731		}	//end of command scope
1732
1733		if !dest.is_empty() {	//flush array buffer if not completed
1734			st.mstk.push(Arc::new(Value::A(std::mem::take(&mut abuf))));
1735			dest.clear();	//SAFETY: remove leftover dangling references
1736		}
1737
1738		if *count == Natural::ZERO {	//all repetitions finished
1739			call.pop();
1740		}
1741		else {	//more to go
1742			mac.rewind();
1743		}
1744	}	//end of macro scope
1745	
1746	Ok(Finished)
1747}