transition to l3
This commit is contained in:
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -1,9 +1,10 @@
|
||||
[root]
|
||||
name = "rusty-wam"
|
||||
version = "0.3.5"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"lalrpop 0.12.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lalrpop-util 0.12.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -197,6 +198,14 @@ dependencies = [
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread-id"
|
||||
version = "2.0.0"
|
||||
@@ -259,6 +268,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b"
|
||||
"checksum strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "50c069df92e4b01425a8bf3576d5d417943a6a7272fbabaf5bd80b1aaa76442e"
|
||||
"checksum term 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3deff8a2b3b6607d6d7cc32ac25c0b33709453ca9cceac006caac51e963cf94a"
|
||||
"checksum termion 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e710c59ecea168019486885ade43ea5329bb9a08a117410052fb81b250f40ae"
|
||||
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
|
||||
"checksum thread_local 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "55dd963dbaeadc08aa7266bf7f91c3154a7805e32bb94b820b769d2ef3b4744d"
|
||||
"checksum unicode-xid 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f69506a2561962651710609304bbb961fa3da598c812f877975a82e48ee144f9"
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
[package]
|
||||
name = "rusty-wam"
|
||||
version = "0.3.5"
|
||||
version = "0.4.0"
|
||||
authors = ["Mark Thom"]
|
||||
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
[dependencies.termion]
|
||||
version = "1.2.0"
|
||||
|
||||
[dependencies.lalrpop-util]
|
||||
version = "0.12.5"
|
||||
|
||||
[build-dependencies.lalrpop]
|
||||
version = "0.12.5"
|
||||
version = "0.12.5"
|
||||
|
||||
|
||||
161
README.md
161
README.md
@@ -8,135 +8,56 @@ pure Prolog.
|
||||
|
||||
## Progress
|
||||
|
||||
The language L2 is implemented as a simple REPL. It supports
|
||||
unification on queries without backtracking, where rules and facts are
|
||||
limited to a single name/arity pairing, in the familiar Prolog
|
||||
syntax. No data types apart from atoms are currently supported.
|
||||
The language L3 is implemented as a simple REPL. L3 is pure Prolog --
|
||||
Prolog without cuts, meta- or extra-logical operators, or side effects
|
||||
of any kind. No data types apart from atoms are currently supported.
|
||||
|
||||
An example of the level of interaction currently supported is:
|
||||
## Tutorial
|
||||
To enter a multi-clause predicate, the brackets ":{" and "}:" are used
|
||||
as delimiters. They must be entirely contained with their own lines.
|
||||
|
||||
For example,
|
||||
```
|
||||
l2> p(Z, Z).
|
||||
l2> ?- p(Z, Z).
|
||||
l3> :{
|
||||
p(f(f(X)), h(W), Y) :- g(W), h(W), f(X).
|
||||
p(X, Y, Z) :- h(Y), z(Z).
|
||||
}:
|
||||
l3> :{
|
||||
h(x).
|
||||
h(y).
|
||||
h(z).
|
||||
}:
|
||||
```
|
||||
|
||||
Single clause predicates can entered without brackets, as in
|
||||
```
|
||||
l3> p(X) :- q(X).
|
||||
l3> f(s).
|
||||
l3> z(Z).
|
||||
```
|
||||
|
||||
Queries are issued as
|
||||
```
|
||||
l3> ?- p(X, Y, Z).
|
||||
```
|
||||
|
||||
Given the above work, the result of the query will be
|
||||
```
|
||||
l3> ?- p(X, Y, Z).
|
||||
yes
|
||||
Z = _0
|
||||
l2> ?- p(Z, z).
|
||||
yes
|
||||
Z = z
|
||||
l2> ?- p(Z, w).
|
||||
yes
|
||||
Z = w
|
||||
l2> clouds(are, nice).
|
||||
l2> ?- p(z, w).
|
||||
no
|
||||
l2> ?- p(w, w).
|
||||
yes
|
||||
l2> ?- clouds(Z, Z).
|
||||
no
|
||||
l2> ?- clouds(are, W).
|
||||
yes
|
||||
W = nice
|
||||
l2> ?- clouds(W, nice).
|
||||
yes
|
||||
W = are
|
||||
l2> ?- p(Z, h(Z, W), f(W)).
|
||||
no
|
||||
l2> p(Z, h(Z, W), f(W)).
|
||||
l2> ?- p(z, h(z, z), f(w)).
|
||||
no
|
||||
l2> ?- p(z, h(z, w), f(w)).
|
||||
yes
|
||||
l2> ?- p(z, h(z, W), f(w)).
|
||||
yes
|
||||
W = w
|
||||
l2> ?- p(Z, h(Z, w), f(Z)).
|
||||
yes
|
||||
Z = w
|
||||
l2> ?- p(z, h(Z, w), f(Z)).
|
||||
no
|
||||
l2> p(f(X), h(Y, f(a)), Y).
|
||||
l2> ?- p(Z, h(Z, W), f(W)).
|
||||
yes
|
||||
Z = f(f(a))
|
||||
W = f(a)
|
||||
l2> p(X, Y) :- q(X, Z), r(Z, Y).
|
||||
l2> q(q, s).
|
||||
l2> r(s, t).
|
||||
l2> ?- p(X, Y).
|
||||
yes
|
||||
X = q
|
||||
Y = t
|
||||
l2> ?- p(q, t).
|
||||
yes
|
||||
l2> ?- p(t, q).
|
||||
no
|
||||
l2> ?- p(q, T).
|
||||
yes
|
||||
T = t
|
||||
l2> ?- p(Q, t).
|
||||
yes
|
||||
Q = q
|
||||
l2> ?- p(t, t).
|
||||
no
|
||||
l2> p(X, Y) :- q(f(f(X)), R), r(S, T).
|
||||
l2> q(f(f(X)), r).
|
||||
l2> ?- p(X, Y).
|
||||
yes
|
||||
Y = _1
|
||||
X = _0
|
||||
l2> q(f(f(x)), r).
|
||||
l2> ?- p(X, Y).
|
||||
yes
|
||||
Y = _1
|
||||
X = x
|
||||
l2> p(X, Y) :- q(X, Y), r(X, Y).
|
||||
l2> q(s, t).
|
||||
l2> r(X, Y) :- r(a).
|
||||
l2> r(a).
|
||||
l2> ?- p(X, Y).
|
||||
yes
|
||||
Y = t
|
||||
X = s
|
||||
l2> ?- p(t, S).
|
||||
no
|
||||
l2> ?- p(t, s).
|
||||
no
|
||||
l2> ?- p(s, T).
|
||||
yes
|
||||
T = t
|
||||
l2> ?- p(S, t).
|
||||
yes
|
||||
S = s
|
||||
l2> p(f(f(a), g(b), X), g(b), h) :- q(X, Y).
|
||||
l2> q(X, Y).
|
||||
l2> ?- p(f(X, Y, Z), g(b), h).
|
||||
yes
|
||||
Z = _4
|
||||
X = f(a)
|
||||
Y = g(b)
|
||||
l2> ?- p(f(X, g(Y), c), g(Z), X).
|
||||
no
|
||||
l2> ?- p(f(X, g(Y), c), g(Z), h).
|
||||
yes
|
||||
Z = b
|
||||
Y = b
|
||||
X = f(a)
|
||||
l2> ?- p(Z, Y, X).
|
||||
yes
|
||||
X = h
|
||||
Z = f(f(a), g(b), _7)
|
||||
Y = g(b)
|
||||
l2> ?- p(f(X, Y, Z), Y, h).
|
||||
yes
|
||||
X = f(a)
|
||||
Z = _4
|
||||
Y = g(b)
|
||||
l2> quit
|
||||
Y = x
|
||||
Z = _2
|
||||
Press ; to continue or A to abort.
|
||||
```
|
||||
|
||||
Pressing ; will backtrack through other possible answers, if any exist.
|
||||
Pressing A will abort the search and return to the prompt.
|
||||
|
||||
Note that the values of variables belonging to successful queries are
|
||||
printed out, on one line each. Uninstantiated variables are denoted by
|
||||
a number preceded by an underscore.
|
||||
a number preceded by an underscore (X = _0 is an example in the
|
||||
above).
|
||||
|
||||
## Occurs check
|
||||
|
||||
|
||||
80
src/l3/and_stack.rs
Normal file
80
src/l3/and_stack.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
use l3::ast::*;
|
||||
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::vec::Vec;
|
||||
|
||||
pub struct Frame {
|
||||
pub global_index: usize,
|
||||
pub e: usize,
|
||||
pub cp: CodePtr,
|
||||
perms: Vec<Addr>
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
fn new(global_index: usize, e: usize, cp: CodePtr, n: usize) -> Self {
|
||||
Frame {
|
||||
global_index: global_index,
|
||||
e: e,
|
||||
cp: cp,
|
||||
perms: vec![Addr::HeapCell(0); n]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AndStack(Vec<Frame>);
|
||||
|
||||
impl AndStack {
|
||||
pub fn new() -> Self {
|
||||
AndStack(Vec::new())
|
||||
}
|
||||
|
||||
pub fn push(&mut self, global_index: usize, e: usize, cp: CodePtr, n: usize) {
|
||||
self.0.push(Frame::new(global_index, e, cp, n));
|
||||
}
|
||||
|
||||
pub fn top(&self) -> Option<&Frame> {
|
||||
self.0.last()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear()
|
||||
}
|
||||
|
||||
// drop the last n frames.
|
||||
pub fn drop_frames(&mut self, n: usize) {
|
||||
let len = self.0.len();
|
||||
self.0.truncate(len - n);
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for AndStack {
|
||||
type Output = Frame;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.0.index(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for AndStack {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
self.0.index_mut(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for Frame {
|
||||
type Output = Addr;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.perms.index(index - 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for Frame {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
self.perms.index_mut(index - 1)
|
||||
}
|
||||
}
|
||||
303
src/l3/ast.rs
Normal file
303
src/l3/ast.rs
Normal file
@@ -0,0 +1,303 @@
|
||||
use std::cell::Cell;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::{Add, AddAssign};
|
||||
use std::vec::Vec;
|
||||
|
||||
pub type Var = String;
|
||||
|
||||
pub type Atom = String;
|
||||
|
||||
pub enum PredicateClause {
|
||||
Fact(Term),
|
||||
Rule(Rule)
|
||||
}
|
||||
|
||||
impl PredicateClause {
|
||||
pub fn name(&self) -> &Atom {
|
||||
match self {
|
||||
&PredicateClause::Fact(ref t) => t.name(),
|
||||
&PredicateClause::Rule(ref rule) => rule.head.0.name()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arity(&self) -> usize {
|
||||
match self {
|
||||
&PredicateClause::Fact(ref t) => t.arity(),
|
||||
&PredicateClause::Rule(ref rule) => rule.head.0.arity()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum TopLevel {
|
||||
Fact(Term),
|
||||
Predicate(Vec<PredicateClause>),
|
||||
Query(Term),
|
||||
Rule(Rule)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Level {
|
||||
Deep, Shallow
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum RegType {
|
||||
Perm(usize),
|
||||
Temp(usize)
|
||||
}
|
||||
|
||||
impl RegType {
|
||||
pub fn reg_num(self) -> usize {
|
||||
match self {
|
||||
RegType::Perm(reg_num) | RegType::Temp(reg_num) => reg_num
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_perm(self) -> bool {
|
||||
match self {
|
||||
RegType::Perm(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum VarReg {
|
||||
ArgAndNorm(RegType, usize),
|
||||
Norm(RegType)
|
||||
}
|
||||
|
||||
impl VarReg {
|
||||
pub fn norm(self) -> RegType {
|
||||
match self {
|
||||
VarReg::ArgAndNorm(reg, _) | VarReg::Norm(reg) => reg
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root_register(self) -> usize {
|
||||
match self {
|
||||
VarReg::ArgAndNorm(_, root) => root,
|
||||
VarReg::Norm(root) => root.reg_num()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Term {
|
||||
Atom(Cell<RegType>, Atom),
|
||||
Clause(Cell<RegType>, Atom, Vec<Box<Term>>),
|
||||
Var(Cell<VarReg>, Var)
|
||||
}
|
||||
|
||||
pub struct Rule {
|
||||
pub head: (Term, Term),
|
||||
pub clauses: Vec<Term>
|
||||
}
|
||||
|
||||
pub enum TermRef<'a> {
|
||||
Atom(Level, &'a Cell<RegType>, &'a Atom),
|
||||
Clause(Level, &'a Cell<RegType>, &'a Atom, &'a Vec<Box<Term>>),
|
||||
Var(Level, &'a Cell<VarReg>, &'a Var)
|
||||
}
|
||||
|
||||
pub enum FactInstruction {
|
||||
GetStructure(Level, Atom, usize, RegType),
|
||||
GetValue(RegType, usize),
|
||||
GetVariable(RegType, usize),
|
||||
UnifyVariable(RegType),
|
||||
UnifyValue(RegType)
|
||||
}
|
||||
|
||||
pub enum QueryInstruction {
|
||||
PutStructure(Level, Atom, usize, RegType),
|
||||
PutValue(RegType, usize),
|
||||
PutVariable(RegType, usize),
|
||||
SetVariable(RegType),
|
||||
SetValue(RegType)
|
||||
}
|
||||
|
||||
pub enum ChoiceInstruction {
|
||||
RetryMeElse(usize),
|
||||
TrustMe,
|
||||
TryMeElse(usize)
|
||||
}
|
||||
|
||||
pub enum ControlInstruction {
|
||||
Allocate(usize),
|
||||
Call(Atom, usize),
|
||||
Deallocate,
|
||||
Proceed
|
||||
}
|
||||
|
||||
pub type CompiledFact = Vec<FactInstruction>;
|
||||
|
||||
pub type CompiledQuery = Vec<QueryInstruction>;
|
||||
|
||||
pub enum Line {
|
||||
Choice(ChoiceInstruction),
|
||||
Control(ControlInstruction),
|
||||
Fact(CompiledFact),
|
||||
Query(CompiledQuery)
|
||||
}
|
||||
|
||||
pub enum LineOrCodeOffset<'a> {
|
||||
Instruction(&'a Line),
|
||||
Offset(usize)
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Line> for LineOrCodeOffset<'a> {
|
||||
fn from(line: &'a Line) -> Self {
|
||||
LineOrCodeOffset::Instruction(line)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Code = Vec<Line>;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Addr {
|
||||
HeapCell(usize),
|
||||
StackCell(usize, usize),
|
||||
Str(usize)
|
||||
}
|
||||
|
||||
impl Addr {
|
||||
pub fn is_ref(self) -> bool {
|
||||
match self {
|
||||
Addr::HeapCell(_) | Addr::StackCell(_, _) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_ref(self) -> Option<Ref> {
|
||||
match self {
|
||||
Addr::HeapCell(hc) => Some(Ref::HeapCell(hc)),
|
||||
Addr::StackCell(fr, sc) => Some(Ref::StackCell(fr, sc)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ref> for Addr {
|
||||
fn from(r: Ref) -> Self {
|
||||
match r {
|
||||
Ref::HeapCell(hc) => Addr::HeapCell(hc),
|
||||
Ref::StackCell(fr, sc) => Addr::StackCell(fr, sc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Ref {
|
||||
HeapCell(usize),
|
||||
StackCell(usize, usize)
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum HeapCellValue {
|
||||
NamedStr(usize, Atom),
|
||||
Ref(Ref),
|
||||
Str(usize)
|
||||
}
|
||||
|
||||
impl From<Addr> for HeapCellValue {
|
||||
fn from(addr: Addr) -> HeapCellValue {
|
||||
match addr {
|
||||
Addr::HeapCell(hc) =>
|
||||
HeapCellValue::Ref(Ref::HeapCell(hc)),
|
||||
Addr::StackCell(fr, sc) =>
|
||||
HeapCellValue::Ref(Ref::StackCell(fr, sc)),
|
||||
Addr::Str(hc) =>
|
||||
HeapCellValue::Str(hc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HeapCellValue {
|
||||
pub fn as_addr(&self, focus: usize) -> Addr {
|
||||
match self {
|
||||
&HeapCellValue::Ref(r) => Addr::from(r),
|
||||
&HeapCellValue::Str(s) => Addr::Str(s),
|
||||
&HeapCellValue::NamedStr(_, _) => Addr::Str(focus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CodePtr {
|
||||
DirEntry(usize),
|
||||
TopLevel
|
||||
}
|
||||
|
||||
impl Default for CodePtr {
|
||||
fn default() -> Self {
|
||||
CodePtr::TopLevel
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for CodePtr {
|
||||
type Output = CodePtr;
|
||||
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
match self {
|
||||
CodePtr::DirEntry(p) => CodePtr::DirEntry(p + rhs),
|
||||
CodePtr::TopLevel => CodePtr::TopLevel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<usize> for CodePtr {
|
||||
fn add_assign(&mut self, rhs: usize) {
|
||||
match self {
|
||||
&mut CodePtr::DirEntry(ref mut p) => *p += rhs,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Heap = Vec<HeapCellValue>;
|
||||
|
||||
pub type Registers = Vec<Addr>;
|
||||
|
||||
impl Term {
|
||||
pub fn subterms(&self) -> usize {
|
||||
match self {
|
||||
&Term::Clause(_, _, ref terms) => terms.len(),
|
||||
_ => 1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &Atom {
|
||||
match self {
|
||||
&Term::Atom(_, ref atom)
|
||||
| &Term::Var(_, ref atom)
|
||||
| &Term::Clause(_, ref atom, _) => atom
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arity(&self) -> usize {
|
||||
match self {
|
||||
&Term::Atom(_, _) | &Term::Var(_, _) => 0,
|
||||
&Term::Clause(_, _, ref child_terms) => child_terms.len()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type HeapVarDict = HashMap<Var, Addr>;
|
||||
|
||||
pub enum EvalResult {
|
||||
EntryFailure,
|
||||
EntrySuccess,
|
||||
InitialQuerySuccess(HeapVarDict),
|
||||
QueryFailure,
|
||||
SubsequentQuerySuccess,
|
||||
}
|
||||
|
||||
impl EvalResult {
|
||||
#[allow(dead_code)]
|
||||
pub fn failed_query(&self) -> bool {
|
||||
if let &EvalResult::QueryFailure = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
447
src/l3/codegen.rs
Normal file
447
src/l3/codegen.rs
Normal file
@@ -0,0 +1,447 @@
|
||||
use l3::ast::*;
|
||||
use l3::iterators::{FactIterator, QueryIterator};
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cmp::max;
|
||||
use std::collections::HashMap;
|
||||
use std::vec::Vec;
|
||||
|
||||
trait CompilationTarget<'a> {
|
||||
type Iterator : Iterator<Item=TermRef<'a>>;
|
||||
|
||||
fn iter(&'a Term) -> Self::Iterator;
|
||||
|
||||
fn to_structure(Level, Atom, usize, RegType) -> Self;
|
||||
|
||||
fn argument_to_variable(RegType, usize) -> Self;
|
||||
fn argument_to_value(RegType, usize) -> Self;
|
||||
fn subterm_to_variable(RegType) -> Self;
|
||||
fn subterm_to_value(RegType) -> Self;
|
||||
|
||||
fn clause_arg_to_instr(RegType) -> Self;
|
||||
}
|
||||
|
||||
impl<'a> CompilationTarget<'a> for FactInstruction {
|
||||
type Iterator = FactIterator<'a>;
|
||||
|
||||
fn iter(term: &'a Term) -> Self::Iterator {
|
||||
term.breadth_first_iter()
|
||||
}
|
||||
|
||||
fn to_structure(lvl: Level, atom: Atom, arity: usize, reg: RegType) -> Self {
|
||||
FactInstruction::GetStructure(lvl, atom, arity, reg)
|
||||
}
|
||||
|
||||
fn argument_to_variable(arg: RegType, val: usize) -> Self {
|
||||
FactInstruction::GetVariable(arg, val)
|
||||
}
|
||||
|
||||
fn argument_to_value(arg: RegType, val: usize) -> Self {
|
||||
FactInstruction::GetValue(arg, val)
|
||||
}
|
||||
|
||||
fn subterm_to_variable(val: RegType) -> Self {
|
||||
FactInstruction::UnifyVariable(val)
|
||||
}
|
||||
|
||||
fn subterm_to_value(val: RegType) -> Self {
|
||||
FactInstruction::UnifyValue(val)
|
||||
}
|
||||
|
||||
fn clause_arg_to_instr(val: RegType) -> Self {
|
||||
FactInstruction::UnifyVariable(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CompilationTarget<'a> for QueryInstruction {
|
||||
type Iterator = QueryIterator<'a>;
|
||||
|
||||
fn iter(term: &'a Term) -> Self::Iterator {
|
||||
term.post_order_iter()
|
||||
}
|
||||
|
||||
fn to_structure(lvl: Level, atom: Atom, arity: usize, reg: RegType) -> Self {
|
||||
QueryInstruction::PutStructure(lvl, atom, arity, reg)
|
||||
}
|
||||
|
||||
fn argument_to_variable(arg: RegType, val: usize) -> Self {
|
||||
QueryInstruction::PutVariable(arg, val)
|
||||
}
|
||||
|
||||
fn argument_to_value(arg: RegType, val: usize) -> Self {
|
||||
QueryInstruction::PutValue(arg, val)
|
||||
}
|
||||
|
||||
fn subterm_to_variable(val: RegType) -> Self {
|
||||
QueryInstruction::SetVariable(val)
|
||||
}
|
||||
|
||||
fn subterm_to_value(val: RegType) -> Self {
|
||||
QueryInstruction::SetValue(val)
|
||||
}
|
||||
|
||||
fn clause_arg_to_instr(val: RegType) -> Self {
|
||||
QueryInstruction::SetValue(val)
|
||||
}
|
||||
}
|
||||
|
||||
struct TermMarker<'a> {
|
||||
bindings: HashMap<&'a Var, VarReg>,
|
||||
arg_c: usize,
|
||||
perm_c: usize,
|
||||
temp_c: usize
|
||||
}
|
||||
|
||||
impl<'a> TermMarker<'a> {
|
||||
fn new() -> TermMarker<'a> {
|
||||
TermMarker { bindings: HashMap::new(),
|
||||
arg_c: 1,
|
||||
perm_c: 1,
|
||||
temp_c: 1 }
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.bindings.clear();
|
||||
self.perm_c = 1;
|
||||
}
|
||||
|
||||
fn contains_var(&self, var: &'a Var) -> bool {
|
||||
self.bindings.contains_key(var)
|
||||
}
|
||||
|
||||
fn get(&self, var: &'a Var) -> VarReg {
|
||||
*self.bindings.get(var).unwrap()
|
||||
}
|
||||
|
||||
fn insert(&mut self, var: &'a Var, r: VarReg) {
|
||||
self.bindings.insert(var, r);
|
||||
}
|
||||
|
||||
fn mark_non_var(&mut self, lvl: Level, cell: &Cell<RegType>) {
|
||||
let reg_type = cell.get();
|
||||
|
||||
if reg_type.reg_num() == 0 {
|
||||
match lvl {
|
||||
Level::Deep if reg_type.is_perm() => {
|
||||
let perm = self.perm_c;
|
||||
self.perm_c += 1;
|
||||
cell.set(RegType::Perm(perm));
|
||||
},
|
||||
Level::Deep => {
|
||||
let temp = self.temp_c;
|
||||
self.temp_c += 1;
|
||||
cell.set(RegType::Temp(temp));
|
||||
},
|
||||
Level::Shallow if reg_type.is_perm() => {
|
||||
let arg = self.arg_c;
|
||||
self.arg_c += 1;
|
||||
cell.set(RegType::Perm(arg));
|
||||
},
|
||||
Level::Shallow => {
|
||||
let arg = self.arg_c;
|
||||
self.arg_c += 1;
|
||||
cell.set(RegType::Temp(arg));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_old_var(&mut self, lvl: Level, var: &'a Var) -> VarReg
|
||||
{
|
||||
let reg = self.get(var);
|
||||
|
||||
match lvl {
|
||||
Level::Deep => VarReg::Norm(reg.norm()),
|
||||
Level::Shallow => {
|
||||
let reg = VarReg::ArgAndNorm(reg.norm(), self.arg_c);
|
||||
|
||||
self.arg_c += 1;
|
||||
self.insert(var, reg);
|
||||
|
||||
reg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_new_var(&mut self, lvl: Level, var: &'a Var, reg: RegType) -> VarReg
|
||||
{
|
||||
let inner_reg = if reg.is_perm() {
|
||||
let perm = self.perm_c;
|
||||
self.perm_c += 1;
|
||||
RegType::Perm(perm)
|
||||
} else {
|
||||
let temp = self.temp_c;
|
||||
self.temp_c += 1;
|
||||
RegType::Temp(temp)
|
||||
};
|
||||
|
||||
let reg = match lvl {
|
||||
Level::Deep => VarReg::Norm(inner_reg),
|
||||
Level::Shallow => {
|
||||
let reg = VarReg::ArgAndNorm(inner_reg, self.arg_c);
|
||||
self.arg_c += 1;
|
||||
reg
|
||||
}
|
||||
};
|
||||
|
||||
self.insert(var, reg);
|
||||
reg
|
||||
}
|
||||
|
||||
fn advance_at_head(&mut self, term: &'a Term) {
|
||||
self.arg_c = 1;
|
||||
self.temp_c = max(term.subterms(), self.temp_c) + 1;
|
||||
}
|
||||
|
||||
fn advance(&mut self, term: &'a Term) {
|
||||
self.arg_c = 1;
|
||||
self.temp_c = term.subterms() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum TermStatus {
|
||||
New, Old, Recurrent
|
||||
}
|
||||
|
||||
pub struct CodeGenerator<'a> {
|
||||
marker: TermMarker<'a>
|
||||
}
|
||||
|
||||
type VariableFixture<'a> = (TermStatus, Vec<&'a Cell<VarReg>>);
|
||||
type VariableFixtures<'a> = HashMap<&'a Var, VariableFixture<'a>>;
|
||||
|
||||
impl<'a> CodeGenerator<'a> {
|
||||
pub fn new() -> Self {
|
||||
CodeGenerator { marker: TermMarker::new() }
|
||||
}
|
||||
|
||||
pub fn vars(&self) -> &HashMap<&Var, VarReg> {
|
||||
&self.marker.bindings
|
||||
}
|
||||
|
||||
fn to_structure<Target>(&mut self,
|
||||
lvl: Level,
|
||||
name: &'a Atom,
|
||||
cell: &'a Cell<RegType>,
|
||||
arity: usize)
|
||||
-> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
self.marker.mark_non_var(lvl, cell);
|
||||
Target::to_structure(lvl, name.clone(), arity, cell.get())
|
||||
}
|
||||
|
||||
fn var_term<Target>(&mut self,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
var: &'a Var)
|
||||
-> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
if !self.marker.contains_var(var) {
|
||||
let reg = self.marker.mark_new_var(lvl, var, cell.get().norm());
|
||||
cell.set(reg);
|
||||
|
||||
match reg {
|
||||
VarReg::ArgAndNorm(arg, norm) =>
|
||||
Target::argument_to_variable(arg, norm),
|
||||
VarReg::Norm(norm) =>
|
||||
Target::subterm_to_variable(norm)
|
||||
}
|
||||
} else {
|
||||
let reg = self.marker.mark_old_var(lvl, var);
|
||||
cell.set(reg);
|
||||
|
||||
match reg {
|
||||
VarReg::ArgAndNorm(arg, norm) =>
|
||||
Target::argument_to_value(arg, norm),
|
||||
VarReg::Norm(norm) =>
|
||||
Target::subterm_to_value(norm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn non_var_subterm<Target>(&mut self, cell: &'a Cell<RegType>) -> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
self.marker.mark_non_var(Level::Deep, cell);
|
||||
Target::clause_arg_to_instr(cell.get())
|
||||
}
|
||||
|
||||
fn subterm_to_instr<Target>(&mut self, subterm: &'a Term) -> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
match subterm {
|
||||
&Term::Atom(ref cell, _) | &Term::Clause(ref cell, _, _) =>
|
||||
self.non_var_subterm(cell),
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
self.var_term(Level::Deep, cell, var)
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_target<Target>(&mut self, term: &'a Term) -> Vec<Target>
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
let iter = Target::iter(term);
|
||||
let mut target = Vec::new();
|
||||
|
||||
for term in iter {
|
||||
match term {
|
||||
TermRef::Atom(lvl, term, atom) =>
|
||||
target.push(self.to_structure(lvl, atom, term, 0)),
|
||||
TermRef::Clause(lvl, term, atom, terms) => {
|
||||
target.push(self.to_structure(lvl, atom, term, terms.len()));
|
||||
|
||||
for subterm in terms {
|
||||
target.push(self.subterm_to_instr(subterm.as_ref()));
|
||||
}
|
||||
},
|
||||
TermRef::Var(lvl @ Level::Shallow, ref cell, ref var) =>
|
||||
target.push(self.var_term(lvl, cell, var)),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
target
|
||||
}
|
||||
|
||||
fn mark_vars_in_term<Iter>(iter: Iter, vs: &mut VariableFixtures<'a>)
|
||||
where Iter : Iterator<Item=TermRef<'a>>
|
||||
{
|
||||
for term in iter {
|
||||
if let TermRef::Var(_, reg_cell, var) = term {
|
||||
let mut status = vs.entry(var)
|
||||
.or_insert((TermStatus::New, Vec::new()));
|
||||
|
||||
status.1.push(reg_cell);
|
||||
|
||||
match status.0 {
|
||||
TermStatus::Old => status.0 = TermStatus::Recurrent,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for &mut (ref mut term_status, ref mut cb) in vs.values_mut() {
|
||||
match *term_status {
|
||||
TermStatus::New => *term_status = TermStatus::Old,
|
||||
TermStatus::Recurrent => {
|
||||
for cell_reg in cb.drain(0..) {
|
||||
cell_reg.set(VarReg::Norm(RegType::Perm(0)));
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_perm_vars(rule: &'a Rule) -> VariableFixtures {
|
||||
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
|
||||
let mut vfs = HashMap::new();
|
||||
|
||||
let iter = p0.breadth_first_iter().chain(p1.breadth_first_iter());
|
||||
|
||||
Self::mark_vars_in_term(iter, &mut vfs);
|
||||
|
||||
for term in clauses {
|
||||
Self::mark_vars_in_term(term.breadth_first_iter(), &mut vfs);
|
||||
}
|
||||
|
||||
vfs
|
||||
}
|
||||
|
||||
fn add_conditional_call(compiled_query: &mut Code, term: &Term) {
|
||||
match term {
|
||||
&Term::Atom(_, ref atom) => {
|
||||
let call = ControlInstruction::Call(atom.clone(), 0);
|
||||
compiled_query.push(Line::Control(call));
|
||||
},
|
||||
&Term::Clause(_, ref atom, ref terms) => {
|
||||
let call = ControlInstruction::Call(atom.clone(), terms.len());
|
||||
compiled_query.push(Line::Control(call));
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile_rule(&mut self, rule: &'a Rule) -> Code {
|
||||
let vfs = Self::mark_perm_vars(&rule);
|
||||
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
|
||||
let mut perm_vars = 0;
|
||||
|
||||
for &(term_status, _) in vfs.values() {
|
||||
if let TermStatus::Recurrent = term_status {
|
||||
perm_vars += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut body = Vec::new();
|
||||
|
||||
body.push(Line::Control(ControlInstruction::Allocate(perm_vars)));
|
||||
|
||||
self.marker.advance(p0);
|
||||
body.push(Line::Fact(self.compile_target(p0)));
|
||||
|
||||
self.marker.advance_at_head(p1);
|
||||
body.push(Line::Query(self.compile_target(p1)));
|
||||
|
||||
Self::add_conditional_call(&mut body, p1);
|
||||
|
||||
body = clauses.iter()
|
||||
.map(|ref term| self.compile_query(term))
|
||||
.fold(body, |mut body, ref mut cqs| {
|
||||
body.append(cqs);
|
||||
body
|
||||
});
|
||||
|
||||
body.push(Line::Control(ControlInstruction::Deallocate));
|
||||
body
|
||||
}
|
||||
|
||||
pub fn compile_fact(&mut self, term: &'a Term) -> Code {
|
||||
self.marker.advance(term);
|
||||
|
||||
let mut compiled_fact = vec![Line::Fact(self.compile_target(term))];
|
||||
let proceed = Line::Control(ControlInstruction::Proceed);
|
||||
|
||||
compiled_fact.push(proceed);
|
||||
compiled_fact
|
||||
}
|
||||
|
||||
pub fn compile_query(&mut self, term: &'a Term) -> Code {
|
||||
self.marker.advance(term);
|
||||
|
||||
let mut compiled_query = vec![Line::Query(self.compile_target(term))];
|
||||
Self::add_conditional_call(&mut compiled_query, term);
|
||||
|
||||
compiled_query
|
||||
}
|
||||
|
||||
pub fn compile_predicate(&mut self, clauses: &'a Vec<PredicateClause>) -> Code
|
||||
{
|
||||
let mut code = Vec::new();
|
||||
|
||||
for (i, clause) in clauses.iter().enumerate() {
|
||||
self.marker.reset();
|
||||
|
||||
let mut clause_code = match clause {
|
||||
&PredicateClause::Fact(ref fact) =>
|
||||
self.compile_fact(fact),
|
||||
&PredicateClause::Rule(ref rule) =>
|
||||
self.compile_rule(rule)
|
||||
};
|
||||
|
||||
let choice = match i {
|
||||
0 => ChoiceInstruction::TryMeElse(clause_code.len() + 1),
|
||||
_ if i == clauses.len() - 1 => ChoiceInstruction::TrustMe,
|
||||
_ => ChoiceInstruction::RetryMeElse(clause_code.len() + 1)
|
||||
};
|
||||
|
||||
code.push(Line::Choice(choice));
|
||||
code.append(&mut clause_code);
|
||||
}
|
||||
|
||||
code
|
||||
}
|
||||
}
|
||||
86
src/l3/heapview.rs
Normal file
86
src/l3/heapview.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use l3::and_stack::*;
|
||||
use l3::ast::*;
|
||||
|
||||
use std::vec::Vec;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum HeapCellView<'a> {
|
||||
Str(usize, &'a Atom),
|
||||
HeapVar(usize),
|
||||
StackVar(usize, usize)
|
||||
}
|
||||
|
||||
pub struct HeapCellViewer<'a> {
|
||||
heap: &'a Heap,
|
||||
and_stack: &'a AndStack,
|
||||
state_stack: Vec<Addr>
|
||||
}
|
||||
|
||||
impl<'a> HeapCellViewer<'a> {
|
||||
pub fn new(heap: &'a Heap, and_stack: &'a AndStack, focus: Addr) -> Self {
|
||||
HeapCellViewer {
|
||||
heap: heap,
|
||||
and_stack: and_stack,
|
||||
state_stack: vec![focus]
|
||||
}
|
||||
}
|
||||
|
||||
fn follow_stack_ref(&mut self, mut fr: usize, mut sc: usize) -> HeapCellView<'a>
|
||||
{
|
||||
loop {
|
||||
match self.and_stack[fr][sc] {
|
||||
Addr::HeapCell(hc) | Addr::Str(hc) =>
|
||||
return self.follow_heap_ref(hc),
|
||||
Addr::StackCell(fr1, sc1) => {
|
||||
if fr1 == fr && sc1 == sc {
|
||||
return HeapCellView::StackVar(fr, sc);
|
||||
}
|
||||
|
||||
fr = fr1; sc = sc1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn follow_heap_ref(&mut self, mut focus: usize) -> HeapCellView<'a> {
|
||||
loop {
|
||||
match &self.heap[focus] {
|
||||
&HeapCellValue::NamedStr(arity, ref name) => {
|
||||
for i in (1 .. arity + 1).rev() {
|
||||
self.state_stack.push(Addr::HeapCell(focus + i));
|
||||
}
|
||||
|
||||
return HeapCellView::Str(arity, name);
|
||||
},
|
||||
&HeapCellValue::Ref(Ref::HeapCell(hc)) => {
|
||||
if focus == hc {
|
||||
return HeapCellView::HeapVar(hc);
|
||||
} else {
|
||||
focus = hc;
|
||||
}
|
||||
},
|
||||
&HeapCellValue::Ref(Ref::StackCell(fr, sc)) =>
|
||||
return self.follow_stack_ref(fr, sc),
|
||||
&HeapCellValue::Str(cell_num) =>
|
||||
focus = cell_num,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for HeapCellViewer<'a> {
|
||||
type Item = HeapCellView<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(addr) = self.state_stack.pop() {
|
||||
match addr {
|
||||
Addr::HeapCell(hc) | Addr::Str(hc) =>
|
||||
return Some(self.follow_heap_ref(hc)),
|
||||
Addr::StackCell(fr, sc) =>
|
||||
return Some(self.follow_stack_ref(fr, sc))
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
255
src/l3/io.rs
Normal file
255
src/l3/io.rs
Normal file
@@ -0,0 +1,255 @@
|
||||
use l3::ast::*;
|
||||
use l3::codegen::*;
|
||||
use l3::l3_parser::*;
|
||||
use l3::machine::*;
|
||||
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::input::TermRead;
|
||||
use termion::event::Key;
|
||||
|
||||
use std::io::{Write, stdin, stdout};
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for FactInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&FactInstruction::GetStructure(Level::Deep, ref name, ref arity, ref r) =>
|
||||
write!(f, "get_structure {}/{}, {}", name, arity, r),
|
||||
&FactInstruction::GetStructure(Level::Shallow, ref name, ref arity, ref r) =>
|
||||
write!(f, "get_structure {}/{}, A{}", name, arity, r.reg_num()),
|
||||
&FactInstruction::GetValue(ref x, ref a) =>
|
||||
write!(f, "get_value {}, A{}", x, a),
|
||||
&FactInstruction::GetVariable(ref x, ref a) =>
|
||||
write!(f, "get_variable {}, A{}", x, a),
|
||||
&FactInstruction::UnifyVariable(ref r) =>
|
||||
write!(f, "unify_variable {}", r),
|
||||
&FactInstruction::UnifyValue(ref r) =>
|
||||
write!(f, "unify_value {}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for QueryInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&QueryInstruction::PutStructure(Level::Deep, ref name, ref arity, ref r) =>
|
||||
write!(f, "put_structure {}/{}, {}", name, arity, r.reg_num()),
|
||||
&QueryInstruction::PutStructure(Level::Shallow, ref name, ref arity, ref r) =>
|
||||
write!(f, "put_structure {}/{}, A{}", name, arity, r.reg_num()),
|
||||
&QueryInstruction::PutValue(ref x, ref a) =>
|
||||
write!(f, "put_value {}, A{}", x, a),
|
||||
&QueryInstruction::PutVariable(ref x, ref a) =>
|
||||
write!(f, "put_variable {}, A{}", x, a),
|
||||
&QueryInstruction::SetVariable(ref r) =>
|
||||
write!(f, "set_variable {}", r),
|
||||
&QueryInstruction::SetValue(ref r) =>
|
||||
write!(f, "set_value {}", r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ControlInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&ControlInstruction::Allocate(num_cells) =>
|
||||
write!(f, "allocate {}", num_cells),
|
||||
&ControlInstruction::Call(ref name, ref arity) =>
|
||||
write!(f, "call {}/{}", name, arity),
|
||||
&ControlInstruction::Deallocate =>
|
||||
write!(f, "deallocate"),
|
||||
&ControlInstruction::Proceed =>
|
||||
write!(f, "proceed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ChoiceInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&ChoiceInstruction::TryMeElse(offset) =>
|
||||
write!(f, "try_me_else {}", offset),
|
||||
&ChoiceInstruction::RetryMeElse(offset) =>
|
||||
write!(f, "retry_me_else {}", offset),
|
||||
&ChoiceInstruction::TrustMe =>
|
||||
write!(f, "trust_me")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Level {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Level::Shallow => write!(f, "A"),
|
||||
&Level::Deep => write!(f, "X")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for VarReg {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&VarReg::Norm(RegType::Perm(reg)) => write!(f, "Y{}", reg),
|
||||
&VarReg::Norm(RegType::Temp(reg)) => write!(f, "X{}", reg),
|
||||
&VarReg::ArgAndNorm(RegType::Perm(reg), arg) =>
|
||||
write!(f, "Y{} A{}", reg, arg),
|
||||
&VarReg::ArgAndNorm(RegType::Temp(reg), arg) =>
|
||||
write!(f, "X{} A{}", reg, arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RegType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&RegType::Perm(val) => write!(f, "Y{}", val),
|
||||
&RegType::Temp(val) => write!(f, "X{}", val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn is_consistent(predicate: &Vec<PredicateClause>) -> bool {
|
||||
let name = predicate.first().unwrap().name();
|
||||
let arity = predicate.first().unwrap().arity();
|
||||
|
||||
for clause in predicate.iter().skip(1) {
|
||||
if !(name == clause.name() && arity == clause.arity()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn print_code(code: &Code) {
|
||||
for clause in code {
|
||||
match clause {
|
||||
&Line::Fact(ref fact) =>
|
||||
for fact_instr in fact {
|
||||
println!("{}", fact_instr);
|
||||
},
|
||||
&Line::Choice(ref choice) =>
|
||||
println!("{}", choice),
|
||||
&Line::Control(ref control) =>
|
||||
println!("{}", control),
|
||||
&Line::Query(ref query) =>
|
||||
for query_instr in query {
|
||||
println!("{}", query_instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read() -> String {
|
||||
let _ = stdout().flush();
|
||||
|
||||
let mut buffer = String::new();
|
||||
let mut result = String::new();
|
||||
|
||||
let stdin = stdin();
|
||||
stdin.read_line(&mut buffer).unwrap();
|
||||
|
||||
if &*buffer.trim() == ":{" {
|
||||
buffer.clear();
|
||||
|
||||
stdin.read_line(&mut buffer).unwrap();
|
||||
|
||||
while &*buffer.trim() != "}:" {
|
||||
result += buffer.as_str();
|
||||
buffer.clear();
|
||||
stdin.read_line(&mut buffer).unwrap();
|
||||
}
|
||||
} else {
|
||||
result = buffer;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn eval(wam: &mut Machine, buffer: &str) -> EvalResult
|
||||
{
|
||||
let result = parse_TopLevel(buffer);
|
||||
let mut cg = CodeGenerator::new();
|
||||
|
||||
match &result {
|
||||
&Ok(TopLevel::Predicate(ref clauses)) => {
|
||||
if is_consistent(clauses) {
|
||||
let compiled_pred = cg.compile_predicate(clauses);
|
||||
wam.add_predicate(clauses, compiled_pred);
|
||||
|
||||
EvalResult::EntrySuccess
|
||||
} else {
|
||||
let msg = r"Error: predicate is inconsistent.
|
||||
Each predicate must have the same name and arity.";
|
||||
|
||||
println!("{}", msg);
|
||||
EvalResult::EntryFailure
|
||||
}
|
||||
},
|
||||
&Ok(TopLevel::Fact(ref fact)) => {
|
||||
let compiled_fact = cg.compile_fact(&fact);
|
||||
wam.add_fact(fact, compiled_fact);
|
||||
EvalResult::EntrySuccess
|
||||
},
|
||||
&Ok(TopLevel::Rule(ref rule)) => {
|
||||
let compiled_rule = cg.compile_rule(&rule);
|
||||
wam.add_rule(rule, compiled_rule);
|
||||
EvalResult::EntrySuccess
|
||||
},
|
||||
&Ok(TopLevel::Query(ref query)) => {
|
||||
let compiled_query = cg.compile_query(&query);
|
||||
wam.run_query(compiled_query, &cg)
|
||||
},
|
||||
&Err(_) => {
|
||||
println!("Grammatical error of some kind!");
|
||||
EvalResult::EntryFailure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(wam: &mut Machine, result: EvalResult) {
|
||||
match result {
|
||||
EvalResult::InitialQuerySuccess(heap_locs) => {
|
||||
println!("yes");
|
||||
|
||||
'outer: loop {
|
||||
let mut result = EvalResult::QueryFailure;
|
||||
let bindings = wam.heap_view(&heap_locs);
|
||||
|
||||
let stdin = stdin();
|
||||
let mut stdout = stdout().into_raw_mode().unwrap();
|
||||
|
||||
write!(stdout, "{}\n\r", bindings).unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
if !wam.or_stack_is_empty() {
|
||||
write!(stdout, "Press ; to continue or A to abort.\n\r").unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
for c in stdin.keys() {
|
||||
match c.unwrap() {
|
||||
Key::Char(';') => {
|
||||
result = wam.continue_query();
|
||||
break;
|
||||
},
|
||||
Key::Char('a') | Key::Char('A') =>
|
||||
break 'outer,
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
|
||||
if let &EvalResult::QueryFailure = &result {
|
||||
write!(stdout, "no\n\r").unwrap();
|
||||
stdout.flush().unwrap();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
EvalResult::QueryFailure => println!("no"),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
175
src/l3/iterators.rs
Normal file
175
src/l3/iterators.rs
Normal file
@@ -0,0 +1,175 @@
|
||||
use l3::ast::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::vec::Vec;
|
||||
|
||||
enum IteratorState<'a> {
|
||||
Atom(Level, &'a Cell<RegType>, &'a Atom),
|
||||
Clause(Level, usize, &'a Cell<RegType>, &'a Atom, &'a Vec<Box<Term>>),
|
||||
IsolatedAtom(&'a Cell<RegType>, &'a Atom),
|
||||
IsolatedVar(&'a Cell<VarReg>, &'a Var),
|
||||
RootClause(usize, &'a Vec<Box<Term>>),
|
||||
Var(Level, &'a Cell<VarReg>, &'a Var)
|
||||
}
|
||||
|
||||
impl<'a> IteratorState<'a> {
|
||||
fn to_state(lvl: Level, term: &'a Term) -> IteratorState<'a>
|
||||
{
|
||||
match term {
|
||||
&Term::Atom(ref cell, ref atom) =>
|
||||
IteratorState::Atom(lvl, cell, atom),
|
||||
&Term::Clause(ref cell, ref atom, ref child_terms) =>
|
||||
IteratorState::Clause(lvl, 0, cell, atom, child_terms),
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
IteratorState::Var(lvl, cell, var)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QueryIterator<'a> {
|
||||
state_stack: Vec<IteratorState<'a>>
|
||||
}
|
||||
|
||||
impl<'a> QueryIterator<'a> {
|
||||
fn push_clause(&mut self,
|
||||
lvl: Level,
|
||||
child_num: usize,
|
||||
cell: &'a Cell<RegType>,
|
||||
name: &'a Atom,
|
||||
child_terms: &'a Vec<Box<Term>>)
|
||||
{
|
||||
self.state_stack.push(IteratorState::Clause(lvl,
|
||||
child_num,
|
||||
cell,
|
||||
name,
|
||||
child_terms));
|
||||
}
|
||||
|
||||
fn push_root_clause(&mut self,
|
||||
child_num: usize,
|
||||
child_terms: &'a Vec<Box<Term>>)
|
||||
{
|
||||
self.state_stack.push(IteratorState::RootClause(child_num, child_terms));
|
||||
}
|
||||
|
||||
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||
self.state_stack.push(IteratorState::to_state(lvl, term));
|
||||
}
|
||||
|
||||
fn new(term: &'a Term) -> QueryIterator<'a> {
|
||||
let state = match term {
|
||||
&Term::Atom(ref cell, ref atom) =>
|
||||
IteratorState::IsolatedAtom(cell, atom),
|
||||
&Term::Clause(_, _, ref terms) =>
|
||||
IteratorState::RootClause(0, terms),
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
IteratorState::IsolatedVar(cell, var)
|
||||
};
|
||||
|
||||
QueryIterator { state_stack: vec![state] }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for QueryIterator<'a> {
|
||||
type Item = TermRef<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(iter_state) = self.state_stack.pop() {
|
||||
match iter_state {
|
||||
IteratorState::Atom(lvl, cell, atom) =>
|
||||
return Some(TermRef::Atom(lvl, cell, atom)),
|
||||
IteratorState::Clause(lvl, child_num, cell, atom, child_terms) => {
|
||||
if child_num == child_terms.len() {
|
||||
return Some(TermRef::Clause(lvl, cell, atom, child_terms));
|
||||
} else {
|
||||
self.push_clause(lvl, child_num + 1, cell, atom, child_terms);
|
||||
self.push_subterm(Level::Deep, child_terms[child_num].as_ref());
|
||||
}
|
||||
},
|
||||
IteratorState::IsolatedAtom(cell, atom) =>
|
||||
return Some(TermRef::Atom(Level::Shallow, cell, atom)),
|
||||
IteratorState::IsolatedVar(cell, var) =>
|
||||
return Some(TermRef::Var(Level::Shallow, cell, var)),
|
||||
IteratorState::RootClause(child_num, child_terms) => {
|
||||
if child_num == child_terms.len() {
|
||||
return None;
|
||||
} else {
|
||||
self.push_root_clause(child_num + 1, child_terms);
|
||||
self.push_subterm(Level::Shallow, child_terms[child_num].as_ref());
|
||||
}
|
||||
},
|
||||
IteratorState::Var(lvl, cell, var) =>
|
||||
return Some(TermRef::Var(lvl, cell, var))
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FactIterator<'a> {
|
||||
state_queue: VecDeque<IteratorState<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> FactIterator<'a> {
|
||||
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||
self.state_queue.push_back(IteratorState::to_state(lvl, term));
|
||||
}
|
||||
|
||||
fn new(term: &'a Term) -> FactIterator<'a> {
|
||||
let states = match term {
|
||||
&Term::Atom(ref cell, ref atom) =>
|
||||
vec![IteratorState::IsolatedAtom(cell, atom)],
|
||||
&Term::Clause(_, _, ref terms) =>
|
||||
vec![IteratorState::RootClause(0, terms)],
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
vec![IteratorState::IsolatedVar(cell, var)]
|
||||
};
|
||||
|
||||
FactIterator { state_queue: VecDeque::from(states) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for FactIterator<'a> {
|
||||
type Item = TermRef<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(state) = self.state_queue.pop_front() {
|
||||
match state {
|
||||
IteratorState::Atom(lvl, cell, atom) =>
|
||||
return Some(TermRef::Atom(lvl, cell, atom)),
|
||||
IteratorState::Clause(lvl, _, cell, atom, child_terms) => {
|
||||
for child_term in child_terms {
|
||||
self.push_subterm(Level::Deep, child_term);
|
||||
}
|
||||
|
||||
return Some(TermRef::Clause(lvl, cell, atom, child_terms));
|
||||
},
|
||||
IteratorState::IsolatedAtom(cell, atom) =>
|
||||
return Some(TermRef::Atom(Level::Shallow, cell, atom)),
|
||||
IteratorState::IsolatedVar(cell, var) =>
|
||||
return Some(TermRef::Var(Level::Shallow, cell, var)),
|
||||
IteratorState::RootClause(_, child_terms) => {
|
||||
for child_term in child_terms {
|
||||
self.push_subterm(Level::Shallow, child_term);
|
||||
}
|
||||
},
|
||||
IteratorState::Var(lvl, cell, var) =>
|
||||
return Some(TermRef::Var(lvl, cell, var))
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Term {
|
||||
pub fn post_order_iter(&self) -> QueryIterator {
|
||||
QueryIterator::new(self)
|
||||
}
|
||||
|
||||
pub fn breadth_first_iter(&self) -> FactIterator {
|
||||
FactIterator::new(self)
|
||||
}
|
||||
}
|
||||
59
src/l3/l3_parser.lalrpop
Normal file
59
src/l3/l3_parser.lalrpop
Normal file
@@ -0,0 +1,59 @@
|
||||
use l3::ast::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
grammar;
|
||||
|
||||
pub TopLevel: TopLevel = {
|
||||
"?-" <t:Term> "." => TopLevel::Query(t),
|
||||
<Predicate> => TopLevel::Predicate(<>),
|
||||
<Rule> "." => TopLevel::Rule(<>),
|
||||
<Term> "." => TopLevel::Fact(<>)
|
||||
};
|
||||
|
||||
Atom : Atom = {
|
||||
r"[a-z][a-z0-9_]*" => <>.trim().to_string(),
|
||||
};
|
||||
|
||||
BoxedTerm : Box<Term> = {
|
||||
<t:Term> => Box::new(t)
|
||||
};
|
||||
|
||||
Clause : Term = {
|
||||
<a:Atom> "(" <ts: (<BoxedTerm> ",")*> <t:BoxedTerm> ")" => {
|
||||
let mut ts = ts;
|
||||
ts.push(t);
|
||||
Term::Clause(Cell::new(RegType::Temp(0)), a, ts)
|
||||
}
|
||||
};
|
||||
|
||||
Predicate : Vec<PredicateClause> = {
|
||||
<pcs: (<PredicateClause>)+> <pc: PredicateClause> => {
|
||||
let mut pcs = pcs;
|
||||
pcs.push(pc);
|
||||
pcs
|
||||
}
|
||||
};
|
||||
|
||||
PredicateClause : PredicateClause = {
|
||||
<Rule> "." => PredicateClause::Rule(<>),
|
||||
<Term> "." => PredicateClause::Fact(<>)
|
||||
};
|
||||
|
||||
Rule : Rule = {
|
||||
<c:Clause> ":-" <h:Term> <cs: ("," <Term>)*> =>
|
||||
Rule { head: (c, h), clauses: cs },
|
||||
<a:Atom> ":-" <h:Term> <cs: ("," <Term>)*> =>
|
||||
Rule { head: (Term::Atom(Cell::new(RegType::Temp(0)), a), h),
|
||||
clauses: cs }
|
||||
};
|
||||
|
||||
Term : Term = {
|
||||
<Clause> => <>,
|
||||
<Atom> => Term::Atom(Cell::new(RegType::Temp(0)), <>),
|
||||
<Var> => Term::Var(Cell::new(VarReg::Norm(RegType::Temp(0))), <>)
|
||||
};
|
||||
|
||||
Var : Var = {
|
||||
r"[A-Z][a-z0-9_]*" => <>.trim().to_string(),
|
||||
};
|
||||
2177
src/l3/l3_parser.rs
Normal file
2177
src/l3/l3_parser.rs
Normal file
File diff suppressed because it is too large
Load Diff
677
src/l3/machine.rs
Normal file
677
src/l3/machine.rs
Normal file
@@ -0,0 +1,677 @@
|
||||
use l3::ast::*;
|
||||
use l3::codegen::*;
|
||||
use l3::heapview::*;
|
||||
use l3::and_stack::*;
|
||||
use l3::or_stack::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::vec::Vec;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum MachineMode {
|
||||
Read,
|
||||
Write
|
||||
}
|
||||
|
||||
struct MachineState {
|
||||
h: usize,
|
||||
s: usize,
|
||||
p: CodePtr,
|
||||
b: usize,
|
||||
e: usize,
|
||||
num_of_args: usize,
|
||||
cp: CodePtr,
|
||||
fail: bool,
|
||||
heap: Heap,
|
||||
mode: MachineMode,
|
||||
and_stack: AndStack,
|
||||
or_stack: OrStack,
|
||||
registers: Registers,
|
||||
trail: Vec<Ref>,
|
||||
tr: usize,
|
||||
hb: usize
|
||||
}
|
||||
|
||||
type CodeDir = HashMap<(Atom, usize), usize>;
|
||||
|
||||
impl Index<RegType> for MachineState {
|
||||
type Output = Addr;
|
||||
|
||||
fn index(&self, reg: RegType) -> &Self::Output {
|
||||
match reg {
|
||||
RegType::Temp(temp) => &self.registers[temp],
|
||||
RegType::Perm(perm) => {
|
||||
let e = self.e;
|
||||
&self.and_stack[e][perm]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<RegType> for MachineState {
|
||||
fn index_mut(&mut self, reg: RegType) -> &mut Self::Output {
|
||||
match reg {
|
||||
RegType::Temp(temp) => &mut self.registers[temp],
|
||||
RegType::Perm(perm) => {
|
||||
let e = self.e;
|
||||
&mut self.and_stack[e][perm]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Machine {
|
||||
ms: MachineState,
|
||||
code: Code,
|
||||
code_dir: CodeDir
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
pub fn new() -> Self {
|
||||
Machine {
|
||||
ms: MachineState::new(),
|
||||
code: Vec::new(),
|
||||
code_dir: HashMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn failed(&self) -> bool {
|
||||
self.ms.fail
|
||||
}
|
||||
|
||||
pub fn add_fact(&mut self, fact: &Term, mut code: Code) {
|
||||
let p = self.code.len();
|
||||
let name = fact.name().clone();
|
||||
let arity = fact.arity();
|
||||
|
||||
self.code.append(&mut code);
|
||||
self.code_dir.insert((name, arity), p);
|
||||
}
|
||||
|
||||
pub fn add_rule(&mut self, rule: &Rule, mut code: Code) {
|
||||
let p = self.code.len();
|
||||
let name = rule.head.0.name().clone();
|
||||
let arity = rule.head.0.arity();
|
||||
|
||||
self.code.append(&mut code);
|
||||
self.code_dir.insert((name, arity), p);
|
||||
}
|
||||
|
||||
pub fn add_predicate(&mut self, pred: &Vec<PredicateClause>, mut code: Code)
|
||||
{
|
||||
let p = self.code.len();
|
||||
let name = pred.first().unwrap().name().clone();
|
||||
let arity = pred.first().unwrap().arity();
|
||||
|
||||
self.code.append(&mut code);
|
||||
self.code_dir.insert((name, arity), p);
|
||||
}
|
||||
|
||||
fn execute_instr<'a>(&mut self, instr_src: LineOrCodeOffset<'a>) -> bool
|
||||
{
|
||||
let mut instr = match instr_src {
|
||||
LineOrCodeOffset::Instruction(instr) => instr,
|
||||
LineOrCodeOffset::Offset(p) => &self.code[p]
|
||||
};
|
||||
|
||||
loop {
|
||||
match instr {
|
||||
&Line::Choice(ref choice_instr) =>
|
||||
self.ms.execute_choice_instr(choice_instr),
|
||||
&Line::Fact(ref fact) => {
|
||||
for fact_instr in fact {
|
||||
self.ms.execute_fact_instr(&fact_instr);
|
||||
}
|
||||
self.ms.p += 1;
|
||||
},
|
||||
&Line::Query(ref query) => {
|
||||
for query_instr in query {
|
||||
self.ms.execute_query_instr(&query_instr);
|
||||
}
|
||||
self.ms.p += 1;
|
||||
},
|
||||
&Line::Control(ref control_instr) =>
|
||||
self.ms.execute_ctrl_instr(&self.code_dir, control_instr),
|
||||
}
|
||||
|
||||
if self.failed() {
|
||||
let p = self.ms
|
||||
.or_stack
|
||||
.top()
|
||||
.map(|fr| fr.bp)
|
||||
.unwrap_or_default();
|
||||
|
||||
if let CodePtr::TopLevel = p {
|
||||
return false;
|
||||
} else {
|
||||
self.ms.fail = false;
|
||||
self.ms.p = p;
|
||||
}
|
||||
}
|
||||
|
||||
match self.ms.p {
|
||||
CodePtr::DirEntry(p) if p < self.code.len() =>
|
||||
instr = &self.code[p],
|
||||
_ => break
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn heap_view(&self, var_dir: &HeapVarDict) -> String {
|
||||
let mut result = String::new();
|
||||
|
||||
for (var, addr) in var_dir {
|
||||
let mut arities = Vec::new();
|
||||
let viewer = HeapCellViewer::new(&self.ms.heap,
|
||||
&self.ms.and_stack,
|
||||
*addr);
|
||||
|
||||
if result != "" {
|
||||
result += "\n\r";
|
||||
}
|
||||
|
||||
result += var.as_str();
|
||||
result += " = ";
|
||||
|
||||
for view in viewer {
|
||||
match arities.pop() {
|
||||
Some(n) => arities.push(n-1),
|
||||
None => {}
|
||||
}
|
||||
|
||||
if !(arities.is_empty() || result.ends_with("(")) {
|
||||
result += ", ";
|
||||
}
|
||||
|
||||
match view {
|
||||
HeapCellView::Str(arity, ref name) => {
|
||||
result += name.as_str();
|
||||
|
||||
if arity > 0 {
|
||||
arities.push(arity);
|
||||
result += "(";
|
||||
}
|
||||
},
|
||||
HeapCellView::HeapVar(cell_num) => {
|
||||
result += "_";
|
||||
result += cell_num.to_string().as_str();
|
||||
},
|
||||
HeapCellView::StackVar(fr, sc) => {
|
||||
result += "_s_";
|
||||
result += fr.to_string().as_str();
|
||||
result += "_";
|
||||
result += sc.to_string().as_str();
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(&0) = arities.last() {
|
||||
result += ")";
|
||||
arities.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn run_query(&mut self, code: Code, cg: &CodeGenerator) -> EvalResult
|
||||
{
|
||||
let mut succeeded = true;
|
||||
let mut heap_locs = HashMap::new();
|
||||
|
||||
for instr in code.iter().take(1) {
|
||||
succeeded = self.execute_instr(LineOrCodeOffset::from(instr));
|
||||
}
|
||||
|
||||
if succeeded {
|
||||
for (var, vr) in cg.vars() {
|
||||
let addr = self.ms.registers[vr.root_register()];
|
||||
heap_locs.insert((*var).clone(), addr);
|
||||
}
|
||||
|
||||
for instr in code.iter().skip(1) {
|
||||
succeeded = self.execute_instr(LineOrCodeOffset::from(instr));
|
||||
if !succeeded {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if succeeded {
|
||||
EvalResult::InitialQuerySuccess(heap_locs)
|
||||
} else {
|
||||
EvalResult::QueryFailure
|
||||
}
|
||||
}
|
||||
|
||||
pub fn or_stack_is_empty(&self) -> bool {
|
||||
self.ms.or_stack.is_empty()
|
||||
}
|
||||
|
||||
pub fn continue_query(&mut self) -> EvalResult
|
||||
{
|
||||
if !self.or_stack_is_empty() {
|
||||
let b = self.ms.b;
|
||||
self.ms.p = self.ms.or_stack[b].bp;
|
||||
|
||||
let succeeded = if let CodePtr::DirEntry(p) = self.ms.p {
|
||||
self.execute_instr(LineOrCodeOffset::Offset(p))
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if succeeded {
|
||||
EvalResult::SubsequentQuerySuccess
|
||||
} else {
|
||||
EvalResult::QueryFailure
|
||||
}
|
||||
} else {
|
||||
EvalResult::QueryFailure
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.ms.reset();
|
||||
}
|
||||
}
|
||||
|
||||
impl MachineState {
|
||||
fn new() -> MachineState {
|
||||
MachineState { h: 0,
|
||||
s: 0,
|
||||
p: CodePtr::TopLevel,
|
||||
b: 0,
|
||||
e: 0,
|
||||
num_of_args: 0,
|
||||
cp: CodePtr::TopLevel,
|
||||
fail: false,
|
||||
heap: Vec::with_capacity(256),
|
||||
mode: MachineMode::Write,
|
||||
and_stack: AndStack::new(),
|
||||
or_stack: OrStack::new(),
|
||||
registers: vec![Addr::HeapCell(0); 32],
|
||||
trail: Vec::new(),
|
||||
tr: 0,
|
||||
hb: 0
|
||||
}
|
||||
}
|
||||
|
||||
fn num_frames(&self) -> usize {
|
||||
self.and_stack.len() + self.or_stack.len()
|
||||
}
|
||||
|
||||
fn store(&self, a: Addr) -> Addr {
|
||||
match a {
|
||||
Addr::HeapCell(r) => self.heap[r].as_addr(r),
|
||||
Addr::StackCell(fr, sc) => self.and_stack[fr][sc],
|
||||
addr => addr
|
||||
}
|
||||
}
|
||||
|
||||
fn deref(&self, a: Addr) -> Addr {
|
||||
let mut a = a;
|
||||
|
||||
loop {
|
||||
let value = self.store(a);
|
||||
|
||||
if value.is_ref() && value != a {
|
||||
a = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
return a;
|
||||
};
|
||||
}
|
||||
|
||||
fn bind(&mut self, r1: Ref, a2: Addr) {
|
||||
let t2 = self.store(a2);
|
||||
|
||||
match r1 {
|
||||
Ref::StackCell(fr, sc) =>
|
||||
self.and_stack[fr][sc] = t2,
|
||||
Ref::HeapCell(hc) =>
|
||||
self.heap[hc] = HeapCellValue::from(t2)
|
||||
};
|
||||
|
||||
self.trail(r1);
|
||||
}
|
||||
|
||||
fn unify(&mut self, a1: Addr, a2: Addr) {
|
||||
let mut pdl = vec![a1, a2];
|
||||
|
||||
self.fail = false;
|
||||
|
||||
while !(pdl.is_empty() || self.fail) {
|
||||
let d1 = self.deref(pdl.pop().unwrap());
|
||||
let d2 = self.deref(pdl.pop().unwrap());
|
||||
|
||||
if d1 != d2 {
|
||||
match (self.store(d1), self.store(d2)) {
|
||||
(Addr::HeapCell(hc), _) =>
|
||||
self.bind(Ref::HeapCell(hc), d2),
|
||||
(_, Addr::HeapCell(hc)) =>
|
||||
self.bind(Ref::HeapCell(hc), d1),
|
||||
(Addr::StackCell(fr, sc), _) =>
|
||||
self.bind(Ref::StackCell(fr, sc), d2),
|
||||
(_, Addr::StackCell(fr, sc)) =>
|
||||
self.bind(Ref::StackCell(fr, sc), d1),
|
||||
(Addr::Str(a1), Addr::Str(a2)) => {
|
||||
let r1 = &self.heap[a1];
|
||||
let r2 = &self.heap[a2];
|
||||
|
||||
if let &HeapCellValue::NamedStr(n1, ref f1) = r1 {
|
||||
if let &HeapCellValue::NamedStr(n2, ref f2) = r2 {
|
||||
if n1 == n2 && *f1 == *f2 {
|
||||
for i in 1 .. n1 + 1 {
|
||||
pdl.push(Addr::HeapCell(a1 + i));
|
||||
pdl.push(Addr::HeapCell(a2 + i));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.fail = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn trail(&mut self, r: Ref) {
|
||||
match r {
|
||||
Ref::HeapCell(hc) => {
|
||||
if hc < self.hb {
|
||||
self.trail.push(r);
|
||||
self.tr += 1;
|
||||
}
|
||||
},
|
||||
Ref::StackCell(fr, _) => {
|
||||
let fr_gi = self.and_stack[fr].global_index;
|
||||
let b_gi = if !self.or_stack.is_empty() {
|
||||
self.or_stack[self.b].global_index
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
if fr_gi < b_gi {
|
||||
self.trail.push(r);
|
||||
self.tr += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unwind_trail(&mut self, a1: usize, a2: usize) {
|
||||
for i in a1 .. a2 {
|
||||
match self.trail[i] {
|
||||
Ref::HeapCell(r) =>
|
||||
self.heap[r] = HeapCellValue::Ref(self.trail[i]),
|
||||
Ref::StackCell(fr, sc) =>
|
||||
self.and_stack[fr][sc] = Addr::StackCell(fr, sc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_query_instr(&mut self, instr: &QueryInstruction) {
|
||||
match instr {
|
||||
&QueryInstruction::PutStructure(_, ref name, arity, reg) => {
|
||||
self.heap.push(HeapCellValue::Str(self.h + 1));
|
||||
self.heap.push(HeapCellValue::NamedStr(arity, name.clone()));
|
||||
|
||||
self[reg] = Addr::Str(self.h + 1);
|
||||
|
||||
self.h += 2;
|
||||
},
|
||||
&QueryInstruction::PutValue(norm, arg) =>
|
||||
self.registers[arg] = self[norm],
|
||||
&QueryInstruction::PutVariable(norm, arg) => {
|
||||
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(self.h)));
|
||||
|
||||
self[norm] = Addr::HeapCell(self.h);
|
||||
self.registers[arg] = Addr::HeapCell(self.h);
|
||||
|
||||
self.h += 1;
|
||||
},
|
||||
&QueryInstruction::SetVariable(reg) => {
|
||||
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(self.h)));
|
||||
self[reg] = Addr::HeapCell(self.h);
|
||||
|
||||
self.h += 1;
|
||||
},
|
||||
&QueryInstruction::SetValue(reg) => {
|
||||
let heap_val = self[reg];
|
||||
self.heap.push(HeapCellValue::from(heap_val));
|
||||
|
||||
self.h += 1;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_fact_instr(&mut self, instr: &FactInstruction) {
|
||||
match instr {
|
||||
&FactInstruction::GetStructure(_, ref name, arity, reg) => {
|
||||
let addr = self.deref(self[reg]);
|
||||
|
||||
match self.store(addr) {
|
||||
Addr::Str(a) => {
|
||||
let result = &self.heap[a];
|
||||
|
||||
if let &HeapCellValue::NamedStr(narity, ref str) = result {
|
||||
if narity == arity && *name == *str {
|
||||
self.s = a + 1;
|
||||
self.mode = MachineMode::Read;
|
||||
} else {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
Addr::HeapCell(_) | Addr::StackCell(_, _) => {
|
||||
self.heap.push(HeapCellValue::Str(self.h + 1));
|
||||
self.heap.push(HeapCellValue::NamedStr(arity, name.clone()));
|
||||
|
||||
let h = self.h;
|
||||
|
||||
self.bind(addr.as_ref().unwrap(), Addr::HeapCell(h));
|
||||
|
||||
self.h += 2;
|
||||
self.mode = MachineMode::Write;
|
||||
}
|
||||
};
|
||||
},
|
||||
&FactInstruction::GetVariable(norm, arg) =>
|
||||
self[norm] = self.registers[arg],
|
||||
&FactInstruction::GetValue(norm, arg) => {
|
||||
let norm_addr = self[norm];
|
||||
let reg_addr = self.registers[arg];
|
||||
|
||||
self.unify(norm_addr, reg_addr);
|
||||
},
|
||||
&FactInstruction::UnifyVariable(reg) => {
|
||||
match self.mode {
|
||||
MachineMode::Read =>
|
||||
self[reg] = self.heap[self.s].as_addr(self.s),
|
||||
MachineMode::Write => {
|
||||
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(self.h)));
|
||||
self[reg] = Addr::HeapCell(self.h);
|
||||
self.h += 1;
|
||||
}
|
||||
};
|
||||
|
||||
self.s += 1;
|
||||
},
|
||||
&FactInstruction::UnifyValue(reg) => {
|
||||
let s = self.s;
|
||||
|
||||
match self.mode {
|
||||
MachineMode::Read => {
|
||||
let reg_addr = self[reg];
|
||||
self.unify(reg_addr, Addr::HeapCell(s));
|
||||
},
|
||||
MachineMode::Write => {
|
||||
let heap_val = self.store(self[reg]);
|
||||
self.heap.push(HeapCellValue::from(heap_val));
|
||||
self.h += 1;
|
||||
}
|
||||
};
|
||||
|
||||
self.s += 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction)
|
||||
{
|
||||
match instr {
|
||||
&ControlInstruction::Allocate(num_cells) => {
|
||||
let num_frames = self.num_frames();
|
||||
|
||||
self.and_stack.push(num_frames + 1, self.e, self.cp, num_cells);
|
||||
|
||||
self.e = self.and_stack.len() - 1;
|
||||
self.p += 1;
|
||||
},
|
||||
&ControlInstruction::Call(ref name, arity) => {
|
||||
let compiled_tl_index = code_dir.get(&(name.clone(), arity))
|
||||
.map(|index| *index);
|
||||
|
||||
match compiled_tl_index {
|
||||
Some(compiled_tl_index) => {
|
||||
self.cp = self.p + 1;
|
||||
self.num_of_args = arity;
|
||||
self.p = CodePtr::DirEntry(compiled_tl_index);
|
||||
},
|
||||
None => self.fail = true
|
||||
};
|
||||
},
|
||||
&ControlInstruction::Deallocate => {
|
||||
let e = self.e;
|
||||
|
||||
let num_frame_e = self.and_stack.top().unwrap().global_index;
|
||||
let num_frame_b = self.or_stack
|
||||
.top()
|
||||
.map(|fr| fr.global_index)
|
||||
.unwrap_or(0);
|
||||
|
||||
self.p = self.and_stack[e].cp;
|
||||
self.e = self.and_stack[e].e;
|
||||
|
||||
if num_frame_e > num_frame_b {
|
||||
let top_e = self.and_stack.top().unwrap().e;
|
||||
self.and_stack.drop_frames(top_e - self.e + 1);
|
||||
}
|
||||
},
|
||||
&ControlInstruction::Proceed =>
|
||||
self.p = self.cp,
|
||||
};
|
||||
}
|
||||
|
||||
fn execute_choice_instr(&mut self, instr: &ChoiceInstruction)
|
||||
{
|
||||
match instr {
|
||||
&ChoiceInstruction::TryMeElse(offset) => {
|
||||
let n = self.num_of_args;
|
||||
let num_frames = self.num_frames();
|
||||
|
||||
self.or_stack.push(num_frames + 1,
|
||||
self.e,
|
||||
self.cp,
|
||||
self.b,
|
||||
self.p + offset,
|
||||
self.tr,
|
||||
self.h,
|
||||
self.num_of_args);
|
||||
|
||||
self.b = self.or_stack.len() - 1;
|
||||
let b = self.b;
|
||||
|
||||
for i in 1 .. n + 1 {
|
||||
self.or_stack[b][i] = self.registers[i];
|
||||
}
|
||||
|
||||
self.hb = self.h;
|
||||
self.p += 1;
|
||||
},
|
||||
&ChoiceInstruction::RetryMeElse(offset) => {
|
||||
let b = self.b;
|
||||
let n = self.or_stack[b].num_args();
|
||||
|
||||
for i in 1 .. n + 1 {
|
||||
self.registers[i] = self.or_stack[b][i];
|
||||
}
|
||||
|
||||
self.e = self.or_stack[b].e;
|
||||
self.cp = self.or_stack[b].cp;
|
||||
|
||||
self.or_stack[b].bp = self.p + offset;
|
||||
|
||||
let old_tr = self.or_stack[b].tr;
|
||||
let curr_tr = self.tr;
|
||||
|
||||
self.unwind_trail(old_tr, curr_tr);
|
||||
self.tr = self.or_stack[b].tr;
|
||||
|
||||
self.trail.truncate(self.tr);
|
||||
self.heap.truncate(self.or_stack[b].h);
|
||||
|
||||
self.h = self.or_stack[b].h;
|
||||
self.hb = self.h;
|
||||
|
||||
self.p += 1;
|
||||
},
|
||||
&ChoiceInstruction::TrustMe => {
|
||||
let b = self.b;
|
||||
let n = self.or_stack[b].num_args();
|
||||
|
||||
for i in 1 .. n + 1 {
|
||||
self.registers[i] = self.or_stack[b][i];
|
||||
}
|
||||
|
||||
self.e = self.or_stack[b].e;
|
||||
self.cp = self.or_stack[b].cp;
|
||||
|
||||
let old_tr = self.or_stack[b].tr;
|
||||
let curr_tr = self.tr;
|
||||
|
||||
self.unwind_trail(old_tr, curr_tr);
|
||||
|
||||
self.tr = self.or_stack[b].tr;
|
||||
self.trail.truncate(self.tr);
|
||||
|
||||
self.h = self.or_stack[b].h;
|
||||
self.heap.truncate(self.h);
|
||||
|
||||
self.b = self.or_stack[b].b;
|
||||
|
||||
self.or_stack.pop();
|
||||
|
||||
self.hb = self.h;
|
||||
self.p += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.h = 0;
|
||||
self.hb = 0;
|
||||
self.e = 0;
|
||||
self.b = 0;
|
||||
self.s = 0;
|
||||
self.tr = 0;
|
||||
self.p = CodePtr::TopLevel;
|
||||
self.cp = CodePtr::TopLevel;
|
||||
self.num_of_args = 0;
|
||||
|
||||
self.fail = false;
|
||||
self.trail.clear();
|
||||
self.heap.clear();
|
||||
self.mode = MachineMode::Write;
|
||||
self.and_stack.clear();
|
||||
self.or_stack.clear();
|
||||
self.registers = vec![Addr::HeapCell(0); 32];
|
||||
}
|
||||
}
|
||||
9
src/l3/mod.rs
Normal file
9
src/l3/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
pub mod and_stack;
|
||||
pub mod ast;
|
||||
pub mod codegen;
|
||||
pub mod heapview;
|
||||
pub mod io;
|
||||
pub mod iterators;
|
||||
pub mod l3_parser;
|
||||
pub mod machine;
|
||||
pub mod or_stack;
|
||||
112
src/l3/or_stack.rs
Normal file
112
src/l3/or_stack.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
use l3::ast::*;
|
||||
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::vec::Vec;
|
||||
|
||||
pub struct Frame {
|
||||
pub global_index: usize,
|
||||
pub e: usize,
|
||||
pub cp: CodePtr,
|
||||
pub b: usize,
|
||||
pub bp: CodePtr,
|
||||
pub tr: usize,
|
||||
pub h: usize,
|
||||
args: Vec<Addr>
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
fn new(global_index: usize,
|
||||
e: usize,
|
||||
cp: CodePtr,
|
||||
b: usize,
|
||||
bp: CodePtr,
|
||||
tr: usize,
|
||||
h: usize,
|
||||
n: usize)
|
||||
-> Self
|
||||
{
|
||||
Frame {
|
||||
global_index: global_index,
|
||||
e: e,
|
||||
cp: cp,
|
||||
b: b,
|
||||
bp: bp,
|
||||
tr: tr,
|
||||
h: h,
|
||||
args: vec![Addr::HeapCell(0); n]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_args(&self) -> usize {
|
||||
self.args.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OrStack(Vec<Frame>);
|
||||
|
||||
impl OrStack {
|
||||
pub fn new() -> Self {
|
||||
OrStack(Vec::new())
|
||||
}
|
||||
|
||||
pub fn push(&mut self,
|
||||
global_index: usize,
|
||||
e: usize,
|
||||
cp: CodePtr,
|
||||
b: usize,
|
||||
bp: CodePtr,
|
||||
tr: usize,
|
||||
h: usize,
|
||||
n: usize)
|
||||
{
|
||||
self.0.push(Frame::new(global_index, e, cp, b, bp, tr, h, n));
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear()
|
||||
}
|
||||
|
||||
pub fn top(&self) -> Option<&Frame> {
|
||||
self.0.last()
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) {
|
||||
self.0.pop();
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for OrStack {
|
||||
type Output = Frame;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.0.index(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for OrStack {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
self.0.index_mut(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for Frame {
|
||||
type Output = Addr;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.args.index(index - 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for Frame {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
self.args.index_mut(index - 1)
|
||||
}
|
||||
}
|
||||
264
src/main.rs
264
src/main.rs
@@ -1,157 +1,203 @@
|
||||
mod l2;
|
||||
extern crate termion;
|
||||
mod l3;
|
||||
|
||||
use l2::ast::*;
|
||||
use l2::codegen::*;
|
||||
use l2::machine::*;
|
||||
|
||||
use std::io::{self, Write};
|
||||
|
||||
fn submit(wam: &mut Machine, buffer: String) -> bool {
|
||||
let result = l2::l2_parser::parse_TopLevel(&*buffer);
|
||||
|
||||
let mut cg = CodeGenerator::new();
|
||||
|
||||
match &result {
|
||||
&Ok(TopLevel::Fact(ref fact)) => {
|
||||
let compiled_fact = cg.compile_fact(&fact);
|
||||
wam.add_fact(fact, compiled_fact);
|
||||
},
|
||||
&Ok(TopLevel::Rule(ref rule)) => {
|
||||
let compiled_rule = cg.compile_rule(&rule);
|
||||
wam.add_rule(rule, compiled_rule);
|
||||
},
|
||||
&Ok(TopLevel::Query(ref query)) => {
|
||||
let compiled_query = cg.compile_query(&query);
|
||||
let output = wam.run_query(compiled_query, &cg);
|
||||
|
||||
match output {
|
||||
Some(result) => {
|
||||
println!("yes");
|
||||
|
||||
if result != "" {
|
||||
println!("{}", result);
|
||||
}
|
||||
},
|
||||
None => println!("no")
|
||||
}
|
||||
},
|
||||
&Err(_) => println!("Grammatical error of some kind!"),
|
||||
};
|
||||
|
||||
let result = wam.failed();
|
||||
wam.reset();
|
||||
result
|
||||
}
|
||||
use l3::io::*;
|
||||
use l3::machine::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[inline]
|
||||
fn submit_ss(wam: &mut Machine, buffer: &'static str) -> bool {
|
||||
submit(wam, String::from(buffer))
|
||||
use l3::ast::*;
|
||||
|
||||
fn submit(wam: &mut Machine, buffer: &str) -> EvalResult {
|
||||
let result = eval(wam, buffer);
|
||||
wam.reset();
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_queries_on_facts() {
|
||||
let mut wam = Machine::new();
|
||||
|
||||
submit_ss(&mut wam, "p(Z, Z).");
|
||||
submit_ss(&mut wam, "clouds(are, nice).");
|
||||
submit(&mut wam, "p(Z, Z).");
|
||||
submit(&mut wam, "clouds(are, nice).");
|
||||
|
||||
// submit_ss returns true on failure, false on success.
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(Z, Z)."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(Z, z)."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(Z, w)."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(z, w)."), true);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(w, w)."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- clouds(Z, Z)."), true);
|
||||
assert_eq!(submit_ss(&mut wam, "?- clouds(are, Z)."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- clouds(Z, nice)."), false);
|
||||
// submit returns true on failure, false on success.
|
||||
assert_eq!(submit(&mut wam, "?- p(Z, Z).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(Z, z).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(Z, w).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(z, w).").failed_query(), true);
|
||||
assert_eq!(submit(&mut wam, "?- p(w, w).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- clouds(Z, Z).").failed_query(), true);
|
||||
assert_eq!(submit(&mut wam, "?- clouds(are, Z).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- clouds(Z, nice).").failed_query(), false);
|
||||
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(Z, h(Z, W), f(W))."), true);
|
||||
assert_eq!(submit(&mut wam, "?- p(Z, h(Z, W), f(W)).").failed_query(), true);
|
||||
|
||||
submit_ss(&mut wam, "p(Z, h(Z, W), f(W)).");
|
||||
submit(&mut wam, "p(Z, h(Z, W), f(W)).");
|
||||
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(z, h(z, z), f(w))."), true);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(z, h(z, w), f(w))."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(z, h(z, W), f(w))."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(Z, h(Z, w), f(Z))."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(z, h(Z, w), f(Z))."), true);
|
||||
assert_eq!(submit(&mut wam, "?- p(z, h(z, z), f(w)).").failed_query(), true);
|
||||
assert_eq!(submit(&mut wam, "?- p(z, h(z, w), f(w)).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(z, h(z, W), f(w)).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(Z, h(Z, w), f(Z)).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(z, h(Z, w), f(Z)).").failed_query(), true);
|
||||
|
||||
submit_ss(&mut wam, "p(f(X), h(Y, f(a)), Y).");
|
||||
submit(&mut wam, "p(f(X), h(Y, f(a)), Y).");
|
||||
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(Z, h(Z, W), f(W))."), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(Z, h(Z, W), f(W)).").failed_query(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_queries_on_rules() {
|
||||
let mut wam = Machine::new();
|
||||
|
||||
submit_ss(&mut wam, "p(X, Y) :- q(X, Z), r(Z, Y).");
|
||||
submit_ss(&mut wam, "q(q, s).");
|
||||
submit_ss(&mut wam, "r(s, t).");
|
||||
submit(&mut wam, "p(X, Y) :- q(X, Z), r(Z, Y).");
|
||||
submit(&mut wam, "q(q, s).");
|
||||
submit(&mut wam, "r(s, t).");
|
||||
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(X, Y)."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(q, t)."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(t, q)."), true);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(q, T)."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(Q, t)."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(t, t)."), true);
|
||||
assert_eq!(submit(&mut wam, "?- p(X, Y).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(q, t).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(t, q).").failed_query(), true);
|
||||
assert_eq!(submit(&mut wam, "?- p(q, T).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(Q, t).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(t, t).").failed_query(), true);
|
||||
|
||||
submit_ss(&mut wam, "p(X, Y) :- q(f(f(X)), R), r(S, T).");
|
||||
submit_ss(&mut wam, "q(f(f(X)), r).");
|
||||
submit(&mut wam, "p(X, Y) :- q(f(f(X)), R), r(S, T).");
|
||||
submit(&mut wam, "q(f(f(X)), r).");
|
||||
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(X, Y)."), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(X, Y).").failed_query(), false);
|
||||
|
||||
submit_ss(&mut wam, "q(f(f(x)), r).");
|
||||
submit(&mut wam, "q(f(f(x)), r).");
|
||||
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(X, Y)."), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(X, Y).").failed_query(), false);
|
||||
|
||||
submit_ss(&mut wam, "p(X, Y) :- q(X, Y), r(X, Y).");
|
||||
submit_ss(&mut wam, "q(s, t).");
|
||||
submit_ss(&mut wam, "r(X, Y) :- r(a).");
|
||||
submit_ss(&mut wam, "r(a).");
|
||||
submit(&mut wam, "p(X, Y) :- q(X, Y), r(X, Y).");
|
||||
submit(&mut wam, "q(s, t).");
|
||||
submit(&mut wam, "r(X, Y) :- r(a).");
|
||||
submit(&mut wam, "r(a).");
|
||||
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(X, Y)."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(t, S)."), true);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(t, s)."), true);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(s, T)."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(S, t)."), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(X, Y).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(t, S).").failed_query(), true);
|
||||
assert_eq!(submit(&mut wam, "?- p(t, s).").failed_query(), true);
|
||||
assert_eq!(submit(&mut wam, "?- p(s, T).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(S, t).").failed_query(), false);
|
||||
|
||||
submit_ss(&mut wam, "p(f(f(a), g(b), X), g(b), h) :- q(X, Y).");
|
||||
submit_ss(&mut wam, "q(X, Y).");
|
||||
submit(&mut wam, "p(f(f(a), g(b), X), g(b), h) :- q(X, Y).");
|
||||
submit(&mut wam, "q(X, Y).");
|
||||
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(f(X, Y, Z), g(b), h)."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(f(X, g(Y), Z), g(Z), X)."), true);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(f(X, g(Y), Z), g(Z), h)."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(Z, Y, X)."), false);
|
||||
assert_eq!(submit_ss(&mut wam, "?- p(f(X, Y, Z), Y, h)."), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(f(X, Y, Z), g(b), h).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(f(X, g(Y), Z), g(Z), X).").failed_query(), true);
|
||||
assert_eq!(submit(&mut wam, "?- p(f(X, g(Y), Z), g(Z), h).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(Z, Y, X).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(f(X, Y, Z), Y, h).").failed_query(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_queries_on_predicates() {
|
||||
let mut wam = Machine::new();
|
||||
|
||||
submit(&mut wam, "p(X, a). p(b, X).");
|
||||
|
||||
assert_eq!(submit(&mut wam, "?- p(x, Y).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(X, a).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(b, X).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(X, X).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(b, a).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(a, b).").failed_query(), true);
|
||||
|
||||
submit(&mut wam, "p(X, Y, a). p(X, a, Y). p(X, Y, a).");
|
||||
|
||||
assert_eq!(submit(&mut wam, "?- p(c, d, X).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(a, a, a).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(b, c, d).").failed_query(), true);
|
||||
|
||||
submit(&mut wam, "p(X, a). p(X, Y) :- q(Z), p(X, X).");
|
||||
|
||||
assert_eq!(submit(&mut wam, "?- p(X, Y).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(x, a).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(X, a).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(X, b).").failed_query(), true);
|
||||
|
||||
submit(&mut wam, "q(z).");
|
||||
|
||||
assert_eq!(submit(&mut wam, "?- p(X, b).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(x, a).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(X, Y).").failed_query(), false);
|
||||
|
||||
submit(&mut wam, "p(X, a). p(X, Y) :- q(Y), p(X, X).");
|
||||
|
||||
assert_eq!(submit(&mut wam, "?- p(X, Y).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(X, b).").failed_query(), true);
|
||||
|
||||
submit(&mut wam, "p(a, z). p(X, Y) :- q(Y), p(X, Y).");
|
||||
|
||||
assert_eq!(submit(&mut wam, "?- p(X, Y).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(X, z).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(a, z).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(a, X).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(b, a).").failed_query(), true);
|
||||
|
||||
submit(&mut wam, "p(X, Y, Z) :- q(X), r(Y), s(Z).
|
||||
p(a, b, Z) :- q(Z).");
|
||||
|
||||
submit(&mut wam, "q(x).");
|
||||
submit(&mut wam, "r(y).");
|
||||
submit(&mut wam, "s(z).");
|
||||
|
||||
assert_eq!(submit(&mut wam, "?- p(X, Y, Z).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(a, b, c).").failed_query(), true);
|
||||
assert_eq!(submit(&mut wam, "?- p(a, b, C).").failed_query(), false);
|
||||
|
||||
submit(&mut wam, "p(X) :- q(X). p(X) :- r(X).");
|
||||
submit(&mut wam, "q(X) :- a.");
|
||||
submit(&mut wam, "r(X) :- s(X, t). r(X) :- t(X, u).");
|
||||
|
||||
submit(&mut wam, "s(x, t).");
|
||||
submit(&mut wam, "t(y, u).");
|
||||
|
||||
assert_eq!(submit(&mut wam, "?- p(X).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(x).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(y).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(z).").failed_query(), true);
|
||||
|
||||
submit(&mut wam, "p(f(f(X)), h(W), Y) :- g(W), h(W), f(X).
|
||||
p(X, Y, Z) :- h(Y), g(W), z(Z).");
|
||||
submit(&mut wam, "g(f(X)) :- z(X). g(X) :- h(X).");
|
||||
submit(&mut wam, "h(w). h(x). h(z).");
|
||||
submit(&mut wam, "f(s).");
|
||||
submit(&mut wam, "z(Z).");
|
||||
|
||||
assert_eq!(submit(&mut wam, "?- p(X, Y, Z).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(X, X, Z).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(f(f(Z)), Y, Z).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(X, X, X).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(X, Y, X).").failed_query(), false);
|
||||
assert_eq!(submit(&mut wam, "?- p(f(f(X)), h(f(X)), Y).").failed_query(), true);
|
||||
}
|
||||
}
|
||||
|
||||
fn l2_repl() {
|
||||
fn l3_repl() {
|
||||
let mut wam = Machine::new();
|
||||
|
||||
|
||||
loop {
|
||||
print!("l2> ");
|
||||
print!("l3> ");
|
||||
|
||||
let _ = io::stdout().flush();
|
||||
let mut buffer = String::new();
|
||||
let buffer = read();
|
||||
|
||||
io::stdin().read_line(&mut buffer).unwrap();
|
||||
|
||||
if &*buffer == "quit\n" {
|
||||
if buffer == "quit\n" {
|
||||
break;
|
||||
} else if &*buffer == "clear\n" {
|
||||
} else if buffer == "clear\n" {
|
||||
wam = Machine::new();
|
||||
continue;
|
||||
}
|
||||
|
||||
submit(&mut wam, buffer);
|
||||
let result = eval(&mut wam, buffer.trim());
|
||||
print(&mut wam, result);
|
||||
|
||||
wam.reset();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
l2_repl();
|
||||
l3_repl();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user