add support for comparison operators.
This commit is contained in:
@@ -49,6 +49,7 @@ The following predicates are built-in to rusty-wam.
|
||||
* Arithmetic support:
|
||||
* is/2 works for `(+)/2`, `(-)/{1,2}`, `(*)/2`, `(//)/2`, `(div)/2`, `(/)/2`, `(rdiv)/2`,
|
||||
`(xor)/2`, `(rem)/2`, `(mod)/2`, `(/\)/2`, `(\/)/2`, `(>>)/2`, `(<<)/2`.
|
||||
* Comparison operators: `>`, `<`, `=<`, `>=`, `=:=`, `=\\=`.
|
||||
* `atomic/1`
|
||||
* `call/N` (1 <= N <= 63)
|
||||
* `catch/3`
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use prolog::ast::*;
|
||||
use prolog::fixtures::*;
|
||||
use prolog::num::{BigInt, Zero};
|
||||
use prolog::ordered_float::{OrderedFloat};
|
||||
|
||||
use std::cmp::{min, max};
|
||||
use std::vec::Vec;
|
||||
@@ -75,13 +73,14 @@ impl<'a> Iterator for ArithExprIterator<'a> {
|
||||
|
||||
pub struct ArithmeticEvaluator<'a> {
|
||||
bindings: &'a AllocVarDict<'a>,
|
||||
target_int: usize,
|
||||
interm: Vec<ArithmeticTerm>,
|
||||
interm_c: usize
|
||||
}
|
||||
|
||||
impl<'a> ArithmeticEvaluator<'a> {
|
||||
pub fn new(bindings: &'a AllocVarDict<'a>) -> Self {
|
||||
ArithmeticEvaluator { bindings, interm: Vec::new(), interm_c: 1 }
|
||||
pub fn new(bindings: &'a AllocVarDict<'a>, target_int: usize) -> Self {
|
||||
ArithmeticEvaluator { bindings, target_int, interm: Vec::new(), interm_c: target_int }
|
||||
}
|
||||
|
||||
fn get_unary_instr(name: &Atom, a1: ArithmeticTerm, t: usize)
|
||||
@@ -213,20 +212,14 @@ impl<'a> ArithmeticEvaluator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let Some(arith_term) = self.interm.pop() {
|
||||
let t = self.target_int;
|
||||
|
||||
match arith_term {
|
||||
n @ ArithmeticTerm::Integer(_) => {
|
||||
let zero = ArithmeticTerm::Integer(BigInt::zero());
|
||||
code.push(arith![add!(zero, n, 1)]);
|
||||
},
|
||||
n @ ArithmeticTerm::Float(_) => {
|
||||
let zero = ArithmeticTerm::Float(OrderedFloat(0f64));
|
||||
code.push(arith![add!(zero, n, 1)]);
|
||||
},
|
||||
r @ ArithmeticTerm::Reg(_) => {
|
||||
let zero = ArithmeticTerm::Integer(BigInt::zero());
|
||||
code.push(arith![add!(zero, r, 1)]);
|
||||
},
|
||||
n @ ArithmeticTerm::Integer(_) => code.push(move_at!(n, t)),
|
||||
n @ ArithmeticTerm::Float(_) => code.push(move_at!(n, t)),
|
||||
r @ ArithmeticTerm::Reg(_) => code.push(move_at!(r, t)),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -328,9 +328,20 @@ impl InlinedQueryTerm {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CompareNumberQT {
|
||||
GreaterThan,
|
||||
LessThan,
|
||||
GreaterThanOrEqual,
|
||||
LessThanOrEqual,
|
||||
NotEqual,
|
||||
Equal
|
||||
}
|
||||
|
||||
pub enum QueryTerm {
|
||||
CallN(Vec<Box<Term>>),
|
||||
Catch(Vec<Box<Term>>),
|
||||
CompareNumber(CompareNumberQT, Vec<Box<Term>>),
|
||||
Cut,
|
||||
Is(Vec<Box<Term>>),
|
||||
Inlined(InlinedQueryTerm),
|
||||
@@ -342,6 +353,7 @@ impl QueryTerm {
|
||||
pub fn arity(&self) -> usize {
|
||||
match self {
|
||||
&QueryTerm::Catch(_) => 3,
|
||||
&QueryTerm::CompareNumber(_, _) => 2,
|
||||
&QueryTerm::Throw(_) => 1,
|
||||
&QueryTerm::Inlined(ref term) => term.arity(),
|
||||
&QueryTerm::Is(_) => 2,
|
||||
@@ -451,6 +463,54 @@ impl Number {
|
||||
&Number::Rational(ref r) => r.is_zero()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gt(self, n2: Number) -> bool {
|
||||
match NumberPair::from(self, n2) {
|
||||
NumberPair::Integer(n1, n2) => n1 > n2,
|
||||
NumberPair::Float(n1, n2) => n1 > n2,
|
||||
NumberPair::Rational(n1, n2) => n1 > n2
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gte(self, n2: Number) -> bool {
|
||||
match NumberPair::from(self, n2) {
|
||||
NumberPair::Integer(n1, n2) => n1 >= n2,
|
||||
NumberPair::Float(n1, n2) => n1 >= n2,
|
||||
NumberPair::Rational(n1, n2) => n1 >= n2
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lt(self, n2: Number) -> bool {
|
||||
match NumberPair::from(self, n2) {
|
||||
NumberPair::Integer(n1, n2) => n1 < n2,
|
||||
NumberPair::Float(n1, n2) => n1 < n2,
|
||||
NumberPair::Rational(n1, n2) => n1 < n2
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lte(self, n2: Number) -> bool {
|
||||
match NumberPair::from(self, n2) {
|
||||
NumberPair::Integer(n1, n2) => n1 <= n2,
|
||||
NumberPair::Float(n1, n2) => n1 <= n2,
|
||||
NumberPair::Rational(n1, n2) => n1 <= n2
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ne(self, n2: Number) -> bool {
|
||||
match NumberPair::from(self, n2) {
|
||||
NumberPair::Integer(n1, n2) => n1 != n2,
|
||||
NumberPair::Float(n1, n2) => n1 != n2,
|
||||
NumberPair::Rational(n1, n2) => n1 != n2
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq(self, n2: Number) -> bool {
|
||||
match NumberPair::from(self, n2) {
|
||||
NumberPair::Integer(n1, n2) => n1 == n2,
|
||||
NumberPair::Float(n1, n2) => n1 == n2,
|
||||
NumberPair::Rational(n1, n2) => n1 == n2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum NumberPair {
|
||||
@@ -675,6 +735,8 @@ pub enum ControlInstruction {
|
||||
Execute(Atom, usize),
|
||||
ExecuteN(usize),
|
||||
Goto(usize, usize), // p, arity.
|
||||
CompareNumberCall(CompareNumberQT),
|
||||
CompareNumberExecute(CompareNumberQT),
|
||||
IsCall(RegType),
|
||||
IsExecute(RegType),
|
||||
Proceed,
|
||||
@@ -728,6 +790,7 @@ pub enum FactInstruction {
|
||||
}
|
||||
|
||||
pub enum QueryInstruction {
|
||||
MoveArithmeticTerm(ArithmeticTerm, usize),
|
||||
GetVariable(RegType, usize),
|
||||
PutConstant(Level, Constant, RegType),
|
||||
PutList(Level, RegType),
|
||||
|
||||
@@ -140,6 +140,14 @@ pub fn build_code_dir() -> (Code, CodeDir, OpDir)
|
||||
op_dir.insert((String::from(">>"), Fixity::In), (YFX, 400));
|
||||
op_dir.insert((String::from("mod"), Fixity::In), (YFX, 400));
|
||||
op_dir.insert((String::from("rem"), Fixity::In), (YFX, 400));
|
||||
|
||||
// arithmetic comparison operators.
|
||||
op_dir.insert((String::from(">"), Fixity::In), (XFX, 700));
|
||||
op_dir.insert((String::from("<"), Fixity::In), (XFX, 700));
|
||||
op_dir.insert((String::from("=\\="), Fixity::In), (XFX, 700));
|
||||
op_dir.insert((String::from("=:="), Fixity::In), (XFX, 700));
|
||||
op_dir.insert((String::from(">="), Fixity::In), (XFX, 700));
|
||||
op_dir.insert((String::from("=<"), Fixity::In), (XFX, 700));
|
||||
|
||||
// there are 63 registers in the VM, so call/N is defined for all 0 <= N <= 62
|
||||
// (an extra register is needed for the predicate name)
|
||||
|
||||
@@ -276,6 +276,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
||||
*ctrl = ControlInstruction::Execute(name, arity),
|
||||
ControlInstruction::CallN(arity) =>
|
||||
*ctrl = ControlInstruction::ExecuteN(arity),
|
||||
ControlInstruction::CompareNumberCall(cmp) =>
|
||||
*ctrl = ControlInstruction::CompareNumberExecute(cmp),
|
||||
ControlInstruction::IsCall(r) =>
|
||||
*ctrl = ControlInstruction::IsExecute(r),
|
||||
ControlInstruction::CatchCall =>
|
||||
@@ -322,6 +324,11 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
||||
}
|
||||
}
|
||||
|
||||
fn call_arith_eval(&self, term: &'a Term, target_int: usize) -> Result<Code, ArithmeticError> {
|
||||
let mut evaluator = ArithmeticEvaluator::new(self.marker.bindings(), target_int);
|
||||
evaluator.eval(term)
|
||||
}
|
||||
|
||||
fn compile_seq(&mut self,
|
||||
iter: ChunkedIterator<'a>,
|
||||
conjunct_info: &ConjunctInfo<'a>,
|
||||
@@ -355,11 +362,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
||||
&QueryTerm::Inlined(ref term) =>
|
||||
self.compile_inlined(term, term_loc, code),
|
||||
&QueryTerm::Is(ref terms) => {
|
||||
let mut arith_code = {
|
||||
let mut evaluator = ArithmeticEvaluator::new(self.marker.bindings());
|
||||
evaluator.eval(terms[1].as_ref())?
|
||||
};
|
||||
|
||||
let mut arith_code = self.call_arith_eval(terms[1].as_ref(), 1)?;
|
||||
code.append(&mut arith_code);
|
||||
|
||||
match terms[0].as_ref() {
|
||||
@@ -390,6 +393,15 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
|
||||
}
|
||||
}
|
||||
},
|
||||
&QueryTerm::CompareNumber(cmp, ref terms) => {
|
||||
let mut larith_code = self.call_arith_eval(terms[0].as_ref(), 1)?;
|
||||
let mut rarith_code = self.call_arith_eval(terms[1].as_ref(), 2)?;
|
||||
|
||||
code.append(&mut larith_code);
|
||||
code.append(&mut rarith_code);
|
||||
|
||||
code.push(compare_number_call!(cmp));
|
||||
},
|
||||
_ if chunk_num == 0 => {
|
||||
self.marker.reset_arg(term.arity());
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ impl fmt::Display for FactInstruction {
|
||||
impl fmt::Display for QueryInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&QueryInstruction::MoveArithmeticTerm(ref at, ref r) =>
|
||||
write!(f, "move_arithmetic_term {}, {}", at, r),
|
||||
&QueryInstruction::GetVariable(ref x, ref a) =>
|
||||
write!(f, "query:get_variable {}, A{}", x, a),
|
||||
&QueryInstruction::PutConstant(Level::Shallow, ref constant, ref r) =>
|
||||
@@ -80,6 +82,19 @@ impl fmt::Display for QueryInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CompareNumberQT {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&CompareNumberQT::GreaterThan => write!(f, ">"),
|
||||
&CompareNumberQT::GreaterThanOrEqual => write!(f, ">="),
|
||||
&CompareNumberQT::LessThan => write!(f, "<"),
|
||||
&CompareNumberQT::LessThanOrEqual => write!(f, "<="),
|
||||
&CompareNumberQT::NotEqual => write!(f, "=\\="),
|
||||
&CompareNumberQT::Equal => write!(f, "=:="),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ControlInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
@@ -93,6 +108,10 @@ impl fmt::Display for ControlInstruction {
|
||||
write!(f, "call_catch"),
|
||||
&ControlInstruction::CatchExecute =>
|
||||
write!(f, "execute_catch"),
|
||||
&ControlInstruction::CompareNumberCall(cmp) =>
|
||||
write!(f, "n_compare_call {}", cmp),
|
||||
&ControlInstruction::CompareNumberExecute(cmp) =>
|
||||
write!(f, "n_compare_execute {}", cmp),
|
||||
&ControlInstruction::ExecuteN(arity) =>
|
||||
write!(f, "execute_N {}", arity),
|
||||
&ControlInstruction::Deallocate =>
|
||||
|
||||
@@ -40,7 +40,7 @@ impl<'a> QueryIterator<'a> {
|
||||
let state = IteratorState::Clause(0, ClauseType::Catch, terms);
|
||||
QueryIterator { state_stack: vec![state] }
|
||||
},
|
||||
&QueryTerm::Is(ref terms) => {
|
||||
&QueryTerm::CompareNumber(_, ref terms) | &QueryTerm::Is(ref terms) => {
|
||||
let state = IteratorState::Clause(0, ClauseType::Is, terms);
|
||||
QueryIterator { state_stack: vec![state] }
|
||||
},
|
||||
@@ -270,7 +270,7 @@ impl<'a> ChunkedIterator<'a>
|
||||
arity = child_terms.len();
|
||||
break;
|
||||
},
|
||||
&QueryTerm::Is(_) => {
|
||||
&QueryTerm::Is(_) | &QueryTerm::CompareNumber(_, _) => {
|
||||
result.push(term);
|
||||
arity = 2;
|
||||
break;
|
||||
|
||||
@@ -1379,6 +1379,10 @@ impl MachineState {
|
||||
|
||||
fn execute_query_instr(&mut self, instr: &QueryInstruction) {
|
||||
match instr {
|
||||
&QueryInstruction::MoveArithmeticTerm(ref at, t) => {
|
||||
let n = try_or_fail!(self, self.get_number(at));
|
||||
self.interms[t - 1] = n;
|
||||
},
|
||||
&QueryInstruction::GetVariable(norm, arg) =>
|
||||
self[norm] = self.registers[arg].clone(),
|
||||
&QueryInstruction::PutConstant(_, ref constant, reg) =>
|
||||
@@ -1740,6 +1744,21 @@ impl MachineState {
|
||||
};
|
||||
}
|
||||
|
||||
fn handle_n_compare(&mut self, cmp: CompareNumberQT) {
|
||||
let n1 = self.interms[0].clone();
|
||||
let n2 = self.interms[1].clone();
|
||||
|
||||
self.fail = match cmp {
|
||||
CompareNumberQT::GreaterThan if !(n1.gt(n2)) => true,
|
||||
CompareNumberQT::GreaterThanOrEqual if !(n1.gte(n2)) => true,
|
||||
CompareNumberQT::LessThan if !(n1.lt(n2)) => true,
|
||||
CompareNumberQT::LessThanOrEqual if !(n1.lte(n2)) => true,
|
||||
CompareNumberQT::NotEqual if !(n1.ne(n2)) => true,
|
||||
CompareNumberQT::Equal if !(n1.eq(n2)) => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction)
|
||||
{
|
||||
match instr {
|
||||
@@ -1819,6 +1838,14 @@ impl MachineState {
|
||||
&ControlInstruction::ThrowExecute => {
|
||||
self.goto_throw();
|
||||
},
|
||||
&ControlInstruction::CompareNumberCall(cmp) => {
|
||||
self.handle_n_compare(cmp);
|
||||
self.p += 1;
|
||||
},
|
||||
&ControlInstruction::CompareNumberExecute(cmp) => {
|
||||
self.handle_n_compare(cmp);
|
||||
self.p = self.cp;
|
||||
},
|
||||
&ControlInstruction::IsCall(r) => {
|
||||
let a1 = self[r].clone();
|
||||
let a2 = Addr::Con(Constant::from(self.interms[0].clone()));
|
||||
|
||||
@@ -16,15 +16,15 @@ macro_rules! deallocate {
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! query {
|
||||
[$($x:expr),+] => (
|
||||
Line::Query(vec![$($x),+])
|
||||
macro_rules! move_at {
|
||||
($at:expr, $r:expr) => (
|
||||
Line::Query(vec![QueryInstruction::MoveArithmeticTerm($at, $r)])
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! arith {
|
||||
($x:expr) => (
|
||||
Line::Arithmetic($x)
|
||||
macro_rules! query {
|
||||
[$($x:expr),+] => (
|
||||
Line::Query(vec![$($x),+])
|
||||
)
|
||||
}
|
||||
|
||||
@@ -50,6 +50,12 @@ macro_rules! fact {
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! compare_number_call {
|
||||
($cmp: expr) => (
|
||||
Line::Control(ControlInstruction::CompareNumberCall($cmp))
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! temp_v {
|
||||
($x:expr) => (
|
||||
RegType::Temp($x)
|
||||
@@ -105,12 +111,6 @@ macro_rules! put_unsafe_value {
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! add {
|
||||
($r1:expr, $r2:expr, $t:expr) => (
|
||||
ArithmeticInstruction::Add($r1, $r2, $t)
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! try_me_else {
|
||||
($o:expr) => (
|
||||
Line::Choice(ChoiceInstruction::TryMeElse($o))
|
||||
|
||||
Reference in New Issue
Block a user