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