]> Repositorios git - scryer-prolog.git/commitdiff
add provisional max_depth option to write_term, speed printing of non-cyclic terms
authorMark Thom <[email protected]>
Sun, 15 Mar 2020 06:09:20 +0000 (00:09 -0600)
committerMark Thom <[email protected]>
Sun, 15 Mar 2020 06:09:20 +0000 (00:09 -0600)
Cargo.toml
src/prolog/clause_types.rs
src/prolog/heap_print.rs
src/prolog/lib/builtins.pl
src/prolog/machine/system_calls.rs
src/prolog/toplevel.pl

index 278c8948ea6825981ed8a18fdf520efe0c4246fb..50f1882b74e3ea46733461b8a41e5a59864ea14e 100644 (file)
@@ -29,3 +29,6 @@ prolog_parser = { version = "0.8.47", default-features = false }
 ref_thread_local = "0.0.0"
 rug = { version = "1.4.0", optional = true }
 rustyline = "6.0.0"
+
+[profile.release]
+debug = true
\ No newline at end of file
index 4ccddfdb5f9dfb9e73d0dab68bbfbde4df53d33f..14d2498f4679c741f350177649ac6406707a0a30 100644 (file)
@@ -535,7 +535,7 @@ impl SystemClauseType {
            ("$use_qualified_module_from_file", 2) =>
                Some(SystemClauseType::REPL(REPLCodePtr::UseQualifiedModuleFromFile)),
             ("$variant", 2) => Some(SystemClauseType::Variant),
-            ("$write_term", 5) => Some(SystemClauseType::WriteTerm),
+            ("$write_term", 6) => Some(SystemClauseType::WriteTerm),
             ("$wam_instructions", 3) => Some(SystemClauseType::WAMInstructions),
             _ => None,
         }
index a5908edec887e938508d2c5bded58c28289961c1..4897c7ae6f1bdd69aef5aeed97f3861eff44c913 100644 (file)
@@ -159,21 +159,21 @@ fn char_to_string(is_quoted: bool, c: char) -> String {
 }
 
 #[derive(Clone)]
-pub enum TokenOrRedirect {
+enum TokenOrRedirect {
     Atom(ClauseName),
     BarAsOp,
     Op(ClauseName, SharedOpDesc),
     NumberedVar(String),
-    CompositeRedirect(DirectedOp),
-    FunctorRedirect,
+    CompositeRedirect(usize, DirectedOp),
+    FunctorRedirect(usize),
     Open,
     Close,
     Comma,
     Space,
     LeftCurly,
     RightCurly,
-    OpenList(Rc<Cell<bool>>),
-    CloseList(Rc<Cell<bool>>),
+    OpenList(Rc<Cell<(bool, usize)>>),
+    CloseList(Rc<Cell<(bool, usize)>>),
     HeadTailSeparator,
 }
 
@@ -315,12 +315,14 @@ pub struct HCPrinter<'a, Outputter> {
     printed_vars: IndexSet<Addr>,
     last_item_idx: usize,
     cyclic_terms: IndexMap<Addr, usize>,
+    non_cyclic_terms: IndexSet<usize>,
     pub(crate) var_names: IndexMap<Addr, Var>,
     pub(crate) numbervars_offset: Integer,
     pub(crate) numbervars: bool,
     pub(crate) quoted: bool,
     pub(crate) ignore_ops: bool,
     pub(crate) print_strings_as_strs: bool,
+    pub(crate) max_depth: usize,
 }
 
 macro_rules! push_space_if_amb {
@@ -430,6 +432,18 @@ fn non_quoted_token<Iter: Iterator<Item = char>>(mut iter: Iter) -> bool {
     }
 }
 
+#[inline]
+fn functor_location(addr: &Addr) -> Option<usize> {
+    Some(match addr {
+        &Addr::Lis(l) => l,
+        &Addr::Str(s) => s,
+        &Addr::PStrLocation(h, _) | &Addr::PStrTail(h, _) => h,
+        _ => {
+            return None;
+        }
+    })
+}
+
 impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
     pub fn new(machine_st: &'a MachineState, op_dir: &'a OpDir, output: Outputter) -> Self {
         HCPrinter {
@@ -446,8 +460,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
             quoted: false,
             ignore_ops: false,
             cyclic_terms: IndexMap::new(),
+            non_cyclic_terms: IndexSet::new(),
             var_names: IndexMap::new(),
             print_strings_as_strs: false,
+            max_depth: 0,
         }
     }
 
@@ -485,53 +501,110 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         requires_space(tail, atom)
     }
 
-    fn enqueue_op(&mut self, ct: ClauseType, spec: SharedOpDesc) {
+    fn enqueue_op(
+        &mut self,
+        iter: &mut HCPreOrderIterator,
+        mut max_depth: usize,
+        ct: ClauseType,
+        spec: SharedOpDesc,
+    ) {
         if is_postfix!(spec.assoc()) {
+            if self.check_max_depth(&mut max_depth) {
+                iter.stack().pop();
+
+                self.state_stack.push(TokenOrRedirect::Op(ct.name(), spec));
+                self.state_stack.push(TokenOrRedirect::Atom(clause_name!("...")));
+
+                return;
+            }
+
             let right_directed_op = DirectedOp::Right(ct.name(), spec.clone());
 
             self.state_stack.push(TokenOrRedirect::Op(ct.name(), spec));
-            self.state_stack
-                .push(TokenOrRedirect::CompositeRedirect(right_directed_op));
+            self.state_stack.push(TokenOrRedirect::CompositeRedirect(
+                max_depth,
+                right_directed_op,
+            ));
         } else if is_prefix!(spec.assoc()) {
             match ct.name().as_str() {
                 "-" | "\\" => {
-                    self.format_prefix_op_with_space(ct.name(), spec);
+                    self.format_prefix_op_with_space(iter, max_depth, ct.name(), spec);
                     return;
                 }
                 _ => {}
             };
 
+            if self.check_max_depth(&mut max_depth) {
+                iter.stack().pop();
+
+                self.state_stack.push(TokenOrRedirect::Atom(clause_name!("...")));
+                self.state_stack.push(TokenOrRedirect::Op(ct.name(), spec));
+
+                return;
+            }
+
             let left_directed_op = DirectedOp::Left(ct.name(), spec.clone());
 
-            self.state_stack
-                .push(TokenOrRedirect::CompositeRedirect(left_directed_op));
+            self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, left_directed_op));
             self.state_stack.push(TokenOrRedirect::Op(ct.name(), spec));
         } else {
             // if is_infix!(spec.assoc())
             match ct.name().as_str() {
                 "|" => {
-                    self.format_bar_separator_op(ct.name(), spec);
+                    self.format_bar_separator_op(iter, max_depth, ct.name(), spec);
                     return;
                 }
                 _ => {}
             };
 
+            if self.check_max_depth(&mut max_depth) {
+                iter.stack().pop();
+                iter.stack().pop();
+
+                self.state_stack.push(TokenOrRedirect::Atom(clause_name!("...")));
+                self.state_stack.push(TokenOrRedirect::Op(ct.name(), spec));
+                self.state_stack.push(TokenOrRedirect::Atom(clause_name!("...")));
+
+                return;
+            }
+
             let left_directed_op = DirectedOp::Left(ct.name(), spec.clone());
             let right_directed_op = DirectedOp::Right(ct.name(), spec.clone());
 
             self.state_stack
-                .push(TokenOrRedirect::CompositeRedirect(left_directed_op));
+                .push(TokenOrRedirect::CompositeRedirect(max_depth, left_directed_op));
             self.state_stack.push(TokenOrRedirect::Op(ct.name(), spec));
             self.state_stack
-                .push(TokenOrRedirect::CompositeRedirect(right_directed_op));
+                .push(TokenOrRedirect::CompositeRedirect(max_depth, right_directed_op));
         }
     }
 
-    fn format_struct(&mut self, arity: usize, name: ClauseName) {
+    fn format_struct(
+        &mut self,
+        iter: &mut HCPreOrderIterator,
+        mut max_depth: usize,
+        arity: usize,
+        name: ClauseName,
+    ) -> bool
+    {
+        if self.check_max_depth(&mut max_depth) {
+            for _ in 0 .. arity {
+                iter.stack().pop();
+            }
+
+            self.state_stack.push(TokenOrRedirect::Close);
+            self.state_stack.push(TokenOrRedirect::Atom(clause_name!("...")));
+            self.state_stack.push(TokenOrRedirect::Open);
+
+            self.state_stack.push(TokenOrRedirect::Atom(name));
+
+            return false;
+        }
+
         self.state_stack.push(TokenOrRedirect::Close);
 
-        for _ in 0..arity {
-            self.state_stack.push(TokenOrRedirect::FunctorRedirect);
+        for _ in 0 .. arity {
+            self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth));
             self.state_stack.push(TokenOrRedirect::Comma);
         }
 
@@ -539,29 +612,77 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         self.state_stack.push(TokenOrRedirect::Open);
 
         self.state_stack.push(TokenOrRedirect::Atom(name));
+
+        true
     }
 
-    fn format_prefix_op_with_space(&mut self, name: ClauseName, spec: SharedOpDesc) {
+    fn format_prefix_op_with_space(
+        &mut self,
+        iter: &mut HCPreOrderIterator,
+        mut max_depth: usize,
+        name: ClauseName,
+        spec: SharedOpDesc)
+    {
+        if self.check_max_depth(&mut max_depth) {
+            iter.stack().pop();
+
+            self.state_stack.push(TokenOrRedirect::Atom(clause_name!("...")));
+            self.state_stack.push(TokenOrRedirect::Space);
+            self.state_stack.push(TokenOrRedirect::Atom(name));
+
+            return;
+        }
+
         let op = DirectedOp::Left(name.clone(), spec);
 
-        self.state_stack.push(TokenOrRedirect::CompositeRedirect(op));
+        self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, op));
         self.state_stack.push(TokenOrRedirect::Space);
         self.state_stack.push(TokenOrRedirect::Atom(name));
     }
 
-    fn format_bar_separator_op(&mut self, name: ClauseName, spec: SharedOpDesc) {
+    fn format_bar_separator_op(
+        &mut self,
+        iter: &mut HCPreOrderIterator,
+        mut max_depth: usize,
+        name: ClauseName,
+        spec: SharedOpDesc,
+    )
+    {
+        if self.check_max_depth(&mut max_depth) {
+            iter.stack().pop();
+
+            self.state_stack.push(TokenOrRedirect::Atom(clause_name!("...")));
+            self.state_stack.push(TokenOrRedirect::BarAsOp);
+            self.state_stack.push(TokenOrRedirect::Atom(clause_name!("...")));
+
+            return;
+        }
+
         let left_directed_op = DirectedOp::Left(name.clone(), spec.clone());
         let right_directed_op = DirectedOp::Right(name.clone(), spec.clone());
 
-        self.state_stack.push(TokenOrRedirect::CompositeRedirect(left_directed_op));
+        self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, left_directed_op));
         self.state_stack.push(TokenOrRedirect::BarAsOp);
-        self.state_stack.push(TokenOrRedirect::CompositeRedirect(right_directed_op));
+        self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, right_directed_op));
     }
 
-    fn format_curly_braces(&mut self) {
+    fn format_curly_braces(&mut self, iter: &mut HCPreOrderIterator, mut max_depth: usize) -> bool
+    {
+        if self.check_max_depth(&mut max_depth) {
+            iter.stack().pop();
+
+            self.state_stack.push(TokenOrRedirect::RightCurly);
+            self.state_stack.push(TokenOrRedirect::Atom(clause_name!("...")));
+            self.state_stack.push(TokenOrRedirect::LeftCurly);
+
+            return false;
+        }
+
         self.state_stack.push(TokenOrRedirect::RightCurly);
-        self.state_stack.push(TokenOrRedirect::FunctorRedirect);
+        self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth));
         self.state_stack.push(TokenOrRedirect::LeftCurly);
+
+        true
     }
 
     fn format_numbered_vars(&mut self, iter: &mut HCPreOrderIterator) -> bool {
@@ -577,29 +698,36 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         false
     }
 
-    fn format_clause(&mut self, iter: &mut HCPreOrderIterator, arity: usize, ct: ClauseType) {
+    fn format_clause(
+        &mut self,
+        iter: &mut HCPreOrderIterator,
+        max_depth: usize,
+        arity: usize,
+        ct: ClauseType,
+    ) -> bool {
         if self.numbervars && is_numbered_var(&ct, arity) {
             if self.format_numbered_vars(iter) {
-                return;
+                return true;
             }
         }
 
         if let Some(spec) = ct.spec() {
             if "." == ct.name().as_str() && is_infix!(spec.assoc()) {
                 if !self.ignore_ops {
-                    self.push_list();
-                    return;
+                    self.push_list(iter, max_depth);
+                    return true;
                 }
             }
 
             if !self.ignore_ops && spec.prec() > 0 {
-                return self.enqueue_op(ct, spec);
+                self.enqueue_op(iter, max_depth, ct, spec);
+                return true;
             }
         }
 
-        match (ct.name().as_str(), arity) {
-            ("{}", 1) if !self.ignore_ops => self.format_curly_braces(),
-            _ => self.format_struct(arity, ct.name()),
+        return match (ct.name().as_str(), arity) {
+            ("{}", 1) if !self.ignore_ops => self.format_curly_braces(iter, max_depth),
+            _ => self.format_struct(iter, max_depth, arity, ct.name()),
         };
     }
 
@@ -646,7 +774,60 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         }
     }
 
-    fn check_for_seen(&mut self, iter: &mut HCPreOrderIterator) -> Option<HeapCellValue> {
+    fn record_children_as_non_cyclic(&mut self, addr: &Addr) {
+        match addr {
+            &Addr::Lis(l) => {
+                let c1 = self.machine_st.store(self.machine_st.deref(Addr::HeapCell(l)));
+                let c2 = self.machine_st.store(self.machine_st.deref(Addr::HeapCell(l + 1)));
+
+                if let Some(c) = functor_location(&c1) {
+                    self.non_cyclic_terms.insert(c);
+                }
+
+                if let Some(c) = functor_location(&c2) {
+                    self.non_cyclic_terms.insert(c);
+                }
+            }
+            &Addr::Str(s) => {
+                let arity =
+                    match &self.machine_st.heap[s] {
+                        HeapCellValue::NamedStr(arity, ..) => {
+                            arity
+                        }
+                        _ => {
+                            unreachable!()
+                        }
+                    };
+
+                for i in 1 .. arity + 1 {
+                    let c = self.machine_st.store(self.machine_st.deref(Addr::HeapCell(s + i)));
+
+                    if let Some(c) = functor_location(&c) {
+                        self.non_cyclic_terms.insert(c);
+                    }
+                }
+            }
+            &Addr::PStrLocation(h, _) | &Addr::PStrTail(h, _) => {
+                let tail = match &self.machine_st.heap[h] {
+                    &HeapCellValue::PartialString(ref pstr) => pstr.tail_addr().clone(),
+                    _ => unreachable!()
+                };
+
+                let tail = self.machine_st.store(self.machine_st.deref(tail));
+
+                if let Some(c) = functor_location(&tail) {
+                    self.non_cyclic_terms.insert(c);
+                }
+            }
+            _ => {
+            }
+        }
+    }
+
+    fn check_for_seen(
+        &mut self,
+        iter: &mut HCPreOrderIterator,
+    ) -> Option<HeapCellValue> {
         iter.stack().last().cloned().and_then(|addr| {
             let addr = self.machine_st.store(self.machine_st.deref(addr));
 
@@ -661,6 +842,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
                         return iter.next();
                     } else {
                         iter.stack().pop();
+                        
                         push_space_if_amb!(self, &var, {
                             self.append_str(&var);
                         });
@@ -669,30 +851,38 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
                     }
                 }
                 None => {
-                    if self.machine_st.is_cyclic_term(addr.clone()) {
-                        match self.cyclic_terms.get(&addr).cloned() {
-                            Some(reps) => {
-                                if reps > 0 {
-                                    self.cyclic_terms.insert(addr, reps - 1);
-                                    iter.next()
-                                } else {
-                                    push_space_if_amb!(self, "...", {
-                                        self.append_str("...");
-                                    });
-
-                                    iter.stack().pop();
-                                    self.cyclic_terms.swap_remove(&addr);
-                                    None
-                                }
-                            }
-                            None => {
+                    let offset = match functor_location(&addr) {
+                        Some(offset) => offset,
+                        None => {
+                            return iter.next();
+                        }
+                    };
+
+                    if !self.non_cyclic_terms.contains(&offset) {
+                        if let Some(reps) = self.cyclic_terms.get(&addr).cloned() {
+                            if reps > 0 {
+                                self.cyclic_terms.insert(addr, reps - 1);
+                            } else {
+                                push_space_if_amb!(self, "...", {
+                                    self.append_str("...");
+                                });
+
+                                iter.stack().pop();
                                 self.cyclic_terms.insert(addr, 2);
-                                iter.next()
+
+                                return None;
                             }
+                        } else if self.machine_st.is_cyclic_term(addr.clone()) {
+                            self.cyclic_terms.insert(addr, 2);
+                        } else {
+                            self.record_children_as_non_cyclic(&addr);
+                            self.non_cyclic_terms.insert(offset);
                         }
                     } else {
-                        iter.next()
+                        self.record_children_as_non_cyclic(&addr);
                     }
+
+                    iter.next()
                 }
             }
         })
@@ -807,8 +997,13 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         }
     }
 
-    fn print_constant(&mut self, iter: &mut HCPreOrderIterator, c: Constant, op: &Option<DirectedOp>)
-    {
+    fn print_constant(
+        &mut self,
+        iter: &mut HCPreOrderIterator,
+        max_depth: usize,
+        c: Constant,
+        op: &Option<DirectedOp>,
+    ) {
         match c {
             Constant::Atom(atom, spec) => {
                 if let Some(_) = fetch_atom_op_spec(atom.clone(), spec, self.op_dir) {
@@ -862,7 +1057,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
                 self.print_string_as_str(iter, n, s);
             }
             Constant::String(n, s) => {
-                self.print_string(iter, n, s);
+                self.print_string(iter, max_depth, n, s);
             }
             Constant::Usize(i) => {
                 self.append_str(&format!("u{}", i));
@@ -891,39 +1086,77 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         iter.stack().pop();
     }
 
-    fn print_string(&mut self, iter: &mut HCPreOrderIterator, offset: usize, s: Rc<String>) {
+    fn print_string(
+        &mut self,
+        iter: &mut HCPreOrderIterator,
+        mut max_depth: usize,
+        offset: usize,
+        s: Rc<String>)
+    {
         if !self.machine_st.machine_flags().double_quotes.is_atom() {
+            if self.check_max_depth(&mut max_depth) {
+                self.state_stack.push(TokenOrRedirect::Atom(clause_name!("...")));
+                return;
+            }
+
             if s.len() <= offset && !self.at_cdr("") {
                 self.append_str("[]");
             } else if self.ignore_ops {
-                let mut paren_count = 0;
+                let mut char_count = 0;
+                let mut byte_len = 0;
 
-                for c in s[offset ..].chars() {
+                let iter: Box<dyn Iterator<Item=char>> =
+                    if self.max_depth == 0 {
+                        Box::new(s[offset ..].chars())
+                    } else {
+                        Box::new(s[offset ..].chars().take(max_depth))
+                    };
+
+                for c in iter {
                     self.print_char('.');
                     self.push_char('(');
 
                     self.print_char(c);
                     self.push_char(',');
 
-                    paren_count += 1;
+                    char_count += 1;
+                    byte_len += c.len_utf8();
                 }
 
-                self.append_str("[]");
+                if self.max_depth > 0 && byte_len < s[offset ..].len() {
+                    self.append_str("...");
+                }  else {
+                    self.append_str("[]");
+                }
 
-                for _ in 0 .. paren_count {
+                for _ in 0 .. char_count {
                     self.push_char(')');
                 }
             } else {
                 self.push_char('[');
 
-                for c in s[offset ..].chars() {
+                let iter: Box<dyn Iterator<Item=char>> =
+                    if self.max_depth == 0 {
+                        Box::new(s[offset ..].chars())
+                    } else {
+                        Box::new(s[offset ..].chars().take(max_depth))
+                    };
+
+                let mut byte_len = 0;
+
+                for c in iter {
                     self.print_char(c);
                     self.push_char(',');
-                }
 
-                self.outputter.truncate(self.outputter.len() - ','.len_utf8());
+                    byte_len += c.len_utf8();
+                }
 
-                self.push_char(']');
+                if self.max_depth > 0 && byte_len < s[offset ..].len() {
+                    self.append_str("...|...]");
+                }  else {
+                    self.outputter.truncate(self.outputter.len() - ','.len_utf8());
+                    self.push_char(']');
+                }
             }
 
             iter.stack().pop();
@@ -939,15 +1172,43 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         }
     }
 
-    fn push_list(&mut self) {
-        let cell = Rc::new(Cell::new(true));
+    fn check_max_depth(&mut self, max_depth: &mut usize) -> bool {
+        if self.max_depth > 0 && *max_depth == 0 {
+            return true;
+        }
+
+        if *max_depth > 0 {
+            *max_depth -= 1;
+        }
+
+        false
+    }
+
+    fn push_list(&mut self, iter: &mut HCPreOrderIterator, mut max_depth: usize) {
+        if self.check_max_depth(&mut max_depth) {
+            iter.stack().pop();
+            iter.stack().pop();
+
+            let cell = Rc::new(Cell::new((true, 0)));
+
+            self.state_stack.push(TokenOrRedirect::CloseList(cell.clone()));
 
-        self.state_stack
-            .push(TokenOrRedirect::CloseList(cell.clone()));
+            self.state_stack.push(TokenOrRedirect::Atom(clause_name!("...")));
+            self.state_stack.push(TokenOrRedirect::HeadTailSeparator); // bar
+            self.state_stack.push(TokenOrRedirect::Atom(clause_name!("...")));
 
-        self.state_stack.push(TokenOrRedirect::FunctorRedirect);
+            self.state_stack.push(TokenOrRedirect::OpenList(cell));
+
+            return;
+        }
+
+        let cell = Rc::new(Cell::new((true, max_depth)));
+
+        self.state_stack.push(TokenOrRedirect::CloseList(cell.clone()));
+
+        self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth));
         self.state_stack.push(TokenOrRedirect::HeadTailSeparator); // bar
-        self.state_stack.push(TokenOrRedirect::FunctorRedirect);
+        self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth));
 
         self.state_stack.push(TokenOrRedirect::OpenList(cell));
     }
@@ -961,6 +1222,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         is_functor_redirect: bool,
         spec: SharedOpDesc,
         negated_operand: bool,
+        max_depth: usize,
     ) {
         let add_brackets = if !self.ignore_ops {
             negated_operand
@@ -987,14 +1249,15 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         }
 
         let ct = ClauseType::from(name.clone(), arity, Some(spec));
-        self.format_clause(iter, arity, ct);
 
-        if add_brackets {
-            self.state_stack.push(TokenOrRedirect::Open);
+        if self.format_clause(iter, max_depth, arity, ct) {
+            if add_brackets {
+                self.state_stack.push(TokenOrRedirect::Open);
 
-            if let Some(ref op) = &op {
-                if op.is_left() && requires_space(op.as_str(), "(") {
-                    self.state_stack.push(TokenOrRedirect::Space);
+                if let Some(ref op) = &op {
+                    if op.is_left() && requires_space(op.as_str(), "(") {
+                        self.state_stack.push(TokenOrRedirect::Space);
+                    }
                 }
             }
         }
@@ -1005,6 +1268,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         iter: &mut HCPreOrderIterator,
         op: Option<DirectedOp>,
         is_functor_redirect: bool,
+        max_depth: usize,
     ) {
         let negated_operand = negated_op_needs_bracketing(iter, &op);
 
@@ -1026,11 +1290,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
                         is_functor_redirect,
                         spec,
                         negated_operand,
+                        max_depth,
                     );
                 } else {
                     push_space_if_amb!(self, name.as_str(), {
                         let ct = ClauseType::from(name, arity, spec);
-                        self.format_clause(iter, arity, ct);
+                        self.format_clause(iter, max_depth, arity, ct);
                     });
                 }
             }
@@ -1039,12 +1304,14 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
                     self.append_str("[]");
                 }
             }
-            HeapCellValue::Addr(Addr::Con(c)) => self.print_constant(iter, c, &op),
+            HeapCellValue::Addr(Addr::Con(c)) => {
+                self.print_constant(iter, max_depth, c, &op);
+            }
             HeapCellValue::Addr(Addr::Lis(_)) | HeapCellValue::Addr(Addr::PStrLocation(..)) => {
                 if self.ignore_ops {
-                    self.format_struct(2, clause_name!("."));
+                    self.format_struct(iter, max_depth, 2, clause_name!("."));
                 } else {
-                    self.push_list();
+                    self.push_list(iter, max_depth);
                 }
             }
             HeapCellValue::Addr(Addr::Stream(stream)) => {
@@ -1099,11 +1366,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
                     TokenOrRedirect::BarAsOp => self.append_str(" | "),
                     TokenOrRedirect::Op(atom, _) => self.print_op(atom.as_str()),
                     TokenOrRedirect::NumberedVar(num_var) => self.append_str(num_var.as_str()),
-                    TokenOrRedirect::CompositeRedirect(op) => {
-                        self.handle_heap_term(&mut iter, Some(op), false)
+                    TokenOrRedirect::CompositeRedirect(max_depth, op) => {
+                        self.handle_heap_term(&mut iter, Some(op), false, max_depth)
                     }
-                    TokenOrRedirect::FunctorRedirect => {
-                        self.handle_heap_term(&mut iter, None, true)
+                    TokenOrRedirect::FunctorRedirect(max_depth) => {
+                        self.handle_heap_term(&mut iter, None, true, max_depth)
                     }
                     TokenOrRedirect::Close => self.push_char(')'),
                     TokenOrRedirect::Open => self.push_char('('),
@@ -1111,11 +1378,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
                         if !self.at_cdr(",") {
                             self.push_char('[');
                         } else {
-                            delimit.set(false);
+                            let (_, max_depth) = delimit.get();
+                            delimit.set((false, max_depth));
                         }
                     }
                     TokenOrRedirect::CloseList(delimit) => {
-                        if delimit.get() {
+                        if delimit.get().0 {
                             self.push_char(']');
                         }
                     }
@@ -1127,7 +1395,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
                 }
             } else if !iter.stack().is_empty() {
                 let spec = self.toplevel_spec.take();
-                self.handle_heap_term(&mut iter, spec, false);
+                self.handle_heap_term(&mut iter, spec, false, self.max_depth);
             } else {
                 break;
             }
index 623d7870f9d27b4f1d29cabf0269eff9743cd379..bf082f35e23a14699b3a7738b69d30676212aa90 100644 (file)
@@ -289,6 +289,7 @@ is_write_option(Functor) :-
     ( Arg == true -> true
     ; Arg == false -> true
     ; Name == variable_names -> must_be_var_names_list(Arg)
+    ; Name == max_depth -> integer(Arg), Arg >= 0
     ; var(Arg) -> throw(error(instantiation_error, write_term/2))
     ; throw(error(domain_error(write_option, Functor), write_term/2))
     ), % 8.14.2.3 e)
@@ -296,6 +297,7 @@ is_write_option(Functor) :-
     ; Name == quoted -> true
     ; Name == numbervars -> true
     ; Name == variable_names -> true
+    ; Name == max_depth -> true
     ; throw(error(domain_error(write_option, Functor), write_term/2))
     ). % 8.14.2.3 e)
 
@@ -338,7 +340,8 @@ write_term(Term, Options) :-
     inst_member_or(Options, numbervars(NumberVars), numbervars(false)),
     inst_member_or(Options, quoted(Quoted), quoted(false)),
     inst_member_or(Options, variable_names(VarNames), variable_names([])),
-    '$write_term'(Term, IgnoreOps, NumberVars, Quoted, VarNames).
+    inst_member_or(Options, max_depth(MaxDepth), max_depth(0)),
+    '$write_term'(Term, IgnoreOps, NumberVars, Quoted, VarNames, MaxDepth).
 
 write(Term) :- write_term(Term, [numbervars(true)]).
 
index 962d3ccee1d783238859bbf9c041e0b557e3b1d7..c4cf4b2b86f748eb93867bf599e5ab2fdc2e07b4 100644 (file)
@@ -2667,6 +2667,7 @@ impl MachineState {
                 let ignore_ops = self.store(self.deref(self[temp_v!(2)].clone()));
                 let numbervars = self.store(self.deref(self[temp_v!(3)].clone()));
                 let quoted = self.store(self.deref(self[temp_v!(4)].clone()));
+                let max_depth = self.store(self.deref(self[temp_v!(6)].clone()));                
 
                 let mut printer = HCPrinter::new(&self, &indices.op_dir, PrinterOutputter::new());
 
@@ -2682,6 +2683,15 @@ impl MachineState {
                     printer.quoted = name.as_str() == "true";
                 }
 
+                if let &Addr::Con(Constant::Integer(ref n)) = &max_depth {
+                    if let Some(n) = n.to_usize() {
+                        printer.max_depth = n;
+                    } else {
+                        self.fail = true;
+                        return Ok(());
+                    }
+                }
+
                 let stub = MachineError::functor_stub(clause_name!("write_term"), 2);
 
                 match self.try_from_list(temp_v!(5), stub.clone()) {
index 6d869e70f586ae28af41d78747fa097d9da9a58d..59d1884d6d958d884a94f67b9b0e9489387b19bd 100644 (file)
        write(' = '),
        (  '$needs_bracketing'(Value, (=)) ->
          write('('),
-         write_term(Value, [quoted(true), variable_names(VarList)]),
+         write_term(Value, [quoted(true), variable_names(VarList), max_depth(0)]),
          write(')')
-       ;  write_term(Value, [quoted(true), variable_names(VarList)])
+       ;  write_term(Value, [quoted(true), variable_names(VarList), max_depth(0)])
        )
     ;  G == [] ->
        write('true')
        write(' = '),
        (  '$needs_bracketing'(Value, (=)) ->
          write('('),
-         write_term(Value, [quoted(true), variable_names(VarList)]),
+         write_term(Value, [quoted(true), variable_names(VarList), max_depth(0)]),
          write(')')
-       ;  write_term(Value, [quoted(true), variable_names(VarList)]),
+       ;  write_term(Value, [quoted(true), variable_names(VarList), max_depth(0)]),
          (  '$trailing_period_is_ambiguous'(Value) ->
             write(' ')
          ;  true
        )
     ;  G == [] ->
        write('true')
-    ;  write_term(G, [quoted(true), variable_names(VarList)])
+    ;  write_term(G, [quoted(true), variable_names(VarList), max_depth(0)])
     ).
 
 '$write_eq'((G1, G2), VarList) :-
     ).
 
 '$print_exception'(E) :-
-    write_term('caught: ', [quoted(false)]),
+    write_term('caught: ', [quoted(false), max_depth(20)]),
     writeq(E),
     nl.