track the parent operator of the current operator in heap_print to emit space if necessary (#1906)
This commit is contained in:
@@ -26,7 +26,6 @@ use std::cell::Cell;
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
use std::net::{IpAddr, TcpListener};
|
use std::net::{IpAddr, TcpListener};
|
||||||
use std::ops::{Range, RangeFrom};
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
/* contains the location, name, precision and Specifier of the parent op. */
|
/* contains the location, name, precision and Specifier of the parent op. */
|
||||||
@@ -64,11 +63,7 @@ impl DirectedOp {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_left(&self) -> bool {
|
fn is_left(&self) -> bool {
|
||||||
if let &DirectedOp::Left(..) = self {
|
matches!(self, DirectedOp::Left(..))
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,8 +325,7 @@ pub trait HCValueOutputter {
|
|||||||
fn ends_with(&self, s: &str) -> bool;
|
fn ends_with(&self, s: &str) -> bool;
|
||||||
fn len(&self) -> usize;
|
fn len(&self) -> usize;
|
||||||
fn truncate(&mut self, len: usize);
|
fn truncate(&mut self, len: usize);
|
||||||
fn range(&self, range: Range<usize>) -> &str;
|
fn as_str(&self) -> &str;
|
||||||
fn range_from(&self, range: RangeFrom<usize>) -> &str;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -386,12 +380,8 @@ impl HCValueOutputter for PrinterOutputter {
|
|||||||
self.contents.truncate(len);
|
self.contents.truncate(len);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn range(&self, index: Range<usize>) -> &str {
|
fn as_str(&self) -> &str {
|
||||||
&self.contents.as_str()[index]
|
&self.contents
|
||||||
}
|
|
||||||
|
|
||||||
fn range_from(&self, index: RangeFrom<usize>) -> &str {
|
|
||||||
&self.contents.as_str().get(index).unwrap_or("")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,6 +482,8 @@ pub struct HCPrinter<'a, Outputter> {
|
|||||||
state_stack: Vec<TokenOrRedirect>,
|
state_stack: Vec<TokenOrRedirect>,
|
||||||
toplevel_spec: Option<DirectedOp>,
|
toplevel_spec: Option<DirectedOp>,
|
||||||
last_item_idx: usize,
|
last_item_idx: usize,
|
||||||
|
num_ops_on_stack: usize,
|
||||||
|
parent_of_first_op: Option<(DirectedOp, usize)>,
|
||||||
pub var_names: IndexMap<HeapCellValue, VarPtr>,
|
pub var_names: IndexMap<HeapCellValue, VarPtr>,
|
||||||
pub numbervars_offset: Integer,
|
pub numbervars_offset: Integer,
|
||||||
pub numbervars: bool,
|
pub numbervars: bool,
|
||||||
@@ -567,6 +559,8 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
state_stack: vec![],
|
state_stack: vec![],
|
||||||
toplevel_spec: None,
|
toplevel_spec: None,
|
||||||
last_item_idx: 0,
|
last_item_idx: 0,
|
||||||
|
num_ops_on_stack: 0,
|
||||||
|
parent_of_first_op: None,
|
||||||
numbervars: false,
|
numbervars: false,
|
||||||
numbervars_offset: Integer::from(0),
|
numbervars_offset: Integer::from(0),
|
||||||
quoted: false,
|
quoted: false,
|
||||||
@@ -580,7 +574,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ambiguity_check(&self, atom: &str) -> bool {
|
fn ambiguity_check(&self, atom: &str) -> bool {
|
||||||
let tail = self.outputter.range_from(self.last_item_idx..);
|
let tail = &self.outputter.as_str()[self.last_item_idx..];
|
||||||
|
|
||||||
if !self.quoted || non_quoted_token(atom.chars()) {
|
if !self.quoted || non_quoted_token(atom.chars()) {
|
||||||
requires_space(tail, atom)
|
requires_space(tail, atom)
|
||||||
@@ -589,7 +583,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enqueue_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) {
|
fn set_parent_of_first_op(&mut self, parent_op: Option<DirectedOp>) {
|
||||||
|
self.parent_of_first_op = parent_op.map(|op| (op, self.last_item_idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enqueue_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc, parent_op: Option<DirectedOp>) {
|
||||||
if is_postfix!(spec.get_spec()) {
|
if is_postfix!(spec.get_spec()) {
|
||||||
if self.max_depth_exhausted(max_depth) {
|
if self.max_depth_exhausted(max_depth) {
|
||||||
self.iter.pop_stack();
|
self.iter.pop_stack();
|
||||||
@@ -600,17 +598,15 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
|
|
||||||
self.state_stack.push(TokenOrRedirect::Op(name, spec));
|
self.state_stack.push(TokenOrRedirect::Op(name, spec));
|
||||||
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
||||||
|
} else {
|
||||||
|
let right_directed_op = DirectedOp::Right(name, spec);
|
||||||
|
|
||||||
return;
|
self.state_stack.push(TokenOrRedirect::Op(name, spec));
|
||||||
|
self.state_stack.push(TokenOrRedirect::CompositeRedirect(
|
||||||
|
max_depth,
|
||||||
|
right_directed_op,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let right_directed_op = DirectedOp::Right(name, spec);
|
|
||||||
|
|
||||||
self.state_stack.push(TokenOrRedirect::Op(name, spec));
|
|
||||||
self.state_stack.push(TokenOrRedirect::CompositeRedirect(
|
|
||||||
max_depth,
|
|
||||||
right_directed_op,
|
|
||||||
));
|
|
||||||
} else if is_prefix!(spec.get_spec()) {
|
} else if is_prefix!(spec.get_spec()) {
|
||||||
if self.max_depth_exhausted(max_depth) {
|
if self.max_depth_exhausted(max_depth) {
|
||||||
self.iter.pop_stack();
|
self.iter.pop_stack();
|
||||||
@@ -620,15 +616,13 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
self.iter.pop_stack();
|
self.iter.pop_stack();
|
||||||
|
|
||||||
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
||||||
self.state_stack.push(TokenOrRedirect::Atom(name));
|
self.state_stack.push(TokenOrRedirect::Op(name, spec));
|
||||||
|
} else {
|
||||||
|
let op = DirectedOp::Left(name, spec);
|
||||||
|
|
||||||
return;
|
self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, op));
|
||||||
|
self.state_stack.push(TokenOrRedirect::Op(name, spec));
|
||||||
}
|
}
|
||||||
|
|
||||||
let op = DirectedOp::Left(name, spec);
|
|
||||||
|
|
||||||
self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, op));
|
|
||||||
self.state_stack.push(TokenOrRedirect::Atom(name));
|
|
||||||
} else {
|
} else {
|
||||||
match name.as_str() {
|
match name.as_str() {
|
||||||
"|" => {
|
"|" => {
|
||||||
@@ -638,15 +632,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
let left_directed_op = DirectedOp::Left(name, spec);
|
|
||||||
let right_directed_op = DirectedOp::Right(name, spec);
|
|
||||||
|
|
||||||
if self.max_depth_exhausted(max_depth) {
|
if self.max_depth_exhausted(max_depth) {
|
||||||
self.iter.pop_stack();
|
self.iter.pop_stack();
|
||||||
self.iter.pop_stack();
|
self.iter.pop_stack();
|
||||||
|
|
||||||
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else if self.check_max_depth(&mut max_depth) {
|
} else if self.check_max_depth(&mut max_depth) {
|
||||||
self.iter.pop_stack();
|
self.iter.pop_stack();
|
||||||
@@ -655,14 +645,21 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
||||||
self.state_stack.push(TokenOrRedirect::Op(name, spec));
|
self.state_stack.push(TokenOrRedirect::Op(name, spec));
|
||||||
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
||||||
|
} else {
|
||||||
|
let left_directed_op = DirectedOp::Left(name, spec);
|
||||||
|
let right_directed_op = DirectedOp::Right(name, spec);
|
||||||
|
|
||||||
return;
|
self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, left_directed_op));
|
||||||
|
self.state_stack.push(TokenOrRedirect::Op(name, spec));
|
||||||
|
self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, right_directed_op));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, left_directed_op));
|
|
||||||
self.state_stack.push(TokenOrRedirect::Op(name, spec));
|
|
||||||
self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, right_directed_op));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.num_ops_on_stack == 0 {
|
||||||
|
self.set_parent_of_first_op(parent_op);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.num_ops_on_stack += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_struct(&mut self, mut max_depth: usize, arity: usize, name: Atom) -> bool {
|
fn format_struct(&mut self, mut max_depth: usize, arity: usize, name: Atom) -> bool {
|
||||||
@@ -776,6 +773,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
arity: usize,
|
arity: usize,
|
||||||
name: Atom,
|
name: Atom,
|
||||||
op_desc: Option<OpDesc>,
|
op_desc: Option<OpDesc>,
|
||||||
|
parent_op: Option<DirectedOp>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if self.numbervars && is_numbered_var(name, arity) {
|
if self.numbervars && is_numbered_var(name, arity) {
|
||||||
if self.format_numbered_vars() {
|
if self.format_numbered_vars() {
|
||||||
@@ -794,7 +792,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !self.ignore_ops && spec.get_prec() > 0 {
|
if !self.ignore_ops && spec.get_prec() > 0 {
|
||||||
self.enqueue_op(max_depth, name, spec);
|
self.enqueue_op(max_depth, name, spec, parent_op);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -976,7 +974,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
Number::Rational(r) => {
|
Number::Rational(r) => {
|
||||||
self.print_rational(max_depth, r);
|
self.print_rational(max_depth, r, *op);
|
||||||
}
|
}
|
||||||
n => {
|
n => {
|
||||||
let output_str = format!("{}", n);
|
let output_str = format!("{}", n);
|
||||||
@@ -1007,7 +1005,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_rational(&mut self, mut max_depth: usize, r: TypedArenaPtr<Rational>) {
|
fn print_rational(
|
||||||
|
&mut self,
|
||||||
|
mut max_depth: usize,
|
||||||
|
r: TypedArenaPtr<Rational>,
|
||||||
|
parent_op: Option<DirectedOp>,
|
||||||
|
) {
|
||||||
if self.check_max_depth(&mut max_depth) {
|
if self.check_max_depth(&mut max_depth) {
|
||||||
self.state_stack.push(TokenOrRedirect::Close);
|
self.state_stack.push(TokenOrRedirect::Close);
|
||||||
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
|
||||||
@@ -1050,15 +1053,15 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
NumberFocus::Denominator(r),
|
NumberFocus::Denominator(r),
|
||||||
left_directed_op,
|
left_directed_op,
|
||||||
));
|
));
|
||||||
|
self.state_stack.push(TokenOrRedirect::Op(rdiv_ct, *op_desc));
|
||||||
self.state_stack
|
|
||||||
.push(TokenOrRedirect::Op(rdiv_ct, *op_desc));
|
|
||||||
|
|
||||||
self.state_stack.push(TokenOrRedirect::NumberFocus(
|
self.state_stack.push(TokenOrRedirect::NumberFocus(
|
||||||
max_depth,
|
max_depth,
|
||||||
NumberFocus::Numerator(r),
|
NumberFocus::Numerator(r),
|
||||||
right_directed_op,
|
right_directed_op,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
self.num_ops_on_stack += 1;
|
||||||
|
self.set_parent_of_first_op(parent_op);
|
||||||
} else {
|
} else {
|
||||||
self.state_stack.push(TokenOrRedirect::Close);
|
self.state_stack.push(TokenOrRedirect::Close);
|
||||||
|
|
||||||
@@ -1347,7 +1350,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: Atom,
|
name: Atom,
|
||||||
arity: usize,
|
arity: usize,
|
||||||
op: &Option<DirectedOp>,
|
op: Option<DirectedOp>,
|
||||||
is_functor_redirect: bool,
|
is_functor_redirect: bool,
|
||||||
op_desc: OpDesc,
|
op_desc: OpDesc,
|
||||||
negated_operand: bool,
|
negated_operand: bool,
|
||||||
@@ -1378,20 +1381,33 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
|
|
||||||
if add_brackets {
|
if add_brackets {
|
||||||
self.state_stack.push(TokenOrRedirect::Close);
|
self.state_stack.push(TokenOrRedirect::Close);
|
||||||
}
|
self.format_clause(max_depth, arity, name, Some(op_desc), op);
|
||||||
|
|
||||||
if self.format_clause(max_depth, arity, name, Some(op_desc)) && add_brackets {
|
|
||||||
self.state_stack.push(TokenOrRedirect::Open);
|
self.state_stack.push(TokenOrRedirect::Open);
|
||||||
|
|
||||||
if let Some(ref op) = &op {
|
let parent_op = self.parent_of_first_op
|
||||||
if !self.outputter.ends_with(" ") {
|
.and_then(|(parent_op, last_item_idx)| {
|
||||||
if op.is_left() {
|
// if parent_op isn't printed to the output string
|
||||||
if op.is_prefix() || requires_space(op.as_atom().as_str(), "(") {
|
// already, then it doesn't border the present op
|
||||||
|
// and we should return None.
|
||||||
|
if self.last_item_idx == last_item_idx {
|
||||||
|
Some(parent_op)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for op in &[op, parent_op] {
|
||||||
|
if let Some(ref op) = &op {
|
||||||
|
if !self.outputter.ends_with(" ") {
|
||||||
|
if op.is_left() && (op.is_prefix() || requires_space(op.as_atom().as_str(), "(")) {
|
||||||
self.state_stack.push(TokenOrRedirect::Space);
|
self.state_stack.push(TokenOrRedirect::Space);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
self.format_clause(max_depth, arity, name, Some(op_desc), op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1500,7 +1516,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
printer.handle_op_as_struct(
|
printer.handle_op_as_struct(
|
||||||
name,
|
name,
|
||||||
arity,
|
arity,
|
||||||
&op,
|
op,
|
||||||
is_functor_redirect,
|
is_functor_redirect,
|
||||||
spec,
|
spec,
|
||||||
negated_operand,
|
negated_operand,
|
||||||
@@ -1508,7 +1524,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
push_space_if_amb!(printer, name.as_str(), {
|
push_space_if_amb!(printer, name.as_str(), {
|
||||||
printer.format_clause(max_depth, arity, name, None);
|
printer.format_clause(max_depth, arity, name, None, op);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if fetch_op_spec(name, arity, printer.op_dir).is_some() {
|
} else if fetch_op_spec(name, arity, printer.op_dir).is_some() {
|
||||||
@@ -1566,7 +1582,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
self.handle_op_as_struct(
|
self.handle_op_as_struct(
|
||||||
name,
|
name,
|
||||||
arity,
|
arity,
|
||||||
&op,
|
op,
|
||||||
is_functor_redirect,
|
is_functor_redirect,
|
||||||
spec,
|
spec,
|
||||||
negated_operand,
|
negated_operand,
|
||||||
@@ -1574,7 +1590,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
push_space_if_amb!(self, name.as_str(), {
|
push_space_if_amb!(self, name.as_str(), {
|
||||||
self.format_clause(max_depth, arity, name, None);
|
self.format_clause(max_depth, arity, name, None, op);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1655,7 +1671,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
|
|||||||
TokenOrRedirect::Atom(atom) => self.print_impromptu_atom(atom),
|
TokenOrRedirect::Atom(atom) => self.print_impromptu_atom(atom),
|
||||||
TokenOrRedirect::BarAsOp => append_str!(self, " | "),
|
TokenOrRedirect::BarAsOp => append_str!(self, " | "),
|
||||||
TokenOrRedirect::Char(c) => print_char!(self, self.quoted, c),
|
TokenOrRedirect::Char(c) => print_char!(self, self.quoted, c),
|
||||||
TokenOrRedirect::Op(atom, _) => self.print_op(atom.as_str()),
|
TokenOrRedirect::Op(atom, ..) => {
|
||||||
|
self.num_ops_on_stack -= 1;
|
||||||
|
self.parent_of_first_op = None;
|
||||||
|
self.print_op(atom.as_str());
|
||||||
|
}
|
||||||
TokenOrRedirect::NumberedVar(num_var) => append_str!(self, &num_var),
|
TokenOrRedirect::NumberedVar(num_var) => append_str!(self, &num_var),
|
||||||
TokenOrRedirect::CompositeRedirect(max_depth, op) => {
|
TokenOrRedirect::CompositeRedirect(max_depth, op) => {
|
||||||
self.handle_heap_term(Some(op), false, max_depth)
|
self.handle_heap_term(Some(op), false, max_depth)
|
||||||
|
|||||||
@@ -998,6 +998,9 @@ test_310 :- test_syntax_error("writeq({\\+ (}).", syntax_error(incomplete_reduct
|
|||||||
|
|
||||||
test_311 :- test_syntax_error("Finis ().", syntax_error(incomplete_reduction)).
|
test_311 :- test_syntax_error("Finis ().", syntax_error(incomplete_reduction)).
|
||||||
|
|
||||||
|
test_318 :- writeq_term_to_chars(+((1*2)^3), C),
|
||||||
|
C == "+ (1*2)^3".
|
||||||
|
|
||||||
run_tests([Test|Tests]) -->
|
run_tests([Test|Tests]) -->
|
||||||
( { call(Test) } ->
|
( { call(Test) } ->
|
||||||
[]
|
[]
|
||||||
|
|||||||
Reference in New Issue
Block a user