From: Mark Thom Date: Mon, 6 Nov 2017 00:12:30 +0000 (-0700) Subject: add provisions for arithmetic support. X-Git-Tag: v0.8.110~682 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=5568d0b3709fd0ad1ec8be673d8a64aff8df0445;p=scryer-prolog.git add provisions for arithmetic support. --- diff --git a/Cargo.lock b/Cargo.lock index 0a442cf1..417e3cda 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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)", diff --git a/Cargo.toml b/Cargo.toml index bfa6fff1..092db674 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rusty-wam" -version = "0.7.1" +version = "0.7.2" authors = ["Mark Thom"] [dependencies] diff --git a/README.md b/README.md index d636b368..b1360480 100644 --- 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 diff --git a/src/main.rs b/src/main.rs index 88391731..64bd07b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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) diff --git a/src/prolog/and_stack.rs b/src/prolog/and_stack.rs index ff17f2b0..c1294be5 100644 --- a/src/prolog/and_stack.rs +++ b/src/prolog/and_stack.rs @@ -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 for AndStack { diff --git a/src/prolog/arithmetic.rs b/src/prolog/arithmetic.rs new file mode 100644 index 00000000..91bcf91a --- /dev/null +++ b/src/prolog/arithmetic.rs @@ -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> +} + +impl<'a> ArithExprIterator<'a> { + fn push_clause(&mut self, child_num: usize, ct: ClauseType<'a>, child_terms: &'a Vec>) + { + 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, head: &'a Term, tail: &'a Term) + { + self.state_stack.push(IteratorState::FinalCons(lvl, cell, head, tail)); + } + + fn new(term: &'a Term) -> Result { + 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, ArithmeticError> { + ArithExprIterator::new(self) + } +} + +impl<'a> Iterator for ArithExprIterator<'a> { + type Item = TermRef<'a>; + + fn next(&mut self) -> Option { + 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, + 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 + { + 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 + { + 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>, deep: bool) + -> Result + { + 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 { + 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) + } +} diff --git a/src/prolog/ast.rs b/src/prolog/ast.rs index 2f509fde..d51756b0 100644 --- a/src/prolog/ast.rs +++ b/src/prolog/ast.rs @@ -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 for ParserError { + fn from(err: ArithmeticError) -> ParserError { + ParserError::Arithmetic(err) + } +} + impl From for ParserError { fn from(err: IOError) -> ParserError { ParserError::IO(err) @@ -265,6 +281,15 @@ impl fmt::Display for Constant { } } +impl From 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, Atom, Vec>), @@ -273,10 +298,11 @@ pub enum Term { Var(Cell, Var) } -pub enum QueryTerm { +pub enum QueryTerm { CallN(Vec>), Catch(Vec>), Cut, + Is(Vec>), IsAtomic(Vec>), IsVar(Vec>), 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, &'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>), Catch(&'a Vec>), Cut, + Is(&'a Vec>), 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), + Integer(BigInt) +} + +impl Add 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 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 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 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), + 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; pub type CompiledQuery = Vec; 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>), + Constant(Level, &'a Cell, &'a Constant), + InitialCons(Level, &'a Cell, &'a Term, &'a Term), + FinalCons(Level, &'a Cell, &'a Term, &'a Term), + Var(Level, &'a Cell, &'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) + } + } +} diff --git a/src/prolog/builtins.rs b/src/prolog/builtins.rs index 08be6bfe..d547b364 100644 --- a/src/prolog/builtins.rs +++ b/src/prolog/builtins.rs @@ -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) diff --git a/src/prolog/codegen.rs b/src/prolog/codegen.rs index 0a531c42..dc5a4c15 100644 --- a/src/prolog/codegen.rs +++ b/src/prolog/codegen.rs @@ -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 { 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) -> Code + pub fn compile_query(&mut self, query: &'a Vec) -> Result { 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) -> 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 { 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) -> Code + pub fn compile_predicate<'b: 'a>(&mut self, clauses: &'b Vec) + -> Result { 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) } } diff --git a/src/prolog/io.rs b/src/prolog/io.rs index 47945189..0b64587f 100644 --- a/src/prolog/io.rs +++ b/src/prolog/io.rs @@ -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::::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::::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::::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), _ => {} }; } diff --git a/src/prolog/iterators.rs b/src/prolog/iterators.rs index ea469b61..6fa0f323 100644 --- a/src/prolog/iterators.rs +++ b/src/prolog/iterators.rs @@ -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>), - Constant(Level, &'a Cell, &'a Constant), - InitialCons(Level, &'a Cell, &'a Term, &'a Term), - FinalCons(Level, &'a Cell, &'a Term, &'a Term), - Var(Level, &'a Cell, &'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> } @@ -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 => { diff --git a/src/prolog/machine.rs b/src/prolog/machine.rs index f99ae486..fcca23c2 100644 --- a/src/prolog/machine.rs +++ b/src/prolog/machine.rs @@ -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 // 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 { + 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; diff --git a/src/prolog/macros.rs b/src/prolog/macros.rs index 4e83e320..1fa4b4d0 100644 --- a/src/prolog/macros.rs +++ b/src/prolog/macros.rs @@ -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) diff --git a/src/prolog/mod.rs b/src/prolog/mod.rs index 0d7e0345..533c3c1b 100644 --- a/src/prolog/mod.rs +++ b/src/prolog/mod.rs @@ -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; diff --git a/src/prolog/parser b/src/prolog/parser index da30488d..ebecfec0 160000 --- a/src/prolog/parser +++ b/src/prolog/parser @@ -1 +1 @@ -Subproject commit da30488df7e1a35f8ab0c0678cea85474a47a7ee +Subproject commit ebecfec050bb67730274fd07be94982cdf33321b