add provisions for arithmetic support.

This commit is contained in:
Mark Thom
2017-11-05 17:12:30 -07:00
parent a70c100e0d
commit 5568d0b370
15 changed files with 792 additions and 118 deletions

2
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
[root] [root]
name = "rusty-wam" name = "rusty-wam"
version = "0.7.1" version = "0.7.2"
dependencies = [ dependencies = [
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "rusty-wam" name = "rusty-wam"
version = "0.7.1" version = "0.7.2"
authors = ["Mark Thom"] authors = ["Mark Thom"]
[dependencies] [dependencies]

View File

@@ -19,15 +19,15 @@ Extend rusty-wam to include the following, among other features:
* ISO Prolog compliant throw/catch (_done_). * ISO Prolog compliant throw/catch (_done_).
* Built-in and user-defined operators of all fixities, with custom * Built-in and user-defined operators of all fixities, with custom
associativity and precedence (_done_). associativity and precedence (_done_).
* Bignum and floating point arithmetic. * Bignum and floating point arithmetic (_in progress_).
* Built-in control operators (`,`, `;`, `->`, etc.). * Built-in control operators (`,`, `;`, `->`, etc.).
* Built-in predicates for list processing and top-level declarative
control (`setup_call_control/3`, `call_with_inference_limit/3`,
etc.)
* Attributed variables using the SICStus Prolog interface and * Attributed variables using the SICStus Prolog interface and
semantics. Adding coroutines like `dif/2`, `freeze/2`, etc. semantics. Adding coroutines like `dif/2`, `freeze/2`, etc.
is straightforward with attributed variables. is straightforward with attributed variables.
* An occurs check. * An occurs check.
* Built-in predicates for list processing and top-level declarative
control (`setup_call_control/3`, `call_with_inference_limit/3`,
etc.)
* Mode declarations. * Mode declarations.
* Extensions for clp(FD). * Extensions for clp(FD).
* `if_` and related predicates, following the developments of the * `if_` and related predicates, following the developments of the
@@ -46,6 +46,8 @@ ideally, very fast) [Shen](http://shenlanguage.org) implementation.
The following predicates are built-in to rusty-wam. The following predicates are built-in to rusty-wam.
* Arithmetic support:
** is/2 works for (+)/2, (-)/{1,2}, (*)/2, (//)/2, (div)/2.
* atomic/1 * atomic/1
* call/N (1 <= N <= 63) * call/N (1 <= N <= 63)
* catch/3 * catch/3
@@ -128,14 +130,11 @@ Note that the values of variables belonging to successful queries are
printed out, on one line each. Uninstantiated variables are denoted by printed out, on one line each. Uninstantiated variables are denoted by
a number preceded by an underscore (`X = _0` in an example above). a number preceded by an underscore (`X = _0` in an example above).
Lastly, rusty-wam supports dynamic operators: Lastly, rusty-wam supports dynamic operators. Using the built-in
arithmetic operators with the usual precedences,
``` ```
prolog> :- op(500, yfx, +). prolog> ?- X = -5 + 3 - (2 * 4) // 8.
prolog> :- op(500, yfx, -).
prolog> :- op(200, fy, -).
prolog> :- op(400, yfx, *).
prolog> :- op(400, yfx, /).
prolog> ?- X = -5 + 3 - (2 * 4) / 8.
true. true.
X = -(+(-(5), 3), /(*(2, 4), 8)). X = -(+(-(5), 3), /(*(2, 4), 8)).
``` ```

View File

@@ -734,6 +734,41 @@ mod tests {
assert_eq!(submit(&mut wam, "?- catch(f(X), E, handle_top(E))."), true); assert_eq!(submit(&mut wam, "?- catch(f(X), E, handle_top(E))."), true);
} }
#[test]
fn test_queries_on_arithmetic()
{
let mut wam = Machine::new();
assert_eq!(submit(&mut wam, "?- X is 1, X is X."), true);
assert_eq!(submit(&mut wam, "?- X is 1, X is X + 1."), false);
assert_eq!(submit(&mut wam, "?- X is 1, X is X + 0."), true);
assert_eq!(submit(&mut wam, "?- X is 1, X is X * 1."), true);
assert_eq!(submit(&mut wam, "?- X is 1, X is X * 2."), false);
assert_eq!(submit(&mut wam, "?- X is 1 + a."), false);
assert_eq!(submit(&mut wam, "?- X is 1 + Y."), false);
assert_eq!(submit(&mut wam, "?- Y is 2 + 2 - 2, X is 1 + Y, X = 3."), true);
assert_eq!(submit(&mut wam, "?- Y is 2 + 2 - 2, X is 1 + Y, X = 2."), false);
assert_eq!(submit(&mut wam, "?- 6 is 6."), true);
assert_eq!(submit(&mut wam, "?- 6 is 3 + 3."), true);
assert_eq!(submit(&mut wam, "?- 6 is 3 * 2."), true);
assert_eq!(submit(&mut wam, "?- 7 is 3 * 2."), false);
assert_eq!(submit(&mut wam, "?- 7 is 3.5 * 2."), false);
assert_eq!(submit(&mut wam, "?- 7.0 is 3.5 * 2."), true);
submit(&mut wam, "f(X) :- X is 5 // 0.");
assert_eq!(submit(&mut wam, "?- catch(f(X), evaluation_error(E), true), E = zero_divisor."),
true);
assert_eq!(submit(&mut wam, "?- X is ((3 + 4) // 2) + 2 - 1 // 1, Y is 2+2, Z is X+Y."),
true);
assert_eq!(submit(&mut wam, "?- X is ((3 + 4) // 2) + 2 - 1 // 1, Y is 2+2, Z = 8, Y is 4."),
true);
}
} }
fn process_buffer(wam: &mut Machine, buffer: &str) fn process_buffer(wam: &mut Machine, buffer: &str)

View File

@@ -12,13 +12,13 @@ pub struct Frame {
} }
impl Frame { impl Frame {
fn new(global_index: usize, e: usize, cp: CodePtr, n: usize) -> Self { fn new(global_index: usize, sz: usize, e: usize, cp: CodePtr, n: usize) -> Self {
Frame { Frame {
global_index: global_index, global_index: global_index,
b0: 0, b0: 0,
e: e, e: e,
cp: cp, cp: cp,
perms: vec![Addr::HeapCell(0); n] perms: (1 .. n+1).map(|i| Addr::StackCell(sz, i)).collect()
} }
} }
} }
@@ -31,7 +31,8 @@ impl AndStack {
} }
pub fn push(&mut self, global_index: usize, e: usize, cp: CodePtr, n: usize) { pub fn push(&mut self, global_index: usize, e: usize, cp: CodePtr, n: usize) {
self.0.push(Frame::new(global_index, e, cp, n)); let len = self.0.len();
self.0.push(Frame::new(global_index, len, e, cp, n));
} }
#[allow(dead_code)] #[allow(dead_code)]
@@ -46,13 +47,6 @@ impl AndStack {
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.0.clear() self.0.clear()
} }
// drop the last n frames.
#[allow(dead_code)]
pub fn drop_frames(&mut self, n: usize) {
let len = self.0.len();
self.0.truncate(len - n);
}
} }
impl Index<usize> for AndStack { impl Index<usize> for AndStack {

233
src/prolog/arithmetic.rs Normal file
View File

@@ -0,0 +1,233 @@
use prolog::ast::*;
use std::cell::Cell;
use std::cmp::{min, max};
use std::vec::Vec;
pub struct ArithExprIterator<'a> {
state_stack: Vec<IteratorState<'a>>
}
impl<'a> ArithExprIterator<'a> {
fn push_clause(&mut self, child_num: usize, ct: ClauseType<'a>, child_terms: &'a Vec<Box<Term>>)
{
self.state_stack.push(IteratorState::Clause(child_num, ct, child_terms));
}
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
self.state_stack.push(IteratorState::to_state(lvl, term));
}
fn push_final_cons(&mut self, lvl: Level, cell: &'a Cell<RegType>, head: &'a Term, tail: &'a Term)
{
self.state_stack.push(IteratorState::FinalCons(lvl, cell, head, tail));
}
fn new(term: &'a Term) -> Result<Self, ArithmeticError> {
let state = match term {
&Term::AnonVar =>
return Err(ArithmeticError::InvalidTerm),
&Term::Clause(_, _, ref terms) =>
IteratorState::Clause(0, ClauseType::Root, terms),
&Term::Constant(ref cell, ref cons) =>
IteratorState::Constant(Level::Shallow, cell, cons),
&Term::Cons(_, _, _) =>
return Err(ArithmeticError::InvalidTerm),
&Term::Var(ref cell, ref var) =>
IteratorState::Var(Level::Shallow, cell, var)
};
Ok(ArithExprIterator { state_stack: vec![state] })
}
}
impl Term {
pub fn arith_expr_iter<'a>(&'a self) -> Result<ArithExprIterator<'a>, ArithmeticError> {
ArithExprIterator::new(self)
}
}
impl<'a> Iterator for ArithExprIterator<'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::AnonVar(lvl) =>
return Some(TermRef::AnonVar(lvl)),
IteratorState::Clause(child_num, ct, child_terms) => {
if child_num == child_terms.len() {
return Some(TermRef::Clause(ct, child_terms));
} else {
self.push_clause(child_num + 1, ct, child_terms);
self.push_subterm(ct.level_of_subterms(), child_terms[child_num].as_ref());
}
},
IteratorState::InitialCons(lvl, cell, head, tail) => {
self.push_final_cons(lvl, cell, head, tail);
self.push_subterm(Level::Deep, tail);
self.push_subterm(Level::Deep, head);
},
IteratorState::FinalCons(lvl, cell, head, tail) =>
return Some(TermRef::Cons(lvl, cell, head, tail)),
IteratorState::Constant(lvl, cell, constant) =>
return Some(TermRef::Constant(lvl, cell, constant)),
IteratorState::Var(lvl, cell, var) =>
return Some(TermRef::Var(lvl, cell, var))
};
}
None
}
}
pub struct ArithmeticEvaluator {
interm: Vec<ArithmeticTerm>,
interm_c: usize
}
impl ArithmeticEvaluator {
pub fn new() -> Self {
ArithmeticEvaluator { interm: Vec::new(), interm_c: 1 }
}
fn get_un_instr(name: &Atom, a1: ArithmeticTerm, t: ArithEvalPlace)
-> Result<ArithmeticInstruction, ArithmeticError>
{
match name.as_str() {
"-" => Ok(ArithmeticInstruction::Neg(a1, t)),
_ => Err(ArithmeticError::InvalidOp)
}
}
fn gen_bin_instr(name: &Atom, a1: ArithmeticTerm, a2: ArithmeticTerm, t: ArithEvalPlace)
-> Result<ArithmeticInstruction, ArithmeticError>
{
match name.as_str() {
"+" => Ok(ArithmeticInstruction::Add(a1, a2, t)),
"-" => Ok(ArithmeticInstruction::Sub(a1, a2, t)),
"//" => Ok(ArithmeticInstruction::IDiv(a1, a2, t)),
"div" => Ok(ArithmeticInstruction::IDiv(a1, a2, t)),
"*" => Ok(ArithmeticInstruction::Mul(a1, a2, t)),
_ => Err(ArithmeticError::InvalidOp)
}
}
fn incr_interm(&mut self) -> usize {
let temp = self.interm_c;
self.interm.push(ArithmeticTerm::Interm(temp));
self.interm_c += 1;
temp
}
fn instr_from_clause<'a>(&mut self, name: &'a Atom, terms: &'a Vec<Box<Term>>, deep: bool)
-> Result<ArithmeticInstruction, ArithmeticError>
{
match terms.len() {
1 => {
let a1 = self.interm.pop().unwrap();
if deep {
let ninterm = if a1.interm_or(0) == 0 {
self.incr_interm()
} else {
self.interm.push(a1.clone());
a1.interm_or(0)
};
Self::get_un_instr(name, a1, ArithEvalPlace::Interm(ninterm))
} else {
Self::get_un_instr(name, a1, ArithEvalPlace::Reg(RegType::Temp(2)))
}
},
2 => {
let a2 = self.interm.pop().unwrap();
let a1 = self.interm.pop().unwrap();
if deep {
let min_interm = min(a1.interm_or(0), a2.interm_or(0));
let ninterm = if min_interm == 0 {
let max_interm = max(a1.interm_or(0), a2.interm_or(0));
if max_interm == 0 {
self.incr_interm()
} else {
self.interm.push(ArithmeticTerm::Interm(max_interm));
self.interm_c = max_interm + 1;
max_interm
}
} else {
self.interm.push(ArithmeticTerm::Interm(min_interm));
self.interm_c = min_interm + 1;
min_interm
};
Self::gen_bin_instr(name, a1, a2, ArithEvalPlace::Interm(ninterm))
} else {
Self::gen_bin_instr(name, a1, a2, ArithEvalPlace::Reg(RegType::Temp(2)))
}
},
_ => Err(ArithmeticError::InvalidOp)
}
}
fn push_constant(&mut self, c: &Constant) -> Result<(), ArithmeticError> {
match c {
&Constant::Float(fl) =>
self.interm.push(ArithmeticTerm::Float(fl)),
&Constant::Integer(ref bi) =>
self.interm.push(ArithmeticTerm::Integer(bi.clone())),
_ =>
return Err(ArithmeticError::InvalidAtom),
}
Ok(())
}
pub fn eval(&mut self, term: &Term) -> Result<Code, ArithmeticError> {
let mut code = Vec::new();
for term_ref in term.arith_expr_iter()? {
match term_ref {
TermRef::Constant(_, _, c) =>
try!(self.push_constant(c)),
TermRef::Var(_, var_reg, _) =>
if var_reg.get().norm().reg_num() == 0 {
return Err(ArithmeticError::UninstantiatedVar);
} else {
self.interm.push(ArithmeticTerm::Reg(var_reg.get().norm()));
},
TermRef::Clause(ClauseType::Deep(_, _, name), terms) => {
code.push(Line::Arithmetic(self.instr_from_clause(name, terms, true)?));
},
TermRef::Clause(ClauseType::Root, terms) => {
let name = term.name().unwrap();
code.push(Line::Arithmetic(self.instr_from_clause(name, terms, false)?));
},
_ =>
return Err(ArithmeticError::InvalidTerm)
}
}
if let Some(arith_term) = self.interm.pop() {
match arith_term {
ArithmeticTerm::Integer(n) => {
let n = Constant::Integer(n);
code.push(query![put_constant!(Level::Shallow, n, temp_v!(2))]);
},
ArithmeticTerm::Float(n) => {
let n = Constant::Float(n);
code.push(query![put_constant!(Level::Shallow, n, temp_v!(2))]);
},
ArithmeticTerm::Reg(r) =>
code.push(query![put_value!(r, 2)]),
_ => return Err(ArithmeticError::InvalidTerm)
};
}
Ok(code)
}
}

View File

@@ -1,4 +1,5 @@
use prolog::num::bigint::BigInt; use prolog::num::bigint::BigInt;
use prolog::num::ToPrimitive;
use prolog::ordered_float::*; use prolog::ordered_float::*;
@@ -8,7 +9,7 @@ use std::collections::{HashMap, VecDeque};
use std::fmt; use std::fmt;
use std::io::Error as IOError; use std::io::Error as IOError;
use std::num::{ParseFloatError}; use std::num::{ParseFloatError};
use std::ops::{Add, AddAssign}; use std::ops::{Add, AddAssign, Sub, Mul, Div, Neg};
use std::str::Utf8Error; use std::str::Utf8Error;
use std::vec::Vec; use std::vec::Vec;
@@ -189,6 +190,14 @@ macro_rules! prefix {
($x:expr) => ($x & (FX | FY)) ($x:expr) => ($x & (FX | FY))
} }
#[derive(Debug, Clone, Copy)]
pub enum ArithmeticError {
InvalidAtom,
InvalidOp,
InvalidTerm,
UninstantiatedVar
}
/* 'TokenTooLong' is hard to detect reliably if we don't process the /* 'TokenTooLong' is hard to detect reliably if we don't process the
input one character at a time. It would be easy to detect if the regex input one character at a time. It would be easy to detect if the regex
library supported matching on iterator inputs, but it currently does library supported matching on iterator inputs, but it currently does
@@ -198,6 +207,7 @@ tokens exceeding 4096 chars in length. */
#[derive(Debug)] #[derive(Debug)]
pub enum ParserError pub enum ParserError
{ {
Arithmetic(ArithmeticError),
CommaArityMismatch, CommaArityMismatch,
UnexpectedEOF, UnexpectedEOF,
FailedMatch(String), FailedMatch(String),
@@ -206,13 +216,19 @@ pub enum ParserError
InadmissibleQueryTerm, InadmissibleQueryTerm,
IncompleteReduction, IncompleteReduction,
InconsistentDeclaration, InconsistentDeclaration,
InconsistentPredicate, InconsistentPredicate,
ParseBigInt, ParseBigInt,
ParseFloat(ParseFloatError), ParseFloat(ParseFloatError),
// TokenTooLong, // TokenTooLong,
Utf8Conversion(Utf8Error) Utf8Conversion(Utf8Error)
} }
impl From<ArithmeticError> for ParserError {
fn from(err: ArithmeticError) -> ParserError {
ParserError::Arithmetic(err)
}
}
impl From<IOError> for ParserError { impl From<IOError> for ParserError {
fn from(err: IOError) -> ParserError { fn from(err: IOError) -> ParserError {
ParserError::IO(err) ParserError::IO(err)
@@ -265,6 +281,15 @@ impl fmt::Display for Constant {
} }
} }
impl From<Number> for Constant {
fn from(n: Number) -> Self {
match n {
Number::Integer(n) => Constant::Integer(n),
Number::Float(f) => Constant::Float(f)
}
}
}
pub enum Term { pub enum Term {
AnonVar, AnonVar,
Clause(Cell<RegType>, Atom, Vec<Box<Term>>), Clause(Cell<RegType>, Atom, Vec<Box<Term>>),
@@ -273,10 +298,11 @@ pub enum Term {
Var(Cell<VarReg>, Var) Var(Cell<VarReg>, Var)
} }
pub enum QueryTerm { pub enum QueryTerm {
CallN(Vec<Box<Term>>), CallN(Vec<Box<Term>>),
Catch(Vec<Box<Term>>), Catch(Vec<Box<Term>>),
Cut, Cut,
Is(Vec<Box<Term>>),
IsAtomic(Vec<Box<Term>>), IsAtomic(Vec<Box<Term>>),
IsVar(Vec<Box<Term>>), IsVar(Vec<Box<Term>>),
Term(Term), Term(Term),
@@ -292,6 +318,8 @@ impl QueryTerm {
QueryTermRef::Catch(terms), QueryTermRef::Catch(terms),
&QueryTerm::Cut => &QueryTerm::Cut =>
QueryTermRef::Cut, QueryTermRef::Cut,
&QueryTerm::Is(ref terms) =>
QueryTermRef::Is(terms),
&QueryTerm::IsAtomic(ref terms) => &QueryTerm::IsAtomic(ref terms) =>
QueryTermRef::IsAtomic(terms.first().unwrap()), QueryTermRef::IsAtomic(terms.first().unwrap()),
&QueryTerm::IsVar(ref terms) => &QueryTerm::IsVar(ref terms) =>
@@ -314,13 +342,14 @@ pub enum ClauseType<'a> {
CallN, CallN,
Catch, Catch,
Deep(Level, &'a Cell<RegType>, &'a Atom), Deep(Level, &'a Cell<RegType>, &'a Atom),
Is,
Root, Root,
Throw, Throw,
} }
impl<'a> ClauseType<'a> { impl<'a> ClauseType<'a> {
pub fn level_of_subterms(self) -> Level { pub fn level_of_subterms(self) -> Level {
match self { match self {
ClauseType::Deep(_, _, _) => Level::Deep, ClauseType::Deep(_, _, _) => Level::Deep,
_ => Level::Shallow _ => Level::Shallow
} }
@@ -354,6 +383,7 @@ pub enum QueryTermRef<'a> {
CallN(&'a Vec<Box<Term>>), CallN(&'a Vec<Box<Term>>),
Catch(&'a Vec<Box<Term>>), Catch(&'a Vec<Box<Term>>),
Cut, Cut,
Is(&'a Vec<Box<Term>>),
IsAtomic(&'a Term), IsAtomic(&'a Term),
IsVar(&'a Term), IsVar(&'a Term),
Term(&'a Term), Term(&'a Term),
@@ -365,6 +395,7 @@ impl<'a> QueryTermRef<'a> {
match self { match self {
QueryTermRef::Catch(_) => 3, QueryTermRef::Catch(_) => 3,
QueryTermRef::Throw(_) => 1, QueryTermRef::Throw(_) => 1,
QueryTermRef::Is(_) => 2,
QueryTermRef::IsAtomic(_) => 1, QueryTermRef::IsAtomic(_) => 1,
QueryTermRef::IsVar(_) => 1, QueryTermRef::IsVar(_) => 1,
QueryTermRef::CallN(terms) => terms.len(), QueryTermRef::CallN(terms) => terms.len(),
@@ -412,6 +443,132 @@ impl IndexedChoiceInstruction {
} }
} }
#[derive(Clone)]
pub enum Number {
Float(OrderedFloat<f64>),
Integer(BigInt)
}
impl Add<Number> for Number {
type Output = Number;
fn add(self, rhs: Number) -> Self::Output {
match (self, rhs) {
(Number::Integer(n1), Number::Integer(n2)) =>
Number::Integer(n1 + n2),
(Number::Float(n1), Number::Float(n2)) =>
Number::Float(OrderedFloat(n1.into_inner() + n2.into_inner())),
(Number::Integer(n1), Number::Float(n2))
| (Number::Float(n2), Number::Integer(n1)) =>
match n1.to_f64() {
Some(n1) => Number::Float(OrderedFloat(n1 + n2.into_inner())),
None => Number::Integer(n1)
}
}
}
}
impl Sub<Number> for Number {
type Output = Number;
fn sub(self, rhs: Number) -> Self::Output {
match (self, rhs) {
(Number::Integer(n1), Number::Integer(n2)) =>
Number::Integer(n1 - n2),
(Number::Float(n1), Number::Float(n2)) =>
Number::Float(OrderedFloat(n1.into_inner() - n2.into_inner())),
(Number::Integer(n1), Number::Float(n2))
| (Number::Float(n2), Number::Integer(n1)) =>
match n1.to_f64() {
Some(n1) => Number::Float(OrderedFloat(n1 - n2.into_inner())),
None => Number::Integer(n1)
}
}
}
}
impl Mul<Number> for Number {
type Output = Number;
fn mul(self, rhs: Number) -> Self::Output {
match (self, rhs) {
(Number::Integer(n1), Number::Integer(n2)) =>
Number::Integer(n1 * n2),
(Number::Float(n1), Number::Float(n2)) =>
Number::Float(OrderedFloat(n1.into_inner() * n2.into_inner())),
(Number::Integer(n1), Number::Float(n2))
| (Number::Float(n2), Number::Integer(n1)) =>
match n1.to_f64() {
Some(n1) => Number::Float(OrderedFloat(n1 * n2.into_inner())),
None => Number::Integer(n1)
}
}
}
}
/*TODO: reserved for proper division.
impl Div<Number> for Number {
type Output = Number;
fn div(self, rhs: Number) -> Self::Output {
match (self, rhs) {
(Number::Integer(n1), Number::Integer(n2)) =>
Number::Integer(n1 / n2),
(Number::Float(n1), Number::Float(n2)) =>
Number::Float(OrderedFloat(n1.into_inner() / n2.into_inner())),
(Number::Integer(n1), Number::Float(n2))
| (Number::Float(n2), Number::Integer(n1)) =>
match n1.to_f64() {
Some(n1) => Number::Float(OrderedFloat(n1 / n2.into_inner())),
None => Number::Integer(n1)
}
}
}
}
*/
impl Neg for Number {
type Output = Number;
fn neg(self) -> Self::Output {
match self {
Number::Integer(n) => Number::Integer(-n),
Number::Float(f) => Number::Float(OrderedFloat(-1.0 * f.into_inner()))
}
}
}
#[derive(Clone)]
pub enum ArithmeticTerm {
Reg(RegType),
Interm(usize),
Float(OrderedFloat<f64>),
Integer(BigInt)
}
impl ArithmeticTerm {
pub fn interm_or(&self, interm: usize) -> usize {
if let &ArithmeticTerm::Interm(interm) = self {
interm
} else {
interm
}
}
}
#[derive(Clone, Copy)]
pub enum ArithEvalPlace {
Interm(usize), Reg(RegType)
}
pub enum ArithmeticInstruction {
Add(ArithmeticTerm, ArithmeticTerm, ArithEvalPlace),
Sub(ArithmeticTerm, ArithmeticTerm, ArithEvalPlace),
Mul(ArithmeticTerm, ArithmeticTerm, ArithEvalPlace),
IDiv(ArithmeticTerm, ArithmeticTerm, ArithEvalPlace),
Neg(ArithmeticTerm, ArithEvalPlace)
}
pub enum BuiltInInstruction { pub enum BuiltInInstruction {
CleanUpBlock, CleanUpBlock,
DuplicateTerm, DuplicateTerm,
@@ -442,7 +599,9 @@ pub enum ControlInstruction {
Goto(usize, usize), // p, arity. Goto(usize, usize), // p, arity.
Proceed, Proceed,
ThrowCall, ThrowCall,
ThrowExecute ThrowExecute,
UnifyCall,
UnifyExecute
} }
impl ControlInstruction { impl ControlInstruction {
@@ -458,6 +617,8 @@ impl ControlInstruction {
&ControlInstruction::ThrowExecute => true, &ControlInstruction::ThrowExecute => true,
&ControlInstruction::Goto(_, _) => true, &ControlInstruction::Goto(_, _) => true,
&ControlInstruction::Proceed => true, &ControlInstruction::Proceed => true,
&ControlInstruction::UnifyCall => true,
&ControlInstruction::UnifyExecute => true,
_ => false _ => false
} }
} }
@@ -508,6 +669,7 @@ pub type CompiledFact = Vec<FactInstruction>;
pub type CompiledQuery = Vec<QueryInstruction>; pub type CompiledQuery = Vec<QueryInstruction>;
pub enum Line { pub enum Line {
Arithmetic(ArithmeticInstruction),
BuiltIn(BuiltInInstruction), BuiltIn(BuiltInInstruction),
Choice(ChoiceInstruction), Choice(ChoiceInstruction),
Control(ControlInstruction), Control(ControlInstruction),
@@ -700,3 +862,29 @@ impl Term {
} }
} }
} }
pub enum IteratorState<'a> {
AnonVar(Level),
Clause(usize, ClauseType<'a>, &'a Vec<Box<Term>>),
Constant(Level, &'a Cell<RegType>, &'a Constant),
InitialCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
FinalCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
Var(Level, &'a Cell<VarReg>, &'a Var)
}
impl<'a> IteratorState<'a> {
pub fn to_state(lvl: Level, term: &'a Term) -> IteratorState<'a> {
match term {
&Term::AnonVar =>
IteratorState::AnonVar(lvl),
&Term::Clause(ref cell, ref atom, ref child_terms) =>
IteratorState::Clause(0, ClauseType::Deep(lvl, cell, atom), child_terms),
&Term::Cons(ref cell, ref head, ref tail) =>
IteratorState::InitialCons(lvl, cell, head.as_ref(), tail.as_ref()),
&Term::Constant(ref cell, ref constant) =>
IteratorState::Constant(lvl, cell, constant),
&Term::Var(ref cell, ref var) =>
IteratorState::Var(lvl, cell, var)
}
}
}

View File

@@ -104,7 +104,7 @@ fn get_builtins() -> Code {
proceed!(), proceed!(),
fact![get_value!(temp_v!(1), 2)], // =/2, 73. fact![get_value!(temp_v!(1), 2)], // =/2, 73.
proceed!(), proceed!(),
succeed!(), // true/0, 75. proceed!() // true/0, 75.
] ]
} }
@@ -118,8 +118,19 @@ pub fn build_code_dir() -> (Code, CodeDir, OpDir)
op_dir.insert((String::from(":-"), Fixity::In), (XFX, 1200)); op_dir.insert((String::from(":-"), Fixity::In), (XFX, 1200));
op_dir.insert((String::from(":-"), Fixity::Pre), (FX, 1200)); op_dir.insert((String::from(":-"), Fixity::Pre), (FX, 1200));
op_dir.insert((String::from("?-"), Fixity::Pre), (FX, 1200)); op_dir.insert((String::from("?-"), Fixity::Pre), (FX, 1200));
// control operators.
op_dir.insert((String::from("\\+"), Fixity::Pre), (FY, 900)); op_dir.insert((String::from("\\+"), Fixity::Pre), (FY, 900));
op_dir.insert((String::from("="), Fixity::In), (XFX, 700)); op_dir.insert((String::from("="), Fixity::In), (XFX, 700));
// arithmetic operators.
op_dir.insert((String::from("is"), Fixity::In), (XFX, 700));
op_dir.insert((String::from("+"), Fixity::In), (YFX, 500));
op_dir.insert((String::from("-"), Fixity::In), (YFX, 500));
op_dir.insert((String::from("//"), Fixity::In), (YFX, 400));
op_dir.insert((String::from("div"), Fixity::In), (YFX, 400));
op_dir.insert((String::from("*"), Fixity::In), (YFX, 400));
op_dir.insert((String::from("-"), Fixity::Pre), (FY, 200));
// there are 63 registers in the VM, so call/N is defined for all 0 <= N <= 62 // 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) // (an extra register is needed for the predicate name)

View File

@@ -1,4 +1,5 @@
use prolog::allocator::*; use prolog::allocator::*;
use prolog::arithmetic::*;
use prolog::ast::*; use prolog::ast::*;
use prolog::fixtures::*; use prolog::fixtures::*;
use prolog::indexing::*; use prolog::indexing::*;
@@ -14,7 +15,10 @@ pub struct CodeGenerator<'a, TermMarker> {
} }
pub enum EvalSession<'a> { pub enum EvalSession<'a> {
EntryFailure(String), OpIsInfixAndPostFix,
NamelessEntry,
ParserError(ParserError),
ImpermissibleEntry(String),
EntrySuccess, EntrySuccess,
InitialQuerySuccess(AllocVarDict<'a>, HeapVarDict<'a>), InitialQuerySuccess(AllocVarDict<'a>, HeapVarDict<'a>),
QueryFailure, QueryFailure,
@@ -256,11 +260,11 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
if let &mut Line::Control(ref mut ctrl) = code.last_mut().unwrap() { if let &mut Line::Control(ref mut ctrl) = code.last_mut().unwrap() {
*ctrl = ControlInstruction::ThrowExecute; *ctrl = ControlInstruction::ThrowExecute;
}, },
QueryTermRef::IsAtomic(_) => { QueryTermRef::Is(_) =>
dealloc_index = code.len(); if let &mut Line::Control(ref mut ctrl) = code.last_mut().unwrap() {
code.push(proceed!()); *ctrl = ControlInstruction::UnifyExecute;
}, },
QueryTermRef::IsVar(_) => { QueryTermRef::IsAtomic(_) | QueryTermRef::IsVar(_) => {
dealloc_index = code.len(); dealloc_index = code.len();
code.push(proceed!()); code.push(proceed!());
}, },
@@ -275,6 +279,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
conjunct_info: &ConjunctInfo<'a>, conjunct_info: &ConjunctInfo<'a>,
code: &mut Code, code: &mut Code,
is_exposed: bool) is_exposed: bool)
-> Result<(), ParserError>
{ {
for (chunk_num, _, terms) in iter { for (chunk_num, _, terms) in iter {
for (i, term) in terms.iter().enumerate() for (i, term) in terms.iter().enumerate()
@@ -300,13 +305,47 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
Line::Cut(CutInstruction::Cut(Terminal::Terminal)) Line::Cut(CutInstruction::Cut(Terminal::Terminal))
}); });
}, },
&QueryTermRef::Is(terms) => {
let mut evaluator = ArithmeticEvaluator::new();
let mut arith_code = evaluator.eval(terms[1].as_ref())?;
code.append(&mut arith_code);
match terms[0].as_ref() {
&Term::Var(ref vr, ref name) => {
let mut target = Vec::new();
self.marker.advance(term_loc, *term);
self.marker.mark_var(name, Level::Shallow, vr, term_loc, &mut target);
code.push(Line::Query(target));
code.push(unify_call!());
},
&Term::Constant(_, Constant::Float(fl)) => {
code.push(query![put_constant!(Level::Shallow,
Constant::Float(fl),
temp_v!(1))]);
code.push(unify_call!());
},
&Term::Constant(_, Constant::Integer(ref bi)) => {
let bi = bi.clone();
code.push(query![put_constant!(Level::Shallow,
Constant::Integer(bi),
temp_v!(1))]);
code.push(unify_call!());
},
_ => {
return Err(ParserError::from(ArithmeticError::InvalidTerm));
}
}
},
&QueryTermRef::IsAtomic(inner_term) => &QueryTermRef::IsAtomic(inner_term) =>
match inner_term { match inner_term {
&Term::AnonVar | &Term::Clause(_, _, _) | &Term::Cons(_, _, _) => { &Term::AnonVar | &Term::Clause(_, _, _) | &Term::Cons(_, _, _) => {
code.push(fail!()); //goto!(61, 0)); // goto false/0. code.push(fail!());
}, },
&Term::Constant(_, _) => { &Term::Constant(_, _) => {
code.push(succeed!()); //goto!(75, 0)); // goto succeed/0. code.push(succeed!());
}, },
&Term::Var(ref vr, ref name) => { &Term::Var(ref vr, ref name) => {
let mut target = Vec::new(); let mut target = Vec::new();
@@ -321,10 +360,10 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
&QueryTermRef::IsVar(inner_term) => &QueryTermRef::IsVar(inner_term) =>
match inner_term { match inner_term {
&Term::Constant(_, _) | &Term::Clause(_, _, _) | &Term::Cons(_, _, _) => { &Term::Constant(_, _) | &Term::Clause(_, _, _) | &Term::Cons(_, _, _) => {
code.push(fail!()); //goto!(61, 0)); // goto false/0. code.push(fail!());
}, },
&Term::AnonVar => { &Term::AnonVar => {
code.push(succeed!()); //goto!(75, 0)); // goto succeed/0. code.push(succeed!());
}, },
&Term::Var(ref vr, ref name) => { &Term::Var(ref vr, ref name) => {
let mut target = Vec::new(); let mut target = Vec::new();
@@ -352,6 +391,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
self.marker.reset_contents(); self.marker.reset_contents();
} }
} }
Ok(())
} }
fn compile_seq_prelude(&mut self, conjunct_info: &ConjunctInfo, body: &mut Code) fn compile_seq_prelude(&mut self, conjunct_info: &ConjunctInfo, body: &mut Code)
@@ -376,7 +417,7 @@ 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) -> Result<Code, ParserError>
{ {
let iter = ChunkedIterator::from_rule(rule); let iter = ChunkedIterator::from_rule(rule);
let conjunct_info = self.collect_var_data(iter); let conjunct_info = self.collect_var_data(iter);
@@ -393,7 +434,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
} }
let iter = ChunkedIterator::from_rule_body(p1, clauses); let iter = ChunkedIterator::from_rule_body(p1, clauses);
self.compile_seq(iter, &conjunct_info, &mut code, false); try!(self.compile_seq(iter, &conjunct_info, &mut code, false));
if conjunct_info.allocates() { if conjunct_info.allocates() {
let index = if let &Line::Control(_) = code.last().unwrap() { let index = if let &Line::Control(_) = code.last().unwrap() {
@@ -408,7 +449,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
} }
Self::compile_cleanup(&mut code, &conjunct_info, clauses.last().unwrap_or(p1).to_ref()); Self::compile_cleanup(&mut code, &conjunct_info, clauses.last().unwrap_or(p1).to_ref());
code Ok(code)
} }
fn mark_unsafe_fact_vars(&self, fact: &mut CompiledFact) fn mark_unsafe_fact_vars(&self, fact: &mut CompiledFact)
@@ -476,7 +517,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
Self::add_conditional_call(code, term, index); Self::add_conditional_call(code, term, index);
} }
pub fn compile_query(&mut self, query: &'a Vec<QueryTerm>) -> Code pub fn compile_query(&mut self, query: &'a Vec<QueryTerm>) -> Result<Code, ParserError>
{ {
let iter = ChunkedIterator::from_term_sequence(query); let iter = ChunkedIterator::from_term_sequence(query);
let conjunct_info = self.collect_var_data(iter); let conjunct_info = self.collect_var_data(iter);
@@ -485,7 +526,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
self.compile_seq_prelude(&conjunct_info, &mut code); self.compile_seq_prelude(&conjunct_info, &mut code);
let iter = ChunkedIterator::from_term_sequence(query); let iter = ChunkedIterator::from_term_sequence(query);
self.compile_seq(iter, &conjunct_info, &mut code, true); try!(self.compile_seq(iter, &conjunct_info, &mut code, true));
if conjunct_info.allocates() { if conjunct_info.allocates() {
let index = if let &Line::Control(_) = code.last().unwrap() { let index = if let &Line::Control(_) = code.last().unwrap() {
@@ -500,7 +541,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
} }
Self::compile_cleanup(&mut code, &conjunct_info, query.last().unwrap().to_ref()); Self::compile_cleanup(&mut code, &conjunct_info, query.last().unwrap().to_ref());
code Ok(code)
} }
fn split_predicate(clauses: &Vec<PredicateClause>) -> Vec<(usize, usize)> fn split_predicate(clauses: &Vec<PredicateClause>) -> Vec<(usize, usize)>
@@ -529,7 +570,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
subseqs subseqs
} }
fn compile_pred_subseq<'b: 'a>(&mut self, clauses: &'b [PredicateClause]) -> Code fn compile_pred_subseq<'b: 'a>(&mut self, clauses: &'b [PredicateClause])
-> Result<Code, ParserError>
{ {
let mut code_body = Vec::new(); let mut code_body = Vec::new();
let mut code_offsets = CodeOffsets::new(); let mut code_offsets = CodeOffsets::new();
@@ -543,7 +585,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
&PredicateClause::Fact(ref fact) => &PredicateClause::Fact(ref fact) =>
self.compile_fact(fact), self.compile_fact(fact),
&PredicateClause::Rule(ref rule) => &PredicateClause::Rule(ref rule) =>
self.compile_rule(rule) try!(self.compile_rule(rule))
}; };
if num_clauses > 1 { if num_clauses > 1 {
@@ -567,17 +609,18 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
let mut code = Vec::new(); let mut code = Vec::new();
code_offsets.add_indices(&mut code, code_body); code_offsets.add_indices(&mut code, code_body);
code Ok(code)
} }
pub fn compile_predicate<'b: 'a>(&mut self, clauses: &'b Vec<PredicateClause>) -> Code pub fn compile_predicate<'b: 'a>(&mut self, clauses: &'b Vec<PredicateClause>)
-> Result<Code, ParserError>
{ {
let mut code = Vec::new(); let mut code = Vec::new();
let split_pred = Self::split_predicate(&clauses); let split_pred = Self::split_predicate(&clauses);
let multi_seq = split_pred.len() > 1; let multi_seq = split_pred.len() > 1;
for (l, r) in split_pred { for (l, r) in split_pred {
let mut code_segment = self.compile_pred_subseq(&clauses[l .. r]); let mut code_segment = try!(self.compile_pred_subseq(&clauses[l .. r]));
if multi_seq { if multi_seq {
let choice = match l { let choice = match l {
@@ -592,6 +635,6 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
code.append(&mut code_segment); code.append(&mut code_segment);
} }
code Ok(code)
} }
} }

View File

@@ -106,7 +106,11 @@ impl fmt::Display for ControlInstruction {
&ControlInstruction::ThrowCall => &ControlInstruction::ThrowCall =>
write!(f, "call_throw"), write!(f, "call_throw"),
&ControlInstruction::ThrowExecute => &ControlInstruction::ThrowExecute =>
write!(f, "execute_throw") write!(f, "execute_throw"),
&ControlInstruction::UnifyCall =>
write!(f, "unify_call"),
&ControlInstruction::UnifyExecute =>
write!(f, "unify_execute"),
} }
} }
} }
@@ -154,10 +158,10 @@ impl fmt::Display for BuiltInInstruction {
write!(f, "set_ball"), write!(f, "set_ball"),
&BuiltInInstruction::Succeed => &BuiltInInstruction::Succeed =>
write!(f, "true"), write!(f, "true"),
&BuiltInInstruction::UnwindStack =>
write!(f, "unwind_stack"),
&BuiltInInstruction::Unify => &BuiltInInstruction::Unify =>
write!(f, "unify"), write!(f, "unify"),
&BuiltInInstruction::UnwindStack =>
write!(f, "unwind_stack")
} }
} }
} }
@@ -188,6 +192,45 @@ impl fmt::Display for IndexingInstruction {
} }
} }
impl fmt::Display for ArithmeticTerm {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&ArithmeticTerm::Reg(r) => write!(f, "{}", r),
&ArithmeticTerm::Interm(i) => write!(f, "@{}", i),
&ArithmeticTerm::Float(fl) => write!(f, "{}", fl),
&ArithmeticTerm::Integer(ref bi) => write!(f, "{}", bi)
}
}
}
impl fmt::Display for ArithEvalPlace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&ArithEvalPlace::Reg(r) =>
write!(f, "{}", r),
&ArithEvalPlace::Interm(i) =>
write!(f, "@{}", i)
}
}
}
impl fmt::Display for ArithmeticInstruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&ArithmeticInstruction::Add(ref a1, ref a2, ref t) =>
write!(f, "add {}, {}, {}", a1, a2, t),
&ArithmeticInstruction::Sub(ref a1, ref a2, ref t) =>
write!(f, "sub {}, {}, {}", a1, a2, t),
&ArithmeticInstruction::Mul(ref a1, ref a2, ref t) =>
write!(f, "mul {}, {}, {}", a1, a2, t),
&ArithmeticInstruction::IDiv(ref a1, ref a2, ref t) =>
write!(f, "idiv {}, {}, {}", a1, a2, t),
&ArithmeticInstruction::Neg(ref a, ref t) =>
write!(f, "neg {}, {}", a, t)
}
}
}
impl fmt::Display for CutInstruction { impl fmt::Display for CutInstruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
@@ -236,6 +279,8 @@ impl fmt::Display for RegType {
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::Arithmetic(ref arith) =>
println!("{}", arith),
&Line::Fact(ref fact) => &Line::Fact(ref fact) =>
for fact_instr in fact { for fact_instr in fact {
println!("{}", fact_instr); println!("{}", fact_instr);
@@ -294,7 +339,11 @@ pub fn eval<'a, 'b: 'a>(wam: &'a mut Machine, tl: &'b TopLevel) -> EvalSession<'
&TopLevel::Predicate(ref clauses) => { &TopLevel::Predicate(ref clauses) => {
let mut cg = CodeGenerator::<DebrayAllocator>::new(); let mut cg = CodeGenerator::<DebrayAllocator>::new();
let compiled_pred = cg.compile_predicate(clauses); let compiled_pred = match cg.compile_predicate(clauses) {
Ok(pred) => pred,
Err(e) => return EvalSession::ParserError(e)
};
wam.add_predicate(clauses, compiled_pred) wam.add_predicate(clauses, compiled_pred)
}, },
&TopLevel::Fact(ref fact) => { &TopLevel::Fact(ref fact) => {
@@ -306,13 +355,21 @@ pub fn eval<'a, 'b: 'a>(wam: &'a mut Machine, tl: &'b TopLevel) -> EvalSession<'
&TopLevel::Rule(ref rule) => { &TopLevel::Rule(ref rule) => {
let mut cg = CodeGenerator::<DebrayAllocator>::new(); let mut cg = CodeGenerator::<DebrayAllocator>::new();
let compiled_rule = cg.compile_rule(rule); let compiled_rule = match cg.compile_rule(rule) {
Ok(rule) => rule,
Err(e) => return EvalSession::ParserError(e)
};
wam.add_rule(rule, compiled_rule) wam.add_rule(rule, compiled_rule)
}, },
&TopLevel::Query(ref query) => { &TopLevel::Query(ref query) => {
let mut cg = CodeGenerator::<DebrayAllocator>::new(); let mut cg = CodeGenerator::<DebrayAllocator>::new();
let compiled_query = cg.compile_query(query); let compiled_query = match cg.compile_query(query) {
Ok(query) => query,
Err(e) => return EvalSession::ParserError(e)
};
wam.submit_query(compiled_query, cg.take_vars()) wam.submit_query(compiled_query, cg.take_vars())
} }
} }
@@ -385,7 +442,10 @@ pub fn print(wam: &mut Machine, result: EvalSession) {
}, },
EvalSession::QueryFailure => println!("false."), EvalSession::QueryFailure => println!("false."),
EvalSession::QueryFailureWithException(e) => println!("{}", error_string(&e)), EvalSession::QueryFailureWithException(e) => println!("{}", error_string(&e)),
EvalSession::EntryFailure(msg) => println!("{}", msg), EvalSession::ImpermissibleEntry(msg) => println!("cannot overwrite builtin {}", msg),
EvalSession::OpIsInfixAndPostFix => println!("cannot define an op to be both postfix and infix."),
EvalSession::NamelessEntry => println!("the predicate head is not an atom or clause."),
EvalSession::ParserError(e) => println!("{:?}", e),
_ => {} _ => {}
}; };
} }

View File

@@ -5,32 +5,6 @@ use std::collections::VecDeque;
use std::iter::*; use std::iter::*;
use std::vec::Vec; use std::vec::Vec;
enum IteratorState<'a> {
AnonVar(Level),
Clause(usize, ClauseType<'a>, &'a Vec<Box<Term>>),
Constant(Level, &'a Cell<RegType>, &'a Constant),
InitialCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
FinalCons(Level, &'a Cell<RegType>, &'a Term, &'a 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::AnonVar =>
IteratorState::AnonVar(lvl),
&Term::Clause(ref cell, ref atom, ref child_terms) =>
IteratorState::Clause(0, ClauseType::Deep(lvl, cell, atom), child_terms),
&Term::Cons(ref cell, ref head, ref tail) =>
IteratorState::InitialCons(lvl, cell, head.as_ref(), tail.as_ref()),
&Term::Constant(ref cell, ref constant) =>
IteratorState::Constant(lvl, cell, constant),
&Term::Var(ref cell, ref var) =>
IteratorState::Var(lvl, cell, var)
}
}
}
pub struct QueryIterator<'a> { pub struct QueryIterator<'a> {
state_stack: Vec<IteratorState<'a>> state_stack: Vec<IteratorState<'a>>
} }
@@ -77,13 +51,17 @@ impl<'a> QueryIterator<'a> {
let state = IteratorState::Clause(0, ClauseType::Catch, terms); let state = IteratorState::Clause(0, ClauseType::Catch, terms);
QueryIterator { state_stack: vec![state] } QueryIterator { state_stack: vec![state] }
}, },
QueryTermRef::Is(terms) => {
let state = IteratorState::Clause(0, ClauseType::Is, terms);
QueryIterator { state_stack: vec![state] }
},
QueryTermRef::IsAtomic(term) | QueryTermRef::IsVar(term) | QueryTermRef::Term(term) => QueryTermRef::IsAtomic(term) | QueryTermRef::IsVar(term) | QueryTermRef::Term(term) =>
Self::from_term(term), Self::from_term(term),
QueryTermRef::Throw(term) => { QueryTermRef::Throw(term) => {
let state = IteratorState::Clause(0, ClauseType::Throw, term); let state = IteratorState::Clause(0, ClauseType::Throw, term);
QueryIterator { state_stack: vec![state] } QueryIterator { state_stack: vec![state] }
}, },
_ => QueryIterator { state_stack: vec![] } QueryTermRef::Cut => QueryIterator { state_stack: vec![] }
} }
} }
} }
@@ -318,6 +296,11 @@ impl<'a> ChunkedIterator<'a>
arity = child_terms.len(); arity = child_terms.len();
break; break;
}, },
QueryTermRef::Is(_) => {
result.push(term);
arity = 2;
break;
},
QueryTermRef::IsAtomic(_) | QueryTermRef::IsVar(_) => QueryTermRef::IsAtomic(_) | QueryTermRef::IsVar(_) =>
result.push(term), result.push(term),
QueryTermRef::Cut => { QueryTermRef::Cut => {

View File

@@ -4,7 +4,10 @@ use prolog::codegen::*;
use prolog::copier::*; use prolog::copier::*;
use prolog::heapview::*; use prolog::heapview::*;
use prolog::and_stack::*; use prolog::and_stack::*;
use prolog::num::Zero;
use prolog::num::bigint::BigInt;
use prolog::or_stack::*; use prolog::or_stack::*;
use prolog::ordered_float::OrderedFloat;
use prolog::fixtures::*; use prolog::fixtures::*;
use std::collections::HashMap; use std::collections::HashMap;
@@ -36,7 +39,8 @@ struct MachineState {
tr: usize, tr: usize,
hb: usize, hb: usize,
block: usize, // an offset into the OR stack. block: usize, // an offset into the OR stack.
ball: (usize, Heap) // heap boundary, and a term copy ball: (usize, Heap), // heap boundary, and a term copy
interms: Vec<Number> // intermediate numbers.
} }
struct DuplicateTerm<'a> { struct DuplicateTerm<'a> {
@@ -225,9 +229,7 @@ impl Machine {
{ {
match self.code_dir.get(&(name.clone(), arity)) { match self.code_dir.get(&(name.clone(), arity)) {
Some(&(PredicateKeyType::BuiltIn, _)) => Some(&(PredicateKeyType::BuiltIn, _)) =>
return EvalSession::EntryFailure(format!("error: cannot replace built-in predicate {}/{}", return EvalSession::ImpermissibleEntry(format!("{}/{}", name, arity)),
name,
arity)),
_ => {} _ => {}
}; };
@@ -246,7 +248,7 @@ impl Machine {
self.code.append(&mut code); self.code.append(&mut code);
self.add_user_code(name, arity, p) self.add_user_code(name, arity, p)
} else { } else {
EvalSession::EntryFailure(format!("error: the fact has no name.")) EvalSession::NamelessEntry
} }
} }
@@ -261,7 +263,7 @@ impl Machine {
self.code.append(&mut code); self.code.append(&mut code);
self.add_user_code(name, arity, p) self.add_user_code(name, arity, p)
} else { } else {
EvalSession::EntryFailure(format!("error: the rule has no name.")) EvalSession::NamelessEntry
} }
} }
@@ -297,6 +299,8 @@ impl Machine {
}; };
match instr { match instr {
&Line::Arithmetic(ref arith_instr) =>
self.ms.execute_arith_instr(arith_instr),
&Line::BuiltIn(ref built_in_instr) => &Line::BuiltIn(ref built_in_instr) =>
self.ms.execute_built_in_instr(&self.code_dir, built_in_instr), self.ms.execute_built_in_instr(&self.code_dir, built_in_instr),
&Line::Choice(ref choice_instr) => &Line::Choice(ref choice_instr) =>
@@ -408,11 +412,11 @@ impl Machine {
while self.ms.p < end_ptr { while self.ms.p < end_ptr {
if let CodePtr::TopLevel(mut cn, p) = self.ms.p { if let CodePtr::TopLevel(mut cn, p) = self.ms.p {
match &self[CodePtr::TopLevel(cn, p)] { match &self[CodePtr::TopLevel(cn, p)] {
&Line::Control(ref ctrl_instr) if ctrl_instr.is_jump_instr() => { &Line::Control(ref ctrl_instr) if ctrl_instr.is_jump_instr() => {
self.record_var_places(cn, alloc_locs, heap_locs); self.record_var_places(cn, alloc_locs, heap_locs);
cn += 1; cn += 1;
}, },
_ => {} _ => {}
} }
@@ -422,10 +426,10 @@ impl Machine {
self.query_stepper(); self.query_stepper();
match self.ms.p { match self.ms.p {
CodePtr::TopLevel(_, p) if p > 0 => {}, CodePtr::TopLevel(_, p) if p > 0 => {},
_ => break _ => break
}; };
} }
} }
fn fail<'a>(&mut self) -> EvalSession<'a> fn fail<'a>(&mut self) -> EvalSession<'a>
@@ -443,21 +447,16 @@ impl Machine {
pub fn submit_decl<'a>(&mut self, decl: &Declaration) -> EvalSession<'a> { pub fn submit_decl<'a>(&mut self, decl: &Declaration) -> EvalSession<'a> {
match decl { match decl {
&Declaration::Op(prec, spec, ref name) => { &Declaration::Op(prec, spec, ref name) => {
lazy_static! {
static ref ERR_STRING: String = String::from("an operator can't be both \
infix and postfix.");
}
if is_infix!(spec) { if is_infix!(spec) {
match self.op_dir.get(&(name.clone(), Fixity::Post)) { match self.op_dir.get(&(name.clone(), Fixity::Post)) {
Some(_) => return EvalSession::EntryFailure(ERR_STRING.clone()), Some(_) => return EvalSession::OpIsInfixAndPostFix,
_ => {} _ => {}
}; };
} }
if is_postfix!(spec) { if is_postfix!(spec) {
match self.op_dir.get(&(name.clone(), Fixity::In)) { match self.op_dir.get(&(name.clone(), Fixity::In)) {
Some(_) => return EvalSession::EntryFailure(ERR_STRING.clone()), Some(_) => return EvalSession::OpIsInfixAndPostFix,
_ => {} _ => {}
}; };
} }
@@ -598,6 +597,18 @@ impl Machine {
} }
} }
macro_rules! try_or_fail {
($s:ident, $e:expr) => {{
match $e {
Ok(val) => val,
Err(msg) => {
$s.throw_exception(string!(msg));
return;
}
}
}}
}
impl MachineState { impl MachineState {
fn new() -> MachineState { fn new() -> MachineState {
MachineState { h: 0, MachineState { h: 0,
@@ -618,7 +629,8 @@ impl MachineState {
tr: 0, tr: 0,
hb: 0, hb: 0,
block: 0, block: 0,
ball: (0, Vec::new()) ball: (0, Vec::new()),
interms: vec![Number::Float(OrderedFloat(0f64)); 256],
} }
} }
@@ -753,7 +765,7 @@ impl MachineState {
Ref::HeapCell(r) => Ref::HeapCell(r) =>
self.heap[r] = HeapCellValue::Ref(Ref::HeapCell(r)), self.heap[r] = HeapCellValue::Ref(Ref::HeapCell(r)),
Ref::StackCell(fr, sc) => Ref::StackCell(fr, sc) =>
self.and_stack[fr][sc] = Addr::StackCell(fr, sc) self.and_stack[fr][sc] = Addr::StackCell(fr, sc)
} }
} }
} }
@@ -821,6 +833,92 @@ impl MachineState {
}; };
} }
fn get_number(&self, at: &ArithmeticTerm) -> Result<Number, &'static str> {
match at {
&ArithmeticTerm::Reg(r) => {
let addr = self[r].clone();
let item = self.deref(addr);
match item {
Addr::Con(Constant::Integer(bi)) =>
Ok(Number::Integer(bi)),
Addr::Con(Constant::Float(fl)) =>
Ok(Number::Float(fl)),
_ =>
Err("is/2: variable not instantiated to number.")
}
},
&ArithmeticTerm::Interm(i) => Ok(self.interms[i].clone()),
&ArithmeticTerm::Float(fl) => Ok(Number::Float(fl)),
&ArithmeticTerm::Integer(ref bi) => Ok(Number::Integer(bi.clone()))
}
}
fn assign_arith(&mut self, t: ArithEvalPlace, n: Number) {
match t {
ArithEvalPlace::Reg(r) =>
self[r] = Addr::Con(Constant::from(n)),
ArithEvalPlace::Interm(i) =>
self.interms[i] = n
}
}
fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) {
match instr {
&ArithmeticInstruction::Add(ref a1, ref a2, t) => {
let n1 = try_or_fail!(self, self.get_number(a1));
let n2 = try_or_fail!(self, self.get_number(a2));
self.assign_arith(t, n1 + n2);
self.p += 1;
},
&ArithmeticInstruction::Sub(ref a1, ref a2, t) => {
let n1 = try_or_fail!(self, self.get_number(a1));
let n2 = try_or_fail!(self, self.get_number(a2));
self.assign_arith(t, n1 - n2);
self.p += 1;
},
&ArithmeticInstruction::Mul(ref a1, ref a2, t) => {
let n1 = try_or_fail!(self, self.get_number(a1));
let n2 = try_or_fail!(self, self.get_number(a2));
self.assign_arith(t, n1 * n2);
self.p += 1;
},
&ArithmeticInstruction::IDiv(ref a1, ref a2, t) => {
let n1 = try_or_fail!(self, self.get_number(a1));
let n2 = try_or_fail!(self, self.get_number(a2));
match (n1, n2) {
(Number::Integer(n1), Number::Integer(n2)) => {
if n2 == BigInt::zero() {
self.throw_exception(functor!("evaluation_error",
1,
[atom!("zero_divisor")]));
return;
}
self.assign_arith(t, Number::Integer(n1 / n2));
self.p += 1;
},
_ => {
self.throw_exception(functor!("evaluation_error",
1,
[atom!("expected_integer_args")]));
return;
}
}
},
&ArithmeticInstruction::Neg(ref a1, t) => {
let n1 = try_or_fail!(self, self.get_number(a1));
self.assign_arith(t, - n1);
self.p += 1;
}
};
}
fn execute_fact_instr(&mut self, instr: &FactInstruction) { fn execute_fact_instr(&mut self, instr: &FactInstruction) {
match instr { match instr {
&FactInstruction::GetConstant(_, ref c, reg) => { &FactInstruction::GetConstant(_, ref c, reg) => {
@@ -1306,13 +1404,6 @@ impl MachineState {
self.write_constant_to_var(addr, &c); self.write_constant_to_var(addr, &c);
self.p += 1; 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::EraseBall => { &BuiltInInstruction::EraseBall => {
self.ball.0 = 0; self.ball.0 = 0;
self.ball.1.truncate(0); self.ball.1.truncate(0);
@@ -1414,16 +1505,26 @@ impl MachineState {
}, },
&BuiltInInstruction::Succeed => { &BuiltInInstruction::Succeed => {
self.p += 1; self.p += 1;
},
&BuiltInInstruction::Unify => {
self.inline_unify();
self.p += 1;
} }
}; };
} }
fn inline_unify(&mut self) {
let a1 = self[temp_v!(1)].clone();
let a2 = self[temp_v!(2)].clone();
self.unify(a1, a2);
}
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 {
&ControlInstruction::Allocate(num_cells) => { &ControlInstruction::Allocate(num_cells) => {
let num_frames = self.num_frames(); let num_frames = self.num_frames();
self.and_stack.push(num_frames + 1, self.e, self.cp, num_cells); self.and_stack.push(num_frames + 1, self.e, self.cp, num_cells);
self.e = self.and_stack.len() - 1; self.e = self.and_stack.len() - 1;
@@ -1473,7 +1574,15 @@ impl MachineState {
}, },
&ControlInstruction::ThrowExecute => { &ControlInstruction::ThrowExecute => {
self.goto_throw(); self.goto_throw();
} },
&ControlInstruction::UnifyCall => {
self.inline_unify();
self.p += 1;
},
&ControlInstruction::UnifyExecute => {
self.inline_unify();
self.p = self.cp;
}
}; };
} }
@@ -1512,7 +1621,7 @@ impl MachineState {
self.registers[i] = self.or_stack[b][i].clone(); self.registers[i] = self.or_stack[b][i].clone();
} }
self.e = self.or_stack[b].e; self.e = self.or_stack[b].e;
self.cp = self.or_stack[b].cp; self.cp = self.or_stack[b].cp;
self.or_stack[b].bp = self.p + 1; self.or_stack[b].bp = self.p + 1;
@@ -1559,7 +1668,7 @@ impl MachineState {
self.hb = self.h; self.hb = self.h;
self.p += l; self.p += l;
} },
}; };
} }
@@ -1598,7 +1707,7 @@ impl MachineState {
self.registers[i] = self.or_stack[b][i].clone(); self.registers[i] = self.or_stack[b][i].clone();
} }
self.e = self.or_stack[b].e; self.e = self.or_stack[b].e;
self.cp = self.or_stack[b].cp; self.cp = self.or_stack[b].cp;
self.or_stack[b].bp = self.p + offset; self.or_stack[b].bp = self.p + offset;

View File

@@ -22,6 +22,12 @@ macro_rules! query {
) )
} }
macro_rules! string {
($str:expr) => {
vec![HeapCellValue::Con(Constant::String(String::from($str)))]
}
}
macro_rules! functor { macro_rules! functor {
($name:expr, $len:expr, [$($args:expr),*]) => {{ ($name:expr, $len:expr, [$($args:expr),*]) => {{
if $len > 0 { if $len > 0 {
@@ -81,6 +87,12 @@ macro_rules! put_var {
) )
} }
macro_rules! put_constant {
($lvl:expr, $cons:expr, $r:expr) => (
QueryInstruction::PutConstant($lvl, $cons, $r)
)
}
macro_rules! put_value { macro_rules! put_value {
($r:expr, $arg:expr) => ( ($r:expr, $arg:expr) => (
QueryInstruction::PutValue($r, $arg) QueryInstruction::PutValue($r, $arg)
@@ -197,6 +209,12 @@ macro_rules! unify {
) )
} }
macro_rules! unify_call {
() => (
Line::Control(ControlInstruction::UnifyCall)
)
}
macro_rules! unwind_stack { macro_rules! unwind_stack {
() => ( () => (
Line::BuiltIn(BuiltInInstruction::UnwindStack) Line::BuiltIn(BuiltInInstruction::UnwindStack)

View File

@@ -7,6 +7,7 @@ pub mod and_stack;
pub mod ast; pub mod ast;
#[macro_use] #[macro_use]
pub mod macros; pub mod macros;
pub mod arithmetic;
pub mod builtins; pub mod builtins;
pub mod codegen; pub mod codegen;
pub mod copier; pub mod copier;