From: Mark Thom Date: Mon, 4 Dec 2017 00:44:51 +0000 (-0700) Subject: add support for comparison operators. X-Git-Tag: v0.8.110~659 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=7b569becbefbf720945e59a1cf4d1c4048948498;p=scryer-prolog.git add support for comparison operators. --- diff --git a/README.md b/README.md index c30fdd4c..4cdaeba8 100644 --- a/README.md +++ b/README.md @@ -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` diff --git a/src/prolog/arithmetic.rs b/src/prolog/arithmetic.rs index ffbcc84f..8f00940e 100644 --- a/src/prolog/arithmetic.rs +++ b/src/prolog/arithmetic.rs @@ -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, 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)), _ => {} }; } diff --git a/src/prolog/ast.rs b/src/prolog/ast.rs index d684d176..9578467b 100644 --- a/src/prolog/ast.rs +++ b/src/prolog/ast.rs @@ -328,9 +328,20 @@ impl InlinedQueryTerm { } } +#[derive(Clone, Copy)] +pub enum CompareNumberQT { + GreaterThan, + LessThan, + GreaterThanOrEqual, + LessThanOrEqual, + NotEqual, + Equal +} + pub enum QueryTerm { CallN(Vec>), Catch(Vec>), + CompareNumber(CompareNumberQT, Vec>), Cut, Is(Vec>), 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), diff --git a/src/prolog/builtins.rs b/src/prolog/builtins.rs index da435d9f..7c074e81 100644 --- a/src/prolog/builtins.rs +++ b/src/prolog/builtins.rs @@ -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) diff --git a/src/prolog/codegen.rs b/src/prolog/codegen.rs index aa573b92..c1784829 100644 --- a/src/prolog/codegen.rs +++ b/src/prolog/codegen.rs @@ -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 { + 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()); diff --git a/src/prolog/io.rs b/src/prolog/io.rs index eae23dcd..ccede041 100644 --- a/src/prolog/io.rs +++ b/src/prolog/io.rs @@ -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 => diff --git a/src/prolog/iterators.rs b/src/prolog/iterators.rs index 190dd1f8..9471619a 100644 --- a/src/prolog/iterators.rs +++ b/src/prolog/iterators.rs @@ -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; diff --git a/src/prolog/machine.rs b/src/prolog/machine.rs index 30ef2721..939b81fb 100644 --- a/src/prolog/machine.rs +++ b/src/prolog/machine.rs @@ -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())); diff --git a/src/prolog/macros.rs b/src/prolog/macros.rs index 8aff2437..d2ec77f8 100644 --- a/src/prolog/macros.rs +++ b/src/prolog/macros.rs @@ -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))