Skip to main content

adc_lang/
lib.rs

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