Files
scryer-prolog/src/heap_print.rs
Skgland b53ef148a0 remove extern crate declaration and fix outfall (macros now need to be imported into scope)
using use declarations in main.rs so that use paths don't need to be updated as well, this will be done in a later commit
2021-02-06 22:12:31 +01:00

1571 lines
49 KiB
Rust

use crate::prolog_parser_rebis::ast::*;
use crate::prolog_parser_rebis::{
alpha_char, alpha_numeric_char, backslash_char, capital_letter_char, char_class, clause_name,
cut_char, decimal_digit_char, graphic_char, graphic_token_char, is_fx, is_infix, is_postfix,
is_prefix, is_xf, is_xfx, is_xfy, is_yfx, semicolon_char, sign_char, single_quote_char,
small_letter_char, solo_char, variable_indicator_char,
};
use crate::clause_types::*;
use crate::forms::*;
use crate::heap_iter::*;
use crate::machine::heap::*;
use crate::machine::machine_indices::*;
use crate::machine::machine_state::*;
use crate::machine::streams::*;
use crate::ordered_float::OrderedFloat;
use crate::rug::{Integer, Rational};
use crate::indexmap::{IndexMap, IndexSet};
use std::cell::Cell;
use std::convert::TryFrom;
use std::iter::{once, FromIterator};
use std::net::{IpAddr, TcpListener};
use std::ops::{Range, RangeFrom};
use std::rc::Rc;
/* contains the location, name, precision and Specifier of the parent op. */
#[derive(Debug, Clone)]
pub enum DirectedOp {
Left(ClauseName, SharedOpDesc),
Right(ClauseName, SharedOpDesc),
}
impl DirectedOp {
#[inline]
fn as_str(&self) -> &str {
match self {
&DirectedOp::Left(ref name, _) | &DirectedOp::Right(ref name, _) => name.as_str(),
}
}
#[inline]
fn is_negative_sign(&self) -> bool {
match self {
&DirectedOp::Left(ref name, ref cell) | &DirectedOp::Right(ref name, ref cell) => {
name.as_str() == "-" && is_prefix!(cell.assoc())
}
}
}
#[inline]
fn is_left(&self) -> bool {
if let &DirectedOp::Left(..) = self {
true
} else {
false
}
}
}
fn needs_bracketing(child_spec: &SharedOpDesc, op: &DirectedOp) -> bool {
match op {
&DirectedOp::Left(ref name, ref cell) => {
let (priority, spec) = cell.get();
if name.as_str() == "-" {
let child_assoc = child_spec.assoc();
if is_prefix!(spec) && (is_postfix!(child_assoc) || is_infix!(child_assoc)) {
return true;
}
}
let is_strict_right = is_yfx!(spec) || is_xfx!(spec) || is_fx!(spec);
child_spec.prec() > priority || (child_spec.prec() == priority && is_strict_right)
}
&DirectedOp::Right(_, ref cell) => {
let (priority, spec) = cell.get();
let is_strict_left = is_xfx!(spec) || is_xfy!(spec) || is_xf!(spec);
if child_spec.prec() > priority || (child_spec.prec() == priority && is_strict_left) {
true
} else if (is_postfix!(spec) || is_infix!(spec)) && !is_postfix!(child_spec.assoc()) {
!SharedOpDesc::ptr_eq(&cell, &child_spec) && child_spec.prec() == priority
} else {
false
}
}
}
}
impl<'a> HCPreOrderIterator<'a> {
/*
* descend into the subtree where the iterator is currently parked
* and check that the leftmost leaf is a number, with every node
* encountered on the way an infix or postfix operator, unblocked
* by brackets.
*/
fn leftmost_leaf_has_property<P>(&self, property_check: P) -> bool
where
P: Fn(Addr, &Heap) -> bool,
{
let mut addr = match self.state_stack.last().cloned() {
Some(addr) => addr,
None => return false,
};
let mut parent_spec = DirectedOp::Left(clause_name!("-"), SharedOpDesc::new(200, FY));
loop {
match self.machine_st.store(self.machine_st.deref(addr)) {
Addr::Str(s) => match &self.machine_st.heap[s] {
&HeapCellValue::NamedStr(_, ref name, Some(ref spec))
if is_postfix!(spec.assoc()) || is_infix!(spec.assoc()) =>
{
if needs_bracketing(spec, &parent_spec) {
return false;
} else {
addr = Addr::HeapCell(s + 1);
parent_spec = DirectedOp::Right(name.clone(), spec.clone());
}
}
_ => {
return false;
}
},
addr => {
return property_check(addr, &self.machine_st.heap);
}
}
}
}
fn immediate_leaf_has_property<P>(&self, property_check: P) -> bool
where
P: Fn(Addr, &Heap) -> bool,
{
let addr = match self.state_stack.last().cloned() {
Some(addr) => addr,
None => return false,
};
let addr = self.machine_st.store(self.machine_st.deref(addr));
property_check(addr, &self.machine_st.heap)
}
}
fn char_to_string(is_quoted: bool, c: char) -> String {
match c {
'\'' if is_quoted => "\\'".to_string(),
'\n' if is_quoted => "\\n".to_string(),
'\r' if is_quoted => "\\r".to_string(),
'\t' if is_quoted => "\\t".to_string(),
'\u{0b}' if is_quoted => "\\v".to_string(), // UTF-8 vertical tab
'\u{0c}' if is_quoted => "\\f".to_string(), // UTF-8 form feed
'\u{08}' if is_quoted => "\\b".to_string(), // UTF-8 backspace
'\u{07}' if is_quoted => "\\a".to_string(), // UTF-8 alert
'"' if is_quoted => "\\\"".to_string(),
'\\' if is_quoted => "\\\\".to_string(),
'\'' | '\n' | '\r' | '\t' | '\u{0b}' | '\u{0c}' | '\u{08}' | '\u{07}' | '"' | '\\' => {
c.to_string()
}
'\u{a0}'..='\u{d6}' => c.to_string(),
'\u{d8}'..='\u{f6}' => c.to_string(),
'\u{f8}'..='\u{74f}' => c.to_string(),
'\x20'..='\x7e' => c.to_string(),
_ => format!("\\x{:x}\\", c as u32),
}
}
#[derive(Debug, Clone)]
enum TokenOrRedirect {
Atom(ClauseName),
BarAsOp,
Op(ClauseName, SharedOpDesc),
NumberedVar(String),
CompositeRedirect(usize, DirectedOp),
FunctorRedirect(usize),
IpAddr(IpAddr),
Number(Number, Option<DirectedOp>),
Open,
Close,
Comma,
RawPtr(*const u8),
Space,
LeftCurly,
RightCurly,
OpenList(Rc<Cell<(bool, usize)>>),
CloseList(Rc<Cell<(bool, usize)>>),
HeadTailSeparator,
}
pub trait HCValueOutputter {
type Output;
fn new() -> Self;
fn push_char(&mut self, c: char);
fn append(&mut self, s: &str);
fn begin_new_var(&mut self);
fn insert(&mut self, index: usize, c: char);
fn result(self) -> Self::Output;
fn ends_with(&self, s: &str) -> bool;
fn len(&self) -> usize;
fn truncate(&mut self, len: usize);
fn range(&self, range: Range<usize>) -> &str;
fn range_from(&self, range: RangeFrom<usize>) -> &str;
}
#[derive(Debug)]
pub struct PrinterOutputter {
contents: String,
}
impl HCValueOutputter for PrinterOutputter {
type Output = String;
fn new() -> Self {
PrinterOutputter {
contents: String::new(),
}
}
fn append(&mut self, contents: &str) {
if requires_space(&self.contents, contents) {
self.push_char(' ');
}
self.contents += contents;
}
fn push_char(&mut self, c: char) {
self.contents.push(c);
}
fn begin_new_var(&mut self) {
if self.contents.len() != 0 {
self.contents += ", ";
}
}
fn insert(&mut self, idx: usize, c: char) {
self.contents.insert(idx, c);
}
fn result(self) -> Self::Output {
self.contents
}
fn ends_with(&self, s: &str) -> bool {
self.contents.ends_with(s)
}
fn len(&self) -> usize {
self.contents.len()
}
fn truncate(&mut self, len: usize) {
self.contents.truncate(len);
}
fn range(&self, index: Range<usize>) -> &str {
&self.contents.as_str()[index]
}
fn range_from(&self, index: RangeFrom<usize>) -> &str {
&self.contents.as_str()[index]
}
}
#[inline]
fn is_numbered_var(ct: &ClauseType, arity: usize) -> bool {
arity == 1 && ct.name().as_str() == "$VAR"
}
#[inline]
fn negated_op_needs_bracketing(iter: &HCPreOrderIterator, op: &Option<DirectedOp>) -> bool {
if let Some(ref op) = op {
op.is_negative_sign()
&& iter.leftmost_leaf_has_property(|addr, heap| match Number::try_from((addr, heap)) {
Ok(Number::Fixnum(n)) => n > 0,
Ok(Number::Float(f)) => f > OrderedFloat(0f64),
Ok(Number::Integer(n)) => &*n > &0,
Ok(Number::Rational(n)) => &*n > &0,
_ => false,
})
} else {
false
}
}
fn numbervar(n: Integer) -> Var {
static CHAR_CODES: [char; 26] = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
];
let i = n.mod_u(26) as usize;
let j = n.div_rem_floor(Integer::from(26));
let j = <(Integer, Integer)>::from(j).0;
if j == 0 {
CHAR_CODES[i].to_string()
} else {
format!("{}{}", CHAR_CODES[i], j)
}
}
impl MachineState {
pub fn numbervar(&self, offset: &Integer, addr: Addr) -> Option<Var> {
let addr = self.store(self.deref(addr));
match Number::try_from((addr, &self.heap)) {
Ok(Number::Fixnum(n)) => {
if n >= 0 {
Some(numbervar(Integer::from(offset + Integer::from(n))))
} else {
None
}
}
Ok(Number::Integer(n)) => {
if &*n >= &0 {
Some(numbervar(Integer::from(offset + &*n)))
} else {
None
}
}
_ => None,
}
}
}
type ReverseHeapVarDict = IndexMap<Addr, Rc<Var>>;
#[derive(Debug)]
pub struct HCPrinter<'a, Outputter> {
outputter: Outputter,
machine_st: &'a MachineState,
op_dir: &'a OpDir,
state_stack: Vec<TokenOrRedirect>,
toplevel_spec: Option<DirectedOp>,
heap_locs: ReverseHeapVarDict,
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 {
($self:expr, $atom:expr, $action:block) => {
if $self.ambiguity_check($atom) {
$self.outputter.push_char(' ');
$action;
} else {
$action;
}
};
}
pub fn requires_space(atom: &str, op: &str) -> bool {
match atom.chars().last() {
Some(ac) => op
.chars()
.next()
.map(|oc| {
if ac == '0' {
oc == '\'' || oc == '(' || alpha_numeric_char!(oc)
} else if alpha_numeric_char!(ac) {
oc == '(' || alpha_numeric_char!(oc)
} else if graphic_token_char!(ac) {
graphic_token_char!(oc)
} else if variable_indicator_char!(ac) {
alpha_numeric_char!(oc)
} else if capital_letter_char!(ac) {
alpha_numeric_char!(oc)
} else if sign_char!(ac) {
sign_char!(oc) || decimal_digit_char!(oc)
} else if single_quote_char!(ac) {
single_quote_char!(oc)
} else {
false
}
})
.unwrap_or(false),
_ => false,
}
}
/*
fn reverse_heap_locs<'a>(machine_st: &'a MachineState) -> ReverseHeapVarDict {
machine_st
.heap_locs
.iter()
.map(|(var, var_addr)| {
(
machine_st.store(machine_st.deref(var_addr.clone())),
var.clone(),
)
})
.collect()
}
*/
fn non_quoted_graphic_token<Iter: Iterator<Item = char>>(mut iter: Iter, c: char) -> bool {
if c == '/' {
return match iter.next() {
None => true,
Some('*') => false, // if we start with comment token, we must quote.
Some(c) => {
if graphic_token_char!(c) {
iter.all(|c| graphic_token_char!(c))
} else {
false
}
}
};
} else if c == '.' {
return match iter.next() {
None => false,
Some(c) => {
if graphic_token_char!(c) {
iter.all(|c| graphic_token_char!(c))
} else {
false
}
}
};
} else {
iter.all(|c| graphic_token_char!(c))
}
}
pub(super) fn non_quoted_token<Iter: Iterator<Item = char>>(mut iter: Iter) -> bool {
if let Some(c) = iter.next() {
if small_letter_char!(c) {
iter.all(|c| alpha_numeric_char!(c))
} else if graphic_token_char!(c) {
non_quoted_graphic_token(iter, c)
} else if semicolon_char!(c) {
iter.next().is_none()
} else if cut_char!(c) {
iter.next().is_none()
} else if c == '[' {
iter.next() == Some(']') && iter.next().is_none()
} else if c == '{' {
iter.next() == Some('}') && iter.next().is_none()
} else if solo_char!(c) {
!(c == '(' || c == ')' || c == '}' || c == ']' || c == ',' || c == '%' || c == '|')
} else {
false
}
} else {
false
}
}
#[inline]
fn functor_location(addr: &Addr) -> Option<usize> {
Some(match addr {
&Addr::Lis(l) => l,
&Addr::Str(s) => s,
&Addr::PStrLocation(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 {
outputter: output,
machine_st,
op_dir,
state_stack: vec![],
heap_locs: ReverseHeapVarDict::new(),
toplevel_spec: None,
printed_vars: IndexSet::new(),
last_item_idx: 0,
numbervars: false,
numbervars_offset: Integer::from(0),
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,
}
}
/*
pub fn from_heap_locs(
machine_st: &'a MachineState,
op_dir: &'a OpDir,
output: Outputter,
) -> Self {
let mut printer = Self::new(machine_st, op_dir, output);
printer.toplevel_spec = Some(DirectedOp::Right(
clause_name!("="),
SharedOpDesc::new(700, XFX),
));
printer.heap_locs = reverse_heap_locs(machine_st);
printer
}
*/
/*
pub fn drop_toplevel_spec(&mut self) {
self.toplevel_spec = None;
}
*/
/*
#[inline]
pub fn see_all_locs(&mut self) {
for key in self.heap_locs.keys().cloned() {
self.printed_vars.insert(key);
}
}
*/
#[inline]
fn ambiguity_check(&self, atom: &str) -> bool {
let tail = self.outputter.range_from(self.last_item_idx..);
requires_space(tail, atom)
}
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(
max_depth,
right_directed_op,
));
} else if is_prefix!(spec.assoc()) {
match ct.name().as_str() {
"-" | "\\" => {
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(
max_depth,
left_directed_op,
));
self.state_stack.push(TokenOrRedirect::Op(ct.name(), spec));
} else {
match ct.name().as_str() {
"|" => {
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(
max_depth,
left_directed_op,
));
self.state_stack.push(TokenOrRedirect::Op(ct.name(), spec));
self.state_stack.push(TokenOrRedirect::CompositeRedirect(
max_depth,
right_directed_op,
));
}
}
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(max_depth));
self.state_stack.push(TokenOrRedirect::Comma);
}
self.state_stack.pop();
self.state_stack.push(TokenOrRedirect::Open);
self.state_stack.push(TokenOrRedirect::Atom(name));
true
}
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(max_depth, op));
self.state_stack.push(TokenOrRedirect::Space);
self.state_stack.push(TokenOrRedirect::Atom(name));
}
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(
max_depth,
left_directed_op,
));
self.state_stack.push(TokenOrRedirect::BarAsOp);
self.state_stack.push(TokenOrRedirect::CompositeRedirect(
max_depth,
right_directed_op,
));
}
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(max_depth));
self.state_stack.push(TokenOrRedirect::LeftCurly);
true
}
fn format_numbered_vars(&mut self, iter: &mut HCPreOrderIterator) -> bool {
let addr = iter.stack().last().cloned().unwrap();
// 7.10.4
if let Some(var) = iter.machine_st().numbervar(&self.numbervars_offset, addr) {
iter.stack().pop();
self.state_stack.push(TokenOrRedirect::NumberedVar(var));
return true;
}
false
}
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 true;
}
}
if let Some(spec) = ct.spec() {
if "." == ct.name().as_str() && is_infix!(spec.assoc()) {
if !self.ignore_ops {
self.push_list(iter, max_depth);
return true;
}
}
if !self.ignore_ops && spec.prec() > 0 {
self.enqueue_op(iter, max_depth, ct, spec);
return true;
}
}
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()),
};
}
#[inline]
fn push_char(&mut self, c: char) {
self.outputter.push_char(c);
self.last_item_idx = self.outputter.len();
}
#[inline]
fn append_str(&mut self, s: &str) {
self.last_item_idx = self.outputter.len();
self.outputter.append(s);
}
fn offset_as_string(&mut self, iter: &mut HCPreOrderIterator, addr: Addr) -> Option<Var> {
if let Some(var) = self.var_names.get(&addr) {
if addr.as_var().is_some() {
return Some(format!("{}", var));
} else {
iter.stack().push(addr);
return None;
}
}
match addr {
Addr::Lis(h) | Addr::Str(h) => Some(format!("{}", h)),
_ => {
if let Some(r) = addr.as_var() {
match r {
Ref::StackCell(fr, sc) => Some(format!("_s_{}_{}", fr, sc)),
Ref::HeapCell(h) | Ref::AttrVar(h) => Some(format!("_{}", h)),
}
} else {
None
}
}
}
}
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, _) => {
let tail = self.machine_st.heap[h + 1].as_addr(h + 1);
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<Addr> {
iter.stack().last().cloned().and_then(|addr| {
let addr = self.machine_st.store(self.machine_st.deref(addr));
if let Some(var) = self.var_names.get(&addr) {
self.heap_locs.insert(addr.clone(), Rc::new(var.clone()));
}
match self.heap_locs.get(&addr).cloned() {
Some(var) => {
if !self.printed_vars.contains(&addr) {
self.printed_vars.insert(addr);
return iter.next();
} else {
iter.stack().pop();
push_space_if_amb!(self, &var, {
self.append_str(&var);
});
return 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);
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 {
self.record_children_as_non_cyclic(&addr);
}
iter.next()
}
}
})
}
fn print_atom(&mut self, atom: &ClauseName) {
let result = self.print_op_addendum(atom.as_str());
push_space_if_amb!(self, result.as_str(), {
self.append_str(&result);
});
}
fn print_op_addendum(&mut self, atom: &str) -> String {
if !self.quoted || non_quoted_token(atom.chars()) {
atom.to_string()
} else if atom == "''" {
"''".to_string()
} else {
let mut result = String::new();
if self.quoted {
result.push('\'');
}
for c in atom.chars() {
result += &char_to_string(self.quoted, c);
}
if self.quoted {
result.push('\'');
}
result
}
}
fn print_op(&mut self, atom: &str) {
let result = if atom == "," {
",".to_string()
} else {
self.print_op_addendum(atom)
};
push_space_if_amb!(self, &result, {
self.append_str(&result);
});
}
#[inline]
fn print_ip_addr(&mut self, ip: IpAddr) {
self.push_char('\'');
self.append_str(&format!("{}", ip));
self.push_char('\'');
}
#[inline]
fn print_raw_ptr(&mut self, ptr: *const u8) {
self.append_str(&format!("0x{:x}", ptr as usize));
}
fn print_number(&mut self, n: Number, op: &Option<DirectedOp>) {
let add_brackets = if let Some(op) = op {
op.is_negative_sign() && n.is_positive()
} else {
false
};
if add_brackets {
self.push_char('(');
}
match n {
Number::Float(fl) => {
if &fl == &OrderedFloat(0f64) {
push_space_if_amb!(self, "0", {
self.append_str("0");
});
} else {
let OrderedFloat(fl) = fl;
let output_str = format!("{0:<20?}", fl);
push_space_if_amb!(self, &output_str, {
self.append_str(&output_str.trim());
});
}
}
Number::Rational(r) => {
self.print_rational(&r, add_brackets);
return;
}
n => {
let output_str = format!("{}", n);
push_space_if_amb!(self, &output_str, {
self.append_str(&output_str);
});
}
}
if add_brackets {
self.push_char(')');
}
}
fn print_rational(&mut self, r: &Rational, add_brackets: bool) {
match self.op_dir.get(&(clause_name!("rdiv"), Fixity::In)) {
Some(OpDirValue(ref spec)) => {
if add_brackets {
self.state_stack.push(TokenOrRedirect::Close);
}
let rdiv_ct = clause_name!("rdiv");
let left_directed_op = if spec.prec() > 0 {
Some(DirectedOp::Left(rdiv_ct.clone(), spec.clone()))
} else {
None
};
let right_directed_op = if spec.prec() > 0 {
Some(DirectedOp::Right(rdiv_ct.clone(), spec.clone()))
} else {
None
};
if spec.prec() > 0 {
self.state_stack.push(TokenOrRedirect::Number(
Number::from(r.denom()),
left_directed_op,
));
self.state_stack
.push(TokenOrRedirect::Op(rdiv_ct, spec.clone()));
self.state_stack.push(TokenOrRedirect::Number(
Number::from(r.numer()),
right_directed_op,
));
} else {
self.state_stack.push(TokenOrRedirect::Close);
self.state_stack
.push(TokenOrRedirect::Number(Number::from(r.denom()), None));
self.state_stack.push(TokenOrRedirect::Comma);
self.state_stack
.push(TokenOrRedirect::Number(Number::from(r.numer()), None));
self.state_stack.push(TokenOrRedirect::Open);
self.state_stack.push(TokenOrRedirect::Atom(rdiv_ct));
}
return;
}
_ => {
unreachable!()
}
}
}
fn print_char(&mut self, is_quoted: bool, c: char) {
if non_quoted_token(once(c)) {
let c = char_to_string(false, c);
push_space_if_amb!(self, &c, {
self.append_str(c.as_str());
});
} else {
let mut result = String::new();
if self.quoted {
result.push('\'');
result += &char_to_string(is_quoted, c);
result.push('\'');
} else {
result += &char_to_string(is_quoted, c);
}
push_space_if_amb!(self, &result, {
self.append_str(result.as_str());
});
}
}
fn print_proper_string(&mut self, buf: String, max_depth: usize) {
self.push_char('"');
let buf = if max_depth == 0 {
String::from_iter(buf.chars().map(|c| char_to_string(self.quoted, c)))
} else {
let mut char_count = 0;
let mut buf = String::from_iter(buf.chars().take(max_depth).map(|c| {
char_count += 1;
char_to_string(self.quoted, c)
}));
if char_count == max_depth {
buf += " ...";
}
buf
};
self.append_str(&buf);
self.push_char('"');
}
fn print_list_like(&mut self, iter: &mut HCPreOrderIterator, addr: Addr, mut max_depth: usize) {
if self.check_max_depth(&mut max_depth) {
iter.stack().pop();
iter.stack().pop();
self.state_stack
.push(TokenOrRedirect::Atom(clause_name!("...")));
return;
}
let mut heap_pstr_iter = self.machine_st.heap_pstr_iter(addr);
let buf = heap_pstr_iter.to_string();
if buf.is_empty() {
self.push_list(iter, max_depth);
return;
}
iter.stack().pop();
iter.stack().pop();
let end_addr = heap_pstr_iter.focus();
let at_cdr = self.at_cdr(",");
if !at_cdr && Addr::EmptyList == end_addr {
if !self.ignore_ops {
self.print_proper_string(buf, max_depth);
return;
}
}
let buf_len = buf.len();
let buf_iter: Box<dyn Iterator<Item = char>> = if self.max_depth == 0 {
Box::new(buf.chars())
} else {
Box::new(buf.chars().take(max_depth))
};
let mut byte_len = 0;
if self.ignore_ops {
let mut char_count = 0;
for c in buf_iter {
self.append_str("'.'");
self.push_char('(');
self.print_char(self.quoted, c);
self.push_char(',');
char_count += 1;
byte_len += c.len_utf8();
}
for _ in 0..char_count {
self.state_stack.push(TokenOrRedirect::Close);
}
if self.max_depth > 0 && buf_len > byte_len {
self.state_stack
.push(TokenOrRedirect::Atom(clause_name!("...")));
} else {
self.state_stack
.push(TokenOrRedirect::FunctorRedirect(max_depth));
iter.stack().push(end_addr);
}
} else {
let switch = if !at_cdr {
self.push_char('[');
true
} else {
false
};
for c in buf_iter {
self.print_char(self.quoted, c);
self.push_char(',');
byte_len += c.len_utf8();
}
self.state_stack
.push(TokenOrRedirect::CloseList(Rc::new(Cell::new((switch, 0)))));
if self.max_depth > 0 && buf_len > byte_len {
self.state_stack
.push(TokenOrRedirect::Atom(clause_name!("...")));
} else {
self.outputter
.truncate(self.outputter.len() - ','.len_utf8());
self.state_stack
.push(TokenOrRedirect::FunctorRedirect(max_depth));
iter.stack().push(end_addr);
}
self.state_stack.push(TokenOrRedirect::HeadTailSeparator);
}
}
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::Atom(clause_name!("...")));
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(max_depth));
self.state_stack.push(TokenOrRedirect::OpenList(cell));
}
fn handle_op_as_struct(
&mut self,
name: ClauseName,
arity: usize,
iter: &mut HCPreOrderIterator,
op: &Option<DirectedOp>,
is_functor_redirect: bool,
spec: SharedOpDesc,
negated_operand: bool,
max_depth: usize,
) {
let add_brackets = if !self.ignore_ops {
negated_operand
|| if let Some(ref op) = op {
if self.numbervars && arity == 1 && name.as_str() == "$VAR" {
!iter.immediate_leaf_has_property(|addr, heap| {
match heap.index_addr(&addr).as_ref() {
&HeapCellValue::Integer(ref n) => &**n >= &0,
&HeapCellValue::Addr(Addr::Fixnum(n)) => n >= 0,
&HeapCellValue::Addr(Addr::Float(f)) => f >= OrderedFloat(0f64),
&HeapCellValue::Rational(ref r) => &**r >= &0,
_ => false,
}
}) && needs_bracketing(&spec, op)
} else {
needs_bracketing(&spec, op)
}
} else {
is_functor_redirect && spec.prec() >= 1000
}
} else {
false
};
if add_brackets {
self.state_stack.push(TokenOrRedirect::Close);
}
let ct = ClauseType::from(name.clone(), arity, Some(spec));
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);
}
}
}
}
}
fn print_tcp_listener(
&mut self,
iter: &mut HCPreOrderIterator,
tcp_listener: &TcpListener,
max_depth: usize,
) {
let (ip, port) = if let Some(addr) = tcp_listener.local_addr().ok() {
(addr.ip(), Number::from(addr.port() as isize))
} else {
let disconnected_atom = clause_name!("$disconnected_tcp_listener");
self.state_stack
.push(TokenOrRedirect::Atom(disconnected_atom));
return;
};
if self.format_struct(iter, max_depth, 1, clause_name!("$tcp_listener")) {
let atom = self.state_stack.pop().unwrap();
self.state_stack.pop();
self.state_stack.pop();
self.state_stack.push(TokenOrRedirect::Number(port, None));
self.state_stack.push(TokenOrRedirect::Comma);
self.state_stack.push(TokenOrRedirect::IpAddr(ip));
self.state_stack.push(TokenOrRedirect::Open);
self.state_stack.push(atom);
}
}
fn print_stream(&mut self, iter: &mut HCPreOrderIterator, stream: &Stream, max_depth: usize) {
if let Some(alias) = &stream.options.alias {
self.print_atom(alias);
} else {
if self.format_struct(iter, max_depth, 1, clause_name!("$stream")) {
let atom = if stream.is_stdout() || stream.is_stdin() {
TokenOrRedirect::Atom(clause_name!("user"))
} else {
TokenOrRedirect::RawPtr(stream.as_ptr())
};
let stream_root = self.state_stack.pop().unwrap();
self.state_stack.pop();
self.state_stack.pop();
self.state_stack.push(atom);
self.state_stack.push(TokenOrRedirect::Open);
self.state_stack.push(stream_root);
}
}
}
fn handle_heap_term(
&mut self,
iter: &mut HCPreOrderIterator,
op: Option<DirectedOp>,
is_functor_redirect: bool,
max_depth: usize,
) {
let negated_operand = negated_op_needs_bracketing(iter, &op);
let addr = match self.check_for_seen(iter) {
Some(addr) => addr,
None => return,
};
match self.machine_st.heap.index_addr(&addr).as_ref() {
&HeapCellValue::NamedStr(arity, ref name, ref spec) => {
let spec =
fetch_op_spec_from_existing(name.clone(), arity, spec.clone(), self.op_dir);
if let Some(spec) = spec {
self.handle_op_as_struct(
name.clone(),
arity,
iter,
&op,
is_functor_redirect,
spec.clone(),
negated_operand,
max_depth,
);
} else {
push_space_if_amb!(self, name.as_str(), {
let ct = ClauseType::from(name.clone(), arity, spec);
self.format_clause(iter, max_depth, arity, ct);
});
}
}
&HeapCellValue::Atom(ref atom, ref spec) => {
if let Some(_) = fetch_atom_op_spec(atom.clone(), spec.clone(), self.op_dir) {
let mut result = String::new();
if let Some(ref op) = op {
if self.outputter.ends_with(&format!(" {}", op.as_str())) {
result.push(' ');
}
result.push('(');
}
result += &self.print_op_addendum(atom.as_str());
if op.is_some() {
result.push(')');
}
push_space_if_amb!(self, &result, {
self.append_str(&result);
});
} else {
push_space_if_amb!(self, atom.as_str(), {
self.print_atom(&atom);
});
}
}
&HeapCellValue::Addr(Addr::Char(c)) => {
self.print_char(self.quoted, c);
}
&HeapCellValue::Addr(Addr::CutPoint(b)) => {
self.append_str(&format!("{}", b));
}
&HeapCellValue::Addr(Addr::EmptyList) => {
if !self.at_cdr("") {
self.append_str("[]");
}
}
&HeapCellValue::Addr(Addr::Float(n)) => {
self.print_number(Number::Float(n), &op);
}
&HeapCellValue::Addr(Addr::Fixnum(n)) => {
self.print_number(Number::Fixnum(n), &op);
}
&HeapCellValue::Addr(Addr::Usize(u)) => {
self.append_str(&format!("{}", u));
}
&HeapCellValue::Addr(Addr::PStrLocation(h, n)) => {
self.print_list_like(iter, Addr::PStrLocation(h, n), max_depth);
}
&HeapCellValue::Addr(Addr::Lis(l)) => {
if self.ignore_ops {
self.format_struct(iter, max_depth, 2, clause_name!("."));
} else {
self.print_list_like(iter, Addr::Lis(l), max_depth);
}
}
&HeapCellValue::Addr(addr) => {
if let Some(offset_str) = self.offset_as_string(iter, addr) {
push_space_if_amb!(self, &offset_str, {
self.append_str(offset_str.as_str());
})
}
}
&HeapCellValue::Integer(ref n) => {
self.print_number(Number::Integer(n.clone()), &op);
}
&HeapCellValue::Rational(ref n) => {
self.print_number(Number::Rational(n.clone()), &op);
}
&HeapCellValue::Stream(ref stream) => {
self.print_stream(iter, stream, max_depth);
}
&HeapCellValue::TcpListener(ref tcp_listener) => {
self.print_tcp_listener(iter, tcp_listener, max_depth);
}
_ => {
unreachable!()
}
}
}
fn at_cdr(&mut self, tr: &str) -> bool {
let len = self.outputter.len();
if self.outputter.ends_with("|") {
self.outputter.truncate(len - "|".len());
self.append_str(tr);
true
} else {
false
}
}
pub fn print(mut self, addr: Addr) -> Outputter {
let mut iter = self.machine_st.pre_order_iter(addr);
loop {
if let Some(loc_data) = self.state_stack.pop() {
match loc_data {
TokenOrRedirect::Atom(atom) => self.print_atom(&atom),
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(max_depth, op) => {
self.handle_heap_term(&mut iter, Some(op), false, max_depth)
}
TokenOrRedirect::FunctorRedirect(max_depth) => {
self.handle_heap_term(&mut iter, None, true, max_depth)
}
TokenOrRedirect::Close => self.push_char(')'),
TokenOrRedirect::IpAddr(ip) => self.print_ip_addr(ip),
TokenOrRedirect::RawPtr(ptr) => self.print_raw_ptr(ptr),
TokenOrRedirect::Open => self.push_char('('),
TokenOrRedirect::OpenList(delimit) => {
if !self.at_cdr(",") {
self.push_char('[');
} else {
let (_, max_depth) = delimit.get();
delimit.set((false, max_depth));
}
}
TokenOrRedirect::CloseList(delimit) => {
if delimit.get().0 {
self.push_char(']');
}
}
TokenOrRedirect::HeadTailSeparator => self.append_str("|"),
TokenOrRedirect::Number(n, op) => self.print_number(n, &op),
TokenOrRedirect::Comma => self.append_str(","),
TokenOrRedirect::Space => self.push_char(' '),
TokenOrRedirect::LeftCurly => self.push_char('{'),
TokenOrRedirect::RightCurly => self.push_char('}'),
}
} else if !iter.stack().is_empty() {
let spec = self.toplevel_spec.take();
self.handle_heap_term(&mut iter, spec, false, self.max_depth);
} else {
break;
}
}
self.outputter
}
}