]> Repositorios git - scryer-prolog.git/commitdiff
add provisions for arithmetic support.
authorMark Thom <[email protected]>
Mon, 6 Nov 2017 00:12:30 +0000 (17:12 -0700)
committerMark Thom <[email protected]>
Mon, 6 Nov 2017 00:12:30 +0000 (17:12 -0700)
15 files changed:
Cargo.lock
Cargo.toml
README.md
src/main.rs
src/prolog/and_stack.rs
src/prolog/arithmetic.rs [new file with mode: 0644]
src/prolog/ast.rs
src/prolog/builtins.rs
src/prolog/codegen.rs
src/prolog/io.rs
src/prolog/iterators.rs
src/prolog/machine.rs
src/prolog/macros.rs
src/prolog/mod.rs
src/prolog/parser

index 0a442cf1573c314a01fddc8afdfbd8c93efc7bf5..417e3cda49eaf6ec9644d7ca9b76d2d682ba2738 100644 (file)
@@ -1,6 +1,6 @@
 [root]
 name = "rusty-wam"
-version = "0.7.1"
+version = "0.7.2"
 dependencies = [
  "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)",
index bfa6fff124254dbf428399911775f8067cee8a89..092db674128df5c7ea29843b35972198245514fe 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "rusty-wam"
-version = "0.7.1"
+version = "0.7.2"
 authors = ["Mark Thom"]
 
 [dependencies]
index d636b368fc4f630e7d94c7e8d071ea6a541dfbe5..b13604802be511c246bc6b3553340fbb81b548f3 100644 (file)
--- a/README.md
+++ b/README.md
@@ -19,15 +19,15 @@ Extend rusty-wam to include the following, among other features:
 * ISO Prolog compliant throw/catch (_done_).
 * Built-in and user-defined operators of all fixities, with custom
   associativity and precedence (_done_). 
-* Bignum and floating point arithmetic.
+* Bignum and floating point arithmetic (_in progress_).
 * 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
   semantics. Adding coroutines like `dif/2`, `freeze/2`, etc.
   is straightforward with attributed variables. 
 * 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.
 * Extensions for clp(FD).
 * `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.
 
+* Arithmetic support:
+  ** is/2 works for (+)/2, (-)/{1,2}, (*)/2, (//)/2, (div)/2.
 * atomic/1
 * call/N (1 <= N <= 63)
 * 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
 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> :- op(500, yfx, -).
-prolog> :- op(200, fy, -).
-prolog> :- op(400, yfx, *).
-prolog> :- op(400, yfx, /).
-prolog> ?- X = -5 + 3 - (2 * 4) / 8.
+prolog> ?- X = -5 + 3 - (2 * 4) // 8.
 true.
 X = -(+(-(5), 3), /(*(2, 4), 8)).
 ```
\ No newline at end of file
index 88391731f2535920f0327817b20f5de2a16f0a1d..64bd07b41fac248850d020d9820e08386fdac1c1 100644 (file)
@@ -734,6 +734,41 @@ mod tests {
 
         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)
index ff17f2b0827105b11566b5019ede34a845a20172..c1294be58a3aab57190e7aed4dd911cae0c4d146 100644 (file)
@@ -12,13 +12,13 @@ pub struct 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 {
             global_index: global_index,
             b0: 0,
             e: e,
             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) {
-        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)]
@@ -46,13 +47,6 @@ impl AndStack {
     pub fn clear(&mut self) {
         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 {
diff --git a/src/prolog/arithmetic.rs b/src/prolog/arithmetic.rs
new file mode 100644 (file)
index 0000000..91bcf91
--- /dev/null
@@ -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)
+    }
+}
index 2f509fde8dbe4d3a28bb37b3546059a75e5c4b9c..d51756b02eebee07813a41982459c3dd4c22eff4 100644 (file)
@@ -1,4 +1,5 @@
 use prolog::num::bigint::BigInt;
+use prolog::num::ToPrimitive;
 
 use prolog::ordered_float::*;
 
@@ -8,7 +9,7 @@ use std::collections::{HashMap, VecDeque};
 use std::fmt;
 use std::io::Error as IOError;
 use std::num::{ParseFloatError};
-use std::ops::{Add, AddAssign};
+use std::ops::{Add, AddAssign, Sub, Mul, Div, Neg};
 use std::str::Utf8Error;
 use std::vec::Vec;
 
@@ -189,6 +190,14 @@ macro_rules! prefix {
     ($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
 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
@@ -198,6 +207,7 @@ tokens exceeding 4096 chars in length. */
 #[derive(Debug)]
 pub enum ParserError
 {
+    Arithmetic(ArithmeticError),
     CommaArityMismatch,
     UnexpectedEOF,
     FailedMatch(String),
@@ -206,13 +216,19 @@ pub enum ParserError
     InadmissibleQueryTerm,
     IncompleteReduction,
     InconsistentDeclaration,
-    InconsistentPredicate,    
+    InconsistentPredicate,
     ParseBigInt,
     ParseFloat(ParseFloatError),
     // TokenTooLong,
     Utf8Conversion(Utf8Error)
 }
 
+impl From<ArithmeticError> for ParserError {
+    fn from(err: ArithmeticError) -> ParserError {
+        ParserError::Arithmetic(err)
+    }
+}
+
 impl From<IOError> for ParserError {
     fn from(err: IOError) -> ParserError {
         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 {
     AnonVar,
     Clause(Cell<RegType>, Atom, Vec<Box<Term>>),
@@ -273,10 +298,11 @@ pub enum Term {
     Var(Cell<VarReg>, Var)
 }
 
-pub enum QueryTerm {    
+pub enum QueryTerm {
     CallN(Vec<Box<Term>>),
     Catch(Vec<Box<Term>>),
     Cut,
+    Is(Vec<Box<Term>>),
     IsAtomic(Vec<Box<Term>>),
     IsVar(Vec<Box<Term>>),
     Term(Term),
@@ -292,6 +318,8 @@ impl QueryTerm {
                 QueryTermRef::Catch(terms),
             &QueryTerm::Cut =>
                 QueryTermRef::Cut,
+            &QueryTerm::Is(ref terms) =>
+                QueryTermRef::Is(terms),
             &QueryTerm::IsAtomic(ref terms) =>
                 QueryTermRef::IsAtomic(terms.first().unwrap()),
             &QueryTerm::IsVar(ref terms) =>
@@ -314,13 +342,14 @@ pub enum ClauseType<'a> {
     CallN,
     Catch,
     Deep(Level, &'a Cell<RegType>, &'a Atom),
+    Is,
     Root,
     Throw,
 }
 
 impl<'a> ClauseType<'a> {
     pub fn level_of_subterms(self) -> Level {
-        match self {            
+        match self {
             ClauseType::Deep(_, _, _) => Level::Deep,
             _ => Level::Shallow
         }
@@ -354,6 +383,7 @@ pub enum QueryTermRef<'a> {
     CallN(&'a Vec<Box<Term>>),
     Catch(&'a Vec<Box<Term>>),
     Cut,
+    Is(&'a Vec<Box<Term>>),
     IsAtomic(&'a Term),
     IsVar(&'a Term),
     Term(&'a Term),
@@ -365,6 +395,7 @@ impl<'a> QueryTermRef<'a> {
         match self {
             QueryTermRef::Catch(_) => 3,
             QueryTermRef::Throw(_) => 1,
+            QueryTermRef::Is(_) => 2,
             QueryTermRef::IsAtomic(_) => 1,
             QueryTermRef::IsVar(_) => 1,
             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 {
     CleanUpBlock,
     DuplicateTerm,
@@ -442,7 +599,9 @@ pub enum ControlInstruction {
     Goto(usize, usize), // p, arity.
     Proceed,
     ThrowCall,
-    ThrowExecute
+    ThrowExecute,
+    UnifyCall,
+    UnifyExecute
 }
 
 impl ControlInstruction {
@@ -458,6 +617,8 @@ impl ControlInstruction {
             &ControlInstruction::ThrowExecute => true,
             &ControlInstruction::Goto(_, _) => true,
             &ControlInstruction::Proceed => true,
+            &ControlInstruction::UnifyCall => true,
+            &ControlInstruction::UnifyExecute => true,            
             _ => false
         }
     }
@@ -508,6 +669,7 @@ pub type CompiledFact = Vec<FactInstruction>;
 pub type CompiledQuery = Vec<QueryInstruction>;
 
 pub enum Line {
+    Arithmetic(ArithmeticInstruction),
     BuiltIn(BuiltInInstruction),
     Choice(ChoiceInstruction),
     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)
+        }
+    }
+}
index 08be6bfe1281f48091b7fd914601a105a1d64f0c..d547b364e510acd95c5264c6fc939554ddbc2709 100644 (file)
@@ -104,7 +104,7 @@ fn get_builtins() -> Code {
          proceed!(),
          fact![get_value!(temp_v!(1), 2)], // =/2, 73.
          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::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::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
     // (an extra register is needed for the predicate name)
index 0a531c42e86077b94e4ad6b83aa77cacc071c307..dc5a4c15751f822531ba0e5cd3d1fbc577234edc 100644 (file)
@@ -1,4 +1,5 @@
 use prolog::allocator::*;
+use prolog::arithmetic::*;
 use prolog::ast::*;
 use prolog::fixtures::*;
 use prolog::indexing::*;
@@ -14,7 +15,10 @@ pub struct CodeGenerator<'a, TermMarker> {
 }
 
 pub enum EvalSession<'a> {
-    EntryFailure(String),
+    OpIsInfixAndPostFix,
+    NamelessEntry,
+    ParserError(ParserError),
+    ImpermissibleEntry(String),
     EntrySuccess,
     InitialQuerySuccess(AllocVarDict<'a>, HeapVarDict<'a>),
     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() {
                     *ctrl = ControlInstruction::ThrowExecute;
                 },
-            QueryTermRef::IsAtomic(_) => {
-                dealloc_index = code.len();
-                code.push(proceed!());
-            },
-            QueryTermRef::IsVar(_) => {
+            QueryTermRef::Is(_) =>
+                if let &mut Line::Control(ref mut ctrl) = code.last_mut().unwrap() {
+                    *ctrl = ControlInstruction::UnifyExecute;
+                },
+            QueryTermRef::IsAtomic(_) | QueryTermRef::IsVar(_) => {
                 dealloc_index = code.len();
                 code.push(proceed!());
             },
@@ -275,6 +279,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
                    conjunct_info: &ConjunctInfo<'a>,
                    code: &mut Code,
                    is_exposed: bool)
+        -> Result<(), ParserError>
     {
         for (chunk_num, _, terms) in iter {
             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))
                         });
                     },
+                    &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) =>
                         match inner_term {
                             &Term::AnonVar | &Term::Clause(_, _, _) | &Term::Cons(_, _, _) => {
-                                code.push(fail!()); //goto!(61, 0)); // goto false/0.
+                                code.push(fail!());
                             },
                             &Term::Constant(_, _) => {
-                                code.push(succeed!()); //goto!(75, 0)); // goto succeed/0.
+                                code.push(succeed!());
                             },
                             &Term::Var(ref vr, ref name) => {
                                 let mut target = Vec::new();
@@ -321,10 +360,10 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
                     &QueryTermRef::IsVar(inner_term) =>
                         match inner_term {
                             &Term::Constant(_, _) | &Term::Clause(_, _, _) | &Term::Cons(_, _, _) => {
-                                code.push(fail!()); //goto!(61, 0)); // goto false/0.
+                                code.push(fail!());
                             },
                             &Term::AnonVar => {
-                                code.push(succeed!()); //goto!(75, 0)); // goto succeed/0.
+                                code.push(succeed!());
                             },
                             &Term::Var(ref vr, ref name) => {
                                 let mut target = Vec::new();
@@ -352,6 +391,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
                 self.marker.reset_contents();
             }
         }
+
+        Ok(())
     }
 
     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 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);
-        self.compile_seq(iter, &conjunct_info, &mut code, false);
+        try!(self.compile_seq(iter, &conjunct_info, &mut code, false));
 
         if conjunct_info.allocates() {
             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());
-        code
+        Ok(code)
     }
 
     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);
     }
 
-    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 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);
 
         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() {
             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());
-        code
+        Ok(code)
     }
 
     fn split_predicate(clauses: &Vec<PredicateClause>) -> Vec<(usize, usize)>
@@ -529,7 +570,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
         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_offsets = CodeOffsets::new();
@@ -543,7 +585,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
                 &PredicateClause::Fact(ref fact) =>
                     self.compile_fact(fact),
                 &PredicateClause::Rule(ref rule) =>
-                    self.compile_rule(rule)
+                    try!(self.compile_rule(rule))
             };
 
             if num_clauses > 1 {
@@ -567,17 +609,18 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
         let mut code = Vec::new();
 
         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 split_pred = Self::split_predicate(&clauses);
         let multi_seq  = split_pred.len() > 1;
 
         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 {
                 let choice = match l {
@@ -592,6 +635,6 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<'a, TermMarker>
             code.append(&mut code_segment);
         }
 
-        code
+        Ok(code)
     }
 }
index 47945189e94a9ee17d24260459fa084261f44626..0b64587f71a88e9833310b5ef94df1ad88750cc6 100644 (file)
@@ -106,7 +106,11 @@ impl fmt::Display for ControlInstruction {
             &ControlInstruction::ThrowCall =>
                 write!(f, "call_throw"),
             &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"),
             &BuiltInInstruction::Succeed =>
                 write!(f, "true"),
+            &BuiltInInstruction::UnwindStack =>
+                write!(f, "unwind_stack"),
             &BuiltInInstruction::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 {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
@@ -236,6 +279,8 @@ impl fmt::Display for RegType {
 pub fn print_code(code: &Code) {
     for clause in code {
         match clause {
+            &Line::Arithmetic(ref arith) =>
+                println!("{}", arith),
             &Line::Fact(ref fact) =>
                 for fact_instr in fact {
                     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) => {
             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)
         },
         &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) => {
             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)
         },
         &TopLevel::Query(ref query) => {
             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())
         }
     }
@@ -385,7 +442,10 @@ pub fn print(wam: &mut Machine, result: EvalSession) {
         },
         EvalSession::QueryFailure => println!("false."),
         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),
         _ => {}
     };
 }
index ea469b61b32d6787bb48cf7117afac60c98d061d..6fa0f323910aa0c00acec8ea981e85cac5fad642 100644 (file)
@@ -5,32 +5,6 @@ use std::collections::VecDeque;
 use std::iter::*;
 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> {
     state_stack: Vec<IteratorState<'a>>
 }
@@ -77,13 +51,17 @@ impl<'a> QueryIterator<'a> {
                 let state = IteratorState::Clause(0, ClauseType::Catch, terms);
                 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) =>
                 Self::from_term(term),
             QueryTermRef::Throw(term) => {
                 let state = IteratorState::Clause(0, ClauseType::Throw, term);
                 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();
                     break;
                 },
+                QueryTermRef::Is(_) => {
+                    result.push(term);
+                    arity = 2;
+                    break;
+                },
                 QueryTermRef::IsAtomic(_) | QueryTermRef::IsVar(_) =>
                     result.push(term),
                 QueryTermRef::Cut => {
index f99ae4864329d255039272a345f1f6112548fe9e..fcca23c2268b00f582fd53a3c0220d66cd8e1370 100644 (file)
@@ -4,7 +4,10 @@ use prolog::codegen::*;
 use prolog::copier::*;
 use prolog::heapview::*;
 use prolog::and_stack::*;
+use prolog::num::Zero;
+use prolog::num::bigint::BigInt;
 use prolog::or_stack::*;
+use prolog::ordered_float::OrderedFloat;
 use prolog::fixtures::*;
 
 use std::collections::HashMap;
@@ -36,7 +39,8 @@ struct MachineState {
     tr: usize,
     hb: usize,
     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> {
@@ -225,9 +229,7 @@ impl Machine {
     {
         match self.code_dir.get(&(name.clone(), arity)) {
             Some(&(PredicateKeyType::BuiltIn, _)) =>
-                return EvalSession::EntryFailure(format!("error: cannot replace built-in predicate {}/{}",
-                                                         name,
-                                                         arity)),
+                return EvalSession::ImpermissibleEntry(format!("{}/{}", name, arity)),
             _ => {}
         };
 
@@ -246,7 +248,7 @@ impl Machine {
             self.code.append(&mut code);
             self.add_user_code(name, arity, p)
         } else {
-            EvalSession::EntryFailure(format!("error: the fact has no name."))
+            EvalSession::NamelessEntry
         }
     }
 
@@ -261,7 +263,7 @@ impl Machine {
             self.code.append(&mut code);
             self.add_user_code(name, arity, p)
         } else {
-            EvalSession::EntryFailure(format!("error: the rule has no name."))
+            EvalSession::NamelessEntry
         }
     }
 
@@ -297,6 +299,8 @@ impl Machine {
         };
 
         match instr {
+            &Line::Arithmetic(ref arith_instr) =>
+                self.ms.execute_arith_instr(arith_instr),
             &Line::BuiltIn(ref built_in_instr) =>
                 self.ms.execute_built_in_instr(&self.code_dir, built_in_instr),
             &Line::Choice(ref choice_instr) =>
@@ -408,11 +412,11 @@ impl Machine {
 
         while self.ms.p < end_ptr {
             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() => {
                         self.record_var_places(cn, alloc_locs, heap_locs);
                         cn += 1;
-                    },                    
+                    },
                     _ => {}
                 }
 
@@ -422,10 +426,10 @@ impl Machine {
             self.query_stepper();
 
             match self.ms.p {
-                CodePtr::TopLevel(_, p) if p > 0 => {},                
+                CodePtr::TopLevel(_, p) if p > 0 => {},
                 _ => break
             };
-        }        
+        }
     }
 
     fn fail<'a>(&mut self) -> EvalSession<'a>
@@ -443,21 +447,16 @@ impl Machine {
     pub fn submit_decl<'a>(&mut self, decl: &Declaration) -> EvalSession<'a> {
         match decl {
             &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) {
                     match self.op_dir.get(&(name.clone(), Fixity::Post)) {
-                        Some(_) => return EvalSession::EntryFailure(ERR_STRING.clone()),
+                        Some(_) => return EvalSession::OpIsInfixAndPostFix,
                         _ => {}
                     };
                 }
 
                 if is_postfix!(spec) {
                     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 {
     fn new() -> MachineState {
         MachineState { h: 0,
@@ -618,7 +629,8 @@ impl MachineState {
                        tr: 0,
                        hb: 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) =>
                     self.heap[r] = HeapCellValue::Ref(Ref::HeapCell(r)),
                 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) {
         match instr {
             &FactInstruction::GetConstant(_, ref c, reg) => {
@@ -1306,13 +1404,6 @@ impl MachineState {
                 self.write_constant_to_var(addr, &c);
                 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 => {
                 self.ball.0 = 0;
                 self.ball.1.truncate(0);
@@ -1414,16 +1505,26 @@ impl MachineState {
             },
             &BuiltInInstruction::Succeed => {
                 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)
     {
         match instr {
-            &ControlInstruction::Allocate(num_cells) => {
+            &ControlInstruction::Allocate(num_cells) => {                
                 let num_frames = self.num_frames();
-
                 self.and_stack.push(num_frames + 1, self.e, self.cp, num_cells);
 
                 self.e = self.and_stack.len() - 1;
@@ -1473,7 +1574,15 @@ impl MachineState {
             },
             &ControlInstruction::ThrowExecute => {
                 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.e = self.or_stack[b].e;
+                self.e  = self.or_stack[b].e;
                 self.cp = self.or_stack[b].cp;
 
                 self.or_stack[b].bp = self.p + 1;
@@ -1559,7 +1668,7 @@ impl MachineState {
 
                 self.hb = self.h;
                 self.p += l;
-            }
+            },
         };
     }
 
@@ -1598,7 +1707,7 @@ impl MachineState {
                     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.or_stack[b].bp = self.p + offset;
index 4e83e320194303b75ac74919a49bc5a7912d8b84..1fa4b4d0f71d6829aafc5f574b2c8987c21b35fb 100644 (file)
@@ -22,6 +22,12 @@ macro_rules! query {
     )
 }
 
+macro_rules! string {
+    ($str:expr) => {
+        vec![HeapCellValue::Con(Constant::String(String::from($str)))]
+    }
+}
+
 macro_rules! functor {
     ($name:expr, $len:expr, [$($args:expr),*]) => {{
         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 {
     ($r:expr, $arg:expr) => (
         QueryInstruction::PutValue($r, $arg)
@@ -197,6 +209,12 @@ macro_rules! unify {
     )
 }
 
+macro_rules! unify_call {
+    () => (
+        Line::Control(ControlInstruction::UnifyCall)
+    )
+}
+
 macro_rules! unwind_stack {
     () => (
         Line::BuiltIn(BuiltInInstruction::UnwindStack)
index 0d7e0345949dff831ff31ee74d75321a7bcdd41e..533c3c1b4ea71fafe2b064a624c19c966ad66718 100644 (file)
@@ -7,6 +7,7 @@ pub mod and_stack;
 pub mod ast;
 #[macro_use]
 pub mod macros;
+pub mod arithmetic;
 pub mod builtins;
 pub mod codegen;
 pub mod copier;
index da30488df7e1a35f8ab0c0678cea85474a47a7ee..ebecfec050bb67730274fd07be94982cdf33321b 160000 (submodule)
@@ -1 +1 @@
-Subproject commit da30488df7e1a35f8ab0c0678cea85474a47a7ee
+Subproject commit ebecfec050bb67730274fd07be94982cdf33321b