1use crate::arena::*;
2use crate::atom_table::*;
3use crate::parser::ast::*;
4use crate::parser::dashu::base::RemEuclid;
5use crate::parser::dashu::integer::Sign;
6use crate::parser::dashu::{ibig, Integer, Rational};
7use crate::{
8 alpha_numeric_char, capital_letter_char, cut_char, decimal_digit_char, graphic_token_char,
9 is_fx, is_infix, is_postfix, is_prefix, is_xf, is_xfx, is_xfy, is_yfx, semicolon_char,
10 sign_char, single_quote_char, small_letter_char, solo_char, variable_indicator_char,
11};
12
13use crate::forms::*;
14use crate::heap_iter::*;
15use crate::machine::heap::*;
16use crate::machine::machine_indices::*;
17use crate::machine::machine_state::pstr_loc_and_offset;
18use crate::machine::partial_string::*;
19use crate::machine::stack::*;
20use crate::machine::streams::*;
21use crate::types::*;
22
23use dashu::base::Signed;
24use ordered_float::OrderedFloat;
25
26use indexmap::IndexMap;
27
28use std::cell::Cell;
29use std::convert::TryFrom;
30use std::iter::once;
31use std::net::{IpAddr, TcpListener};
32use std::rc::Rc;
33use std::sync::Arc;
34
35#[derive(Debug, Copy, Clone)]
37pub(crate) enum DirectedOp {
38 Left(Atom, OpDesc),
39 Right(Atom, OpDesc),
40}
41
42impl DirectedOp {
43 #[inline]
44 fn as_atom(&self) -> Atom {
45 match self {
46 &DirectedOp::Left(name, _) | &DirectedOp::Right(name, _) => name,
47 }
48 }
49
50 #[inline]
51 fn is_prefix(&self) -> bool {
52 match self {
53 &DirectedOp::Left(_name, cell) | &DirectedOp::Right(_name, cell) => {
54 is_prefix!(cell.get_spec() as u32)
55 }
56 }
57 }
58
59 #[inline]
60 fn is_negative_sign(&self) -> bool {
61 match self {
62 &DirectedOp::Left(name, cell) | &DirectedOp::Right(name, cell) => {
63 name == atom!("-") && is_prefix!(cell.get_spec() as u32)
64 }
65 }
66 }
67
68 #[inline]
69 fn is_left(&self) -> bool {
70 matches!(self, DirectedOp::Left(..))
71 }
72}
73
74fn needs_bracketing(child_desc: OpDesc, op: &DirectedOp) -> bool {
75 match op {
76 DirectedOp::Left(name, cell) => {
77 let (priority, spec) = cell.get();
78
79 if &*name.as_str() == "-" {
80 let child_assoc = child_desc.get_spec();
81 if is_prefix!(spec) && (is_postfix!(child_assoc) || is_infix!(child_assoc)) {
82 return true;
83 }
84 }
85
86 let is_strict_right = is_yfx!(spec) || is_xfx!(spec) || is_fx!(spec);
87 child_desc.get_prec() > priority
88 || (child_desc.get_prec() == priority && is_strict_right)
89 }
90 DirectedOp::Right(_, cell) => {
91 let (priority, spec) = cell.get();
92 let is_strict_left = is_xfx!(spec) || is_xfy!(spec) || is_xf!(spec);
93
94 if child_desc.get_prec() > priority
95 || (child_desc.get_prec() == priority && is_strict_left)
96 {
97 true
98 } else if (is_postfix!(spec) || is_infix!(spec)) && !is_postfix!(child_desc.get_spec())
99 {
100 *cell != child_desc && child_desc.get_prec() == priority
101 } else {
102 false
103 }
104 }
105 }
106}
107
108impl<'a, ElideLists> StackfulPreOrderHeapIter<'a, ElideLists> {
109 fn leftmost_leaf_has_property<P>(&self, op_dir: &OpDir, property_check: P) -> bool
116 where
117 P: Fn(HeapCellValue) -> bool,
118 {
119 let mut h = match self.stack_last() {
120 Some(h) => h,
121 None => return false,
122 };
123
124 let mut parent_spec = DirectedOp::Left(atom!("-"), OpDesc::build_with(200, FY as u8));
125
126 loop {
127 let cell = self.read_cell(h);
128
129 read_heap_cell!(cell,
130 (HeapCellValueTag::Str, s) => {
131 read_heap_cell!(self.heap[s],
132 (HeapCellValueTag::Atom, (name, _arity)) => {
133 if let Some(spec) = fetch_atom_op_spec(name, None, op_dir) {
134 if is_postfix!(spec.get_spec() as u32) || is_infix!(spec.get_spec() as u32) {
135 if needs_bracketing(spec, &parent_spec) {
136 return false;
137 } else {
138 h = IterStackLoc::iterable_loc(s + 1, HeapOrStackTag::Heap);
139 parent_spec = DirectedOp::Right(name, spec);
140 continue;
141 }
142 }
143 }
144
145 return false;
146 }
147 _ => {
148 return false;
149 }
150 )
151 }
152 _ => {
153 return property_check(cell);
154 }
155 )
156 }
157 }
158
159 fn immediate_leaf_has_property<P>(&self, property_check: P) -> bool
160 where
161 P: Fn(HeapCellValue) -> bool,
162 {
163 let cell = match self.stack_last() {
164 Some(h) => self.read_cell(h),
165 None => return false,
166 };
167
168 property_check(cell)
169 }
170}
171
172fn char_to_string(is_quoted: bool, c: char) -> String {
173 match c {
174 '\'' if is_quoted => "\\'".to_string(),
175 '\n' if is_quoted => "\\n".to_string(),
176 '\r' if is_quoted => "\\r".to_string(),
177 '\t' if is_quoted => "\\t".to_string(),
178 '\u{0b}' if is_quoted => "\\v".to_string(), '\u{0c}' if is_quoted => "\\f".to_string(), '\u{08}' if is_quoted => "\\b".to_string(), '\u{07}' if is_quoted => "\\a".to_string(), '\\' if is_quoted => "\\\\".to_string(),
183 ' ' | '\'' | '\n' | '\r' | '\t' | '\u{0b}' | '\u{0c}' | '\u{08}' | '\u{07}' | '"'
184 | '\\' => c.to_string(),
185 _ => {
186 if c.is_whitespace() || c.is_control() {
187 format!("\\x{:x}\\", c as u32)
189 } else {
190 c.to_string()
191 }
192 }
193 }
194}
195
196#[derive(Clone, Copy, Debug)]
197enum NumberFocus {
198 Unfocused(Number),
199 Denominator(TypedArenaPtr<Rational>),
200 Numerator(TypedArenaPtr<Rational>),
201}
202
203impl NumberFocus {
204 fn is_negative(&self) -> bool {
205 match self {
206 NumberFocus::Unfocused(n) => n.is_negative(),
207 NumberFocus::Denominator(r) | NumberFocus::Numerator(r) => **r < Rational::from(0),
208 }
209 }
210}
211
212#[derive(Debug, Clone, Copy)]
213struct CommaSeparatedCharList {
214 pstr: PartialString,
215 offset: usize,
216 max_depth: usize,
217 end_cell: HeapCellValue,
218 end_h: Option<usize>,
219}
220
221#[derive(Debug, Clone)]
222enum TokenOrRedirect {
223 Atom(Atom),
224 BarAsOp,
225 Char(char),
226 Op(Atom, OpDesc),
227 NumberedVar(String),
228 CompositeRedirect(usize, DirectedOp),
229 CurlyBracketRedirect(usize),
230 FunctorRedirect(usize),
231 #[allow(unused)]
232 IpAddr(IpAddr),
233 NumberFocus(usize, NumberFocus, Option<DirectedOp>),
234 Open,
235 Close,
236 Comma,
237 RawPtr(*const ArenaHeader),
238 Space,
239 LeftCurly,
240 RightCurly,
241 ChildOpenList,
242 ChildCloseList,
243 OpenList(Rc<Cell<(bool, usize)>>),
244 CloseList(Rc<Cell<(bool, usize)>>),
245 HeadTailSeparator,
246 StackPop,
247 CommaSeparatedCharList(CommaSeparatedCharList),
248}
249
250pub(crate) fn requires_space(atom: &str, op: &str) -> bool {
251 match atom.chars().last() {
252 Some(ac) => op
253 .chars()
254 .next()
255 .map(|oc| {
256 if ac == '0' {
257 oc == '\'' || oc == '(' || alpha_numeric_char!(oc)
258 } else if alpha_numeric_char!(ac) {
259 oc == '(' || alpha_numeric_char!(oc)
260 } else if graphic_token_char!(ac) {
261 graphic_token_char!(oc)
262 } else if variable_indicator_char!(ac) || capital_letter_char!(ac) {
263 alpha_numeric_char!(oc)
264 } else if sign_char!(ac) {
265 sign_char!(oc) || decimal_digit_char!(oc)
266 } else if single_quote_char!(ac) {
267 single_quote_char!(oc)
268 } else {
269 false
270 }
271 })
272 .unwrap_or(false),
273 _ => false,
274 }
275}
276
277fn non_quoted_graphic_token<Iter: Iterator<Item = char>>(mut iter: Iter, c: char) -> bool {
278 if c == '/' {
279 match iter.next() {
280 None => true,
281 Some('*') => false, Some(c) => {
283 if graphic_token_char!(c) {
284 iter.all(|c| graphic_token_char!(c))
285 } else {
286 false
287 }
288 }
289 }
290 } else if c == '.' {
291 match iter.next() {
292 None => false,
293 Some(c) => {
294 if graphic_token_char!(c) {
295 iter.all(|c| graphic_token_char!(c))
296 } else {
297 false
298 }
299 }
300 }
301 } else {
302 iter.all(|c| graphic_token_char!(c))
303 }
304}
305
306pub(super) fn non_quoted_token<Iter: Iterator<Item = char>>(mut iter: Iter) -> bool {
307 if let Some(c) = iter.next() {
308 if small_letter_char!(c) {
309 iter.all(|c| alpha_numeric_char!(c))
310 } else if graphic_token_char!(c) {
311 non_quoted_graphic_token(iter, c)
312 } else if semicolon_char!(c) || cut_char!(c) {
313 iter.next().is_none()
314 } else if c == '[' {
315 iter.next() == Some(']') && iter.next().is_none()
316 } else if c == '{' {
317 iter.next() == Some('}') && iter.next().is_none()
318 } else if solo_char!(c) {
319 !(c == '(' || c == ')' || c == '}' || c == ']' || c == ',' || c == '%' || c == '|')
320 } else {
321 false
322 }
323 } else {
324 false
325 }
326}
327
328#[allow(clippy::len_without_is_empty)]
329pub trait HCValueOutputter {
330 type Output;
331
332 fn new() -> Self;
333 fn push_char(&mut self, c: char);
334 fn append(&mut self, s: &str);
335 fn begin_new_var(&mut self);
336 fn insert(&mut self, index: usize, c: char);
337 fn result(self) -> Self::Output;
338 fn ends_with(&self, s: &str) -> bool;
339 fn len(&self) -> usize;
340 fn truncate(&mut self, len: usize);
341 fn as_str(&self) -> &str;
342}
343
344#[derive(Debug)]
345pub struct PrinterOutputter {
346 contents: String,
347}
348
349impl HCValueOutputter for PrinterOutputter {
350 type Output = String;
351
352 fn new() -> Self {
353 PrinterOutputter {
354 contents: String::new(),
355 }
356 }
357
358 fn append(&mut self, contents: &str) {
359 if requires_space(&self.contents, contents) {
360 self.push_char(' ');
361 }
362
363 self.contents += contents;
364 }
365
366 fn push_char(&mut self, c: char) {
367 self.contents.push(c);
368 }
369
370 fn begin_new_var(&mut self) {
371 if !self.contents.is_empty() {
372 self.contents += ", ";
373 }
374 }
375
376 fn insert(&mut self, idx: usize, c: char) {
377 self.contents.insert(idx, c);
378 }
379
380 fn result(self) -> Self::Output {
381 self.contents
382 }
383
384 fn ends_with(&self, s: &str) -> bool {
385 self.contents.ends_with(s)
386 }
387
388 fn len(&self) -> usize {
389 self.contents.len()
390 }
391
392 fn truncate(&mut self, len: usize) {
393 self.contents.truncate(len);
394 }
395
396 fn as_str(&self) -> &str {
397 &self.contents
398 }
399}
400
401#[inline(always)]
402fn is_numbered_var(name: Atom, arity: usize) -> bool {
403 arity == 1 && name == atom!("$VAR")
404}
405
406#[inline]
407fn negated_op_needs_bracketing(
408 iter: &StackfulPreOrderHeapIter<ListElider>,
409 op_dir: &OpDir,
410 op: &Option<DirectedOp>,
411) -> bool {
412 if let Some(ref op) = op {
413 op.is_negative_sign()
414 && iter.leftmost_leaf_has_property(op_dir, |addr| match Number::try_from(addr) {
415 Ok(Number::Fixnum(n)) => n.get_num() > 0,
416 Ok(Number::Float(OrderedFloat(f))) => f > 0f64,
417 Ok(Number::Integer(n)) => n.is_positive(),
418 Ok(Number::Rational(n)) => n.is_positive(),
419 _ => false,
420 })
421 } else {
422 false
423 }
424}
425
426macro_rules! push_char {
427 ($self:ident, $c:expr) => {{
428 $self.outputter.push_char($c);
429 $self.last_item_idx = $self.outputter.len();
430 }};
431}
432
433macro_rules! append_str {
434 ($self:ident, $s:expr) => {{
435 $self.last_item_idx = $self.outputter.len();
436 $self.outputter.append($s);
437 }};
438}
439
440macro_rules! print_char {
441 ($self:ident, $is_quoted:expr, $c:expr) => {
442 if non_quoted_token(once($c)) {
443 let result = char_to_string(false, $c);
444
445 push_space_if_amb!($self, &result, {
446 append_str!($self, &result);
447 });
448 } else {
449 let mut result = String::new();
450
451 if $self.quoted {
452 result.push('\'');
453 result += &char_to_string($is_quoted, $c);
454 result.push('\'');
455 } else {
456 result += &char_to_string($is_quoted, $c);
457 }
458
459 push_space_if_amb!($self, &result, {
460 append_str!($self, result.as_str());
461 });
462 }
463 };
464}
465
466pub fn fmt_float(mut fl: f64) -> String {
467 if OrderedFloat(fl) == -0f64 {
468 fl = 0f64;
469 }
470
471 let mut buffer = ryu::Buffer::new();
472 let fl_str = buffer.format(fl);
473
474 if let Some(e_index) = fl_str.find('e') {
481 if !fl_str[0..e_index].contains('.') {
482 return fl_str[0..e_index].to_string() + ".0" + &fl_str[e_index..];
483 }
484 }
485
486 fl_str.to_string()
487}
488
489#[derive(Debug)]
490pub struct HCPrinter<'a, Outputter> {
491 outputter: Outputter,
492 iter: StackfulPreOrderHeapIter<'a, ListElider>,
493 atom_tbl: Arc<AtomTable>,
494 op_dir: &'a OpDir,
495 state_stack: Vec<TokenOrRedirect>,
496 toplevel_spec: Option<DirectedOp>,
497 last_item_idx: usize,
498 parent_of_first_op: Option<(DirectedOp, usize)>,
499 pub var_names: IndexMap<HeapCellValue, VarPtr>,
500 pub numbervars_offset: Integer,
501 pub numbervars: bool,
502 pub quoted: bool,
503 pub ignore_ops: bool,
504 pub print_strings_as_strs: bool,
505 pub max_depth: usize,
506 pub double_quotes: bool,
507}
508
509macro_rules! push_space_if_amb {
510 ($self:expr, $atom:expr, $action:block) => {
511 if $self.ambiguity_check($atom) {
512 $self.outputter.push_char(' ');
513 $action;
514 } else {
515 $action;
516 }
517 };
518}
519
520pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option<String> {
521 fn numbervar(n: Integer) -> String {
522 static CHAR_CODES: [char; 26] = [
523 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
524 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
525 ];
526
527 let i: usize = (&n).rem_euclid(ibig!(26)).try_into().unwrap();
528 let j = n / ibig!(26);
529
530 if j.is_zero() {
531 CHAR_CODES[i].to_string()
532 } else {
533 format!("{}{}", CHAR_CODES[i], j)
534 }
535 }
536
537 match Number::try_from(addr) {
538 Ok(Number::Fixnum(n)) if n.get_num() >= 0 => {
539 Some(numbervar(offset + Integer::from(n.get_num())))
540 }
541 Ok(Number::Integer(n)) if !n.is_negative() => Some(numbervar(Integer::from(offset + &*n))),
542 _ => None,
543 }
544}
545
546impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
547 pub fn new(
548 heap: &'a mut Heap,
549 atom_tbl: Arc<AtomTable>,
550 stack: &'a mut Stack,
551 op_dir: &'a OpDir,
552 output: Outputter,
553 cell: HeapCellValue,
554 ) -> Self {
555 HCPrinter {
556 outputter: output,
557 iter: stackful_preorder_iter(heap, stack, cell),
558 atom_tbl,
559 op_dir,
560 state_stack: vec![],
561 toplevel_spec: None,
562 last_item_idx: 0,
563 parent_of_first_op: None,
564 numbervars: false,
565 numbervars_offset: Integer::from(0),
566 quoted: false,
567 ignore_ops: false,
568 var_names: IndexMap::new(),
569 print_strings_as_strs: false,
570 max_depth: 0,
571 double_quotes: false,
572 }
573 }
574
575 #[inline]
576 fn ambiguity_check(&self, atom: &str) -> bool {
577 let tail = &self.outputter.as_str()[self.last_item_idx..];
578
579 if atom == "," || !self.quoted || non_quoted_token(atom.chars()) {
580 requires_space(tail, atom)
581 } else {
582 requires_space(tail, "'")
583 }
584 }
585
586 fn set_parent_of_first_op(&mut self, parent_op: Option<DirectedOp>) {
587 if let Some(op) = parent_op {
588 if op.is_left() && op.is_prefix() {
589 self.parent_of_first_op = Some((op, self.last_item_idx));
590 }
591 }
592 }
593
594 fn enqueue_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) {
595 if is_postfix!(spec.get_spec()) {
596 if self.max_depth_exhausted(max_depth) {
597 self.iter.pop_stack();
598 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
599 } else if self.check_max_depth(&mut max_depth) {
600 self.iter.pop_stack();
601
602 self.state_stack.push(TokenOrRedirect::Op(name, spec));
603 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
604 } else {
605 let right_directed_op = DirectedOp::Right(name, spec);
606
607 self.state_stack.push(TokenOrRedirect::Op(name, spec));
608 self.state_stack.push(TokenOrRedirect::CompositeRedirect(
609 max_depth,
610 right_directed_op,
611 ));
612 }
613 } else if is_prefix!(spec.get_spec()) {
614 if self.max_depth_exhausted(max_depth) {
615 self.iter.pop_stack();
616 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
617 return;
618 } else if self.check_max_depth(&mut max_depth) {
619 self.iter.pop_stack();
620
621 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
622 self.state_stack.push(TokenOrRedirect::Op(name, spec));
623 } else {
624 let op = DirectedOp::Left(name, spec);
625
626 self.state_stack
627 .push(TokenOrRedirect::CompositeRedirect(max_depth, op));
628 self.state_stack.push(TokenOrRedirect::Op(name, spec));
629 }
630 } else {
631 if let "|" = &*name.as_str() {
632 self.format_bar_separator_op(max_depth, name, spec);
633 return;
634 };
635
636 if self.max_depth_exhausted(max_depth) {
637 self.iter.pop_stack();
638 self.iter.pop_stack();
639
640 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
641 } else if self.check_max_depth(&mut max_depth) {
642 if is_xfy!(spec.get_spec()) {
643 let left_directed_op = DirectedOp::Left(name, spec);
644
645 self.state_stack
646 .push(TokenOrRedirect::CompositeRedirect(0, left_directed_op));
647
648 self.state_stack.push(TokenOrRedirect::Op(name, spec));
649 self.state_stack.push(TokenOrRedirect::StackPop);
650 } else {
651 let right_directed_op = DirectedOp::Right(name, spec);
653
654 self.state_stack.push(TokenOrRedirect::StackPop);
655 self.state_stack.push(TokenOrRedirect::Op(name, spec));
656 self.state_stack
657 .push(TokenOrRedirect::CompositeRedirect(0, right_directed_op));
658 }
659 } else {
660 let left_directed_op = DirectedOp::Left(name, spec);
661 let right_directed_op = DirectedOp::Right(name, spec);
662
663 self.state_stack.push(TokenOrRedirect::CompositeRedirect(
664 max_depth,
665 left_directed_op,
666 ));
667
668 self.state_stack.push(TokenOrRedirect::Op(name, spec));
669 self.state_stack.push(TokenOrRedirect::CompositeRedirect(
670 max_depth,
671 right_directed_op,
672 ));
673 }
674 }
675 }
676
677 fn format_struct(&mut self, mut max_depth: usize, arity: usize, name: Atom) -> bool {
678 if self.check_max_depth(&mut max_depth) {
679 for _ in 0..arity {
680 self.iter.pop_stack();
681 }
682
683 if arity > 0 {
684 self.state_stack.push(TokenOrRedirect::Close);
685 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
686 self.state_stack.push(TokenOrRedirect::Open);
687 }
688
689 self.state_stack.push(TokenOrRedirect::Atom(name));
690
691 return false;
692 }
693
694 if arity > 0 {
695 self.state_stack.push(TokenOrRedirect::Close);
696
697 for _ in 0..arity {
698 self.state_stack
699 .push(TokenOrRedirect::FunctorRedirect(max_depth));
700 self.state_stack.push(TokenOrRedirect::Comma);
701 }
702
703 self.state_stack.pop();
704
705 self.state_stack.push(TokenOrRedirect::Open);
706 }
707
708 self.state_stack.push(TokenOrRedirect::Atom(name));
709
710 true
711 }
712
713 fn format_bar_separator_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) {
714 if self.check_max_depth(&mut max_depth) {
715 self.iter.pop_stack();
716 self.iter.pop_stack();
717
718 let ellipsis_atom = atom!("...");
719
720 self.state_stack.push(TokenOrRedirect::Atom(ellipsis_atom));
721 self.state_stack.push(TokenOrRedirect::BarAsOp);
722 self.state_stack.push(TokenOrRedirect::Atom(ellipsis_atom));
723
724 return;
725 }
726
727 let left_directed_op = DirectedOp::Left(name, spec);
728 let right_directed_op = DirectedOp::Right(name, spec);
729
730 self.state_stack.push(TokenOrRedirect::CompositeRedirect(
731 max_depth,
732 left_directed_op,
733 ));
734
735 self.state_stack.push(TokenOrRedirect::BarAsOp);
736
737 self.state_stack.push(TokenOrRedirect::CompositeRedirect(
738 max_depth,
739 right_directed_op,
740 ));
741 }
742
743 fn format_curly_braces(&mut self, mut max_depth: usize) -> bool {
744 if self.check_max_depth(&mut max_depth) {
745 self.iter.pop_stack();
746
747 let ellipsis_atom = atom!("...");
748
749 self.state_stack.push(TokenOrRedirect::RightCurly);
750 self.state_stack.push(TokenOrRedirect::Atom(ellipsis_atom));
751 self.state_stack.push(TokenOrRedirect::LeftCurly);
752
753 return false;
754 }
755
756 self.state_stack.push(TokenOrRedirect::RightCurly);
757 self.state_stack
758 .push(TokenOrRedirect::CurlyBracketRedirect(max_depth));
759 self.state_stack.push(TokenOrRedirect::LeftCurly);
760
761 true
762 }
763
764 fn format_numbered_vars(&mut self) -> bool {
765 let h = self.iter.stack_last().unwrap();
766
767 let cell = self.iter.read_cell(h);
768 let cell = heap_bound_store(self.iter.heap, heap_bound_deref(self.iter.heap, cell));
769
770 if let Some(var) = numbervar(&self.numbervars_offset, cell) {
772 self.iter.pop_stack();
773 self.state_stack.push(TokenOrRedirect::NumberedVar(var));
774 return true;
775 }
776
777 false
778 }
779
780 fn format_clause(
781 &mut self,
782 max_depth: usize,
783 arity: usize,
784 name: Atom,
785 op_desc: Option<OpDesc>,
786 ) -> bool {
787 if self.numbervars && is_numbered_var(name, arity) && self.format_numbered_vars() {
788 return true;
789 }
790
791 let dot_atom = atom!(".");
792
793 if let Some(spec) = op_desc {
794 if dot_atom == name && is_infix!(spec.get_spec()) && !self.ignore_ops {
795 self.push_list(max_depth);
796 return true;
797 }
798
799 if !self.ignore_ops && spec.get_prec() > 0 {
800 self.enqueue_op(max_depth, name, spec);
801 return true;
802 }
803 }
804
805 match (name, arity) {
806 (atom!("{}"), 1) if !self.ignore_ops => self.format_curly_braces(max_depth),
807 _ => self.format_struct(max_depth, arity, name),
808 }
809 }
810
811 fn offset_as_string(&mut self, h: IterStackLoc) -> Option<String> {
812 let cell = self.iter.read_cell(h);
813
814 if let Some(var) = self.var_names.get(&cell) {
815 read_heap_cell!(cell,
816 (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
817 return Some(var.borrow().to_string());
818 }
819 _ => {
820 self.iter.push_stack(h);
821 return None;
822 }
823 );
824 }
825
826 read_heap_cell!(cell,
827 (HeapCellValueTag::Lis | HeapCellValueTag::Str, h) => {
828 Some(format!("{}", h))
829 }
830 (HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => {
831 Some(format!("_{}", h))
832 }
833 (HeapCellValueTag::StackVar, h) => {
834 Some(format!("_s_{}", h))
835 }
836 _ => {
837 None
838 }
839 )
840 }
841
842 fn check_for_seen(&mut self, max_depth: &mut usize) -> Option<HeapCellValue> {
843 if let Some(mut orig_cell) = self.iter.next() {
844 loop {
845 let is_cyclic = orig_cell.get_forwarding_bit();
846
847 let cell =
848 heap_bound_store(self.iter.heap, heap_bound_deref(self.iter.heap, orig_cell));
849 let cell = unmark_cell_bits!(cell);
850
851 match self.var_names.get(&cell).cloned() {
852 Some(var) if cell.is_var() => {
853 let var_str = var.borrow().to_string();
860
861 push_space_if_amb!(self, &var_str, {
862 append_str!(self, &var_str);
863 });
864
865 return None;
866 }
867 var_opt => {
868 if is_cyclic && cell.is_compound(self.iter.heap) {
869 read_heap_cell!(cell,
871 (HeapCellValueTag::Lis, vh) => {
872 if self.iter.heap[vh].get_forwarding_bit() {
873 self.iter.pop_stack();
874 }
875
876 if self.iter.heap[vh+1].get_forwarding_bit() {
877 self.iter.pop_stack();
878 }
879 }
880 _ => {}
881 );
882
883 match var_opt {
884 Some(var) => {
885 let var_str = var.borrow().to_string();
888
889 push_space_if_amb!(self, &var_str, {
890 append_str!(self, &var_str);
891 });
892 }
893 None => {
894 if self.max_depth == 0 || *max_depth == 0 {
895 push_space_if_amb!(self, "...", {
897 append_str!(self, "...");
898 });
899 } else {
900 debug_assert!(cell.is_ref());
901
902 if cell.get_tag() == HeapCellValueTag::Lis {
911 *max_depth -= 1;
912 }
913
914 let h = cell.get_value() as usize;
915 self.iter.push_stack(IterStackLoc::iterable_loc(
916 h,
917 HeapOrStackTag::Heap,
918 ));
919
920 if let Some(cell) = self.iter.next() {
921 orig_cell = cell;
922 continue;
923 }
924 }
925 }
926 }
927
928 return None;
929 }
930
931 return Some(cell);
932 }
933 }
934 }
935 } else {
936 while self.iter.pop_stack().is_none() {}
937 None
938 }
939 }
940
941 fn print_impromptu_atom(&mut self, atom: Atom) {
942 let result = self.print_op_addendum(&atom.as_str());
943
944 push_space_if_amb!(self, result.as_str(), {
945 append_str!(self, &result);
946 });
947 }
948
949 fn print_op_addendum(&mut self, atom: &str) -> String {
950 if !self.quoted || non_quoted_token(atom.chars()) {
951 atom.to_string()
952 } else if atom == "''" {
953 "''".to_string()
954 } else {
955 let mut result = String::new();
956
957 if self.quoted {
958 result.push('\'');
959 }
960
961 for c in atom.chars() {
962 result += &char_to_string(self.quoted, c);
963 }
964
965 if self.quoted {
966 result.push('\'');
967 }
968
969 result
970 }
971 }
972
973 fn print_op(&mut self, atom: &str) {
974 let result = if atom == "," {
975 ",".to_string()
976 } else {
977 self.print_op_addendum(atom)
978 };
979
980 push_space_if_amb!(self, &result, {
981 append_str!(self, &result);
982 });
983 }
984
985 #[inline]
986 fn print_ip_addr(&mut self, ip: IpAddr) {
987 push_char!(self, '\'');
988 append_str!(self, &format!("{}", ip));
989 push_char!(self, '\'');
990 }
991
992 #[inline]
993 fn print_raw_ptr(&mut self, ptr: *const ArenaHeader) {
994 append_str!(self, &format!("0x{:x}", ptr as *const u8 as usize));
995 }
996
997 fn print_number(&mut self, max_depth: usize, n: NumberFocus, op: &Option<DirectedOp>) {
998 let (add_brackets, op_is_prefix) = if let Some(op) = op {
999 (op.is_negative_sign() && !n.is_negative(), op.is_prefix())
1000 } else {
1001 (false, false)
1002 };
1003
1004 if add_brackets {
1005 if op_is_prefix && !self.outputter.ends_with(" ") {
1006 push_char!(self, ' ');
1007 }
1008
1009 push_char!(self, '(');
1010 }
1011
1012 match n {
1013 NumberFocus::Unfocused(n) => match n {
1014 Number::Float(OrderedFloat(fl)) => {
1015 let output_str = fmt_float(fl);
1016
1017 push_space_if_amb!(self, &output_str, {
1018 append_str!(self, &output_str);
1019 });
1020 }
1021 Number::Rational(r) => {
1022 self.print_rational(max_depth, r, *op);
1023 }
1024 n => {
1025 let output_str = format!("{}", n);
1026
1027 push_space_if_amb!(self, &output_str, {
1028 append_str!(self, &output_str);
1029 });
1030 }
1031 },
1032 NumberFocus::Denominator(r) => {
1033 let output_str = format!("{}", r.denominator());
1034
1035 push_space_if_amb!(self, &output_str, {
1036 append_str!(self, &output_str);
1037 });
1038 }
1039 NumberFocus::Numerator(r) => {
1040 let output_str = format!("{}", r.numerator());
1041
1042 push_space_if_amb!(self, &output_str, {
1043 append_str!(self, &output_str);
1044 });
1045 }
1046 }
1047
1048 if add_brackets {
1049 push_char!(self, ')');
1050 }
1051 }
1052
1053 fn print_rational(
1054 &mut self,
1055 mut max_depth: usize,
1056 r: TypedArenaPtr<Rational>,
1057 parent_op: Option<DirectedOp>,
1058 ) {
1059 if self.check_max_depth(&mut max_depth) {
1060 self.state_stack.push(TokenOrRedirect::Close);
1061 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
1062 self.state_stack.push(TokenOrRedirect::Open);
1063
1064 self.state_stack.push(TokenOrRedirect::Atom(atom!("rdiv")));
1065
1066 return;
1067 }
1068
1069 match self.op_dir.get(&(atom!("rdiv"), Fixity::In)) {
1070 Some(op_desc) => {
1071 if r.is_int() {
1072 let output_str = format!("{}", r);
1073
1074 push_space_if_amb!(self, &output_str, {
1075 append_str!(self, &output_str);
1076 });
1077
1078 return;
1079 }
1080
1081 let rdiv_ct = atom!("rdiv");
1082
1083 let left_directed_op = if op_desc.get_prec() > 0 {
1084 Some(DirectedOp::Left(rdiv_ct, *op_desc))
1085 } else {
1086 None
1087 };
1088
1089 let right_directed_op = if op_desc.get_prec() > 0 {
1090 Some(DirectedOp::Right(rdiv_ct, *op_desc))
1091 } else {
1092 None
1093 };
1094
1095 if !self.ignore_ops && op_desc.get_prec() > 0 {
1096 self.state_stack.push(TokenOrRedirect::NumberFocus(
1097 max_depth,
1098 NumberFocus::Denominator(r),
1099 left_directed_op,
1100 ));
1101 self.state_stack
1102 .push(TokenOrRedirect::Op(rdiv_ct, *op_desc));
1103 self.state_stack.push(TokenOrRedirect::NumberFocus(
1104 max_depth,
1105 NumberFocus::Numerator(r),
1106 right_directed_op,
1107 ));
1108
1109 self.set_parent_of_first_op(parent_op);
1110 } else {
1111 self.state_stack.push(TokenOrRedirect::Close);
1112
1113 self.state_stack.push(TokenOrRedirect::NumberFocus(
1114 max_depth,
1115 NumberFocus::Denominator(r),
1116 None,
1117 ));
1118
1119 self.state_stack.push(TokenOrRedirect::Comma);
1120
1121 self.state_stack.push(TokenOrRedirect::NumberFocus(
1122 max_depth,
1123 NumberFocus::Numerator(r),
1124 None,
1125 ));
1126
1127 self.state_stack.push(TokenOrRedirect::Open);
1128 self.state_stack.push(TokenOrRedirect::Atom(rdiv_ct));
1129 }
1130 }
1131 _ => {
1132 unreachable!()
1133 }
1134 }
1135 }
1136
1137 fn print_string_as_functor(&mut self, focus: usize, max_depth: &mut usize) -> bool {
1139 let iter = HeapPStrIter::new(self.iter.heap, focus);
1140
1141 for (char_count, c) in iter.chars().enumerate() {
1142 if self.check_max_depth(max_depth) {
1143 if char_count > 0 {
1144 self.state_stack.push(TokenOrRedirect::Close);
1145 }
1146
1147 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
1148 return true;
1149 }
1150
1151 append_str!(self, "'.'");
1152 push_char!(self, '(');
1153
1154 print_char!(self, self.quoted, c);
1155 push_char!(self, ',');
1156
1157 self.state_stack.push(TokenOrRedirect::Close);
1158 }
1159
1160 false
1161 }
1162
1163 fn print_proper_string(&mut self, focus: usize, max_depth: usize) {
1166 push_char!(self, '"');
1167
1168 let iter = HeapPStrIter::new(self.iter.heap, focus);
1169 let char_to_string = |c: char| {
1170 match c {
1173 '\\' => "\\\\".to_string(),
1174 '"' => "\\\"".to_string(),
1175 _ => char_to_string(self.quoted, c),
1176 }
1177 };
1178
1179 if max_depth == 0 {
1180 for c in iter.chars() {
1181 for c in char_to_string(c).chars() {
1182 push_char!(self, c);
1183 }
1184 }
1185 } else {
1186 let mut char_count = 0;
1187
1188 for c in iter.chars().take(max_depth) {
1189 char_count += 1;
1190
1191 for c in char_to_string(c).chars() {
1192 push_char!(self, c);
1193 }
1194 }
1195
1196 if char_count == max_depth {
1197 append_str!(self, " ...");
1198 }
1199 }
1200
1201 push_char!(self, '"');
1202 }
1203
1204 fn remove_list_children(&mut self, h: usize) {
1205 match self.iter.heap[h].get_tag() {
1206 HeapCellValueTag::Lis => {
1207 self.iter.pop_stack();
1208 self.iter.pop_stack();
1209 }
1210 HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset => {
1211 self.iter.pop_stack();
1212 }
1213 HeapCellValueTag::CStr => {}
1214 _ => {
1215 unreachable!();
1216 }
1217 }
1218 }
1219
1220 fn print_list_like(&mut self, mut max_depth: usize) {
1221 let focus = self.iter.focus();
1222 let mut heap_pstr_iter = HeapPStrIter::new(self.iter.heap, focus.value() as usize);
1223
1224 if heap_pstr_iter.next().is_some() {
1225 for _ in heap_pstr_iter.by_ref() {}
1226 } else {
1227 return self.push_list(max_depth);
1228 }
1229
1230 let end_h = heap_pstr_iter.focus();
1231 let end_cell = heap_pstr_iter.focus;
1232
1233 if self.check_max_depth(&mut max_depth) {
1234 self.remove_list_children(focus.value() as usize);
1235 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
1236 return;
1237 }
1238
1239 let at_cdr = self.outputter.ends_with("|");
1240
1241 if self.double_quotes && !self.ignore_ops && end_cell.is_string_terminator(self.iter.heap) {
1242 self.remove_list_children(focus.value() as usize);
1243 return self.print_proper_string(focus.value() as usize, max_depth);
1244 }
1245
1246 if self.ignore_ops {
1247 self.at_cdr(",");
1248 self.remove_list_children(focus.value() as usize);
1249
1250 if !self.print_string_as_functor(focus.value() as usize, &mut max_depth) {
1251 if end_cell == empty_list_as_cell!() {
1252 if !self.at_cdr("") {
1253 append_str!(self, "[]");
1254 }
1255 } else {
1256 self.state_stack
1257 .push(TokenOrRedirect::FunctorRedirect(max_depth));
1258 self.iter
1259 .push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap));
1260 }
1261 }
1262 } else {
1263 let value = heap_bound_store(
1264 self.iter.heap,
1265 heap_bound_deref(self.iter.heap, self.iter.read_cell(focus)),
1266 );
1267
1268 read_heap_cell!(value,
1269 (HeapCellValueTag::Lis) => {
1270 self.push_list(max_depth)
1271 }
1272 _ => {
1273 let switch = Rc::new(Cell::new((!at_cdr, 0)));
1274 let switch = self.close_list(switch);
1275
1276 let (h, offset) = pstr_loc_and_offset(self.iter.heap, focus.value() as usize);
1277
1278 let offset = offset.get_num() as usize;
1279 let tag = value.get_tag();
1280
1281 let end_h = if tag == HeapCellValueTag::PStrOffset {
1282 self.iter.pop_stack();
1286 Some(end_h)
1287 } else {
1288 None
1289 };
1290
1291 if !self.max_depth_exhausted(max_depth) {
1292 let pstr = cell_as_string!(self.iter.heap[h]);
1293 self.state_stack.push(TokenOrRedirect::CommaSeparatedCharList(CommaSeparatedCharList {
1294 pstr, offset, max_depth, end_cell, end_h,
1295 }));
1296 } else {
1297 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
1298 }
1299
1300 self.open_list(switch);
1301 }
1302 );
1303 }
1304 }
1305
1306 #[inline]
1307 fn max_depth_exhausted(&self, max_depth: usize) -> bool {
1308 self.max_depth > 0 && max_depth == 0
1309 }
1310
1311 fn check_max_depth(&self, max_depth: &mut usize) -> bool {
1312 if self.max_depth > 0 && *max_depth == 0 {
1313 return true;
1314 }
1315
1316 if *max_depth > 0 {
1317 *max_depth -= 1;
1318 }
1319
1320 false
1321 }
1322
1323 fn close_list(&mut self, switch: Rc<Cell<(bool, usize)>>) -> Option<Rc<Cell<(bool, usize)>>> {
1324 if let Some(TokenOrRedirect::Op(_, op_desc)) = self.state_stack.last() {
1325 if is_postfix!(op_desc.get_spec()) || is_infix!(op_desc.get_spec()) {
1326 self.state_stack.push(TokenOrRedirect::ChildCloseList);
1327 return None;
1328 }
1329 }
1330
1331 self.state_stack
1332 .push(TokenOrRedirect::CloseList(switch.clone()));
1333 Some(switch)
1334 }
1335
1336 fn open_list(&mut self, switch: Option<Rc<Cell<(bool, usize)>>>) {
1337 self.state_stack.push(match switch {
1338 Some(switch) => TokenOrRedirect::OpenList(switch),
1339 None => TokenOrRedirect::ChildOpenList,
1340 });
1341 }
1342
1343 fn push_list(&mut self, mut max_depth: usize) {
1344 if self.max_depth_exhausted(max_depth) {
1345 self.iter.pop_stack();
1346 self.iter.pop_stack();
1347
1348 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
1349
1350 return;
1351 } else if self.check_max_depth(&mut max_depth) {
1352 self.iter.pop_stack();
1353 self.iter.pop_stack();
1354
1355 let cell = Rc::new(Cell::new((true, 0)));
1356
1357 let switch = self.close_list(cell);
1358
1359 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
1360 self.open_list(switch);
1361
1362 return;
1363 }
1364
1365 let cell = Rc::new(Cell::new((true, max_depth)));
1366
1367 let switch = self.close_list(cell);
1368
1369 self.state_stack
1370 .push(TokenOrRedirect::FunctorRedirect(max_depth));
1371 self.state_stack.push(TokenOrRedirect::HeadTailSeparator); self.state_stack
1373 .push(TokenOrRedirect::FunctorRedirect(max_depth + 1));
1374
1375 self.open_list(switch);
1376 }
1377
1378 #[allow(clippy::too_many_arguments)]
1379 fn handle_op_as_struct(
1380 &mut self,
1381 name: Atom,
1382 arity: usize,
1383 op: Option<DirectedOp>,
1384 is_functor_redirect: bool,
1385 op_desc: OpDesc,
1386 negated_operand: bool,
1387 max_depth: usize,
1388 ) {
1389 let add_brackets = if !self.ignore_ops {
1390 negated_operand
1391 || if let Some(ref op) = op {
1392 if self.numbervars && arity == 1 && name == atom!("$VAR") {
1393 !self.iter.immediate_leaf_has_property(|addr| {
1394 match Number::try_from(addr) {
1395 Ok(Number::Integer(n)) => (*n).sign() == Sign::Positive,
1396 Ok(Number::Fixnum(n)) => n.get_num() >= 0,
1397 Ok(Number::Float(f)) => f >= OrderedFloat(0f64),
1398 Ok(Number::Rational(r)) => (*r).sign() == Sign::Positive,
1399 _ => false,
1400 }
1401 }) && needs_bracketing(op_desc, op)
1402 } else {
1403 needs_bracketing(op_desc, op)
1404 }
1405 } else {
1406 is_functor_redirect && op_desc.get_prec() >= 1000
1407 }
1408 } else {
1409 false
1410 };
1411
1412 if add_brackets {
1413 self.state_stack.push(TokenOrRedirect::Close);
1414 self.format_clause(max_depth, arity, name, Some(op_desc));
1415 self.state_stack.push(TokenOrRedirect::Open);
1416
1417 if !self.outputter.ends_with(" ") {
1418 let parent_op = self
1419 .parent_of_first_op
1420 .and_then(|(parent_op, last_item_idx)| {
1421 if self.last_item_idx == last_item_idx {
1425 Some(parent_op)
1426 } else {
1427 None
1428 }
1429 });
1430
1431 for op in &[op, parent_op] {
1432 if let Some(ref op) = &op {
1433 if op.is_left()
1434 && (op.is_prefix() || requires_space(&op.as_atom().as_str(), "("))
1435 {
1436 self.state_stack.push(TokenOrRedirect::Space);
1437 return;
1438 }
1439 }
1440 }
1441 }
1442 } else {
1443 self.format_clause(max_depth, arity, name, Some(op_desc));
1444 }
1445 }
1446
1447 fn print_tcp_listener(&mut self, tcp_listener: &TcpListener, max_depth: usize) {
1448 let (ip, port) = if let Ok(addr) = tcp_listener.local_addr() {
1449 (addr.ip(), addr.port())
1450 } else {
1451 let disconnected_atom = atom!("$disconnected_tcp_listener");
1452 self.state_stack
1453 .push(TokenOrRedirect::Atom(disconnected_atom));
1454
1455 return;
1456 };
1457
1458 let tcp_listener_atom = atom!("$tcp_listener");
1459
1460 if self.format_struct(max_depth, 1, tcp_listener_atom) {
1461 let atom = self.state_stack.pop().unwrap();
1462
1463 self.state_stack.pop();
1464 self.state_stack.pop();
1465
1466 self.state_stack.push(TokenOrRedirect::NumberFocus(
1467 max_depth,
1468 NumberFocus::Unfocused(Number::Fixnum(Fixnum::build_with(port as i64))),
1469 None,
1470 ));
1471 self.state_stack.push(TokenOrRedirect::Comma);
1472 self.state_stack.push(TokenOrRedirect::IpAddr(ip));
1473
1474 self.state_stack.push(TokenOrRedirect::Open);
1475 self.state_stack.push(atom);
1476 }
1477 }
1478
1479 fn print_index_ptr(&mut self, index_ptr: IndexPtr, max_depth: usize) {
1480 if self.format_struct(max_depth, 1, atom!("$index_ptr")) {
1481 let atom = self.state_stack.pop().unwrap();
1482
1483 self.state_stack.pop();
1484 self.state_stack.pop();
1485
1486 let offset = if index_ptr.is_undefined() || index_ptr.is_dynamic_undefined() {
1487 TokenOrRedirect::Atom(atom!("undefined"))
1488 } else {
1489 let idx = index_ptr.p() as i64;
1490
1491 TokenOrRedirect::NumberFocus(
1492 max_depth,
1493 NumberFocus::Unfocused(Number::Fixnum(Fixnum::build_with(idx))),
1494 None,
1495 )
1496 };
1497
1498 self.state_stack.push(offset);
1499 self.state_stack.push(TokenOrRedirect::Open);
1500 self.state_stack.push(atom);
1501 }
1502 }
1503
1504 fn print_stream(&mut self, stream: Stream, max_depth: usize) {
1505 if let Some(alias) = stream.options().get_alias() {
1506 self.print_impromptu_atom(alias);
1507 } else {
1508 let stream_atom = atom!("$stream");
1509
1510 if self.format_struct(max_depth, 1, stream_atom) {
1511 let atom = if stream.is_stdout() || stream.is_stdin() {
1512 TokenOrRedirect::Atom(atom!("user"))
1513 } else {
1514 TokenOrRedirect::RawPtr(stream.as_ptr())
1515 };
1516
1517 let stream_root = self.state_stack.pop().unwrap();
1518
1519 self.state_stack.pop();
1520 self.state_stack.pop();
1521
1522 self.state_stack.push(atom);
1523 self.state_stack.push(TokenOrRedirect::Open);
1524 self.state_stack.push(stream_root);
1525 }
1526 }
1527 }
1528
1529 fn print_comma_separated_char_list(&mut self, char_list: CommaSeparatedCharList) {
1530 let CommaSeparatedCharList {
1531 pstr,
1532 offset,
1533 max_depth,
1534 end_cell,
1535 end_h,
1536 } = char_list;
1537 let pstr_str = pstr.as_str_from(offset);
1538
1539 if let Some(c) = pstr_str.chars().next() {
1540 let offset = offset + c.len_utf8();
1541
1542 if !self.max_depth_exhausted(max_depth) {
1543 self.state_stack
1544 .push(TokenOrRedirect::CommaSeparatedCharList(
1545 CommaSeparatedCharList {
1546 pstr,
1547 offset,
1548 max_depth: max_depth.saturating_sub(1),
1549 end_cell,
1550 end_h,
1551 },
1552 ));
1553
1554 let max_depth_allows = self.max_depth == 0 || max_depth > 1;
1555
1556 if max_depth_allows && pstr_str.chars().nth(1).is_some() {
1557 self.state_stack.push(TokenOrRedirect::Comma);
1558 }
1559
1560 self.state_stack.push(TokenOrRedirect::Char(c));
1561 } else {
1562 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
1563 self.state_stack.push(TokenOrRedirect::HeadTailSeparator);
1564 }
1565 } else if self.max_depth_exhausted(max_depth) {
1566 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
1567 self.state_stack.push(TokenOrRedirect::HeadTailSeparator);
1568 } else if end_cell != empty_list_as_cell!() {
1569 if let Some(end_h) = end_h {
1570 self.iter
1571 .push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap));
1572 }
1573
1574 self.state_stack
1575 .push(TokenOrRedirect::FunctorRedirect(max_depth + 1));
1576 self.state_stack.push(TokenOrRedirect::HeadTailSeparator);
1577 }
1578 }
1579
1580 fn handle_heap_term(
1581 &mut self,
1582 op: Option<DirectedOp>,
1583 is_functor_redirect: bool,
1584 mut max_depth: usize,
1585 ) {
1586 let negated_operand = negated_op_needs_bracketing(&self.iter, self.op_dir, &op);
1587
1588 let addr = match self.check_for_seen(&mut max_depth) {
1589 Some(addr) => addr,
1590 None => return,
1591 };
1592
1593 let print_struct = |printer: &mut Self, name: Atom, arity: usize| {
1594 if name == atom!("[]") && arity == 0 {
1595 if let Some(TokenOrRedirect::CloseList(_) | TokenOrRedirect::ChildCloseList) =
1596 printer.state_stack.last()
1597 {
1598 if printer.at_cdr("") {
1599 return;
1600 }
1601 }
1602
1603 append_str!(printer, "[]");
1604 } else if arity > 0 {
1605 if let Some(spec) = fetch_op_spec(name, arity, printer.op_dir) {
1606 printer.handle_op_as_struct(
1607 name,
1608 arity,
1609 op,
1610 is_functor_redirect,
1611 spec,
1612 negated_operand,
1613 max_depth,
1614 );
1615 } else {
1616 push_space_if_amb!(printer, &*name.as_str(), {
1617 printer.format_clause(max_depth, arity, name, None);
1618 });
1619 }
1620 } else if fetch_op_spec(name, arity, printer.op_dir).is_some() {
1621 let mut result = String::new();
1622
1623 if let Some(ref op) = op {
1624 let op_is_prefix = op.is_prefix() && op.is_left();
1625
1626 if op_is_prefix
1627 || printer
1628 .outputter
1629 .ends_with(&format!(" {}", op.as_atom().as_str()))
1630 {
1631 result.push(' ');
1632 }
1633
1634 result.push('(');
1635 }
1636
1637 result += &printer.print_op_addendum(&name.as_str());
1638
1639 if op.is_some() {
1640 result.push(')');
1641 }
1642
1643 push_space_if_amb!(printer, &result, {
1644 append_str!(printer, &result);
1645 });
1646 } else {
1647 push_space_if_amb!(printer, &name.as_str(), {
1648 printer.print_impromptu_atom(name);
1649 });
1650 }
1651 };
1652
1653 if !addr.is_var()
1654 && !addr.is_compound(self.iter.heap)
1655 && self.max_depth_exhausted(max_depth)
1656 {
1657 if !(addr == atom_as_cell!(atom!("[]")) && self.at_cdr("")) {
1658 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
1659 }
1660
1661 return;
1662 }
1663
1664 read_heap_cell!(addr,
1665 (HeapCellValueTag::Atom, (name, arity)) => {
1666 print_struct(self, name, arity);
1667 }
1668 (HeapCellValueTag::Char, c) => {
1669 let name = AtomTable::build_with(&self.atom_tbl, &String::from(c));
1670 print_struct(self, name, 0);
1671 }
1672 (HeapCellValueTag::Str, s) => {
1673 let (name, arity) = cell_as_atom_cell!(self.iter.heap[s])
1674 .get_name_and_arity();
1675
1676 if let Some(spec) = fetch_op_spec(name, arity, self.op_dir) {
1677 self.handle_op_as_struct(
1678 name,
1679 arity,
1680 op,
1681 is_functor_redirect,
1682 spec,
1683 negated_operand,
1684 max_depth,
1685 );
1686 } else {
1687 push_space_if_amb!(self, &*name.as_str(), {
1688 self.format_clause(max_depth, arity, name, None);
1689 });
1690 }
1691 }
1692 (HeapCellValueTag::Fixnum | HeapCellValueTag::CutPoint, n) => {
1693 self.print_number(max_depth, NumberFocus::Unfocused(Number::Fixnum(n)), &op);
1694 }
1695 (HeapCellValueTag::F64, f) => {
1696 self.print_number(max_depth, NumberFocus::Unfocused(Number::Float(*f)), &op);
1697 }
1698 (HeapCellValueTag::CStr | HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset) => {
1699 self.print_list_like(max_depth);
1700 }
1701 (HeapCellValueTag::Lis) => {
1702 if self.ignore_ops {
1703 let period_atom = atom!(".");
1704 self.format_struct(max_depth, 2, period_atom);
1705 } else {
1706 self.print_list_like(max_depth);
1707 }
1708 }
1709 (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
1710 let h = self.iter.focus();
1711
1712 if let Some(offset_str) = self.offset_as_string(h) {
1713 push_space_if_amb!(self, &offset_str, {
1714 append_str!(self, offset_str.as_str());
1715 })
1716 }
1717 }
1718 (HeapCellValueTag::Cons, c) => {
1719 match_untyped_arena_ptr!(c,
1720 (ArenaHeaderTag::Integer, n) => {
1721 self.print_number(max_depth, NumberFocus::Unfocused(Number::Integer(n)), &op);
1722 }
1723 (ArenaHeaderTag::Rational, r) => {
1724 self.print_number(max_depth, NumberFocus::Unfocused(Number::Rational(r)), &op);
1725 }
1726 (ArenaHeaderTag::Stream, stream) => {
1727 self.print_stream(stream, max_depth);
1728 }
1729 (ArenaHeaderTag::TcpListener, listener) => {
1730 self.print_tcp_listener(&listener, max_depth);
1731 }
1732 (ArenaHeaderTag::Dropped, _value) => {
1733 self.print_impromptu_atom(atom!("$dropped_value"));
1734 }
1735 (ArenaHeaderTag::IndexPtr, index_ptr) => {
1736 self.print_index_ptr(*index_ptr, max_depth);
1737 }
1738 _ => {
1739 }
1740 );
1741 }
1742 _ => {
1743 unreachable!()
1744 }
1745 );
1746 }
1747
1748 fn at_cdr(&mut self, tr: &str) -> bool {
1749 let len = self.outputter.len();
1750
1751 if self.outputter.ends_with("|") {
1752 self.outputter.truncate(len - "|".len());
1753 append_str!(self, tr);
1754 true
1755 } else {
1756 false
1757 }
1758 }
1759
1760 pub fn print(mut self) -> Outputter {
1761 let spec = self.toplevel_spec.take();
1762 self.handle_heap_term(spec, false, self.max_depth);
1763
1764 while let Some(loc_data) = self.state_stack.pop() {
1765 match loc_data {
1766 TokenOrRedirect::Atom(atom) => self.print_impromptu_atom(atom),
1767 TokenOrRedirect::BarAsOp => append_str!(self, "|"),
1768 TokenOrRedirect::Char(c) => print_char!(self, self.quoted, c),
1769 TokenOrRedirect::Op(atom, op) => {
1770 self.print_op(&atom.as_str());
1771
1772 if is_prefix!(op.get_spec()) {
1773 self.set_parent_of_first_op(Some(DirectedOp::Left(atom, op)));
1774 }
1775 }
1776 TokenOrRedirect::NumberedVar(num_var) => append_str!(self, &num_var),
1777 TokenOrRedirect::CompositeRedirect(max_depth, op) => {
1778 self.handle_heap_term(Some(op), false, max_depth)
1779 }
1780 TokenOrRedirect::CurlyBracketRedirect(max_depth) => {
1781 self.handle_heap_term(None, false, max_depth)
1782 }
1783 TokenOrRedirect::FunctorRedirect(max_depth) => {
1784 self.handle_heap_term(None, true, max_depth)
1785 }
1786 TokenOrRedirect::Close => push_char!(self, ')'),
1787 TokenOrRedirect::IpAddr(ip) => self.print_ip_addr(ip),
1788 TokenOrRedirect::RawPtr(ptr) => self.print_raw_ptr(ptr),
1789 TokenOrRedirect::Open => push_char!(self, '('),
1790 TokenOrRedirect::ChildOpenList => {
1791 push_char!(self, '[');
1792 }
1793 TokenOrRedirect::ChildCloseList => {
1794 push_char!(self, ']');
1795 }
1796 TokenOrRedirect::OpenList(delimit) => {
1797 if !self.at_cdr(",") {
1798 push_char!(self, '[');
1799 } else {
1800 let (_, max_depth) = delimit.get();
1801 delimit.set((false, max_depth));
1802 }
1803 }
1804 TokenOrRedirect::CloseList(delimit) => {
1805 if delimit.get().0 {
1806 push_char!(self, ']');
1807 }
1808 }
1809 TokenOrRedirect::HeadTailSeparator => append_str!(self, "|"),
1810 TokenOrRedirect::NumberFocus(max_depth, n, op) => {
1811 self.print_number(max_depth, n, &op);
1812 }
1813 TokenOrRedirect::Comma => append_str!(self, ","),
1814 TokenOrRedirect::Space => push_char!(self, ' '),
1815 TokenOrRedirect::LeftCurly => push_char!(self, '{'),
1816 TokenOrRedirect::RightCurly => push_char!(self, '}'),
1817 TokenOrRedirect::StackPop => {
1818 self.iter.pop_stack();
1819 self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
1820 }
1821 TokenOrRedirect::CommaSeparatedCharList(char_list) => {
1822 self.print_comma_separated_char_list(char_list);
1823 }
1824 }
1825 }
1826
1827 self.outputter
1828 }
1829}
1830
1831#[cfg(test)]
1832mod tests {
1833 use super::*;
1834
1835 use crate::machine::mock_wam::*;
1836
1837 #[test]
1838 #[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
1839 fn term_printing_tests() {
1840 let mut wam = MockWAM::new();
1841
1842 let f_atom = atom!("f");
1843 let a_atom = atom!("a");
1844 let b_atom = atom!("b");
1845 let c_atom = atom!("c");
1846
1847 wam.machine_st
1848 .heap
1849 .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)]));
1850
1851 {
1852 let printer = HCPrinter::new(
1853 &mut wam.machine_st.heap,
1854 Arc::clone(&wam.machine_st.atom_tbl),
1855 &mut wam.machine_st.stack,
1856 &wam.op_dir,
1857 PrinterOutputter::new(),
1858 heap_loc_as_cell!(0),
1859 );
1860
1861 let output = printer.print();
1862
1863 assert_eq!(output.result(), "f(a,b)");
1864 }
1865
1866 all_cells_unmarked(&wam.machine_st.heap);
1867
1868 wam.machine_st.heap.clear();
1869
1870 wam.machine_st.heap.extend(functor!(
1871 f_atom,
1872 [
1873 atom(a_atom),
1874 atom(b_atom),
1875 atom(a_atom),
1876 cell(str_loc_as_cell!(0))
1877 ]
1878 ));
1879
1880 {
1881 let printer = HCPrinter::new(
1882 &mut wam.machine_st.heap,
1883 Arc::clone(&wam.machine_st.atom_tbl),
1884 &mut wam.machine_st.stack,
1885 &wam.op_dir,
1886 PrinterOutputter::new(),
1887 heap_loc_as_cell!(0),
1888 );
1889
1890 let output = printer.print();
1891
1892 assert_eq!(output.result(), "f(a,b,a,...)");
1893 }
1894
1895 all_cells_unmarked(&wam.machine_st.heap);
1896
1897 wam.machine_st.heap.clear();
1898
1899 wam.machine_st.heap.push(list_loc_as_cell!(1));
1901 wam.machine_st.heap.push(list_loc_as_cell!(1));
1902 wam.machine_st.heap.push(list_loc_as_cell!(1));
1903
1904 {
1905 let printer = HCPrinter::new(
1906 &mut wam.machine_st.heap,
1907 Arc::clone(&wam.machine_st.atom_tbl),
1908 &mut wam.machine_st.stack,
1909 &wam.op_dir,
1910 PrinterOutputter::new(),
1911 heap_loc_as_cell!(0),
1912 );
1913
1914 let output = printer.print();
1915
1916 assert_eq!(output.result(), "[...|...]");
1917
1918 let mut printer = HCPrinter::new(
1919 &mut wam.machine_st.heap,
1920 Arc::clone(&wam.machine_st.atom_tbl),
1921 &mut wam.machine_st.stack,
1922 &wam.op_dir,
1923 PrinterOutputter::new(),
1924 heap_loc_as_cell!(0),
1925 );
1926
1927 printer
1928 .var_names
1929 .insert(list_loc_as_cell!(1), VarPtr::from("L"));
1930
1931 let output = printer.print();
1932
1933 assert_eq!(output.result(), "[L|L]");
1934 }
1935
1936 all_cells_unmarked(&wam.machine_st.heap);
1937
1938 wam.machine_st.heap.clear();
1939
1940 let functor = functor!(f_atom, [atom(a_atom), atom(b_atom), atom(b_atom)]);
1941
1942 wam.machine_st.heap.push(list_loc_as_cell!(1));
1943 wam.machine_st.heap.push(str_loc_as_cell!(5));
1944 wam.machine_st.heap.push(list_loc_as_cell!(3));
1945 wam.machine_st.heap.push(str_loc_as_cell!(5));
1946 wam.machine_st.heap.push(empty_list_as_cell!());
1947
1948 wam.machine_st.heap.extend(functor);
1949
1950 {
1951 let printer = HCPrinter::new(
1952 &mut wam.machine_st.heap,
1953 Arc::clone(&wam.machine_st.atom_tbl),
1954 &mut wam.machine_st.stack,
1955 &wam.op_dir,
1956 PrinterOutputter::new(),
1957 heap_loc_as_cell!(0),
1958 );
1959
1960 let output = printer.print();
1961
1962 assert_eq!(output.result(), "[f(a,b,b),f(a,b,b)]");
1963 }
1964
1965 all_cells_unmarked(&wam.machine_st.heap);
1966
1967 wam.machine_st.heap[4] = list_loc_as_cell!(1);
1968
1969 {
1970 let printer = HCPrinter::new(
1971 &mut wam.machine_st.heap,
1972 Arc::clone(&wam.machine_st.atom_tbl),
1973 &mut wam.machine_st.stack,
1974 &wam.op_dir,
1975 PrinterOutputter::new(),
1976 heap_loc_as_cell!(0),
1977 );
1978
1979 let output = printer.print();
1980
1981 assert_eq!(output.result(), "[f(a,b,b),f(a,b,b)|...]");
1982 }
1983
1984 all_cells_unmarked(&wam.machine_st.heap);
1985
1986 {
1987 let mut printer = HCPrinter::new(
1988 &mut wam.machine_st.heap,
1989 Arc::clone(&wam.machine_st.atom_tbl),
1990 &mut wam.machine_st.stack,
1991 &wam.op_dir,
1992 PrinterOutputter::new(),
1993 heap_loc_as_cell!(0),
1994 );
1995
1996 printer
1997 .var_names
1998 .insert(list_loc_as_cell!(1), VarPtr::from("L"));
1999
2000 let output = printer.print();
2001
2002 assert_eq!(output.result(), "[f(a,b,b),f(a,b,b)|L]");
2003 }
2004
2005 all_cells_unmarked(&wam.machine_st.heap);
2006
2007 wam.machine_st.heap.clear();
2009 wam.machine_st.heap.push(list_loc_as_cell!(1));
2010
2011 for idx in 0..3000 {
2012 wam.machine_st.heap.push(heap_loc_as_cell!(2 * idx + 1));
2013 wam.machine_st.heap.push(list_loc_as_cell!(2 * idx + 2 + 1));
2014 }
2015
2016 wam.machine_st.heap.push(empty_list_as_cell!());
2017
2018 {
2019 let mut printer = HCPrinter::new(
2020 &mut wam.machine_st.heap,
2021 Arc::clone(&wam.machine_st.atom_tbl),
2022 &mut wam.machine_st.stack,
2023 &wam.op_dir,
2024 PrinterOutputter::new(),
2025 heap_loc_as_cell!(0),
2026 );
2027
2028 printer.max_depth = 5;
2029
2030 let output = printer.print();
2031
2032 assert_eq!(output.result(), "[_1,_3,_5,_7,_9|...]");
2033 }
2034
2035 all_cells_unmarked(&wam.machine_st.heap);
2036
2037 wam.machine_st.heap.clear();
2038
2039 put_partial_string(&mut wam.machine_st.heap, "abc", &wam.machine_st.atom_tbl);
2040
2041 {
2042 let printer = HCPrinter::new(
2043 &mut wam.machine_st.heap,
2044 Arc::clone(&wam.machine_st.atom_tbl),
2045 &mut wam.machine_st.stack,
2046 &wam.op_dir,
2047 PrinterOutputter::new(),
2048 pstr_loc_as_cell!(0),
2049 );
2050
2051 let output = printer.print();
2052
2053 assert_eq!(output.result(), "[a,b,c|_1]");
2054 }
2055
2056 all_cells_unmarked(&wam.machine_st.heap);
2057
2058 wam.machine_st.heap.pop();
2059
2060 wam.machine_st.heap.push(list_loc_as_cell!(2));
2061
2062 wam.machine_st.heap.push(atom_as_cell!(a_atom));
2063 wam.machine_st.heap.push(list_loc_as_cell!(4));
2064 wam.machine_st.heap.push(atom_as_cell!(b_atom));
2065 wam.machine_st.heap.push(list_loc_as_cell!(6));
2066 wam.machine_st.heap.push(atom_as_cell!(c_atom));
2067 wam.machine_st.heap.push(empty_list_as_cell!());
2068
2069 {
2070 let mut printer = HCPrinter::new(
2071 &mut wam.machine_st.heap,
2072 Arc::clone(&wam.machine_st.atom_tbl),
2073 &mut wam.machine_st.stack,
2074 &wam.op_dir,
2075 PrinterOutputter::new(),
2076 heap_loc_as_cell!(0),
2077 );
2078
2079 printer.double_quotes = true;
2080
2081 let output = printer.print();
2082
2083 assert_eq!(output.result(), "\"abcabc\"");
2084 }
2085
2086 all_cells_unmarked(&wam.machine_st.heap);
2087
2088 wam.machine_st.heap.clear();
2089
2090 assert_eq!(
2091 &wam.parse_and_print_term("=(X,[a,b,c|X]).").unwrap(),
2092 "=(X,[a,b,c|X])"
2093 );
2094
2095 all_cells_unmarked(&wam.machine_st.heap);
2096
2097 assert_eq!(
2098 &wam.parse_and_print_term("[a,b,\"a\",[a,b,c]].").unwrap(),
2099 "[a,b,[a],[a,b,c]]"
2100 );
2101
2102 all_cells_unmarked(&wam.machine_st.heap);
2103
2104 assert_eq!(
2105 &wam.parse_and_print_term("[\"abc\",e,f,[g,e,h,Y,v|[X,Y]]].")
2106 .unwrap(),
2107 "[[a,b,c],e,f,[g,e,h,Y,v,X,Y]]"
2108 );
2109
2110 all_cells_unmarked(&wam.machine_st.heap);
2111
2112 assert_eq!(&wam.parse_and_print_term("f((a,b)).").unwrap(), "f((a,b))");
2113
2114 all_cells_unmarked(&wam.machine_st.heap);
2115
2116 wam.op_dir
2117 .insert((atom!("+"), Fixity::In), OpDesc::build_with(500, YFX as u8));
2118 wam.op_dir
2119 .insert((atom!("*"), Fixity::In), OpDesc::build_with(400, YFX as u8));
2120
2121 assert_eq!(
2122 &wam.parse_and_print_term("[a|[] + b].").unwrap(),
2123 "[a|[]+b]"
2124 );
2125
2126 all_cells_unmarked(&wam.machine_st.heap);
2127
2128 assert_eq!(
2129 &wam.parse_and_print_term("[a|[b|c]*d].").unwrap(),
2130 "[a|[b|c]*d]"
2131 );
2132
2133 all_cells_unmarked(&wam.machine_st.heap);
2134
2135 wam.op_dir
2136 .insert((atom!("fy"), Fixity::Pre), OpDesc::build_with(9, FY as u8));
2137
2138 wam.op_dir
2139 .insert((atom!("yf"), Fixity::Post), OpDesc::build_with(9, YF as u8));
2140
2141 assert_eq!(
2142 &wam.parse_and_print_term("(fy (fy 1)yf)yf.").unwrap(),
2143 "(fy (fy 1)yf)yf"
2144 );
2145
2146 assert_eq!(
2147 &wam.parse_and_print_term("fy(fy(yf(fy(1)))).").unwrap(),
2148 "fy fy (fy 1)yf"
2149 );
2150 }
2151}