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