* call/N as a built-in meta-predicate (_done_).
* ISO Prolog compliant throw/catch (_done_).
* Built-in and user-defined operators of all fixities, with custom
- associativity and precedence (_done_).
+ associativity and precedence (_done_).
* Bignum, rational number and floating point arithmetic (_done_).
* Built-in control operators (`,`, `;`, `->`, etc.) (_done_).
* Built-in predicates for list processing and top-level declarative
control (`setup_call_control/3`, `call_with_inference_limit/3`,
etc.) (_in progress_).
-* Add a rudimentary module system.
+* Add a rudimentary module system.
* Attributed variables using the SICStus Prolog interface and
semantics. Adding coroutines like `dif/2`, `freeze/2`, etc.
- is straightforward with attributed variables.
+ is straightforward with attributed variables.
* An occurs check.
* Mode declarations.
* Extensions for clp(FD).
* `if_` and related predicates, following the developments of the
paper "Indexing `dif/2`".
* Strings, blobs, and other data types.
-
+
## Phase 3
Use the WAM code produced by the completed code generator to target LLVM
`(xor)/2`, `(rem)/2`, `(mod)/2`, `(/\)/2`, `(\/)/2`, `(>>)/2`, `(<<)/2`.
* Comparison operators: `>`, `<`, `=<`, `>=`, `=:=`, `=\=`.
* `(\+)/1`
+* `(==)/2`
+* `(\==)/2`
* `(=)/2`
* `(=..)/2`
* `(->)/2`
member(X, [X|_]).
member(X, [_|Xs]) :- member(X, Xs).
}:
-prolog> ?- member(X, [a, b, c]).
+prolog> ?- member(X, [a, b, c]).
true .
X = a ;
X = b ;
Catch(Vec<Box<Term>>),
Cut,
Display(Vec<Box<Term>>),
- DuplicateTerm(Vec<Box<Term>>),
+ DuplicateTerm(Vec<Box<Term>>),
+ Eq(Vec<Box<Term>>),
Functor(Vec<Box<Term>>),
Ground(Vec<Box<Term>>),
Inlined(InlinedQueryTerm),
Is(Vec<Box<Term>>),
Jump(JumpStub),
+ NotEq(Vec<Box<Term>>),
SetupCallCleanup(Vec<Box<Term>>),
Term(Term),
Throw(Vec<Box<Term>>)
&QueryTerm::Catch(_) => 3,
&QueryTerm::Display(_) => 1,
&QueryTerm::Throw(_) => 1,
- &QueryTerm::DuplicateTerm(_) => 2,
+ &QueryTerm::DuplicateTerm(_) => 2,
+ &QueryTerm::Eq(_) => 2,
&QueryTerm::Functor(_) => 3,
&QueryTerm::Ground(_) => 1,
&QueryTerm::Inlined(ref term) => term.arity(),
&QueryTerm::Is(_) => 2,
&QueryTerm::Jump(ref vars) => vars.len(),
+ &QueryTerm::NotEq(_) => 2,
&QueryTerm::CallN(ref terms) => terms.len(),
&QueryTerm::Cut => 0,
&QueryTerm::SetupCallCleanup(_) => 3,
Deep(Level, &'a Cell<RegType>, &'a TabledRc<Atom>, Option<Fixity>),
Display,
DuplicateTerm,
+ Eq,
Functor,
Ground,
Is,
+ NotEq,
Root(&'a TabledRc<Atom>),
SetupCallCleanup,
Throw,
&ClauseType::Display => "display",
&ClauseType::Deep(_, _, name, _) => name.as_str(),
&ClauseType::DuplicateTerm => "duplicate_term",
+ &ClauseType::Eq => "==",
&ClauseType::Functor => "functor",
&ClauseType::Ground => "ground",
&ClauseType::Is => "is",
+ &ClauseType::NotEq => "\\==",
&ClauseType::Root(name) => name.as_str(),
&ClauseType::SetupCallCleanup => "setup_call_cleanup",
&ClauseType::Throw => "throw"
DisplayExecute,
Deallocate,
DuplicateTermCall,
- DuplicateTermExecute,
+ DuplicateTermExecute,
+ EqCall,
+ EqExecute,
Execute(TabledRc<Atom>, usize),
ExecuteN(usize),
FunctorCall,
JmpByExecute(usize, usize),
IsCall(RegType, ArithmeticTerm),
IsExecute(RegType, ArithmeticTerm),
+ NotEqCall,
+ NotEqExecute,
Proceed,
ThrowCall,
ThrowExecute,
&ControlInstruction::DisplayExecute => true,
&ControlInstruction::DuplicateTermCall => true,
&ControlInstruction::DuplicateTermExecute => true,
+ &ControlInstruction::EqCall => true,
+ &ControlInstruction::EqExecute => true,
&ControlInstruction::Execute(_, _) => true,
&ControlInstruction::CallN(_) => true,
&ControlInstruction::ExecuteN(_) => true,
&ControlInstruction::FunctorCall => true,
&ControlInstruction::FunctorExecute => true,
+ &ControlInstruction::NotEqCall => true,
+ &ControlInstruction::NotEqExecute => true,
&ControlInstruction::ThrowCall => true,
&ControlInstruction::ThrowExecute => true,
&ControlInstruction::GetCleanerCall => true,
&ControlInstruction::IsCall(..) => true,
&ControlInstruction::IsExecute(..) => true,
&ControlInstruction::JmpByCall(..) => true,
- &ControlInstruction::JmpByExecute(..) => true,
+ &ControlInstruction::JmpByExecute(..) => true,
_ => false
}
}
restore_cut_policy!(), // restore_cut_policy/0, 382.
proceed!(),
ground_execute!(), // ground/1, 384.
+ eq_execute!(), // (==)/2, 385.
+ not_eq_execute!(), // (\==)/2, 386.
]
}
op_dir.insert((tabled_rc!("=..", atom_tbl), Fixity::In), (XFX, 700));
op_dir.insert((tabled_rc!("==", atom_tbl), Fixity::In), (XFX, 700));
+ op_dir.insert((tabled_rc!("\\==", atom_tbl), Fixity::In), (XFX, 700));
// there are 63 registers in the VM, so call/N is defined for all 0 <= N <= 62
// (an extra register is needed for the predicate name)
code_dir.insert((tabled_rc!("nonvar", atom_tbl), 1), (PredicateKeyType::BuiltIn, 380));
code_dir.insert((tabled_rc!("ground", atom_tbl), 1), (PredicateKeyType::BuiltIn, 384));
+ code_dir.insert((tabled_rc!("==", atom_tbl), 2), (PredicateKeyType::BuiltIn, 385));
+ code_dir.insert((tabled_rc!("\\==", atom_tbl), 2), (PredicateKeyType::BuiltIn, 386));
(builtin_code, code_dir, op_dir)
}
code.push(Line::Control(ControlInstruction::CatchCall)),
&QueryTerm::Display(_) =>
code.push(Line::Control(ControlInstruction::DisplayCall)),
- &QueryTerm::DuplicateTerm(_) =>
+ &QueryTerm::DuplicateTerm(_) =>
code.push(Line::Control(ControlInstruction::DuplicateTermCall)),
+ &QueryTerm::Eq(_) =>
+ code.push(Line::Control(ControlInstruction::EqCall)),
&QueryTerm::Ground(_) =>
- code.push(Line::Control(ControlInstruction::GroundCall)),
+ code.push(Line::Control(ControlInstruction::GroundCall)),
&QueryTerm::Functor(_) =>
code.push(Line::Control(ControlInstruction::FunctorCall)),
&QueryTerm::Inlined(_) =>
&QueryTerm::Jump(ref vars) => {
code.push(jmp_call!(vars.len(), 0));
},
+ &QueryTerm::NotEq(_) =>
+ code.push(Line::Control(ControlInstruction::NotEqCall)),
&QueryTerm::Term(Term::Constant(_, Constant::Atom(ref atom))) => {
let call = ControlInstruction::Call(atom.clone(), 0, pvs);
code.push(Line::Control(call));
*ctrl = ControlInstruction::DisplayExecute,
ControlInstruction::DuplicateTermCall =>
*ctrl = ControlInstruction::DuplicateTermExecute,
+ ControlInstruction::EqCall =>
+ *ctrl = ControlInstruction::EqExecute,
ControlInstruction::GroundCall =>
*ctrl = ControlInstruction::GroundExecute,
ControlInstruction::FunctorCall =>
*ctrl = ControlInstruction::FunctorExecute,
ControlInstruction::JmpByCall(arity, offset) =>
*ctrl = ControlInstruction::JmpByExecute(arity, offset),
+ ControlInstruction::NotEqCall =>
+ *ctrl = ControlInstruction::NotEqExecute,
ControlInstruction::CatchCall =>
*ctrl = ControlInstruction::CatchExecute,
ControlInstruction::ThrowCall =>
},
&QueryTerm::Inlined(ref term) =>
self.compile_inlined(term, term_loc, code)?,
- _ if chunk_num == 0 => {
- self.marker.reset_arg(term.arity());
-
- let iter = term.post_order_iter();
- let query = self.compile_target(iter, term_loc, is_exposed);
-
- if !query.is_empty() {
- code.push(Line::Query(query));
- }
-
- Self::add_conditional_call(code, term, conjunct_info.perm_vars());
- },
_ => {
- let num_vars = conjunct_info.perm_vs.vars_above_threshold(i + 1);
- self.compile_query_line(term, term_loc, code, num_vars, is_exposed);
+ let num_perm_vars = if chunk_num == 0 {
+ conjunct_info.perm_vars()
+ } else {
+ conjunct_info.perm_vs.vars_above_threshold(i + 1)
+ };
+
+ self.compile_query_line(term, term_loc, code, num_perm_vars, is_exposed);
},
};
}
fn compile_query_line(&mut self, term: &'a QueryTerm, term_loc: GenContext,
- code: &mut Code, index: usize, is_exposed: bool)
+ code: &mut Code, num_perm_vars_left: usize, is_exposed: bool)
{
self.marker.reset_arg(term.arity());
- let iter = term.post_order_iter();
- let compiled_query = self.compile_target(iter, term_loc, is_exposed);
+ let iter = term.post_order_iter();
+ let query = self.compile_target(iter, term_loc, is_exposed);
- if !compiled_query.is_empty() {
- code.push(Line::Query(compiled_query));
+ if !query.is_empty() {
+ code.push(Line::Query(query));
}
- Self::add_conditional_call(code, term, index);
+ Self::add_conditional_call(code, term, num_perm_vars_left);
}
pub fn compile_query(&mut self, query: &'a Vec<QueryTerm>) -> Result<Code, ParserError>
HeapCellPostOrderIterator::new(HeapCellPreOrderIterator::new(self, a))
}
- pub fn acyclic_pre_order_iter<'a>(&'a self, a: Addr) -> HeapCellAcyclicIterator<HeapCellPreOrderIterator<'a>>
+ pub fn acyclic_pre_order_iter<'a>(&'a self, a: Addr)
+ -> HeapCellAcyclicIterator<HeapCellPreOrderIterator<'a>>
{
HeapCellAcyclicIterator::new(HeapCellPreOrderIterator::new(self, a))
}
+
+ pub fn zipped_acyclic_pre_order_iter<'a>(&'a self, a1: Addr, a2: Addr)
+ -> HeapCellZippedAcyclicIterator<HeapCellPreOrderIterator<'a>>
+ {
+ HeapCellZippedAcyclicIterator::new(HeapCellPreOrderIterator::new(self, a1),
+ HeapCellPreOrderIterator::new(self, a2))
+ }
}
pub trait MutStackHeapCellIterator {
self.iter.next()
}
}
+
+pub struct HeapCellZippedAcyclicIterator<HeapCellIter> {
+ i1: HeapCellIter,
+ i2: HeapCellIter,
+ seen: HashSet<(Addr, Addr)>
+}
+
+impl<HeapCellIter: MutStackHeapCellIterator> HeapCellZippedAcyclicIterator<HeapCellIter>
+{
+ pub fn new(i1: HeapCellIter, i2: HeapCellIter) -> Self {
+ HeapCellZippedAcyclicIterator { i1, i2, seen: HashSet::new() }
+ }
+}
+
+impl<HeapCellIter> Iterator for HeapCellZippedAcyclicIterator<HeapCellIter>
+ where HeapCellIter: Iterator<Item=HeapCellValue>
+ + MutStackHeapCellIterator
+{
+ type Item = (HeapCellValue, HeapCellValue);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ while let (Some(a1), Some(a2)) = (self.i1.stack().pop(), self.i2.stack().pop()) {
+ if self.seen.contains(&(a1.clone(), a2.clone())) {
+ continue;
+ } else {
+ self.i1.stack().push(a1.clone());
+ self.i2.stack().push(a2.clone());
+ self.seen.insert((a1, a2));
+
+ break;
+ }
+ }
+
+ if let (Some(v1), Some(v2)) = (self.i1.next(), self.i2.next()) {
+ Some((v1, v2))
+ } else {
+ None
+ }
+ }
+}
write!(f, "call_duplicate_term"),
&ControlInstruction::DuplicateTermExecute =>
write!(f, "execute_duplicate_term"),
+ &ControlInstruction::EqCall =>
+ write!(f, "eq_call"),
+ &ControlInstruction::EqExecute =>
+ write!(f, "eq_execute"),
&ControlInstruction::ExecuteN(arity) =>
write!(f, "execute_N {}", arity),
&ControlInstruction::FunctorCall =>
write!(f, "jmp_by_call {}/{}", offset, arity),
&ControlInstruction::JmpByExecute(arity, offset) =>
write!(f, "jmp_by_execute {}/{}", offset, arity),
+ &ControlInstruction::NotEqCall =>
+ write!(f, "neq_call"),
+ &ControlInstruction::NotEqExecute =>
+ write!(f, "neq_execute"),
&ControlInstruction::Proceed =>
write!(f, "proceed"),
&ControlInstruction::ThrowCall =>
let state = TermIterState::Clause(0, ClauseType::SetupCallCleanup, terms);
QueryIterator { state_stack: vec![state] }
},
+ &QueryTerm::Eq(ref terms) => {
+ let state = TermIterState::Clause(0, ClauseType::Eq, terms);
+ QueryIterator { state_stack: vec![state] }
+ },
&QueryTerm::Functor(ref terms) => {
let state = TermIterState::Clause(0, ClauseType::Functor, terms);
QueryIterator { state_stack: vec![state] }
let state = TermIterState::Clause(0, ClauseType::Ground, terms);
QueryIterator { state_stack: vec![state] }
},
- &QueryTerm::Inlined(InlinedQueryTerm::CompareNumber(qt, ref terms)) => {
+ &QueryTerm::Inlined(InlinedQueryTerm::CompareNumber(qt, ref terms)) => {
let state = TermIterState::Clause(0, ClauseType::CompareNumber(qt), terms);
- QueryIterator { state_stack: vec![state] }
+ QueryIterator { state_stack: vec![state] }
},
&QueryTerm::Is(ref terms) => {
let state = TermIterState::Clause(0, ClauseType::Is, terms);
| &QueryTerm::Inlined(InlinedQueryTerm::IsFloat(ref terms))
| &QueryTerm::Inlined(InlinedQueryTerm::IsRational(ref terms))
| &QueryTerm::Inlined(InlinedQueryTerm::IsCompound(ref terms))
- | &QueryTerm::Inlined(InlinedQueryTerm::IsString(ref terms)) =>
+ | &QueryTerm::Inlined(InlinedQueryTerm::IsString(ref terms)) =>
Self::from_term(terms[0].as_ref()),
+ &QueryTerm::NotEq(ref terms) => {
+ let state = TermIterState::Clause(0, ClauseType::NotEq, terms);
+ QueryIterator { state_stack: vec![state] }
+ },
&QueryTerm::Term(ref term) =>
Self::from_term(term),
&QueryTerm::Throw(ref term) => {
arity = 3;
break;
},
- &QueryTerm::Is(_) | &QueryTerm::DuplicateTerm(_) => {
+ &QueryTerm::Eq(_) | &QueryTerm::NotEq(_)
+ | &QueryTerm::Is(_) | &QueryTerm::DuplicateTerm(_) => {
result.push(term);
arity = 2;
break;
if self.term_loc.chunk_num() > 0 {
self.deep_cut_encountered = true;
}
- },
+ },
};
item = self.iter.next();
self.unify(Addr::HeapCell(old_h), a2);
}
- /* TODO: needs careful consideration of cyclic terms.
// returns true on failure.
fn eq_test(&self) -> bool
{
let a1 = self.store(self.deref(self[temp_v!(1)].clone()));
let a2 = self.store(self.deref(self[temp_v!(2)].clone()));
- let iter1 = self.acyclic_pre_order_iter(a1);
- let iter2 = self.acyclic_pre_order_iter(a2);
+ let iter = self.zipped_acyclic_pre_order_iter(a1, a2);
- for (v1, v2) in iter1.zip(iter2) {
+ for (v1, v2) in iter {
match (v1, v2) {
(HeapCellValue::NamedStr(ar1, n1, _), HeapCellValue::NamedStr(ar2, n2, _)) =>
if ar1 != ar2 || *n1 != *n2 {
(HeapCellValue::Addr(a1), HeapCellValue::Addr(a2)) =>
if a1 != a2 {
return true;
- },
- _ => {
- return true;
- }
+ },
+ _ => return true
}
}
false
}
- */
// returns true on failure.
fn ground_test(&self) -> bool
self.duplicate_term();
self.p = self.cp;
},
+ &ControlInstruction::EqCall => {
+ self.fail = self.eq_test();
+ self.p += 1;
+ },
+ &ControlInstruction::EqExecute => {
+ self.fail = self.eq_test();
+ self.p = self.cp;
+ },
&ControlInstruction::GroundCall => {
self.fail = self.ground_test();
self.p += 1;
self.b0 = self.b;
self.p += offset;
},
+ &ControlInstruction::NotEqCall => {
+ self.fail = !self.eq_test();
+ self.p += 1;
+ },
+ &ControlInstruction::NotEqExecute => {
+ self.fail = !self.eq_test();
+ self.p = self.cp;
+ },
&ControlInstruction::Proceed =>
self.p = self.cp,
&ControlInstruction::ThrowCall => {
Line::Control(ControlInstruction::GroundExecute)
)
}
+
+macro_rules! eq_execute {
+ () => (
+ Line::Control(ControlInstruction::EqExecute)
+ )
+}
+
+macro_rules! not_eq_execute {
+ () => (
+ Line::Control(ControlInstruction::NotEqExecute)
+ )
+}
-Subproject commit 5de18ac4728701492cc19fd08d32b40f4d5918a3
+Subproject commit b50ed579fdb245f2d42fcd05b546c23f077b0991
assert_prolog_failure!(&mut wam, "?- B = f(A), ground(A).");
assert_prolog_success!(&mut wam, "?- ground(x), ground(f(x)), X = f(x), ground(g(f(X), [a,b])).");
+
+ assert_prolog_success!(&mut wam, "?- A = f(A), g(A, B) == g(f(A), B).");
+ assert_prolog_failure!(&mut wam, "?- A = f(A), g(A, B) == g(f(A), b).");
+ assert_prolog_failure!(&mut wam, "?- A == B.");
+ assert_prolog_failure!(&mut wam, "?- A == 12.1.");
+ assert_prolog_success!(&mut wam, "?- X = x, f(X, x) == f(x, X).");
+
+ assert_prolog_failure!(&mut wam, "?- A = f(A), g(A, B) \\== g(f(A), B).");
+ assert_prolog_success!(&mut wam, "?- A = f(A), g(A, B) \\== g(f(A), b).");
+ assert_prolog_success!(&mut wam, "?- A \\== B.");
+ assert_prolog_success!(&mut wam, "?- A \\== 12.1.");
+ assert_prolog_failure!(&mut wam, "?- X = x, f(X, x) \\== f(x, X).");
}
#[test]
assert_prolog_success!(&mut wam, "?- catch(setup_call_cleanup(true,throw(goal),throw(cl)), Pat, true).",
[["Pat = goal"]]);
assert_prolog_success!(&mut wam, "?- catch(( setup_call_cleanup(true,(G=1;G=2),throw(cl)), throw(cont)), Pat, true).",
- [["Pat = cont", "G = _1"]]);
+ [["Pat = cont", "G = _1"]]);
}