}
}
+#[derive(Clone, Copy)]
+pub enum CompareTermQT {
+ GreaterThan,
+ LessThan,
+ GreaterThanOrEqual,
+ LessThanOrEqual,
+ NotEqual,
+ Equal
+}
+
+impl CompareTermQT {
+ fn name<'a>(self) -> &'a str {
+ match self {
+ CompareTermQT::GreaterThan => "@>",
+ CompareTermQT::LessThan => "@<",
+ CompareTermQT::GreaterThanOrEqual => "@>=",
+ CompareTermQT::LessThanOrEqual => "@=<",
+ CompareTermQT::NotEqual => "\\=@=",
+ CompareTermQT::Equal => "=@="
+ }
+ }
+}
+
// vars of predicate, toplevel offset. Vec<Term> is always a vector
// of vars (we get their adjoining cells this way).
pub type JumpStub = Vec<Term>;
Arg(Vec<Box<Term>>),
CallN(Vec<Box<Term>>),
Catch(Vec<Box<Term>>),
+ CompareTerm(CompareTermQT, Vec<Box<Term>>),
Cut,
Display(Vec<Box<Term>>),
DuplicateTerm(Vec<Box<Term>>),
match self {
&QueryTerm::Arg(_) => 3,
&QueryTerm::Catch(_) => 3,
+ &QueryTerm::CompareTerm(..) => 2,
&QueryTerm::Display(_) => 1,
&QueryTerm::Throw(_) => 1,
&QueryTerm::DuplicateTerm(_) => 2,
CallN,
Catch,
CompareNumber(CompareNumberQT),
+ CompareTerm(CompareTermQT),
Deep(Level, &'a Cell<RegType>, &'a TabledRc<Atom>, Option<Fixity>),
Display,
DuplicateTerm,
&ClauseType::CallN => "call",
&ClauseType::Catch => "catch",
&ClauseType::CompareNumber(qt) => qt.name(),
+ &ClauseType::CompareTerm(qt) => qt.name(),
&ClauseType::Display => "display",
&ClauseType::Deep(_, _, name, _) => name.as_str(),
&ClauseType::DuplicateTerm => "duplicate_term",
&ClauseType::Eq => "==",
&ClauseType::Functor => "functor",
- &ClauseType::Ground => "ground",
+ &ClauseType::Ground => "ground",
&ClauseType::Is => "is",
&ClauseType::NotEq => "\\==",
&ClauseType::Root(name) => name.as_str(),
Rational(Rc<Ratio<BigInt>>)
}
+impl PartialOrd for Number {
+ fn partial_cmp(&self, other: &Number) -> Option<Ordering> {
+ match NumberPair::from(self.clone(), other.clone()) {
+ NumberPair::Integer(n1, n2) =>
+ Some(n1.cmp(&n2)),
+ NumberPair::Float(n1, n2) =>
+ Some(n1.cmp(&n2)),
+ NumberPair::Rational(n1, n2) =>
+ Some(n1.cmp(&n2))
+ }
+ }
+}
+
+impl Ord for Number {
+ fn cmp(&self, other: &Number) -> Ordering {
+ match NumberPair::from(self.clone(), other.clone()) {
+ NumberPair::Integer(n1, n2) =>
+ n1.cmp(&n2),
+ NumberPair::Float(n1, n2) =>
+ n1.cmp(&n2),
+ NumberPair::Rational(n1, n2) =>
+ n1.cmp(&n2)
+ }
+ }
+}
+
impl fmt::Display for Number {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
}
pub enum BuiltInInstruction {
- CleanUpBlock,
+ CleanUpBlock,
EraseBall,
Fail,
GetArgCall,
GetArgExecute,
GetBall,
GetCurrentBlock,
- GetCutPoint(RegType),
+ GetCutPoint(RegType),
InstallCleaner,
InstallNewBlock,
- InternalCallN,
+ InternalCallN,
ResetBlock,
RestoreCutPolicy,
SetBall,
CatchExecute,
CheckCpExecute,
CompareNumber(CompareNumberQT, ArithmeticTerm, ArithmeticTerm),
+ CompareTermCall(CompareTermQT),
+ CompareTermExecute(CompareTermQT),
DisplayCall,
DisplayExecute,
Deallocate,
GotoCall(usize, usize), // p, arity.
GotoExecute(usize, usize), // p, arity.
GroundCall,
- GroundExecute,
+ GroundExecute,
JmpByCall(usize, usize), // arity, global_offset.
JmpByExecute(usize, usize),
IsCall(RegType, ArithmeticTerm),
&ControlInstruction::Call(_, _, _) => true,
&ControlInstruction::CatchCall => true,
&ControlInstruction::CatchExecute => true,
- &ControlInstruction::CompareNumber(..) => true,
+ &ControlInstruction::CompareNumber(..) => true,
+ &ControlInstruction::CompareTermCall(..) => true,
+ &ControlInstruction::CompareTermExecute(..) => true,
&ControlInstruction::DisplayCall => true,
&ControlInstruction::DisplayExecute => true,
&ControlInstruction::DuplicateTermCall => true,
&ControlInstruction::EqCall => true,
&ControlInstruction::EqExecute => true,
&ControlInstruction::Execute(_, _) => true,
- &ControlInstruction::CallN(_) => true,
+ &ControlInstruction::CallN(_) => true,
&ControlInstruction::ExecuteN(_) => true,
&ControlInstruction::FunctorCall => true,
&ControlInstruction::FunctorExecute => true,
&ControlInstruction::IsCall(..) => true,
&ControlInstruction::IsExecute(..) => true,
&ControlInstruction::JmpByCall(..) => true,
- &ControlInstruction::JmpByExecute(..) => true,
+ &ControlInstruction::JmpByExecute(..) => true,
_ => false
}
}
retry_me_else!(12),
allocate!(2),
query![put_value!(temp_v!(3), 1)],
- reset_block!(),
+ reset_block!(),
query![put_var!(perm_v!(1), 1)],
get_ball!(),
get_level!(perm_v!(2)),
goto_call!(342, 0), // goto run_cleaners_with_handling/0, 342.
query![put_unsafe_value!(1, 1)],
deallocate!(),
- goto_execute!(59, 1), // goto throw/1, 59.
+ goto_execute!(59, 1), // goto throw/1, 59.
trust_me!(),
allocate!(0),
goto_call!(354, 0), // goto run_cleaners_without_handling/0, 354.
ground_execute!(), // ground/1, 384.
eq_execute!(), // (==)/2, 385.
not_eq_execute!(), // (\==)/2, 386.
+ compare_term_execute!(term_cmp_gte!()), // (@>=)/2, 387.
+ compare_term_execute!(term_cmp_lte!()), // (@=<)/2, 388.
+ compare_term_execute!(term_cmp_gt!()), // (@>)/2, 389.
+ compare_term_execute!(term_cmp_lt!()), // (@<)/2, 390.
]
}
op_dir.insert((tabled_rc!("=..", atom_tbl), Fixity::In), (XFX, 700));
op_dir.insert((tabled_rc!("==", atom_tbl), Fixity::In), (XFX, 700));
op_dir.insert((tabled_rc!("\\==", atom_tbl), Fixity::In), (XFX, 700));
-
+ op_dir.insert((tabled_rc!("@=<", atom_tbl), Fixity::In), (XFX, 700));
+ op_dir.insert((tabled_rc!("@>=", atom_tbl), Fixity::In), (XFX, 700));
+ op_dir.insert((tabled_rc!("@<", atom_tbl), Fixity::In), (XFX, 700));
+ op_dir.insert((tabled_rc!("@>", atom_tbl), Fixity::In), (XFX, 700));
+
// there are 63 registers in the VM, so call/N is defined for all 0 <= N <= 62
// (an extra register is needed for the predicate name)
for arity in 0 .. 63 {
code_dir.insert((tabled_rc!("ground", atom_tbl), 1), (PredicateKeyType::BuiltIn, 384));
code_dir.insert((tabled_rc!("==", atom_tbl), 2), (PredicateKeyType::BuiltIn, 385));
- code_dir.insert((tabled_rc!("\\==", atom_tbl), 2), (PredicateKeyType::BuiltIn, 386));
-
+ code_dir.insert((tabled_rc!("\\==", atom_tbl), 2), (PredicateKeyType::BuiltIn, 386));
+ code_dir.insert((tabled_rc!("@>=", atom_tbl), 2), (PredicateKeyType::BuiltIn, 387));
+ code_dir.insert((tabled_rc!("@=<", atom_tbl), 2), (PredicateKeyType::BuiltIn, 388));
+ code_dir.insert((tabled_rc!("@>", atom_tbl), 2), (PredicateKeyType::BuiltIn, 389));
+ code_dir.insert((tabled_rc!("@<", atom_tbl), 2), (PredicateKeyType::BuiltIn, 390));
+
(builtin_code, code_dir, op_dir)
}
},
&QueryTerm::Catch(_) =>
code.push(Line::Control(ControlInstruction::CatchCall)),
+ &QueryTerm::CompareTerm(qt, _) =>
+ code.push(Line::Control(ControlInstruction::CompareTermCall(qt))),
&QueryTerm::Display(_) =>
code.push(Line::Control(ControlInstruction::DisplayCall)),
&QueryTerm::DuplicateTerm(_) =>
*ctrl = ControlInstruction::Execute(name, arity),
ControlInstruction::CallN(arity) =>
*ctrl = ControlInstruction::ExecuteN(arity),
+ ControlInstruction::CompareTermCall(qt) =>
+ *ctrl = ControlInstruction::CompareTermExecute(qt),
ControlInstruction::DisplayCall =>
*ctrl = ControlInstruction::DisplayExecute,
ControlInstruction::DuplicateTermCall =>
}
}
+impl fmt::Display for CompareTermQT {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ &CompareTermQT::GreaterThan => write!(f, "@>"),
+ &CompareTermQT::GreaterThanOrEqual => write!(f, "@>="),
+ &CompareTermQT::LessThan => write!(f, "@<"),
+ &CompareTermQT::LessThanOrEqual => write!(f, "@<="),
+ &CompareTermQT::NotEqual => write!(f, "\\=@="),
+ &CompareTermQT::Equal => write!(f, "=@="),
+ }
+ }
+}
+
+
impl fmt::Display for ControlInstruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
write!(f, "catch_execute"),
&ControlInstruction::CheckCpExecute =>
write!(f, "check_cp_execute"),
+ &ControlInstruction::CompareTermCall(qt) =>
+ write!(f, "compare_term_call {}", qt),
+ &ControlInstruction::CompareTermExecute(qt) =>
+ write!(f, "compare_term_execute {}", qt),
&ControlInstruction::DisplayCall =>
write!(f, "display_call"),
&ControlInstruction::DisplayExecute =>
let state = TermIterState::Clause(0, ClauseType::CompareNumber(qt), terms);
QueryIterator { state_stack: vec![state] }
},
+ &QueryTerm::CompareTerm(qt, ref terms) => {
+ let state = TermIterState::Clause(0, ClauseType::CompareTerm(qt), terms);
+ QueryIterator { state_stack: vec![state] }
+ },
&QueryTerm::Is(ref terms) => {
let state = TermIterState::Clause(0, ClauseType::Is, terms);
QueryIterator { state_stack: vec![state] }
arity = vars.len();
break;
},
+ &QueryTerm::CompareTerm(..) => {
+ result.push(term);
+ arity = 2;
+ break;
+ },
&QueryTerm::Term(ref inner_term) =>
if let GenContext::Head = self.term_loc {
result.push(term);
use prolog::or_stack::*;
use prolog::tabled_rc::*;
-use std::cmp::max;
+use std::cmp::{max, Ordering};
use std::rc::Rc;
macro_rules! try_or_fail {
self.p += 1;
}
+ fn compare_term(&mut self, qt: CompareTermQT) {
+ let a1 = self[temp_v!(1)].clone();
+ let a2 = self[temp_v!(2)].clone();
+
+ match self.compare_term_test(a1, a2) {
+ Ordering::Greater =>
+ match qt {
+ CompareTermQT::GreaterThan | CompareTermQT::GreaterThanOrEqual => return,
+ _ => self.fail = true
+ },
+ Ordering::Equal =>
+ match qt {
+ CompareTermQT::GreaterThanOrEqual | CompareTermQT::LessThanOrEqual => return,
+ _ => self.fail = true
+ },
+ Ordering::Less =>
+ match qt {
+ CompareTermQT::LessThan | CompareTermQT::LessThanOrEqual => return,
+ _ => self.fail = true
+ }
+ };
+ }
+
+ fn compare_term_test(&self, a1: Addr, a2: Addr) -> Ordering {
+ let iter = self.zipped_acyclic_pre_order_iter(a1, a2);
+
+ for (v1, v2) in iter {
+ match (v1, v2) {
+ (HeapCellValue::Addr(Addr::HeapCell(hc1)),
+ HeapCellValue::Addr(Addr::HeapCell(hc2))) =>
+ if hc1 != hc2 {
+ return hc1.cmp(&hc2);
+ } else {
+ continue;
+ },
+ (HeapCellValue::Addr(Addr::HeapCell(_)), _) =>
+ return Ordering::Less,
+ (HeapCellValue::Addr(Addr::StackCell(fr1, sc1)),
+ HeapCellValue::Addr(Addr::StackCell(fr2, sc2))) =>
+ if fr1 > fr2 {
+ return Ordering::Greater;
+ } else if fr1 < fr2 || sc1 < sc2 {
+ return Ordering::Less;
+ } else if sc1 > sc2 {
+ return Ordering::Greater;
+ } else {
+ continue;
+ },
+ (HeapCellValue::Addr(Addr::StackCell(..)),
+ HeapCellValue::Addr(Addr::HeapCell(_))) =>
+ return Ordering::Greater,
+ (HeapCellValue::Addr(Addr::StackCell(..)), _) =>
+ return Ordering::Less,
+ (HeapCellValue::Addr(Addr::Con(Constant::Number(..))),
+ HeapCellValue::Addr(Addr::HeapCell(_))) =>
+ return Ordering::Greater,
+ (HeapCellValue::Addr(Addr::Con(Constant::Number(..))),
+ HeapCellValue::Addr(Addr::StackCell(..))) =>
+ return Ordering::Greater,
+ (HeapCellValue::Addr(Addr::Con(Constant::Number(n1))),
+ HeapCellValue::Addr(Addr::Con(Constant::Number(n2)))) =>
+ if n1 != n2 {
+ return n1.cmp(&n2);
+ } else {
+ continue;
+ },
+ (HeapCellValue::Addr(Addr::Con(Constant::Number(_))), _) =>
+ return Ordering::Less,
+ (HeapCellValue::Addr(Addr::Con(Constant::String(..))),
+ HeapCellValue::Addr(Addr::HeapCell(_))) =>
+ return Ordering::Greater,
+ (HeapCellValue::Addr(Addr::Con(Constant::String(..))),
+ HeapCellValue::Addr(Addr::StackCell(..))) =>
+ return Ordering::Greater,
+ (HeapCellValue::Addr(Addr::Con(Constant::String(_))),
+ HeapCellValue::Addr(Addr::Con(Constant::Number(_)))) =>
+ return Ordering::Greater,
+ (HeapCellValue::Addr(Addr::Con(Constant::String(s1))),
+ HeapCellValue::Addr(Addr::Con(Constant::String(s2)))) =>
+ if s1 != s2 {
+ return s1.cmp(&s2);
+ } else {
+ continue;
+ },
+ (HeapCellValue::Addr(Addr::Con(Constant::String(_))), _) =>
+ return Ordering::Less,
+ (HeapCellValue::Addr(Addr::Con(Constant::Atom(..))),
+ HeapCellValue::Addr(Addr::HeapCell(_))) =>
+ return Ordering::Greater,
+ (HeapCellValue::Addr(Addr::Con(Constant::Atom(..))),
+ HeapCellValue::Addr(Addr::StackCell(..))) =>
+ return Ordering::Greater,
+ (HeapCellValue::Addr(Addr::Con(Constant::Atom(_))),
+ HeapCellValue::Addr(Addr::Con(Constant::Number(_)))) =>
+ return Ordering::Greater,
+ (HeapCellValue::Addr(Addr::Con(Constant::Atom(_))),
+ HeapCellValue::Addr(Addr::Con(Constant::String(_)))) =>
+ return Ordering::Greater,
+ (HeapCellValue::Addr(Addr::Con(Constant::Atom(s1))),
+ HeapCellValue::Addr(Addr::Con(Constant::Atom(s2)))) =>
+ if s1 != s2 {
+ return s1.cmp(&s2);
+ } else {
+ continue;
+ },
+ (HeapCellValue::Addr(Addr::Con(Constant::Atom(_))), _) =>
+ return Ordering::Less,
+ (HeapCellValue::NamedStr(ar1, n1, _), HeapCellValue::NamedStr(ar2, n2, _)) =>
+ if ar1 < ar2 {
+ return Ordering::Less;
+ } else if ar1 > ar2 {
+ return Ordering::Greater;
+ } else if *n1 != *n2 {
+ return n1.cmp(&n2);
+ } else {
+ continue;
+ },
+ (HeapCellValue::Addr(Addr::Lis(_)), HeapCellValue::Addr(Addr::Lis(_))) =>
+ continue,
+ (HeapCellValue::Addr(Addr::Lis(_)), HeapCellValue::NamedStr(ar, n, _))
+ | (HeapCellValue::NamedStr(ar, n, _), HeapCellValue::Addr(Addr::Lis(_))) =>
+ if ar == 2 && *n == "." {
+ continue;
+ } else if ar < 2 {
+ return Ordering::Greater;
+ } else if ar > 2 {
+ return Ordering::Less;
+ } else {
+ return n.cmp(&String::from("."));
+ },
+ (HeapCellValue::NamedStr(..), _) =>
+ return Ordering::Greater,
+ (HeapCellValue::Addr(Addr::Lis(_)), _) =>
+ return Ordering::Greater,
+ _ => {}
+ }
+ };
+
+ Ordering::Equal
+ }
+
fn reset_block(&mut self, addr: Addr) {
// let addr = self.deref(self[temp_v!(1)].clone());
cut_policy: &mut Box<CutPolicy>,
instr: &BuiltInInstruction)
{
- match instr {
+ match instr {
&BuiltInInstruction::GetArgCall =>
try_or_fail!(self, {
let val = self.try_get_arg();
self.or_stack.truncate(self.b);
self.fail = true;
- },
+ },
&BuiltInInstruction::InternalCallN =>
self.handle_internal_call_n(code_dir),
&BuiltInInstruction::Fail => {
// returns true on failure.
fn eq_test(&self) -> bool
{
- let a1 = self.store(self.deref(self[temp_v!(1)].clone()));
- let a2 = self.store(self.deref(self[temp_v!(2)].clone()));
+ let a1 = self[temp_v!(1)].clone();
+ let a2 = self[temp_v!(2)].clone();
let iter = self.zipped_acyclic_pre_order_iter(a1, a2);
(HeapCellValue::Addr(a1), HeapCellValue::Addr(a2)) =>
if a1 != a2 {
return true;
- },
+ },
_ => return true
}
}
}
};
},
+ &ControlInstruction::CompareTermCall(qt) => {
+ self.compare_term(qt);
+ self.p += 1;
+ },
+ &ControlInstruction::CompareTermExecute(qt) => {
+ self.compare_term(qt);
+ self.p = self.cp;
+ },
&ControlInstruction::Deallocate => {
let e = self.e;
&ControlInstruction::NotEqExecute => {
self.fail = !self.eq_test();
self.p = self.cp;
- },
+ },
&ControlInstruction::Proceed =>
self.p = self.cp,
&ControlInstruction::ThrowCall => {
Line::Control(ControlInstruction::NotEqExecute)
)
}
+
+macro_rules! compare_term_call {
+ ($qt:expr) => (
+ Line::Control(ControlInstruction::CompareTermCall($qt))
+ )
+}
+
+macro_rules! compare_term_execute {
+ ($qt:expr) => (
+ Line::Control(ControlInstruction::CompareTermExecute($qt))
+ )
+}
+
+macro_rules! term_cmp_gt {
+ () => (
+ CompareTermQT::GreaterThan
+ )
+}
+
+macro_rules! term_cmp_lt {
+ () => (
+ CompareTermQT::LessThan
+ )
+}
+
+macro_rules! term_cmp_gte {
+ () => (
+ CompareTermQT::GreaterThanOrEqual
+ )
+}
+
+macro_rules! term_cmp_lte {
+ () => (
+ CompareTermQT::LessThanOrEqual
+ )
+}
+
+macro_rules! term_cmp_ne {
+ () => (
+ CompareTermQT::NotEqual
+ )
+}
+
+macro_rules! term_cmp_eq {
+ () => (
+ CompareTermQT::Equal
+ )
+}
-Subproject commit b50ed579fdb245f2d42fcd05b546c23f077b0991
+Subproject commit 2e9979b9d20abb5b130e788606e3ea8f688a5600
assert_prolog_success!(&mut wam, "?- A \\== B.");
assert_prolog_success!(&mut wam, "?- A \\== 12.1.");
assert_prolog_failure!(&mut wam, "?- X = x, f(X, x) \\== f(x, X).");
+
+ assert_prolog_success!(&mut wam, "?- X @=< Y.");
+ assert_prolog_failure!(&mut wam, "?- X @>= Y.");
+ assert_prolog_failure!(&mut wam, "?- X @> Y.");
+ assert_prolog_success!(&mut wam, "?- X @>= X.");
+ assert_prolog_failure!(&mut wam, "?- atom @=< \"string\".");
+ assert_prolog_success!(&mut wam, "?- atom @=< atom.");
+ assert_prolog_failure!(&mut wam, "?- atom @=< aaa.");
+ assert_prolog_success!(&mut wam, "?- atom @>= \"string\".");
+ assert_prolog_success!(&mut wam, "?- X is 3 + 3, X @>= Y.");
+ assert_prolog_success!(&mut wam, "?- f(X) @>= f(X).");
+ assert_prolog_success!(&mut wam, "?- f(X) @>= a.");
+ assert_prolog_failure!(&mut wam, "?- f(X) @=< a.");
+ assert_prolog_success!(&mut wam, "?- [1,2] @=< [1,2].");
+ assert_prolog_failure!(&mut wam, "?- [1,2,3] @=< [1,2].");
+ assert_prolog_success!(&mut wam, "?- [] @=< [1,2].");
+ assert_prolog_failure!(&mut wam, "?- [] @< 1.");
+ assert_prolog_failure!(&mut wam, "?- [] @< \"string\".");
+ assert_prolog_failure!(&mut wam, "?- [] @< atom.");
+ assert_prolog_success!(&mut wam, "?- atom @< [].");
+ assert_prolog_failure!(&mut wam, "?- 1.1 @< 1.");
+ assert_prolog_success!(&mut wam, "?- 1.0 @=< 1.");
+ assert_prolog_success!(&mut wam, "?- 1 @=< 1.0."); //TODO: currently this succeeds. make it fail.
}
#[test]