cleanup code generation, add throw/catch, copy_term, var, atomic, not, false builtins
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1,6 +1,6 @@
|
|||||||
[root]
|
[root]
|
||||||
name = "rusty-wam"
|
name = "rusty-wam"
|
||||||
version = "0.6.6"
|
version = "0.6.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lalrpop 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lalrpop 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lalrpop-util 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lalrpop-util 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rusty-wam"
|
name = "rusty-wam"
|
||||||
version = "0.6.6"
|
version = "0.6.7"
|
||||||
authors = ["Mark Thom"]
|
authors = ["Mark Thom"]
|
||||||
|
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ argument indexing, and conjunctive queries.
|
|||||||
Extend rusty-wam to include the following, among other features:
|
Extend rusty-wam to include the following, among other features:
|
||||||
|
|
||||||
* call/N as a built-in meta-predicate (_done_).
|
* call/N as a built-in meta-predicate (_done_).
|
||||||
* ISO Prolog compliant throw/catch.
|
* ISO Prolog compliant throw/catch (_done_).
|
||||||
* Built-in and user-defined operators of all fixities,
|
* Built-in and user-defined operators of all fixities,
|
||||||
with custom associativity and precedence.
|
with custom associativity and precedence.
|
||||||
* Bignum and floating point arithmetic.
|
* Bignum and floating point arithmetic.
|
||||||
|
|||||||
30
src/main.rs
30
src/main.rs
@@ -671,6 +671,36 @@ mod tests {
|
|||||||
assert_eq!(submit(&mut wam, "?- call(call(call(call(call(call)))))."), false);
|
assert_eq!(submit(&mut wam, "?- call(call(call(call(call(call)))))."), false);
|
||||||
assert_eq!(submit(&mut wam, "?- call(call(call(call(call(call(p(X)))))))."), true);
|
assert_eq!(submit(&mut wam, "?- call(call(call(call(call(call(p(X)))))))."), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_queries_on_exceptions()
|
||||||
|
{
|
||||||
|
let mut wam = Machine::new();
|
||||||
|
|
||||||
|
submit(&mut wam, "f(a). f(_) :- throw(stuff).");
|
||||||
|
submit(&mut wam, "handle(stuff).");
|
||||||
|
|
||||||
|
assert_eq!(submit(&mut wam, "?- catch(f(X), Exception, handle(Exception))."), true);
|
||||||
|
|
||||||
|
submit(&mut wam, "f(a). f(X) :- g(X).");
|
||||||
|
submit(&mut wam, "g(x). g(y). g(z).");
|
||||||
|
submit(&mut wam, "handle(x). handle(y).");
|
||||||
|
|
||||||
|
assert_eq!(submit(&mut wam, "?- catch(f(X), X, handle(X))."), true);
|
||||||
|
assert_eq!(submit(&mut wam, "?- catch(f(a), _, handle(X))."), true);
|
||||||
|
assert_eq!(submit(&mut wam, "?- catch(f(b), _, handle(X))."), false);
|
||||||
|
|
||||||
|
submit(&mut wam, "g(x). g(X) :- throw(x).");
|
||||||
|
|
||||||
|
assert_eq!(submit(&mut wam, "?- catch(f(X), x, handle(X))."), true);
|
||||||
|
assert_eq!(submit(&mut wam, "?- catch(f(X), x, handle(z))."), true);
|
||||||
|
assert_eq!(submit(&mut wam, "?- catch(f(z), x, handle(x))."), true);
|
||||||
|
assert_eq!(submit(&mut wam, "?- catch(f(z), x, handle(y))."), true);
|
||||||
|
assert_eq!(submit(&mut wam, "?- catch(f(z), x, handle(z))."), false);
|
||||||
|
|
||||||
|
//TODO: write more tests: multi-layered throw/catch, catch
|
||||||
|
// within catch, throw within catch, etc.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_buffer(wam: &mut Machine, buffer: &str)
|
fn process_buffer(wam: &mut Machine, buffer: &str)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ pub trait Allocator<'a>
|
|||||||
fn bindings_mut(&mut self) -> &mut AllocVarDict<'a>;
|
fn bindings_mut(&mut self) -> &mut AllocVarDict<'a>;
|
||||||
|
|
||||||
fn take_bindings(self) -> AllocVarDict<'a>;
|
fn take_bindings(self) -> AllocVarDict<'a>;
|
||||||
|
|
||||||
fn drain_var_data(&mut self, vs: VariableFixtures<'a>) -> VariableFixtures<'a>
|
fn drain_var_data(&mut self, vs: VariableFixtures<'a>) -> VariableFixtures<'a>
|
||||||
{
|
{
|
||||||
let mut perm_vs = VariableFixtures::new();
|
let mut perm_vs = VariableFixtures::new();
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ impl GenContext {
|
|||||||
GenContext::Head => 0,
|
GenContext::Head => 0,
|
||||||
GenContext::Mid(cn) | GenContext::Last(cn) => cn
|
GenContext::Mid(cn) | GenContext::Last(cn) => cn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum PredicateClause {
|
pub enum PredicateClause {
|
||||||
@@ -77,7 +77,7 @@ impl TopLevel {
|
|||||||
ref mut clauses })
|
ref mut clauses })
|
||||||
=>
|
=>
|
||||||
{
|
{
|
||||||
iter = Box::new(once(head));
|
iter = Box::new(iter.chain(once(head)));
|
||||||
iter = Box::new(iter.chain(clauses.iter_mut()));
|
iter = Box::new(iter.chain(clauses.iter_mut()));
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -85,7 +85,7 @@ impl TopLevel {
|
|||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
iter
|
iter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,6 +145,7 @@ impl Default for VarReg {
|
|||||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||||
pub enum Constant {
|
pub enum Constant {
|
||||||
Atom(Atom),
|
Atom(Atom),
|
||||||
|
UInt64(usize),
|
||||||
EmptyList
|
EmptyList
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,14 +164,6 @@ pub enum QueryTerm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl QueryTerm {
|
impl QueryTerm {
|
||||||
pub fn arity(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
&QueryTerm::Term(ref term) => term.arity(),
|
|
||||||
&QueryTerm::CallN(ref terms) => terms.len(),
|
|
||||||
_ => 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_ref(&self) -> QueryTermRef {
|
pub fn to_ref(&self) -> QueryTermRef {
|
||||||
match self {
|
match self {
|
||||||
&QueryTerm::CallN(ref terms) =>
|
&QueryTerm::CallN(ref terms) =>
|
||||||
@@ -178,7 +171,7 @@ impl QueryTerm {
|
|||||||
&QueryTerm::Cut =>
|
&QueryTerm::Cut =>
|
||||||
QueryTermRef::Cut,
|
QueryTermRef::Cut,
|
||||||
&QueryTerm::Term(ref term) =>
|
&QueryTerm::Term(ref term) =>
|
||||||
QueryTermRef::Term(term)
|
QueryTermRef::Term(term),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,7 +200,7 @@ impl<'a> ClauseType<'a> {
|
|||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum TermRef<'a> {
|
pub enum TermRef<'a> {
|
||||||
AnonVar(Level),
|
AnonVar(Level),
|
||||||
Cons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
Cons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
||||||
Constant(Level, &'a Cell<RegType>, &'a Constant),
|
Constant(Level, &'a Cell<RegType>, &'a Constant),
|
||||||
Clause(ClauseType<'a>, &'a Vec<Box<Term>>),
|
Clause(ClauseType<'a>, &'a Vec<Box<Term>>),
|
||||||
@@ -219,7 +212,7 @@ impl<'a> TermRef<'a> {
|
|||||||
match self {
|
match self {
|
||||||
TermRef::AnonVar(lvl)
|
TermRef::AnonVar(lvl)
|
||||||
| TermRef::Cons(lvl, _, _, _)
|
| TermRef::Cons(lvl, _, _, _)
|
||||||
| TermRef::Constant(lvl, _, _)
|
| TermRef::Constant(lvl, _, _)
|
||||||
| TermRef::Var(lvl, _, _) => lvl,
|
| TermRef::Var(lvl, _, _) => lvl,
|
||||||
TermRef::Clause(ClauseType::Root, _) => Level::Shallow,
|
TermRef::Clause(ClauseType::Root, _) => Level::Shallow,
|
||||||
TermRef::Clause(ClauseType::Deep(lvl, _, _), _) => lvl,
|
TermRef::Clause(ClauseType::Deep(lvl, _, _), _) => lvl,
|
||||||
@@ -232,26 +225,17 @@ impl<'a> TermRef<'a> {
|
|||||||
pub enum QueryTermRef<'a> {
|
pub enum QueryTermRef<'a> {
|
||||||
CallN(&'a Vec<Box<Term>>),
|
CallN(&'a Vec<Box<Term>>),
|
||||||
Cut,
|
Cut,
|
||||||
Term(&'a Term)
|
Term(&'a Term),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> QueryTermRef<'a> {
|
impl<'a> QueryTermRef<'a> {
|
||||||
pub fn arity(self) -> usize {
|
pub fn arity(self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
QueryTermRef::Term(term) => term.arity(),
|
|
||||||
QueryTermRef::CallN(terms) => terms.len(),
|
QueryTermRef::CallN(terms) => terms.len(),
|
||||||
_ => 0
|
QueryTermRef::Cut => 0,
|
||||||
|
QueryTermRef::Term(term) => term.arity(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_callable(self) -> bool {
|
|
||||||
match self {
|
|
||||||
QueryTermRef::Term(&Term::Clause(_, _, _))
|
|
||||||
| QueryTermRef::Term(&Term::Constant(_, Constant::Atom(_)))
|
|
||||||
| QueryTermRef::CallN(_) => true,
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ChoiceInstruction {
|
pub enum ChoiceInstruction {
|
||||||
@@ -292,17 +276,30 @@ impl IndexedChoiceInstruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum BuiltInInstruction {
|
pub enum BuiltInInstruction {
|
||||||
InternalCallN
|
CleanUpBlock,
|
||||||
|
CopyTerm,
|
||||||
|
Fail,
|
||||||
|
GetBall,
|
||||||
|
GetCurrentBlock,
|
||||||
|
Goto(usize, usize),
|
||||||
|
InstallNewBlock,
|
||||||
|
InternalCallN,
|
||||||
|
IsAtomic,
|
||||||
|
IsVar,
|
||||||
|
ResetBlock,
|
||||||
|
SetBall,
|
||||||
|
Unify,
|
||||||
|
UnwindStack
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ControlInstruction {
|
pub enum ControlInstruction {
|
||||||
Allocate(usize),
|
Allocate(usize),
|
||||||
Call(Atom, usize, usize),
|
Call(Atom, usize, usize),
|
||||||
CallN(usize),
|
CallN(usize),
|
||||||
ExecuteN(usize),
|
|
||||||
Deallocate,
|
Deallocate,
|
||||||
Execute(Atom, usize),
|
Execute(Atom, usize),
|
||||||
|
ExecuteN(usize),
|
||||||
Proceed
|
Proceed
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,7 +544,7 @@ impl Term {
|
|||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arity(&self) -> usize {
|
pub fn arity(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
&Term::Clause(_, _, ref child_terms) => child_terms.len(),
|
&Term::Clause(_, _, ref child_terms) => child_terms.len(),
|
||||||
@@ -555,4 +552,3 @@ impl Term {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
125
src/prolog/builtins.rs
Normal file
125
src/prolog/builtins.rs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
use prolog::ast::*;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub type PredicateKey = (Atom, usize); // name, arity, type.
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum PredicateKeyType {
|
||||||
|
BuiltIn,
|
||||||
|
User
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CodeDir = HashMap<PredicateKey, (PredicateKeyType, usize)>;
|
||||||
|
|
||||||
|
fn get_builtins() -> Code {
|
||||||
|
vec![internal_call_n!(), // callN/N, 0.
|
||||||
|
is_atomic!(), // atomic/1, 1.
|
||||||
|
proceed!(),
|
||||||
|
is_var!(), // var/1, 3.
|
||||||
|
proceed!(),
|
||||||
|
allocate!(4), // catch/3, 5.
|
||||||
|
query![get_var_in_query!(perm_v!(2), 1),
|
||||||
|
get_var_in_query!(perm_v!(3), 2),
|
||||||
|
get_var_in_query!(perm_v!(1), 3),
|
||||||
|
put_var!(perm_v!(4), 1)],
|
||||||
|
get_current_block!(),
|
||||||
|
query![put_value!(perm_v!(2), 1),
|
||||||
|
put_value!(perm_v!(3), 2),
|
||||||
|
put_value!(perm_v!(1), 3),
|
||||||
|
put_unsafe_value!(4, 4)],
|
||||||
|
deallocate!(),
|
||||||
|
goto!(11, 4),
|
||||||
|
try_me_else!(9), // catch/4, 11.
|
||||||
|
allocate!(3),
|
||||||
|
query![get_var_in_query!(perm_v!(3), 1),
|
||||||
|
get_var_in_query!(perm_v!(2), 4),
|
||||||
|
put_var!(perm_v!(1), 1)],
|
||||||
|
install_new_block!(),
|
||||||
|
query![put_value!(perm_v!(3), 1)],
|
||||||
|
call_n!(1),
|
||||||
|
query![put_value!(perm_v!(2), 1),
|
||||||
|
put_unsafe_value!(1, 2)],
|
||||||
|
deallocate!(),
|
||||||
|
goto!(42, 2), // goto end_block/2 at 42.
|
||||||
|
trust_me!(),
|
||||||
|
allocate!(4),
|
||||||
|
query![get_var_in_query!(perm_v!(2), 2),
|
||||||
|
get_var_in_query!(perm_v!(1), 3),
|
||||||
|
get_var_in_query!(temp_v!(2), 1),
|
||||||
|
put_value!(temp_v!(4), 1)],
|
||||||
|
reset_block!(),
|
||||||
|
query![put_var!(perm_v!(4), 1)],
|
||||||
|
get_ball!(),
|
||||||
|
query![put_value!(perm_v!(4), 1),
|
||||||
|
put_var!(perm_v!(3), 2)],
|
||||||
|
copy_term!(),
|
||||||
|
query![put_unsafe_value!(3, 1),
|
||||||
|
put_value!(perm_v!(2), 2),
|
||||||
|
put_value!(perm_v!(1), 3)],
|
||||||
|
deallocate!(),
|
||||||
|
goto!(31, 2), // goto handle_ball/2.
|
||||||
|
try_me_else!(10), // handle_ball/2, 31.
|
||||||
|
allocate!(2),
|
||||||
|
get_level!(),
|
||||||
|
query![get_var_in_query!(perm_v!(2), 3)],
|
||||||
|
unify!(),
|
||||||
|
cut!(non_terminal!()),
|
||||||
|
query![put_value!(perm_v!(2), 1)],
|
||||||
|
deallocate!(),
|
||||||
|
execute_n!(1),
|
||||||
|
trust_me!(),
|
||||||
|
unwind_stack!(),
|
||||||
|
try_me_else!(8), // end_block/2, 42.
|
||||||
|
allocate!(1),
|
||||||
|
query![get_var_in_query!(perm_v!(1), 1),
|
||||||
|
put_value!(temp_v!(2), 1)],
|
||||||
|
clean_up_block!(),
|
||||||
|
query![put_value!(perm_v!(1), 1)],
|
||||||
|
deallocate!(),
|
||||||
|
reset_block!(),
|
||||||
|
proceed!(),
|
||||||
|
trust_me!(),
|
||||||
|
allocate!(0),
|
||||||
|
query![get_var_in_query!(temp_v!(3), 1),
|
||||||
|
put_value!(temp_v!(2), 1)],
|
||||||
|
reset_block!(),
|
||||||
|
deallocate!(),
|
||||||
|
goto!(58, 0), // goto false.
|
||||||
|
set_ball!(), // throw/1, 56.
|
||||||
|
unwind_stack!(),
|
||||||
|
fail!(), // false/0, 58.
|
||||||
|
proceed!(),
|
||||||
|
try_me_else!(7), // not/1, 60.
|
||||||
|
allocate!(1),
|
||||||
|
get_level!(),
|
||||||
|
call_n!(1),
|
||||||
|
cut!(non_terminal!()),
|
||||||
|
deallocate!(),
|
||||||
|
goto!(58, 0), // goto false.
|
||||||
|
trust_me!(),
|
||||||
|
proceed!(),
|
||||||
|
copy_term!(), // copy_term/2, 69.
|
||||||
|
proceed!()]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_code_dir() -> (Code, CodeDir) {
|
||||||
|
let mut code_dir = HashMap::new();
|
||||||
|
let builtin_code = get_builtins();
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
for arity in 0 .. 63 {
|
||||||
|
code_dir.insert((String::from("call"), arity), (PredicateKeyType::BuiltIn, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
code_dir.insert((String::from("atomic"), 1), (PredicateKeyType::BuiltIn, 1));
|
||||||
|
code_dir.insert((String::from("var"), 1), (PredicateKeyType::BuiltIn, 3));
|
||||||
|
code_dir.insert((String::from("false"), 0), (PredicateKeyType::BuiltIn, 58));
|
||||||
|
code_dir.insert((String::from("not"), 1), (PredicateKeyType::BuiltIn, 60));
|
||||||
|
code_dir.insert((String::from("copy_term"), 2), (PredicateKeyType::BuiltIn, 69));
|
||||||
|
code_dir.insert((String::from("catch"), 3), (PredicateKeyType::BuiltIn, 5));
|
||||||
|
code_dir.insert((String::from("throw"), 1), (PredicateKeyType::BuiltIn, 56));
|
||||||
|
|
||||||
|
(builtin_code, code_dir)
|
||||||
|
}
|
||||||
@@ -21,6 +21,34 @@ pub enum EvalSession<'a> {
|
|||||||
SubsequentQuerySuccess,
|
SubsequentQuerySuccess,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ConjunctInfo<'a> {
|
||||||
|
pub perm_vs: VariableFixtures<'a>,
|
||||||
|
pub num_of_chunks: usize,
|
||||||
|
pub has_deep_cut: bool,
|
||||||
|
pub has_catch: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ConjunctInfo<'a>
|
||||||
|
{
|
||||||
|
fn new(perm_vs: VariableFixtures<'a>, num_of_chunks: usize, has_deep_cut: bool, has_catch: bool)
|
||||||
|
-> Self
|
||||||
|
{
|
||||||
|
ConjunctInfo { perm_vs, num_of_chunks, has_deep_cut, has_catch }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allocates(&self) -> bool {
|
||||||
|
self.perm_vs.size() > 0 || self.num_of_chunks > 1 || self.has_deep_cut || self.has_catch
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perm_vars(&self) -> usize {
|
||||||
|
self.perm_vs.size() + self.perm_var_offset()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perm_var_offset(&self) -> usize {
|
||||||
|
self.has_deep_cut as usize + 2 * (self.has_catch as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
||||||
{
|
{
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@@ -47,7 +75,6 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
|||||||
*self.var_count.get(var).unwrap()
|
*self.var_count.get(var).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn add_or_increment_void_instr<Target>(target: &mut Vec<Target>)
|
fn add_or_increment_void_instr<Target>(target: &mut Vec<Target>)
|
||||||
where Target: CompilationTarget<'a>
|
where Target: CompilationTarget<'a>
|
||||||
{
|
{
|
||||||
@@ -145,15 +172,14 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
|||||||
target
|
target
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_var_data(&mut self, iter: ChunkedIterator<'a>) -> (VariableFixtures<'a>, bool)
|
fn collect_var_data(&mut self, mut iter: ChunkedIterator<'a>) -> ConjunctInfo<'a>
|
||||||
{
|
{
|
||||||
let mut vs = VariableFixtures::new();
|
let mut vs = VariableFixtures::new();
|
||||||
let has_deep_cut = iter.contains_deep_cut();
|
let at_rule_head = iter.at_rule_head();
|
||||||
let has_head = iter.at_head();
|
|
||||||
|
|
||||||
for (chunk_num, (last_term_arity, terms)) in iter.enumerate() {
|
while let Some((chunk_num, last_term_arity, terms)) = iter.next() {
|
||||||
for (i, term_or_cut_ref) in terms.iter().enumerate() {
|
for (i, term_or_cut_ref) in terms.iter().enumerate() {
|
||||||
let term_loc = if chunk_num == 0 && i == 0 && has_head {
|
let term_loc = if chunk_num == 0 && i == 0 && at_rule_head {
|
||||||
GenContext::Head
|
GenContext::Head
|
||||||
} else if i < terms.len() - 1 {
|
} else if i < terms.len() - 1 {
|
||||||
GenContext::Mid(chunk_num)
|
GenContext::Mid(chunk_num)
|
||||||
@@ -169,15 +195,21 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vs.populate_restricting_sets();
|
let num_of_chunks = iter.chunk_num();
|
||||||
vs.set_perm_vals(has_deep_cut);
|
let has_deep_cut = iter.encountered_deep_cut();
|
||||||
|
let has_catch = iter.encountered_catch();
|
||||||
|
|
||||||
(vs, has_deep_cut)
|
vs.populate_restricting_sets();
|
||||||
|
vs.set_perm_vals(has_deep_cut, has_catch);
|
||||||
|
|
||||||
|
let vs = self.marker.drain_var_data(vs);
|
||||||
|
|
||||||
|
ConjunctInfo::new(vs, num_of_chunks, has_deep_cut, has_catch)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_conditional_call(compiled_query: &mut Code, qt: QueryTermRef, pvs: usize)
|
fn add_conditional_call(compiled_query: &mut Code, qt: QueryTermRef, pvs: usize)
|
||||||
{
|
{
|
||||||
match qt {
|
match qt {
|
||||||
QueryTermRef::CallN(terms) => {
|
QueryTermRef::CallN(terms) => {
|
||||||
let call = ControlInstruction::CallN(terms.len());
|
let call = ControlInstruction::CallN(terms.len());
|
||||||
compiled_query.push(Line::Control(call));
|
compiled_query.push(Line::Control(call));
|
||||||
@@ -194,18 +226,18 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lco(body: &mut Code, toc: &QueryTerm) -> usize
|
fn lco(body: &mut Code, toc: QueryTermRef<'a>) -> usize
|
||||||
{
|
{
|
||||||
let last_arity = toc.arity();
|
let last_arity = toc.arity();
|
||||||
let mut dealloc_index = body.len() - 1;
|
let mut dealloc_index = body.len() - 1;
|
||||||
|
|
||||||
match toc {
|
match toc {
|
||||||
&QueryTerm::Term(Term::Clause(_, ref name, _))
|
QueryTermRef::Term(&Term::Clause(_, ref name, _))
|
||||||
| &QueryTerm::Term(Term::Constant(_, Constant::Atom(ref name))) =>
|
| QueryTermRef::Term(&Term::Constant(_, Constant::Atom(ref name))) =>
|
||||||
if let &mut Line::Control(ref mut ctrl) = body.last_mut().unwrap() {
|
if let &mut Line::Control(ref mut ctrl) = body.last_mut().unwrap() {
|
||||||
*ctrl = ControlInstruction::Execute(name.clone(), last_arity);
|
*ctrl = ControlInstruction::Execute(name.clone(), last_arity);
|
||||||
},
|
},
|
||||||
&QueryTerm::CallN(ref terms) =>
|
QueryTermRef::CallN(terms) =>
|
||||||
if let &mut Line::Control(ref mut ctrl) = body.last_mut().unwrap() {
|
if let &mut Line::Control(ref mut ctrl) = body.last_mut().unwrap() {
|
||||||
*ctrl = ControlInstruction::ExecuteN(terms.len());
|
*ctrl = ControlInstruction::ExecuteN(terms.len());
|
||||||
},
|
},
|
||||||
@@ -216,102 +248,71 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn compile_seq(&mut self,
|
fn compile_seq(&mut self,
|
||||||
clauses: &'a [QueryTerm],
|
iter: ChunkedIterator<'a>,
|
||||||
vs: &VariableFixtures<'a>,
|
conjunct_info: &ConjunctInfo<'a>,
|
||||||
body: &mut Code,
|
body: &mut Code,
|
||||||
is_exposed: bool)
|
is_exposed: bool)
|
||||||
{
|
{
|
||||||
let iter = ChunkedIterator::from_term_sequence(clauses);
|
for (chunk_num, _, terms) in iter {
|
||||||
|
for (i, term) in terms.iter().enumerate()
|
||||||
for (chunk_num, (_, terms)) in iter.enumerate() {
|
{
|
||||||
self.marker.reset_contents();
|
|
||||||
|
|
||||||
for (i, term) in terms.iter().enumerate() {
|
|
||||||
let term_loc = if i + 1 < terms.len() {
|
let term_loc = if i + 1 < terms.len() {
|
||||||
GenContext::Mid(chunk_num)
|
GenContext::Mid(chunk_num)
|
||||||
} else {
|
} else {
|
||||||
GenContext::Last(chunk_num)
|
GenContext::Last(chunk_num)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut body_appendage = match term {
|
match term {
|
||||||
&QueryTermRef::Cut if i + 1 < terms.len() =>
|
&QueryTermRef::Cut if i + 1 < terms.len() => {
|
||||||
vec![Line::Cut(CutInstruction::Cut(Terminal::Non))],
|
body.push(if chunk_num == 0 {
|
||||||
&QueryTermRef::Cut =>
|
Line::Cut(CutInstruction::NeckCut(Terminal::Non))
|
||||||
vec![Line::Cut(CutInstruction::Cut(Terminal::Terminal))],
|
} else {
|
||||||
|
Line::Cut(CutInstruction::Cut(Terminal::Non))
|
||||||
|
});
|
||||||
|
},
|
||||||
|
&QueryTermRef::Cut => {
|
||||||
|
body.push(if chunk_num == 0 {
|
||||||
|
Line::Cut(CutInstruction::NeckCut(Terminal::Terminal))
|
||||||
|
} else {
|
||||||
|
Line::Cut(CutInstruction::Cut(Terminal::Terminal))
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_ if chunk_num == 0 => {
|
||||||
|
self.marker.advance(GenContext::Head, *term);
|
||||||
|
|
||||||
|
let iter = term.post_order_iter();
|
||||||
|
body.push(Line::Query(self.compile_target(iter, term_loc, is_exposed)));
|
||||||
|
Self::add_conditional_call(body, *term, conjunct_info.perm_vars());
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let num_vars = vs.vars_above_threshold(i + 1);
|
let num_vars = conjunct_info.perm_vs.vars_above_threshold(i + 1);
|
||||||
self.compile_query_line(*term, term_loc, num_vars, is_exposed)
|
self.compile_query_line(*term, term_loc, body, num_vars, is_exposed);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
body.append(&mut body_appendage);
|
self.marker.reset_contents();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_seq_prelude(&mut self,
|
fn compile_seq_prelude(&mut self, conjunct_info: &ConjunctInfo, body: &mut Code)
|
||||||
num_clauses: usize,
|
|
||||||
vs: &VariableFixtures<'a>,
|
|
||||||
deep_cuts: bool,
|
|
||||||
body: &mut Code)
|
|
||||||
-> usize
|
|
||||||
{
|
{
|
||||||
if num_clauses > 0 {
|
if conjunct_info.allocates() {
|
||||||
let perm_vars = vs.vars_above_threshold(0) + deep_cuts as usize;
|
let perm_vars = conjunct_info.perm_vars();
|
||||||
|
|
||||||
body.push(Line::Control(ControlInstruction::Allocate(perm_vars)));
|
body.push(Line::Control(ControlInstruction::Allocate(perm_vars)));
|
||||||
|
|
||||||
if deep_cuts {
|
if conjunct_info.has_deep_cut {
|
||||||
body.push(Line::Cut(CutInstruction::GetLevel));
|
body.push(Line::Cut(CutInstruction::GetLevel));
|
||||||
}
|
}
|
||||||
|
|
||||||
return perm_vars;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_neck_cut_or(&mut self,
|
fn compile_cleanup(body: &mut Code, conjunct_info: &ConjunctInfo, toc: QueryTermRef<'a>)
|
||||||
p1: &'a QueryTerm,
|
|
||||||
body: &mut Code,
|
|
||||||
perm_vars: usize,
|
|
||||||
is_exposed: bool,
|
|
||||||
at_end: bool)
|
|
||||||
{
|
|
||||||
match p1 {
|
|
||||||
&QueryTerm::Cut => {
|
|
||||||
let term = if at_end {
|
|
||||||
Terminal::Terminal
|
|
||||||
} else {
|
|
||||||
Terminal::Non
|
|
||||||
};
|
|
||||||
|
|
||||||
body.push(Line::Cut(CutInstruction::NeckCut(term)));
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
let p1 = p1.to_ref();
|
|
||||||
|
|
||||||
self.marker.advance(GenContext::Head, p1);
|
|
||||||
|
|
||||||
let term_loc = if p1.is_callable() {
|
|
||||||
GenContext::Last(0)
|
|
||||||
} else {
|
|
||||||
GenContext::Mid(0)
|
|
||||||
};
|
|
||||||
|
|
||||||
let iter = p1.post_order_iter();
|
|
||||||
body.push(Line::Query(self.compile_target(iter, term_loc, is_exposed)));
|
|
||||||
|
|
||||||
Self::add_conditional_call(body, p1, perm_vars);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compile_cleanup(body: &mut Code, num_clauses: usize, toc: &QueryTerm)
|
|
||||||
{
|
{
|
||||||
let dealloc_index = Self::lco(body, toc);
|
let dealloc_index = Self::lco(body, toc);
|
||||||
|
|
||||||
if num_clauses > 0 {
|
if conjunct_info.allocates() {
|
||||||
body.insert(dealloc_index, Line::Control(ControlInstruction::Deallocate));
|
body.insert(dealloc_index, Line::Control(ControlInstruction::Deallocate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -319,25 +320,23 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
|||||||
pub fn compile_rule<'b: 'a>(&mut self, rule: &'b Rule) -> Code
|
pub fn compile_rule<'b: 'a>(&mut self, rule: &'b Rule) -> Code
|
||||||
{
|
{
|
||||||
let iter = ChunkedIterator::from_rule(rule);
|
let iter = ChunkedIterator::from_rule(rule);
|
||||||
let (mut vs, deep_cuts) = self.collect_var_data(iter);
|
let conjunct_info = self.collect_var_data(iter);
|
||||||
vs = self.marker.drain_var_data(vs);
|
|
||||||
|
|
||||||
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
|
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
|
||||||
let mut code = Vec::new();
|
let mut code = Vec::new();
|
||||||
|
|
||||||
self.marker.advance(GenContext::Head, QueryTermRef::Term(p0));
|
self.marker.advance(GenContext::Head, QueryTermRef::Term(p0));
|
||||||
|
self.compile_seq_prelude(&conjunct_info, &mut code);
|
||||||
let perm_vars = self.compile_seq_prelude(clauses.len(), &vs, deep_cuts, &mut code);
|
|
||||||
|
|
||||||
if p0.is_clause() {
|
if p0.is_clause() {
|
||||||
let iter = FactInstruction::iter(p0);
|
let iter = FactInstruction::iter(p0);
|
||||||
code.push(Line::Fact(self.compile_target(iter, GenContext::Head, false)));
|
code.push(Line::Fact(self.compile_target(iter, GenContext::Head, false)));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.compile_neck_cut_or(p1, &mut code, perm_vars, false, clauses.len() == 0);
|
let iter = ChunkedIterator::from_rule_body(p1, clauses);
|
||||||
self.compile_seq(clauses, &vs, &mut code, false);
|
self.compile_seq(iter, &conjunct_info, &mut code, false);
|
||||||
|
|
||||||
if perm_vars > 0 {
|
if conjunct_info.allocates() {
|
||||||
let index = if let &Line::Control(_) = code.last().unwrap() {
|
let index = if let &Line::Control(_) = code.last().unwrap() {
|
||||||
code.len() - 2
|
code.len() - 2
|
||||||
} else {
|
} else {
|
||||||
@@ -345,12 +344,11 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let &mut Line::Query(ref mut query) = &mut code[index] {
|
if let &mut Line::Query(ref mut query) = &mut code[index] {
|
||||||
vs.mark_unsafe_vars_in_rule(p0, query);
|
conjunct_info.perm_vs.mark_unsafe_vars_in_rule(p0, query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::compile_cleanup(&mut code, clauses.len(), clauses.last().unwrap_or(p1));
|
Self::compile_cleanup(&mut code, &conjunct_info, clauses.last().unwrap_or(p1).to_ref());
|
||||||
|
|
||||||
code
|
code
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,10 +381,9 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
|||||||
|
|
||||||
pub fn compile_fact<'b: 'a>(&mut self, term: &'b Term) -> Code
|
pub fn compile_fact<'b: 'a>(&mut self, term: &'b Term) -> Code
|
||||||
{
|
{
|
||||||
let iter = ChunkedIterator::from_term(term, true);
|
let iter = ChunkedIterator::from_fact(term);
|
||||||
let (vs, _) = self.collect_var_data(iter);
|
|
||||||
self.marker.drain_var_data(vs);
|
self.collect_var_data(iter);
|
||||||
|
|
||||||
self.marker.advance(GenContext::Head, QueryTermRef::Term(term));
|
self.marker.advance(GenContext::Head, QueryTermRef::Term(term));
|
||||||
|
|
||||||
let mut code = Vec::new();
|
let mut code = Vec::new();
|
||||||
@@ -408,42 +405,32 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
|||||||
fn compile_query_line(&mut self,
|
fn compile_query_line(&mut self,
|
||||||
term: QueryTermRef<'a>,
|
term: QueryTermRef<'a>,
|
||||||
term_loc: GenContext,
|
term_loc: GenContext,
|
||||||
|
code: &mut Code,
|
||||||
index: usize,
|
index: usize,
|
||||||
is_exposed: bool)
|
is_exposed: bool)
|
||||||
-> Code
|
|
||||||
{
|
{
|
||||||
self.marker.advance(term_loc, term);
|
self.marker.advance(term_loc, term);
|
||||||
|
|
||||||
let mut code = Vec::new();
|
|
||||||
|
|
||||||
let iter = term.post_order_iter();
|
let iter = term.post_order_iter();
|
||||||
let compiled_query = Line::Query(self.compile_target(iter, term_loc, is_exposed));
|
let compiled_query = Line::Query(self.compile_target(iter, term_loc, is_exposed));
|
||||||
|
|
||||||
code.push(compiled_query);
|
code.push(compiled_query);
|
||||||
|
|
||||||
Self::add_conditional_call(&mut code, term, index);
|
Self::add_conditional_call(code, term, index);
|
||||||
|
|
||||||
code
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_query(&mut self, query: &'a Vec<QueryTerm>) -> Code
|
pub fn compile_query(&mut self, query: &'a Vec<QueryTerm>) -> Code
|
||||||
{
|
{
|
||||||
let iter = ChunkedIterator::from_term_sequence(query);
|
let iter = ChunkedIterator::from_term_sequence(query);
|
||||||
let (mut vs, deep_cuts) = self.collect_var_data(iter);
|
let conjunct_info = self.collect_var_data(iter);
|
||||||
let p1 = query.first().unwrap();
|
|
||||||
|
|
||||||
vs = self.marker.drain_var_data(vs);
|
let mut code = Vec::new();
|
||||||
|
self.compile_seq_prelude(&conjunct_info, &mut code);
|
||||||
|
|
||||||
let mut code = Vec::new();
|
let iter = ChunkedIterator::from_term_sequence(query);
|
||||||
let perm_vars = self.compile_seq_prelude(query.len() - 1,
|
self.compile_seq(iter, &conjunct_info, &mut code, true);
|
||||||
&vs,
|
|
||||||
deep_cuts,
|
|
||||||
&mut code);
|
|
||||||
|
|
||||||
self.compile_neck_cut_or(p1, &mut code, perm_vars, true, query.len() == 1);
|
if conjunct_info.allocates() {
|
||||||
self.compile_seq(&query[1 .. ], &vs, &mut code, true);
|
|
||||||
|
|
||||||
if perm_vars > 0 {
|
|
||||||
let index = if let &Line::Control(_) = code.last().unwrap() {
|
let index = if let &Line::Control(_) = code.last().unwrap() {
|
||||||
code.len() - 2
|
code.len() - 2
|
||||||
} else {
|
} else {
|
||||||
@@ -451,12 +438,11 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let &mut Line::Query(ref mut query) = &mut code[index] {
|
if let &mut Line::Query(ref mut query) = &mut code[index] {
|
||||||
vs.mark_unsafe_vars_in_query(query);
|
conjunct_info.perm_vs.mark_unsafe_vars_in_query(query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::compile_cleanup(&mut code, query.len() - 1, query.last().unwrap());
|
Self::compile_cleanup(&mut code, &conjunct_info, query.last().unwrap().to_ref());
|
||||||
|
|
||||||
code
|
code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -214,7 +214,11 @@ impl<'a> VariableFixtures<'a>
|
|||||||
self.0.values()
|
self.0.values()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_perm_vals(&self, has_deep_cuts: bool)
|
pub fn size(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_perm_vals(&self, has_deep_cuts: bool, has_catch: bool)
|
||||||
{
|
{
|
||||||
let mut values_vec : Vec<_> = self.values()
|
let mut values_vec : Vec<_> = self.values()
|
||||||
.filter_map(|ref v| {
|
.filter_map(|ref v| {
|
||||||
@@ -227,7 +231,7 @@ impl<'a> VariableFixtures<'a>
|
|||||||
|
|
||||||
values_vec.sort_by_key(|ref v| v.0);
|
values_vec.sort_by_key(|ref v| v.0);
|
||||||
|
|
||||||
let offset = has_deep_cuts as usize;
|
let offset = has_deep_cuts as usize + 2 * has_catch as usize;
|
||||||
|
|
||||||
for (i, (_, cells)) in values_vec.into_iter().rev().enumerate() {
|
for (i, (_, cells)) in values_vec.into_iter().rev().enumerate() {
|
||||||
for cell in cells {
|
for cell in cells {
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ impl fmt::Display for Constant {
|
|||||||
&Constant::Atom(ref atom) =>
|
&Constant::Atom(ref atom) =>
|
||||||
write!(f, "{}", atom),
|
write!(f, "{}", atom),
|
||||||
&Constant::EmptyList =>
|
&Constant::EmptyList =>
|
||||||
write!(f, "[]")
|
write!(f, "[]"),
|
||||||
|
&Constant::UInt64(integer) =>
|
||||||
|
write!(f, "u{}", integer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,27 +199,70 @@ impl fmt::Display for RegType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Wait until constexprs are supported in stable before trying to
|
||||||
|
// switch to this.
|
||||||
|
|
||||||
|
struct ClauseRewriter { field: fn(&mut Vec<Box<Term>>) -> QueryTerm }
|
||||||
|
|
||||||
|
impl Clone for ClauseRewriter {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
ClauseRewriter { field: self.field }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ClauseRewriters {
|
||||||
|
rewriter_map: HashMap<&'static str, ClauseRewriter>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClauseRewriters {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut rewriter_map =
|
||||||
|
[("call", ClauseRewriter { field: rewrite_call_N })]//,
|
||||||
|
//("catch", rewrite_catch),
|
||||||
|
//("throw", rewrite_throw)]
|
||||||
|
.iter().cloned().collect();
|
||||||
|
|
||||||
|
ClauseRewriters { rewriter_map: rewriter_map }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, name: &str) -> Option<&ClauseRewriter> {
|
||||||
|
self.rewriter_map.get(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
fn rewrite_call_n(terms: &mut Vec<Box<Term>>) -> QueryTerm {
|
||||||
|
let mut new_terms = Vec::with_capacity(0);
|
||||||
|
swap(&mut new_terms, terms);
|
||||||
|
|
||||||
|
QueryTerm::CallN(new_terms)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rewrite_clause(name: &Atom, terms: &mut Vec<Box<Term>>) -> Option<QueryTerm>
|
||||||
|
{
|
||||||
|
if name == "call" {
|
||||||
|
Some(rewrite_call_n(terms))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_code(input: &str) -> Option<TopLevel>
|
pub fn parse_code(input: &str) -> Option<TopLevel>
|
||||||
{
|
{
|
||||||
match parse_TopLevel(input) {
|
match parse_TopLevel(input) {
|
||||||
Ok(mut tl) => {
|
Ok(mut tl) => {
|
||||||
for query in tl.query_iter_mut() {
|
for query in tl.query_iter_mut() {
|
||||||
let cts = match query {
|
let new_query = match query {
|
||||||
&mut QueryTerm::Term(Term::Clause(_, ref name, ref mut cts)) => {
|
&mut QueryTerm::Term(Term::Clause(_, ref name, ref mut cts)) =>
|
||||||
if name == "call" {
|
rewrite_clause(name, cts),
|
||||||
let mut new_cts = Vec::with_capacity(0);
|
&mut QueryTerm::Term(Term::Var(_, _)) =>
|
||||||
swap(&mut new_cts, cts);
|
Some(QueryTerm::CallN(Vec::new())),
|
||||||
|
|
||||||
Some(new_cts)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => None
|
_ => None
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(cts) = cts {
|
if let Some(mut new_query) = new_query {
|
||||||
swap(&mut QueryTerm::CallN(cts), query);
|
swap(&mut new_query, query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,11 +289,11 @@ fn is_consistent(predicate: &Vec<PredicateClause>) -> bool {
|
|||||||
pub fn print_code(code: &Code) {
|
pub fn print_code(code: &Code) {
|
||||||
for clause in code {
|
for clause in code {
|
||||||
match clause {
|
match clause {
|
||||||
&Line::BuiltIn(_) => {},
|
|
||||||
&Line::Fact(ref fact) =>
|
&Line::Fact(ref fact) =>
|
||||||
for fact_instr in fact {
|
for fact_instr in fact {
|
||||||
println!("{}", fact_instr);
|
println!("{}", fact_instr);
|
||||||
},
|
},
|
||||||
|
&Line::BuiltIn(_) => {},
|
||||||
&Line::Cut(ref cut) =>
|
&Line::Cut(ref cut) =>
|
||||||
println!("{}", cut),
|
println!("{}", cut),
|
||||||
&Line::Choice(ref choice) =>
|
&Line::Choice(ref choice) =>
|
||||||
@@ -301,11 +346,11 @@ pub fn eval<'a, 'b: 'a>(wam: &'a mut Machine, tl: &'b TopLevel) -> EvalSession<'
|
|||||||
|
|
||||||
if is_consistent(clauses) {
|
if is_consistent(clauses) {
|
||||||
let compiled_pred = cg.compile_predicate(clauses);
|
let compiled_pred = cg.compile_predicate(clauses);
|
||||||
wam.add_predicate(clauses, compiled_pred)
|
wam.add_predicate(clauses, compiled_pred)
|
||||||
} else {
|
} else {
|
||||||
let msg = r"Error: predicate is inconsistent.
|
let msg = r"Error: predicate is inconsistent.
|
||||||
Each predicate must have the same name and arity.";
|
Each predicate must have the same name and arity.";
|
||||||
|
|
||||||
EvalSession::EntryFailure(String::from(msg))
|
EvalSession::EntryFailure(String::from(msg))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -53,13 +53,16 @@ impl<'a> QueryIterator<'a> {
|
|||||||
fn from_term(term: &'a Term) -> Self {
|
fn from_term(term: &'a Term) -> Self {
|
||||||
let state = match term {
|
let state = match term {
|
||||||
&Term::AnonVar =>
|
&Term::AnonVar =>
|
||||||
IteratorState::AnonVar(Level::Shallow),
|
//IteratorState::AnonVar(Level::Shallow),
|
||||||
|
return QueryIterator { state_stack: vec![] },
|
||||||
&Term::Clause(_, _, ref terms) =>
|
&Term::Clause(_, _, ref terms) =>
|
||||||
IteratorState::Clause(0, ClauseType::Root, terms),
|
IteratorState::Clause(0, ClauseType::Root, terms),
|
||||||
&Term::Cons(ref cell, ref head, ref tail) =>
|
&Term::Cons(_, _, _) =>
|
||||||
IteratorState::InitialCons(Level::Shallow, cell, head.as_ref(), tail.as_ref()),
|
//IteratorState::InitialCons(Level::Shallow, cell, head.as_ref(), tail.as_ref()),
|
||||||
&Term::Constant(ref cell, ref constant) =>
|
return QueryIterator { state_stack: vec![] },
|
||||||
IteratorState::Constant(Level::Shallow, cell, constant),
|
&Term::Constant(_, _) =>
|
||||||
|
//IteratorState::Constant(Level::Shallow, cell, constant),
|
||||||
|
return QueryIterator { state_stack: vec![] },
|
||||||
&Term::Var(ref cell, ref var) =>
|
&Term::Var(ref cell, ref var) =>
|
||||||
IteratorState::Var(Level::Shallow, cell, var)
|
IteratorState::Var(Level::Shallow, cell, var)
|
||||||
};
|
};
|
||||||
@@ -169,7 +172,7 @@ impl<'a> Iterator for FactIterator<'a> {
|
|||||||
self.push_subterm(ct.level_of_subterms(), child_term);
|
self.push_subterm(ct.level_of_subterms(), child_term);
|
||||||
}
|
}
|
||||||
|
|
||||||
match ct {
|
match ct {
|
||||||
ClauseType::Deep(_, _, _) =>
|
ClauseType::Deep(_, _, _) =>
|
||||||
return Some(TermRef::Clause(ct, child_terms)),
|
return Some(TermRef::Clause(ct, child_terms)),
|
||||||
_ =>
|
_ =>
|
||||||
@@ -206,22 +209,24 @@ impl Term {
|
|||||||
|
|
||||||
pub struct ChunkedIterator<'a>
|
pub struct ChunkedIterator<'a>
|
||||||
{
|
{
|
||||||
at_head: bool,
|
term_loc: GenContext,
|
||||||
iter: Box<Iterator<Item=QueryTermRef<'a>> + 'a>,
|
iter: Box<Iterator<Item=QueryTermRef<'a>> + 'a>,
|
||||||
deep_cut_encountered: bool
|
deep_cut_encountered: bool,
|
||||||
|
catch_encountered: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ChunkedIterator<'a>
|
impl<'a> ChunkedIterator<'a>
|
||||||
{
|
{
|
||||||
pub fn from_term(term: &'a Term, at_head: bool) -> Self
|
pub fn from_fact(term: &'a Term) -> Self
|
||||||
{
|
{
|
||||||
let inner_iter: Box<Iterator<Item=QueryTermRef<'a>>> =
|
let inner_iter: Box<Iterator<Item=QueryTermRef<'a>>> =
|
||||||
Box::new(once(QueryTermRef::Term(term)));
|
Box::new(once(QueryTermRef::Term(term)));
|
||||||
|
|
||||||
ChunkedIterator {
|
ChunkedIterator {
|
||||||
at_head: at_head,
|
term_loc: GenContext::Head,
|
||||||
iter: inner_iter,
|
iter: inner_iter,
|
||||||
deep_cut_encountered: false
|
deep_cut_encountered: false,
|
||||||
|
catch_encountered: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,9 +235,35 @@ impl<'a> ChunkedIterator<'a>
|
|||||||
let iter = terms.iter().map(|c| c.to_ref());
|
let iter = terms.iter().map(|c| c.to_ref());
|
||||||
|
|
||||||
ChunkedIterator {
|
ChunkedIterator {
|
||||||
at_head: false,
|
term_loc: GenContext::Last(0),
|
||||||
iter: Box::new(iter),
|
iter: Box::new(iter),
|
||||||
deep_cut_encountered: false
|
deep_cut_encountered: false,
|
||||||
|
catch_encountered: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iterate_over_query_term(p1: &'a QueryTerm) -> Box<Iterator<Item=QueryTermRef<'a>> + 'a>
|
||||||
|
{
|
||||||
|
match p1 {
|
||||||
|
&QueryTerm::CallN(ref child_terms) =>
|
||||||
|
Box::new(once(QueryTermRef::CallN(child_terms))),
|
||||||
|
&QueryTerm::Term(ref p1) =>
|
||||||
|
Box::new(once(QueryTermRef::Term(p1))),
|
||||||
|
&QueryTerm::Cut =>
|
||||||
|
Box::new(once(QueryTermRef::Cut))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_rule_body(p1: &'a QueryTerm, clauses: &'a Vec<QueryTerm>) -> Self
|
||||||
|
{
|
||||||
|
let inner_iter = Self::iterate_over_query_term(p1);
|
||||||
|
let iter = inner_iter.chain(clauses.iter().map(|c| c.to_ref()));
|
||||||
|
|
||||||
|
ChunkedIterator {
|
||||||
|
term_loc: GenContext::Last(0),
|
||||||
|
iter: Box::new(iter),
|
||||||
|
deep_cut_encountered: false,
|
||||||
|
catch_encountered: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,82 +272,87 @@ impl<'a> ChunkedIterator<'a>
|
|||||||
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
|
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
|
||||||
let iter = once(QueryTermRef::Term(p0));
|
let iter = once(QueryTermRef::Term(p0));
|
||||||
|
|
||||||
let inner_iter : Box<Iterator<Item=QueryTermRef<'a>>> = match p1 {
|
let inner_iter = Self::iterate_over_query_term(p1);
|
||||||
&QueryTerm::CallN(ref child_terms) =>
|
|
||||||
Box::new(once(QueryTermRef::CallN(child_terms))),
|
|
||||||
&QueryTerm::Term(ref p1) =>
|
|
||||||
Box::new(once(QueryTermRef::Term(p1))),
|
|
||||||
_ => Box::new(empty())
|
|
||||||
};
|
|
||||||
|
|
||||||
let iter = iter.chain(inner_iter.chain(clauses.iter().map(|c| c.to_ref())));
|
let iter = iter.chain(inner_iter.chain(clauses.iter().map(|c| c.to_ref())));
|
||||||
|
|
||||||
ChunkedIterator {
|
ChunkedIterator {
|
||||||
at_head: true,
|
term_loc: GenContext::Head,
|
||||||
iter: Box::new(iter),
|
iter: Box::new(iter),
|
||||||
deep_cut_encountered: false
|
deep_cut_encountered: false,
|
||||||
|
catch_encountered: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains_deep_cut(&self) -> bool {
|
pub fn encountered_deep_cut(&self) -> bool {
|
||||||
self.deep_cut_encountered
|
self.deep_cut_encountered
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn at_head(&self) -> bool {
|
pub fn encountered_catch(&self) -> bool {
|
||||||
self.at_head
|
self.catch_encountered
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_chunk(&mut self, term: QueryTermRef<'a>) -> (usize, Vec<QueryTermRef<'a>>)
|
pub fn at_rule_head(&self) -> bool {
|
||||||
{
|
self.term_loc == GenContext::Head
|
||||||
let mut result = vec![term];
|
}
|
||||||
let mut arity = 0;
|
|
||||||
|
|
||||||
while let Some(term) = self.iter.next() {
|
pub fn chunk_num(&self) -> usize {
|
||||||
|
self.term_loc.chunk_num()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_chunk(&mut self, term: QueryTermRef<'a>, mut result: Vec<QueryTermRef<'a>>)
|
||||||
|
-> (usize, usize, Vec<QueryTermRef<'a>>)
|
||||||
|
{
|
||||||
|
let mut arity = 0;
|
||||||
|
let mut item = Some(term);
|
||||||
|
|
||||||
|
while let Some(term) = item {
|
||||||
match term {
|
match term {
|
||||||
QueryTermRef::Term(inner_term) => {
|
QueryTermRef::Term(inner_term) => {
|
||||||
result.push(term);
|
if let GenContext::Head = self.term_loc {
|
||||||
|
result.push(term);
|
||||||
|
self.term_loc = GenContext::Last(0);
|
||||||
|
} else {
|
||||||
|
result.push(term);
|
||||||
|
|
||||||
if inner_term.is_callable() {
|
if inner_term.is_callable() {
|
||||||
arity = inner_term.arity();
|
arity = inner_term.arity();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
QueryTermRef::CallN(child_terms) => {
|
QueryTermRef::CallN(child_terms) => {
|
||||||
result.push(term);
|
result.push(term);
|
||||||
arity = child_terms.len() + 1;
|
arity = child_terms.len() + 1;
|
||||||
break;
|
break;
|
||||||
},
|
},
|
||||||
_ => {
|
QueryTermRef::Cut => {
|
||||||
result.push(term);
|
result.push(term);
|
||||||
self.deep_cut_encountered = true;
|
|
||||||
}
|
if self.term_loc.chunk_num() > 0 {
|
||||||
|
self.deep_cut_encountered = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
item = self.iter.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
(arity, result)
|
let chunk_num = self.term_loc.chunk_num();
|
||||||
|
|
||||||
|
if let &mut GenContext::Last(ref mut chunk_num) = &mut self.term_loc {
|
||||||
|
*chunk_num += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
(chunk_num, arity, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for ChunkedIterator<'a>
|
impl<'a> Iterator for ChunkedIterator<'a>
|
||||||
{
|
{
|
||||||
// the last term arity, and the reference.
|
// the chunk number, last term arity, and vector of references.
|
||||||
type Item = (usize, Vec<QueryTermRef<'a>>);
|
type Item = (usize, usize, Vec<QueryTermRef<'a>>);
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
loop {
|
self.iter.next().map(|term| self.take_chunk(term, Vec::new()))
|
||||||
match self.iter.next() {
|
|
||||||
None => return None,
|
|
||||||
Some(QueryTermRef::Term(term)) if self.at_head => {
|
|
||||||
self.at_head = false;
|
|
||||||
return Some(self.take_chunk(QueryTermRef::Term(term)));
|
|
||||||
},
|
|
||||||
Some(QueryTermRef::Term(term)) if term.is_callable() =>
|
|
||||||
return Some((term.arity(), vec![QueryTermRef::Term(term)])),
|
|
||||||
Some(QueryTermRef::CallN(child_terms)) =>
|
|
||||||
return Some((child_terms.len() + 1, vec![QueryTermRef::CallN(child_terms)])),
|
|
||||||
Some(term_or_cut_ref) =>
|
|
||||||
return Some(self.take_chunk(term_or_cut_ref))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use prolog::ast::*;
|
use prolog::ast::*;
|
||||||
|
use prolog::builtins::*;
|
||||||
use prolog::codegen::*;
|
use prolog::codegen::*;
|
||||||
use prolog::heapview::*;
|
use prolog::heapview::*;
|
||||||
use prolog::and_stack::*;
|
use prolog::and_stack::*;
|
||||||
@@ -33,18 +34,10 @@ struct MachineState {
|
|||||||
trail: Vec<Ref>,
|
trail: Vec<Ref>,
|
||||||
tr: usize,
|
tr: usize,
|
||||||
hb: usize,
|
hb: usize,
|
||||||
|
block: usize, // an offset into the OR stack.
|
||||||
|
ball: Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
enum PredicateKeyType {
|
|
||||||
BuiltIn,
|
|
||||||
User
|
|
||||||
}
|
|
||||||
|
|
||||||
type PredicateKey = (Atom, usize); // name, arity, type.
|
|
||||||
|
|
||||||
type CodeDir = HashMap<PredicateKey, (PredicateKeyType, usize)>;
|
|
||||||
|
|
||||||
pub struct Machine {
|
pub struct Machine {
|
||||||
ms: MachineState,
|
ms: MachineState,
|
||||||
code: Code,
|
code: Code,
|
||||||
@@ -96,14 +89,7 @@ impl Index<CodePtr> for Machine {
|
|||||||
|
|
||||||
impl Machine {
|
impl Machine {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut code_dir = HashMap::new();
|
let (code, code_dir) = build_code_dir();
|
||||||
let code = vec![Line::BuiltIn(BuiltInInstruction::InternalCallN)];
|
|
||||||
|
|
||||||
// there are 64 registers in the VM, so call/N is defined for all 0 <= N <= 63
|
|
||||||
// (an extra register is needed for the predicate name)
|
|
||||||
for arity in 0 .. 64 {
|
|
||||||
code_dir.insert((String::from("call"), arity), (PredicateKeyType::BuiltIn, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
Machine {
|
Machine {
|
||||||
ms: MachineState::new(),
|
ms: MachineState::new(),
|
||||||
@@ -196,8 +182,8 @@ impl Machine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match instr {
|
match instr {
|
||||||
&Line::BuiltIn(ref builtin_instr) =>
|
&Line::BuiltIn(ref built_in_instr) =>
|
||||||
self.ms.execute_builtin_instr(&self.code_dir, builtin_instr),
|
self.ms.execute_built_in_instr(&self.code_dir, built_in_instr),
|
||||||
&Line::Choice(ref choice_instr) =>
|
&Line::Choice(ref choice_instr) =>
|
||||||
self.ms.execute_choice_instr(choice_instr),
|
self.ms.execute_choice_instr(choice_instr),
|
||||||
&Line::Cut(ref cut_instr) =>
|
&Line::Cut(ref cut_instr) =>
|
||||||
@@ -382,14 +368,20 @@ impl Machine {
|
|||||||
|
|
||||||
while let Some(view) = viewer.next() {
|
while let Some(view) = viewer.next() {
|
||||||
match view {
|
match view {
|
||||||
|
CellView::Con(&Constant::UInt64(integer)) =>
|
||||||
|
result += integer.to_string().as_str(),
|
||||||
CellView::Con(&Constant::EmptyList) =>
|
CellView::Con(&Constant::EmptyList) =>
|
||||||
result += "[]",
|
result += "[]",
|
||||||
CellView::Con(&Constant::Atom(ref atom)) =>
|
CellView::Con(&Constant::Atom(ref atom)) =>
|
||||||
result += atom.as_str(),
|
result += atom.as_str(),
|
||||||
CellView::HeapVar(cell_num) | CellView::StackVar(_, cell_num) => {
|
CellView::HeapVar(cell_num) => {
|
||||||
result += "_";
|
result += "_";
|
||||||
result += cell_num.to_string().as_str();
|
result += cell_num.to_string().as_str();
|
||||||
},
|
},
|
||||||
|
CellView::StackVar(_, cell_num) => {
|
||||||
|
result += "s_";
|
||||||
|
result += cell_num.to_string().as_str();
|
||||||
|
},
|
||||||
CellView::Str(_, ref name) =>
|
CellView::Str(_, ref name) =>
|
||||||
result += name.as_str(),
|
result += name.as_str(),
|
||||||
CellView::TToken(TToken::Bar) => {
|
CellView::TToken(TToken::Bar) => {
|
||||||
@@ -449,6 +441,8 @@ impl MachineState {
|
|||||||
trail: Vec::new(),
|
trail: Vec::new(),
|
||||||
tr: 0,
|
tr: 0,
|
||||||
hb: 0,
|
hb: 0,
|
||||||
|
block: 0,
|
||||||
|
ball: Addr::Con(Constant::UInt64(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -630,27 +624,32 @@ impl MachineState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_constant_to_var(&mut self, addr: Addr, c: &Constant) {
|
||||||
|
let addr = self.deref(addr);
|
||||||
|
|
||||||
|
match self.store(addr) {
|
||||||
|
Addr::HeapCell(hc) => {
|
||||||
|
self.heap[hc] = HeapCellValue::Con(c.clone());
|
||||||
|
self.trail(Ref::HeapCell(hc));
|
||||||
|
},
|
||||||
|
Addr::StackCell(fr, sc) => {
|
||||||
|
self.and_stack[fr][sc] = Addr::Con(c.clone());
|
||||||
|
self.trail(Ref::StackCell(fr, sc));
|
||||||
|
},
|
||||||
|
Addr::Con(c1) => {
|
||||||
|
if c1 != *c {
|
||||||
|
self.fail = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => self.fail = true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn execute_fact_instr(&mut self, instr: &FactInstruction) {
|
fn execute_fact_instr(&mut self, instr: &FactInstruction) {
|
||||||
match instr {
|
match instr {
|
||||||
&FactInstruction::GetConstant(_, ref constant, reg) => {
|
&FactInstruction::GetConstant(_, ref c, reg) => {
|
||||||
let addr = self.deref(self[reg].clone());
|
let addr = self[reg].clone();
|
||||||
|
self.write_constant_to_var(addr, c);
|
||||||
match self.store(addr) {
|
|
||||||
Addr::HeapCell(hc) => {
|
|
||||||
self.heap[hc] = HeapCellValue::Con(constant.clone());
|
|
||||||
self.trail(Ref::HeapCell(hc));
|
|
||||||
},
|
|
||||||
Addr::StackCell(fr, sc) => {
|
|
||||||
self.and_stack[fr][sc] = Addr::Con(constant.clone());
|
|
||||||
self.trail(Ref::StackCell(fr, sc));
|
|
||||||
},
|
|
||||||
Addr::Con(c) => {
|
|
||||||
if c != *constant {
|
|
||||||
self.fail = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => self.fail = true
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
&FactInstruction::GetList(_, reg) => {
|
&FactInstruction::GetList(_, reg) => {
|
||||||
let addr = self.deref(self[reg].clone());
|
let addr = self.deref(self[reg].clone());
|
||||||
@@ -722,24 +721,8 @@ impl MachineState {
|
|||||||
&FactInstruction::UnifyConstant(ref c) => {
|
&FactInstruction::UnifyConstant(ref c) => {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
MachineMode::Read => {
|
MachineMode::Read => {
|
||||||
let addr = self.deref(Addr::HeapCell(self.s));
|
let addr = Addr::HeapCell(self.s);
|
||||||
|
self.write_constant_to_var(addr, c);
|
||||||
match self.store(addr) {
|
|
||||||
Addr::HeapCell(hc) => {
|
|
||||||
self.heap[hc] = HeapCellValue::Con(c.clone());
|
|
||||||
self.trail(Ref::HeapCell(hc));
|
|
||||||
},
|
|
||||||
Addr::StackCell(fr, sc) => {
|
|
||||||
self.and_stack[fr][sc] = Addr::Con(c.clone());
|
|
||||||
self.trail(Ref::StackCell(fr, sc));
|
|
||||||
},
|
|
||||||
Addr::Con(c1) => {
|
|
||||||
if c1 != *c {
|
|
||||||
self.fail = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => self.fail = true
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
MachineMode::Write => {
|
MachineMode::Write => {
|
||||||
self.heap.push(HeapCellValue::Con(c.clone()));
|
self.heap.push(HeapCellValue::Con(c.clone()));
|
||||||
@@ -1009,7 +992,7 @@ impl MachineState {
|
|||||||
fn try_execute_predicate(&mut self, code_dir: &CodeDir, name: Atom, arity: usize)
|
fn try_execute_predicate(&mut self, code_dir: &CodeDir, name: Atom, arity: usize)
|
||||||
{
|
{
|
||||||
let compiled_tl_index = code_dir.get(&(name, arity)).map(|index| index.1);
|
let compiled_tl_index = code_dir.get(&(name, arity)).map(|index| index.1);
|
||||||
|
|
||||||
match compiled_tl_index {
|
match compiled_tl_index {
|
||||||
Some(compiled_tl_index) => {
|
Some(compiled_tl_index) => {
|
||||||
self.num_of_args = arity;
|
self.num_of_args = arity;
|
||||||
@@ -1020,6 +1003,96 @@ impl MachineState {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy_term(L1, L2) uses Cheney's algorithm to copy the term at
|
||||||
|
// L1 to L2. forwarding_terms is kept to restore the innards of L1
|
||||||
|
// after it's been copied to L2.
|
||||||
|
fn copy_term(&mut self, a: Addr)
|
||||||
|
{
|
||||||
|
let mut forward_trail = Vec::new(); // list of (Ref, HeapCellValue) items.
|
||||||
|
let mut scan = self.h;
|
||||||
|
let old_h = self.h;
|
||||||
|
|
||||||
|
self.heap.push(HeapCellValue::from(a));
|
||||||
|
self.h += 1;
|
||||||
|
|
||||||
|
while scan < self.h {
|
||||||
|
match self.heap[scan].clone() {
|
||||||
|
HeapCellValue::Con(_) | HeapCellValue::NamedStr(_, _) =>
|
||||||
|
scan += 1,
|
||||||
|
HeapCellValue::Lis(a) => {
|
||||||
|
let hcv = self.heap[a].clone();
|
||||||
|
self.heap.push(hcv);
|
||||||
|
|
||||||
|
let hcv = self.heap[a+1].clone();
|
||||||
|
self.heap.push(hcv);
|
||||||
|
|
||||||
|
self.heap[scan] = HeapCellValue::Lis(self.h);
|
||||||
|
|
||||||
|
self.h += 2;
|
||||||
|
scan += 1;
|
||||||
|
},
|
||||||
|
HeapCellValue::Ref(r) => {
|
||||||
|
let ra = Addr::from(r);
|
||||||
|
let rd = self.store(self.deref(ra.clone()));
|
||||||
|
|
||||||
|
match rd {
|
||||||
|
Addr::HeapCell(hc) if hc >= old_h => {
|
||||||
|
self.heap[scan] = HeapCellValue::Ref(Ref::HeapCell(hc));
|
||||||
|
scan += 1;
|
||||||
|
},
|
||||||
|
_ if ra == rd => {
|
||||||
|
self.heap[scan] = HeapCellValue::Ref(Ref::HeapCell(scan));
|
||||||
|
|
||||||
|
match r {
|
||||||
|
Ref::HeapCell(hc) =>
|
||||||
|
self.heap[hc] = HeapCellValue::Ref(Ref::HeapCell(scan)),
|
||||||
|
Ref::StackCell(fr, sc) =>
|
||||||
|
self.and_stack[fr][sc] = Addr::HeapCell(scan),
|
||||||
|
};
|
||||||
|
|
||||||
|
forward_trail.push((r, HeapCellValue::Ref(r)));
|
||||||
|
scan += 1;
|
||||||
|
},
|
||||||
|
_ => self.heap[scan] = HeapCellValue::from(rd)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
HeapCellValue::Str(s) => {
|
||||||
|
match self.heap[s].clone() {
|
||||||
|
HeapCellValue::NamedStr(arity, name) => {
|
||||||
|
self.heap[scan] = HeapCellValue::Str(self.h);
|
||||||
|
self.heap[s] = HeapCellValue::Str(self.h);
|
||||||
|
|
||||||
|
forward_trail.push((Ref::HeapCell(s),
|
||||||
|
HeapCellValue::NamedStr(arity, name.clone())));
|
||||||
|
|
||||||
|
self.heap.push(HeapCellValue::NamedStr(arity, name));
|
||||||
|
self.h += 1;
|
||||||
|
|
||||||
|
for i in 0 .. arity {
|
||||||
|
let hcv = self.heap[s + 1 + i].clone();
|
||||||
|
self.heap.push(hcv);
|
||||||
|
self.h += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
HeapCellValue::Str(o) =>
|
||||||
|
self.heap[scan] = HeapCellValue::Str(o),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
scan += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (r, hcv) in forward_trail {
|
||||||
|
match r {
|
||||||
|
Ref::HeapCell(hc) => self.heap[hc] = hcv,
|
||||||
|
Ref::StackCell(fr, sc) => self.and_stack[fr][sc] = hcv.as_addr(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_internal_call_n(&mut self, code_dir: &CodeDir)
|
fn handle_internal_call_n(&mut self, code_dir: &CodeDir)
|
||||||
{
|
{
|
||||||
let arity = self.num_of_args + 1;
|
let arity = self.num_of_args + 1;
|
||||||
@@ -1032,7 +1105,7 @@ impl MachineState {
|
|||||||
if arity > 1 {
|
if arity > 1 {
|
||||||
self.registers[arity - 1] = pred;
|
self.registers[arity - 1] = pred;
|
||||||
|
|
||||||
if let Some((name, arity)) = self.setup_call_n(arity - 1) {
|
if let Some((name, arity)) = self.setup_call_n(arity - 1) {
|
||||||
self.try_execute_predicate(code_dir, name, arity);
|
self.try_execute_predicate(code_dir, name, arity);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1073,6 +1146,133 @@ impl MachineState {
|
|||||||
Some((name, arity + narity - 1))
|
Some((name, arity + narity - 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_null_ball(&self) -> bool
|
||||||
|
{
|
||||||
|
if let &Addr::Con(Constant::UInt64(0)) = &self.ball {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_built_in_instr(&mut self, code_dir: &CodeDir, instr: &BuiltInInstruction)
|
||||||
|
{
|
||||||
|
match instr {
|
||||||
|
&BuiltInInstruction::CopyTerm => {
|
||||||
|
let old_h = self.h;
|
||||||
|
let a = self[temp_v!(1)].clone();
|
||||||
|
self.copy_term(a);
|
||||||
|
|
||||||
|
let a2 = self[temp_v!(2)].clone();
|
||||||
|
self.unify(Addr::HeapCell(old_h), a2);
|
||||||
|
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
&BuiltInInstruction::GetCurrentBlock => {
|
||||||
|
let c = Constant::UInt64(self.block);
|
||||||
|
let addr = self[temp_v!(1)].clone();
|
||||||
|
|
||||||
|
self.write_constant_to_var(addr, &c);
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
&BuiltInInstruction::Unify => {
|
||||||
|
let a1 = self[temp_v!(1)].clone();
|
||||||
|
let a2 = self[temp_v!(2)].clone();
|
||||||
|
|
||||||
|
self.unify(a1, a2);
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
&BuiltInInstruction::GetBall => {
|
||||||
|
let addr = self.store(self.deref(self[temp_v!(1)].clone()));
|
||||||
|
let ball = self.ball.clone();
|
||||||
|
|
||||||
|
if self.has_null_ball() {
|
||||||
|
self.fail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match addr.as_ref() {
|
||||||
|
Some(r) => {
|
||||||
|
self.bind(r, ball);
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
_ => self.fail = true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
&BuiltInInstruction::SetBall => {
|
||||||
|
self.ball = self[temp_v!(1)].clone();
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
&BuiltInInstruction::CleanUpBlock => {
|
||||||
|
let nb = self.store(self.deref(self[temp_v!(1)].clone()));
|
||||||
|
|
||||||
|
match nb {
|
||||||
|
Addr::Con(Constant::UInt64(nb)) => {
|
||||||
|
let b = self.b - 1;
|
||||||
|
|
||||||
|
if nb > 0 && self.or_stack[b].b == nb {
|
||||||
|
self.b = self.or_stack[nb - 1].b;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
_ => self.fail = true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
&BuiltInInstruction::InstallNewBlock => {
|
||||||
|
self.block = self.b;
|
||||||
|
let c = Constant::UInt64(self.block);
|
||||||
|
let addr = self[temp_v!(1)].clone();
|
||||||
|
|
||||||
|
self.write_constant_to_var(addr, &c);
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
&BuiltInInstruction::ResetBlock => {
|
||||||
|
let addr = self.deref(self[temp_v!(1)].clone());
|
||||||
|
|
||||||
|
match self.store(addr) {
|
||||||
|
Addr::Con(Constant::UInt64(b)) => {
|
||||||
|
self.block = b;
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
_ => self.fail = true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
&BuiltInInstruction::UnwindStack => {
|
||||||
|
self.b = self.block;
|
||||||
|
self.fail = true;
|
||||||
|
},
|
||||||
|
&BuiltInInstruction::IsAtomic => {
|
||||||
|
let d = self.deref(self[temp_v!(1)].clone());
|
||||||
|
|
||||||
|
match d {
|
||||||
|
Addr::Con(_) => self.p += 1,
|
||||||
|
_ => self.fail = true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
&BuiltInInstruction::IsVar => {
|
||||||
|
let d = self.deref(self[temp_v!(1)].clone());
|
||||||
|
|
||||||
|
match d {
|
||||||
|
Addr::HeapCell(_) | Addr::StackCell(_,_) =>
|
||||||
|
self.p += 1,
|
||||||
|
_ => self.fail = true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
&BuiltInInstruction::InternalCallN =>
|
||||||
|
self.handle_internal_call_n(code_dir),
|
||||||
|
&BuiltInInstruction::Fail => {
|
||||||
|
self.fail = true;
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
&BuiltInInstruction::Goto(p, arity) => {
|
||||||
|
self.num_of_args = arity;
|
||||||
|
self.b0 = self.b;
|
||||||
|
self.p = CodePtr::DirEntry(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction)
|
fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction)
|
||||||
{
|
{
|
||||||
match instr {
|
match instr {
|
||||||
@@ -1195,13 +1395,6 @@ impl MachineState {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_builtin_instr(&mut self, code_dir: &CodeDir, instr: &BuiltInInstruction)
|
|
||||||
{
|
|
||||||
match instr {
|
|
||||||
&BuiltInInstruction::InternalCallN => self.handle_internal_call_n(code_dir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute_choice_instr(&mut self, instr: &ChoiceInstruction)
|
fn execute_choice_instr(&mut self, instr: &ChoiceInstruction)
|
||||||
{
|
{
|
||||||
match instr {
|
match instr {
|
||||||
@@ -1220,7 +1413,7 @@ impl MachineState {
|
|||||||
self.num_of_args);
|
self.num_of_args);
|
||||||
|
|
||||||
self.b = self.or_stack.len();
|
self.b = self.or_stack.len();
|
||||||
let b = self.b - 1;
|
let b = self.b - 1;
|
||||||
|
|
||||||
for i in 1 .. n + 1 {
|
for i in 1 .. n + 1 {
|
||||||
self.or_stack[b][i] = self.registers[i].clone();
|
self.or_stack[b][i] = self.registers[i].clone();
|
||||||
|
|||||||
193
src/prolog/macros.rs
Normal file
193
src/prolog/macros.rs
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
macro_rules! internal_call_n {
|
||||||
|
() => (
|
||||||
|
Line::BuiltIn(BuiltInInstruction::InternalCallN)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! allocate {
|
||||||
|
($cells:expr) => (
|
||||||
|
Line::Control(ControlInstruction::Allocate($cells))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! deallocate {
|
||||||
|
() => (
|
||||||
|
Line::Control(ControlInstruction::Deallocate)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! query {
|
||||||
|
[$($x:expr),+] => (
|
||||||
|
Line::Query(vec![$($x),+])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! temp_v {
|
||||||
|
($x:expr) => (
|
||||||
|
RegType::Temp($x)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! perm_v {
|
||||||
|
($x:expr) => (
|
||||||
|
RegType::Perm($x)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! get_var_in_query {
|
||||||
|
($r:expr, $arg:expr) => (
|
||||||
|
QueryInstruction::GetVariable($r, $arg)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! put_var {
|
||||||
|
($r:expr, $arg:expr) => (
|
||||||
|
QueryInstruction::PutVariable($r, $arg)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! put_value {
|
||||||
|
($r:expr, $arg:expr) => (
|
||||||
|
QueryInstruction::PutValue($r, $arg)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! put_unsafe_value {
|
||||||
|
($r:expr, $arg:expr) => (
|
||||||
|
QueryInstruction::PutUnsafeValue($r, $arg)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! try_me_else {
|
||||||
|
($o:expr) => (
|
||||||
|
Line::Choice(ChoiceInstruction::TryMeElse($o))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! is_atomic {
|
||||||
|
() => (
|
||||||
|
Line::BuiltIn(BuiltInInstruction::IsAtomic)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! is_var {
|
||||||
|
() => (
|
||||||
|
Line::BuiltIn(BuiltInInstruction::IsVar)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
macro_rules! retry_me_else {
|
||||||
|
($o:expr) => (
|
||||||
|
Line::Choice(ChoiceInstruction::RetryMeElse($o))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
macro_rules! trust_me {
|
||||||
|
() => (
|
||||||
|
Line::Choice(ChoiceInstruction::TrustMe)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! call_n {
|
||||||
|
($arity:expr) => (
|
||||||
|
Line::Control(ControlInstruction::CallN($arity))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! execute_n {
|
||||||
|
($arity:expr) => (
|
||||||
|
Line::Control(ControlInstruction::ExecuteN($arity))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! proceed {
|
||||||
|
() => (
|
||||||
|
Line::Control(ControlInstruction::Proceed)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! non_terminal {
|
||||||
|
() => (
|
||||||
|
Terminal::Non
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! cut {
|
||||||
|
($term:expr) => (
|
||||||
|
Line::Cut(CutInstruction::Cut($term))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! get_current_block {
|
||||||
|
() => (
|
||||||
|
Line::BuiltIn(BuiltInInstruction::GetCurrentBlock)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! install_new_block {
|
||||||
|
() => (
|
||||||
|
Line::BuiltIn(BuiltInInstruction::InstallNewBlock)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! goto {
|
||||||
|
($line:expr, $arity:expr) => (
|
||||||
|
Line::BuiltIn(BuiltInInstruction::Goto($line, $arity))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! reset_block {
|
||||||
|
() => (
|
||||||
|
Line::BuiltIn(BuiltInInstruction::ResetBlock)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! get_ball {
|
||||||
|
() => (
|
||||||
|
Line::BuiltIn(BuiltInInstruction::GetBall)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! unify {
|
||||||
|
() => (
|
||||||
|
Line::BuiltIn(BuiltInInstruction::Unify)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! unwind_stack {
|
||||||
|
() => (
|
||||||
|
Line::BuiltIn(BuiltInInstruction::UnwindStack)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! clean_up_block {
|
||||||
|
() => (
|
||||||
|
Line::BuiltIn(BuiltInInstruction::CleanUpBlock)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! set_ball {
|
||||||
|
() => (
|
||||||
|
Line::BuiltIn(BuiltInInstruction::SetBall)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! fail {
|
||||||
|
() => (
|
||||||
|
Line::BuiltIn(BuiltInInstruction::Fail)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! copy_term {
|
||||||
|
() => (
|
||||||
|
Line::BuiltIn(BuiltInInstruction::CopyTerm)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! get_level {
|
||||||
|
() => (
|
||||||
|
Line::Cut(CutInstruction::GetLevel)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -8,8 +8,12 @@ pub mod heapview;
|
|||||||
pub mod indexing;
|
pub mod indexing;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod iterators;
|
pub mod iterators;
|
||||||
|
#[macro_use]
|
||||||
|
pub mod macros;
|
||||||
pub mod naive_allocator;
|
pub mod naive_allocator;
|
||||||
pub mod prolog_parser;
|
pub mod prolog_parser;
|
||||||
pub mod machine;
|
pub mod machine;
|
||||||
pub mod or_stack;
|
pub mod or_stack;
|
||||||
pub mod targets;
|
pub mod targets;
|
||||||
|
|
||||||
|
pub mod builtins;
|
||||||
|
|||||||
Reference in New Issue
Block a user