[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)",
[package]
name = "rusty-wam"
-version = "0.7.1"
+version = "0.7.2"
authors = ["Mark Thom"]
[dependencies]
* 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
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
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
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)
}
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()
}
}
}
}
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)]
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 {
--- /dev/null
+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)
+ }
+}
use prolog::num::bigint::BigInt;
+use prolog::num::ToPrimitive;
use prolog::ordered_float::*;
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;
($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
#[derive(Debug)]
pub enum ParserError
{
+ Arithmetic(ArithmeticError),
CommaArityMismatch,
UnexpectedEOF,
FailedMatch(String),
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)
}
}
+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>>),
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),
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) =>
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
}
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),
match self {
QueryTermRef::Catch(_) => 3,
QueryTermRef::Throw(_) => 1,
+ QueryTermRef::Is(_) => 2,
QueryTermRef::IsAtomic(_) => 1,
QueryTermRef::IsVar(_) => 1,
QueryTermRef::CallN(terms) => terms.len(),
}
}
+#[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,
Goto(usize, usize), // p, arity.
Proceed,
ThrowCall,
- ThrowExecute
+ ThrowExecute,
+ UnifyCall,
+ UnifyExecute
}
impl ControlInstruction {
&ControlInstruction::ThrowExecute => true,
&ControlInstruction::Goto(_, _) => true,
&ControlInstruction::Proceed => true,
+ &ControlInstruction::UnifyCall => true,
+ &ControlInstruction::UnifyExecute => true,
_ => false
}
}
pub type CompiledQuery = Vec<QueryInstruction>;
pub enum Line {
+ Arithmetic(ArithmeticInstruction),
BuiltIn(BuiltInInstruction),
Choice(ChoiceInstruction),
Control(ControlInstruction),
}
}
}
+
+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)
+ }
+ }
+}
proceed!(),
fact![get_value!(temp_v!(1), 2)], // =/2, 73.
proceed!(),
- succeed!(), // true/0, 75.
+ proceed!() // true/0, 75.
]
}
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)
use prolog::allocator::*;
+use prolog::arithmetic::*;
use prolog::ast::*;
use prolog::fixtures::*;
use prolog::indexing::*;
}
pub enum EvalSession<'a> {
- EntryFailure(String),
+ OpIsInfixAndPostFix,
+ NamelessEntry,
+ ParserError(ParserError),
+ ImpermissibleEntry(String),
EntrySuccess,
InitialQuerySuccess(AllocVarDict<'a>, HeapVarDict<'a>),
QueryFailure,
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!());
},
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()
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();
&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();
self.marker.reset_contents();
}
}
+
+ Ok(())
}
fn compile_seq_prelude(&mut self, conjunct_info: &ConjunctInfo, body: &mut Code)
}
}
- 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);
}
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() {
}
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)
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);
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() {
}
Self::compile_cleanup(&mut code, &conjunct_info, query.last().unwrap().to_ref());
- code
+ Ok(code)
}
fn split_predicate(clauses: &Vec<PredicateClause>) -> Vec<(usize, usize)>
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();
&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 {
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 {
code.append(&mut code_segment);
}
- code
+ Ok(code)
}
}
&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"),
}
}
}
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")
}
}
}
}
}
+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 {
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);
&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) => {
&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())
}
}
},
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),
_ => {}
};
}
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>>
}
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![] }
}
}
}
arity = child_terms.len();
break;
},
+ QueryTermRef::Is(_) => {
+ result.push(term);
+ arity = 2;
+ break;
+ },
QueryTermRef::IsAtomic(_) | QueryTermRef::IsVar(_) =>
result.push(term),
QueryTermRef::Cut => {
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;
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> {
{
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)),
_ => {}
};
self.code.append(&mut code);
self.add_user_code(name, arity, p)
} else {
- EvalSession::EntryFailure(format!("error: the fact has no name."))
+ EvalSession::NamelessEntry
}
}
self.code.append(&mut code);
self.add_user_code(name, arity, p)
} else {
- EvalSession::EntryFailure(format!("error: the rule has no name."))
+ EvalSession::NamelessEntry
}
}
};
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) =>
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;
- },
+ },
_ => {}
}
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>
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,
_ => {}
};
}
}
}
+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,
tr: 0,
hb: 0,
block: 0,
- ball: (0, Vec::new())
+ ball: (0, Vec::new()),
+ interms: vec![Number::Float(OrderedFloat(0f64)); 256],
}
}
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)
}
}
}
};
}
+ 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) => {
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);
},
&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;
},
&ControlInstruction::ThrowExecute => {
self.goto_throw();
- }
+ },
+ &ControlInstruction::UnifyCall => {
+ self.inline_unify();
+ self.p += 1;
+ },
+ &ControlInstruction::UnifyExecute => {
+ self.inline_unify();
+ self.p = self.cp;
+ }
};
}
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;
self.hb = self.h;
self.p += l;
- }
+ },
};
}
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;
)
}
+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 {
)
}
+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)
)
}
+macro_rules! unify_call {
+ () => (
+ Line::Control(ControlInstruction::UnifyCall)
+ )
+}
+
macro_rules! unwind_stack {
() => (
Line::BuiltIn(BuiltInInstruction::UnwindStack)
pub mod ast;
#[macro_use]
pub mod macros;
+pub mod arithmetic;
pub mod builtins;
pub mod codegen;
pub mod copier;
-Subproject commit da30488df7e1a35f8ab0c0678cea85474a47a7ee
+Subproject commit ebecfec050bb67730274fd07be94982cdf33321b