use std::cell::Cell;
use std::collections::{HashMap, HashSet};
use std::iter::once;
+use std::ops::{Range, RangeFrom};
use std::rc::Rc;
/* contains the location, name, precision and Specifier of the parent op. */
OpenList(Rc<Cell<bool>>),
CloseList(Rc<Cell<bool>>),
HeadTailSeparator,
- Space
}
pub trait HCValueOutputter {
fn push_char(&mut self, char);
fn append(&mut self, &str);
fn begin_new_var(&mut self);
- fn insert_from_end(&mut self, usize, char);
+ fn insert(&mut self, usize, char);
fn result(self) -> Self::Output;
fn ends_with(&self, &str) -> bool;
fn len(&self) -> usize;
fn truncate(&mut self, usize);
+ fn range(&self, Range<usize>) -> &str;
+ fn range_from(&self, RangeFrom<usize>) -> &str;
}
pub struct PrinterOutputter {
}
}
- fn insert_from_end(&mut self, idx: usize, c: char) {
- let len = self.contents.len();
- self.contents.insert(len - idx, c);
+ fn insert(&mut self, idx: usize, c: char) {
+ self.contents.insert(idx, c);
}
fn result(self) -> Self::Output {
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]
state_stack: Vec<TokenOrRedirect>,
heap_locs: ReverseHeapVarDict,
printed_vars: HashSet<Addr>,
+ last_item_idx: usize,
pub(crate) numbervars: bool,
pub(crate) quoted: bool,
pub(crate) ignore_ops: bool
}
macro_rules! push_space_if_amb {
- ($self:expr, $atom:expr, $op:expr, $action:block) => (
- match $self.ambiguity_check($atom, $op) {
- Some(DirectedOp::Left(lop, (_, spec))) =>
- if is_infix!(spec) {
- $self.outputter.insert_from_end(lop.as_str().len(), ' ');
- $self.outputter.push_char(' ');
- $action;
- } else {
- $self.outputter.push_char(' ');
- $action;
- },
- Some(DirectedOp::Right(..)) => {
- $action;
- $self.outputter.push_char(' ');
- },
- None => $action
- };
+ ($self:expr, $atom:expr, $action:block) => (
+ if $self.ambiguity_check($atom) {
+ if !$self.outputter.range(0 .. $self.last_item_idx).ends_with(" ") {
+ $self.outputter.insert($self.last_item_idx, ' ');
+ }
+
+ $self.outputter.push_char(' ');
+ $action;
+ } else {
+ $action;
+ }
)
}
fn continues_with_append(atom: &str, op: &str) -> bool {
- match atom.chars().next() {
+ match atom.chars().last() {
Some(ac) => op.chars().next().map(|oc| {
if alpha_char!(ac) {
alpha_numeric_char!(oc)
state_stack: vec![],
heap_locs: ReverseHeapVarDict::new(),
printed_vars: HashSet::new(),
+ last_item_idx: 0,
numbervars: false,
quoted: false,
ignore_ops: false }
}
}
- // return op itself if there is an ambiguity to indicate the direction the op
- // lies, None otherwise.
- fn ambiguity_check(&self, atom: &str, op: &Option<DirectedOp>) -> Option<DirectedOp>
+ #[inline]
+ fn ambiguity_check(&self, atom: &str) -> bool
{
- match op {
- &Some(DirectedOp::Left(ref lop, (priority, spec))) if is_infix!(spec) =>
- if self.outputter.ends_with(&format!(" {}", lop.as_str())) {
- Some(DirectedOp::Left(lop.clone(), (priority, spec)))
- } else if continues_with_append(lop.as_str(), atom) {
- Some(DirectedOp::Left(lop.clone(), (priority, spec)))
- } else {
- None
- },
- &Some(DirectedOp::Left(ref lop, spec))
- if continues_with_append(lop.as_str(), atom) =>
- Some(DirectedOp::Left(lop.clone(), spec)),
- &Some(DirectedOp::Right(ref rop, spec))
- if continues_with_append(atom, rop.as_str()) =>
- Some(DirectedOp::Right(rop.clone(), spec)),
- _ =>
- None
- }
+ let tail = self.outputter.range_from(self.last_item_idx ..);
+ continues_with_append(tail, atom)
}
fn enqueue_op(&mut self, ct: ClauseType, spec: (usize, Specifier)) {
self.format_struct(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(&self, addr: Addr) -> Option<String> {
match addr {
Addr::HeapCell(h) | Addr::Lis(h) | Addr::Str(h) =>
}
}
- fn check_for_seen(&mut self, iter: &mut HCPreOrderIterator, op: &Option<DirectedOp>)
- -> Option<HeapCellValue>
+ 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));
return iter.next();
} else {
iter.stack().pop();
- push_space_if_amb!(self, &var, op, {
- self.outputter.append(&var);
+ push_space_if_amb!(self, &var, {
+ self.append_str(&var);
});
return None;
iter.stack().pop();
if let Some(offset_str) = self.offset_as_string(addr) {
- push_space_if_amb!(self, &offset_str, op, {
- self.outputter.append(offset_str.as_str());
+ push_space_if_amb!(self, &offset_str, {
+ self.append_str(offset_str.as_str());
});
}
fn print_atom(&mut self, atom: &ClauseName, spec: Option<(usize, Specifier)>) {
match atom.as_str() {
- "" => self.outputter.append("''"),
- ";" | "!" => self.outputter.append(atom.as_str()),
+ "" => self.append_str("''"),
+ ";" | "!" => self.append_str(atom.as_str()),
s => if spec.is_some() || !self.quoted || non_quoted_token(s.chars()) {
- self.outputter.append(atom.as_str())
+ self.append_str(atom.as_str())
} else {
if self.quoted {
- self.outputter.push_char('\'');
+ self.push_char('\'');
}
for c in atom.as_str().chars() {
}
if self.quoted {
- self.outputter.push_char('\'');
+ self.push_char('\'');
}
}
}
fn print_char(&mut self, c: char) {
match c {
- '\n' => self.outputter.append("\\n"),
- '\r' => self.outputter.append("\\r"),
- '\t' => self.outputter.append("\\t"),
- '\u{0b}' => self.outputter.append("\\v"), // UTF-8 vertical tab
- '\u{0c}' => self.outputter.append("\\f"), // UTF-8 form feed
- '\u{08}' => self.outputter.append("\\b"), // UTF-8 backspace
- '\u{07}' => self.outputter.append("\\a"), // UTF-8 alert
- '\x20' ... '\x7e' => self.outputter.push_char(c),
- _ => self.outputter.append(&format!("\\x{:x}", c as u32))
+ '\n' => self.append_str("\\n"),
+ '\r' => self.append_str("\\r"),
+ '\t' => self.append_str("\\t"),
+ '\u{0b}' => self.append_str("\\v"), // UTF-8 vertical tab
+ '\u{0c}' => self.append_str("\\f"), // UTF-8 form feed
+ '\u{08}' => self.append_str("\\b"), // UTF-8 backspace
+ '\u{07}' => self.append_str("\\a"), // UTF-8 alert
+ '\x20' ... '\x7e' => self.push_char(c),
+ _ => self.append_str(&format!("\\x{:x}", c as u32))
};
}
Constant::Atom(ref atom, Some(spec)) => {
if let Some(ref op) = op {
if self.outputter.ends_with(&format!(" {}", op.as_str())) {
- self.outputter.push_char(' ');
+ self.push_char(' ');
}
- self.outputter.push_char('(');
+ self.push_char('(');
}
self.print_atom(atom, Some(spec));
if op.is_some() {
- self.outputter.push_char(')');
+ self.push_char(')');
}
},
Constant::Atom(ref atom, None) =>
- push_space_if_amb!(self, atom.as_str(), &op, {
+ push_space_if_amb!(self, atom.as_str(), {
self.print_atom(atom, None);
}),
Constant::Char(c) if non_quoted_token(once(c)) =>
self.print_char(c),
Constant::Char(c) =>
if self.quoted {
- self.outputter.push_char('\'');
+ self.push_char('\'');
self.print_char(c);
- self.outputter.push_char('\'');
+ self.push_char('\'');
} else {
self.print_char(c);
},
Constant::EmptyList =>
- self.outputter.append("[]"),
+ self.append_str("[]"),
Constant::Number(Number::Float(fl)) =>
if &fl == &OrderedFloat(0f64) {
- push_space_if_amb!(self, "0", &op, {
- self.outputter.append("0");
+ push_space_if_amb!(self, "0", {
+ self.append_str("0");
});
} else {
let output_str = format!("{}", fl);
- push_space_if_amb!(self, &output_str, &op, {
- self.outputter.append(&output_str);
+ push_space_if_amb!(self, &output_str, {
+ self.append_str(&output_str);
});
},
Constant::Number(n) => {
let output_str = format!("{}", n);
- push_space_if_amb!(self, &output_str, &op, {
- self.outputter.append(&output_str);
+ push_space_if_amb!(self, &output_str, {
+ self.append_str(&output_str);
});
},
Constant::String(s) =>
}
} else if s.is_expandable() {
if !self.at_cdr(" | _") {
- self.outputter.push_char('_');
+ self.push_char('_');
}
} else if !self.at_cdr("") {
- self.outputter.append("[]");
+ self.append_str("[]");
}
} else { // for now, == DoubleQuotes::Atom
let borrowed_str = s.borrow();
- self.outputter.append("\"");
- self.outputter.append(&borrowed_str[s.cursor() ..]);
- self.outputter.append("\"");
+ self.push_char('"');
+ self.append_str(&borrowed_str[s.cursor() ..]);
+ self.push_char('"');
},
Constant::Usize(i) =>
- self.outputter.append(&format!("u{}", i))
+ self.append_str(&format!("u{}", i))
}
}
fn handle_heap_term(&mut self, iter: &mut HCPreOrderIterator, op: Option<DirectedOp>,
is_functor_redirect: bool)
{
- let heap_val = match self.check_for_seen(iter, &op) {
+ let heap_val = match self.check_for_seen(iter) {
Some(heap_val) => heap_val,
None => return
};
match heap_val {
- HeapCellValue::NamedStr(arity, ref name, Some(spec)) => {
+ HeapCellValue::NamedStr(arity, ref name, Some(spec)) => {
let add_brackets = if !self.ignore_ops {
if let Some(ref op) = &op {
needs_bracketing(spec, op)
} else {
false
};
-
+
if add_brackets {
self.state_stack.push(TokenOrRedirect::Close);
}
if add_brackets {
self.state_stack.push(TokenOrRedirect::Open);
}
-
- if let Some(op) = op {
- if !add_brackets && self.outputter.ends_with(&format!(" {}", op.as_str())) {
- self.state_stack.push(TokenOrRedirect::Space);
- }
- }
},
HeapCellValue::NamedStr(0, name, fixity) =>
- push_space_if_amb!(self, name.as_str(), &op, {
+ push_space_if_amb!(self, name.as_str(), {
let ct = ClauseType::from(name, 0, fixity);
self.format_clause(iter, 0, ct);
}),
},
HeapCellValue::Addr(Addr::Con(Constant::EmptyList)) =>
if !self.at_cdr("") {
- self.outputter.append("[]");
+ self.append_str("[]");
},
HeapCellValue::Addr(Addr::Con(c)) =>
self.print_constant(c, &op),
},
HeapCellValue::Addr(addr) =>
if let Some(offset_str) = self.offset_as_string(addr) {
- push_space_if_amb!(self, &offset_str, &op, {
- self.outputter.append(offset_str.as_str());
+ push_space_if_amb!(self, &offset_str, {
+ self.append_str(offset_str.as_str());
})
}
}
let len = self.outputter.len();
if self.outputter.ends_with(" | ") {
- self.outputter.truncate(len - 3);
- self.outputter.append(tr);
+ self.outputter.truncate(len - " | ".len());
+ self.append_str(tr);
true
} else {
TokenOrRedirect::Op(atom, spec) =>
self.print_atom(&atom, Some(spec)),
TokenOrRedirect::NumberedVar(num_var) =>
- self.outputter.append(num_var.as_str()),
+ self.append_str(num_var.as_str()),
TokenOrRedirect::CompositeRedirect(op) =>
self.handle_heap_term(&mut iter, Some(op), false),
TokenOrRedirect::FunctorRedirect =>
self.handle_heap_term(&mut iter, None, true),
- TokenOrRedirect::Space =>
- self.outputter.push_char(' '),
TokenOrRedirect::Close =>
- self.outputter.push_char(')'),
+ self.push_char(')'),
TokenOrRedirect::Open =>
- self.outputter.push_char('('),
+ self.push_char('('),
TokenOrRedirect::OpenList(delimit) =>
if !self.at_cdr(", ") {
- self.outputter.push_char('[');
+ self.push_char('[');
} else {
delimit.set(false);
},
TokenOrRedirect::CloseList(delimit) =>
if delimit.get() {
- self.outputter.push_char(']');
+ self.push_char(']');
},
TokenOrRedirect::HeadTailSeparator =>
- self.outputter.append(" | "),
+ self.append_str(" | "),
TokenOrRedirect::Comma =>
- self.outputter.append(", ")
+ self.append_str(", ")
}
} else if !iter.stack().is_empty() {
self.handle_heap_term(&mut iter, None, false);
use std::collections::HashSet;
use std::mem::swap;
+use std::ops::{Range, RangeFrom};
pub struct TestOutputter {
results: Vec<HashSet<String>>,
}
}
- fn insert_from_end(&mut self, idx: usize, c: char) {
- let len = self.focus.len();
- self.focus.insert(len - idx, c);
+ fn insert(&mut self, idx: usize, c: char) {
+ self.focus.insert(idx, c);
}
-
+
fn result(self) -> Self::Output {
self.results
}
fn truncate(&mut self, len: usize) {
self.focus.truncate(len);
}
+
+ fn range(&self, index: Range<usize>) -> &str {
+ &self.focus.as_str()[index]
+ }
+
+ fn range_from(&self, index: RangeFrom<usize>) -> &str {
+ &self.focus.as_str()[index]
+ }
}
pub fn collect_test_output(wam: &mut Machine, alloc_locs: AllocVarDict, mut heap_locs: HeapVarDict)
assert_prolog_success!(&mut wam, "?- reverse(_, _).");
submit(&mut wam, ":- use_module(library(lists), []).");
-
+
assert_prolog_success!(&mut wam, "?- catch(reverse(_, _), error(existence_error(procedure, P), _), true).",
[["P = reverse/2"]]);
}
[["X = (:-):-(:-)"]]);
assert_prolog_success!(&mut wam, "?- X = (a:-b,c).",
[["X = a:-b,c"]]);
+ assert_prolog_success!(&mut wam, "?- X = f((f:-a,b,c)).",
+ [["X = f((f:-a,b,c))"]]);
+ assert_prolog_success!(&mut wam, "?- X = f((f:-a,(b,c))).",
+ [["X = f((f:-a,b,c))"]]);
+ assert_prolog_success!(&mut wam, "?- X = f((a,b,c)).",
+ [["X = f((a,b,c))"]]);
+ assert_prolog_success!(&mut wam, "?- X = f((a,(b,c))).",
+ [["X = f((a,b,c))"]]);
+ assert_prolog_success!(&mut wam, "?- X = f(((a,b),c)).",
+ [["X = f(((a,b),c))"]]);
+ assert_prolog_success!(&mut wam, "?- X = f(((a,b),(c, d))).",
+ [["X = f(((a,b),c,d))"]]);
}
#[test]
let mut wam = Machine::new();
submit(&mut wam, ":- use_module(library(dcgs)).");
-
+
// test case by YeGoblynQueene from hacker news.
compile_user_module(&mut wam,
" ability(destroy, X) --> destroy(X).
permanent(X) --> [land], land(X).
spell(X) --> [sorcery], sorcery(X).
spell(X) --> [instant], instant(X).
-
+
creature('Llanowar Elves') --> [].
artifact('Ankh of Mishra') --> [].
land('Mountain') --> [].
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X), matcher(X, Y), partial_string(\"def\", Y).",
[["X = [a, b, c, d, e, f | _]",
- "Y = [d, e, f | _]"]]);
+ "Y = [d, e, f | _]"]]);
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X), matcher(X, Y), partial_string(\"def\", Y),
Y = \"defghijkl\".",
[["X = [a, b, c, d, e, f, g, h, i, j, k, l]",
\"defghijkl\" = Y.",
[["X = [a, b, c, d, e, f, g, h, i, j, k, l]",
"Y = [d, e, f, g, h, i, j, k, l]"]]);
-
+
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X), matcher(X, Y), Y = [d, e, f | G].",
[["X = [a, b, c, d, e, f | _]", "Y = [d, e, f | _]", "G = _"]]);
assert_prolog_success!(&mut wam, "?- partial_string(\"abc\", X), matcher(X, Y), [d, e, f | G] = Y.",
["X = [b, d | _]", "Y = [d | _]"]]);
assert_prolog_success!(&mut wam, "?- partial_string(\"bc\", X), matcher(X, Y).",
[["X = [b, c | _]", "Y = [c | _]"]]);
-
+
submit(&mut wam, "f(\"appendy jones\").
f(\"appendy smithers jones\").
f(\"appendy o'toole\").");