authors = ["Mark Thom"]
[dependencies]
+downcast = "0.9.1"
num = "0.2"
ordered-float = "0.5.0"
-downcast = "0.9.1"
[dependencies.termion]
version = "1.4.0"
\ No newline at end of file
* Built-in control operators (`,`, `;`, `->`, etc.) (_done_).
* A revised, not-terrible module system (_done, I think_).
* Built-in predicates for list processing and top-level declarative
- control (`setup_call_control/3`, `call_with_inference_limit/3`,
+ control (`setup_call_cleanup/3`, `call_with_inference_limit/3`,
etc.) (_done_)
* Default representation of strings as list of chars, using a packed
internal representation (_done_).
The following predicates are built-in to rusty-wam.
* Arithmetic support:
- * `is/2` works for `(+)/2`, `(-)/{1,2}`, `(*)/2`, `(//)/2`, `(div)/2`, `(/)/2`, `(rdiv)/2`,
+ * `is/2` works for `(+)/2`, `(-)/{1,2}`, `(*)/2`, `(//)/2`, `(^)/2`, `(div)/2`, `(/)/2`, `(rdiv)/2`,
`(xor)/2`, `(rem)/2`, `(mod)/2`, `(/\)/2`, `(\/)/2`, `(>>)/2`, `(<<)/2`.
* Comparison operators: `>`, `<`, `=<`, `>=`, `=:=`, `=\=`.
* `(:)/2`
-> Result<ArithmeticInstruction, ArithmeticError>
{
match name.as_str() {
+ "abs" => Ok(ArithmeticInstruction::Abs(a1, t)),
"-" => Ok(ArithmeticInstruction::Neg(a1, t)),
_ => Err(ArithmeticError::InvalidOp)
}
"div" => Ok(ArithmeticInstruction::FIDiv(a1, a2, t)),
"rdiv" => Ok(ArithmeticInstruction::RDiv(a1, a2, t)),
"*" => Ok(ArithmeticInstruction::Mul(a1, a2, t)),
+ "^" => Ok(ArithmeticInstruction::Pow(a1, a2, t)),
">>" => Ok(ArithmeticInstruction::Shr(a1, a2, t)),
"<<" => Ok(ArithmeticInstruction::Shl(a1, a2, t)),
"/\\" => Ok(ArithmeticInstruction::And(a1, a2, t)),
-use prolog::num::bigint::BigInt;
-use prolog::num::{Float, ToPrimitive, Zero};
-use prolog::num::rational::Ratio;
+use prolog::num::bigint::{BigInt, BigUint};
+use prolog::num::{Float, Integer, One, Signed, ToPrimitive, Zero};
+use prolog::num::rational::{BigRational, Ratio};
use prolog::ordered_float::*;
use prolog::string_list::*;
use prolog::tabled_rc::*;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::io::Error as IOError;
-use std::ops::{Add, AddAssign, Div, Index, IndexMut, Sub, Mul, Neg};
+use std::ops::{Add, AddAssign, Div, Index, IndexMut, Sub, Mul, MulAssign, Neg};
use std::rc::Rc;
use std::str::Utf8Error;
use std::vec::Vec;
InvalidAtom,
InvalidOp,
InvalidTerm,
+ NoRoots,
UninstantiatedVar
}
(&Constant::Number(ref n1), &Constant::Number(ref n2)) =>
n1 == n2,
(&Constant::String(ref s1), &Constant::String(ref s2)) =>
- s1 == s2,
+ s1 == s2,
(&Constant::EmptyList, &Constant::EmptyList) =>
true,
(&Constant::Usize(u1), &Constant::Usize(u2)) =>
_ => {}
};
}
-
+
pub fn arity(&self) -> usize {
match self {
&QueryTerm::Clause(_, _, ref subterms, ..) => subterms.len(),
}
}
-impl ClauseName {
+impl ClauseName {
pub fn as_str(&self) -> &str {
match self {
&ClauseName::BuiltIn(s) => s,
}
}
+fn binary_pow<T>(mut n: T, mut power: BigUint) -> T
+ where T: Clone + Mul + One,
+ for<'a> T: MulAssign<&'a T>
+{
+ if power.is_zero() {
+ return T::one();
+ }
+
+ let mut oddand = T::one();
+ let one = BigUint::one();
+
+ while power > one {
+ if power.is_odd() {
+ oddand *= &n;
+ }
+
+ n = n.clone() * n;
+ power >>= 1;
+ }
+
+ n * oddand
+}
+
+fn rational_pow(r1: BigRational, r2: BigRational) -> Result<BigRational, ArithmeticError>
+{
+ #[inline]
+ fn to_unsigned(n: &BigInt) -> Result<BigUint, ArithmeticError> {
+ n.abs().to_biguint().ok_or(ArithmeticError::NoRoots)
+ };
+
+ #[inline]
+ fn to_big_rational(n: BigUint) -> BigRational {
+ BigRational::from_integer(BigInt::from(n))
+ };
+
+ let r2 = r2.reduced(); // so that gcd(numer, denom) = 1
+ let n = to_unsigned(r2.denom())?;
+
+ if n == BigUint::one() {
+ return if r2.is_positive() {
+ Ok(binary_pow(r1, to_unsigned(&r2.numer())?))
+ } else if r2.is_negative() {
+ Ok(binary_pow(r1, to_unsigned(&r2.numer())?).recip())
+ } else {
+ Ok(BigRational::one())
+ };
+ }
+
+ if (n.is_even() && r1.is_negative()) || (r2.is_negative() && r1.is_zero()) {
+ return Err(ArithmeticError::NoRoots);
+ }
+
+ let sgn = r1.signum();
+ let r1 = r1 * &sgn; // set r1 to its absolute value.
+
+ let epsilon = BigRational::new_raw(BigInt::one(), BigInt::from(10000));
+ let n1 = n.clone() - BigUint::one(); // n -1
+
+ // 1 + r1 / (n-1) is a good initial point.
+ let mut x_i = BigRational::one() + r1.clone() / to_big_rational(n1.clone());
+ let mut x_i_n1 = binary_pow(x_i.clone(), n1.clone()); // x_i^{n-1}
+ let mut delta_x_i = BigRational::one();
+
+ while delta_x_i.abs() > epsilon {
+ x_i = x_i.reduced();
+ x_i_n1 = x_i_n1.reduced();
+
+ let r_quot = r1.clone() / &x_i_n1; // r1 / x_i^{n-1}
+ let r_n = to_big_rational(n.clone());
+
+ delta_x_i = ( r_quot - &x_i ) / &r_n;
+
+ x_i += &delta_x_i;
+ x_i_n1 = binary_pow(x_i.clone(), n1.clone());
+ }
+
+ if r2.is_positive() {
+ Ok(binary_pow(sgn * x_i, to_unsigned(r2.numer())?))
+ } else {
+ Ok(binary_pow(sgn * x_i, to_unsigned(r2.numer())?).recip())
+ }
+}
+
+fn pow_float(f1: f64, f2: f64) -> Result<Number, ArithmeticError> {
+ let result = OrderedFloat(f1.powf(f2));
+
+ if result.is_finite() {
+ Ok(Number::Float(result))
+ } else {
+ Err(ArithmeticError::NoRoots)
+ }
+}
+
+fn rational_to_f64(r: &BigRational) -> Option<f64> {
+ match (r.numer().to_f64(), r.denom().to_f64()) {
+ (Some(ref f1), Some(ref f2)) if f2.is_normal() => Some(*f1 / *f2),
+ _ => None
+ }
+}
+
impl Number {
+ pub fn pow(self, other: Number) -> Result<Self, ArithmeticError> {
+ match NumberPair::from(self, other) {
+ NumberPair::Integer(n1, n2) =>
+ if let Some(n2) = n2.to_biguint() {
+ Ok(Number::Integer(Rc::new(binary_pow((*n1).clone(), n2))))
+ } else if n1.is_zero() {
+ Err(ArithmeticError::NoRoots)
+ } else {
+ let r1 = Ratio::new(BigInt::one(), (*n1).clone());
+ let n2 = n2.abs().to_biguint().unwrap();
+
+ Ok(Number::Rational(Rc::new(binary_pow(r1, n2))))
+ },
+ NumberPair::Float(n1, n2) =>
+ pow_float(n1.into_inner(), n2.into_inner()),
+ NumberPair::Rational(r1, r2) => {
+ if let (Some(f1), Some(f2)) = (rational_to_f64(&r1), rational_to_f64(&r2)) {
+ if let Ok(result) = pow_float(f1, f2) {
+ return Ok(result);
+ }
+ }
+
+ let root = rational_pow((*r1).clone(), (*r2).clone())?;
+ Ok(Number::Rational(Rc::new(root)))
+ }
+ }
+ }
+
+ #[inline]
pub fn is_zero(&self) -> bool {
match self {
&Number::Float(fl) => fl.into_inner().is_zero(),
&Number::Rational(ref r) => r.is_zero()
}
}
+
+ #[inline]
+ pub fn abs(&self) -> Self {
+ match self {
+ &Number::Float(ref fl) => Number::Float(OrderedFloat(fl.into_inner().abs())),
+ &Number::Integer(ref n) => Number::Integer(Rc::new((*n).clone().abs())),
+ &Number::Rational(ref r) => Number::Rational(Rc::new((*r).clone().abs()))
+ }
+ }
}
pub enum NumberPair {
Add(ArithmeticTerm, ArithmeticTerm, usize),
Sub(ArithmeticTerm, ArithmeticTerm, usize),
Mul(ArithmeticTerm, ArithmeticTerm, usize),
+ Pow(ArithmeticTerm, ArithmeticTerm, usize),
IDiv(ArithmeticTerm, ArithmeticTerm, usize),
FIDiv(ArithmeticTerm, ArithmeticTerm, usize),
RDiv(ArithmeticTerm, ArithmeticTerm, usize),
Or(ArithmeticTerm, ArithmeticTerm, usize),
Mod(ArithmeticTerm, ArithmeticTerm, usize),
Rem(ArithmeticTerm, ArithmeticTerm, usize),
- Neg(ArithmeticTerm, usize)
+ Abs(ArithmeticTerm, usize),
+ Neg(ArithmeticTerm, usize),
}
#[derive(Clone)]
impl fmt::Display for ArithmeticInstruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
+ &ArithmeticInstruction::Abs(ref a1, ref t) =>
+ write!(f, "abs {}, @{}", a1, t),
&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::Pow(ref a1, ref a2, ref t) =>
+ write!(f, "pow {}, {}, @{}", a1, a2, t),
&ArithmeticInstruction::Div(ref a1, ref a2, ref t) =>
write!(f, "div {}, {}, @{}", a1, a2, t),
&ArithmeticInstruction::IDiv(ref a1, ref a2, ref t) =>
:- op(400, yfx, /).
-:- module(builtins, [(=)/2, (+)/2, (*)/2, (-)/2, (/)/2, (/\)/2,
+:- module(builtins, [(=)/2, (+)/2, (^)/2, (*)/2, (-)/2, (/)/2, (/\)/2,
(\/)/2, (is)/2, (xor)/2, (div)/2, (//)/2, (rdiv)/2, (<<)/2,
(>>)/2, (mod)/2, (rem)/2, (>)/2, (<)/2, (=\=)/2, (=:=)/2,
(-)/1, (>=)/2, (=<)/2, (,)/2, (->)/2, (;)/2, (=..)/2, (==)/2,
:- op(500, yfx, +).
:- op(500, yfx, -).
:- op(400, yfx, *).
+:- op(200, xfy, ^).
:- op(500, yfx, /\).
:- op(500, yfx, \/).
:- op(500, yfx, xor).
// IntOverflow,
// Undefined,
// Underflow,
- ZeroDivisor
+ ZeroDivisor,
+ NoRoots
}
impl EvalError {
// EvalError::IntOverflow => "int_overflow",
// EvalError::Undefined => "undefined",
// EvalError::Underflow => "underflow",
- EvalError::ZeroDivisor => "zero_divisor"
+ EvalError::ZeroDivisor => "zero_divisor",
+ EvalError::NoRoots => "no_roots"
}
}
}
pub fn machine_flags(&self) -> MachineFlags {
self.flags
}
-
+
fn next_global_index(&self) -> usize {
max(if self.and_stack.len() > 0 { self.and_stack[self.e].global_index } else { 0 },
if self.b > 0 { self.or_stack[self.b - 1].global_index } else { 0 }) + 1
(Addr::Lis(a1), Addr::Con(Constant::String(ref s)))
| (Addr::Con(Constant::String(ref s)), Addr::Lis(a1))
if self.flags.double_quotes.is_chars() =>
- if let Some(c) = s.head() {
+ if let Some(c) = s.head() {
pdl.push(Addr::Con(Constant::String(s.tail())));
pdl.push(Addr::HeapCell(a1 + 1));
-
+
pdl.push(Addr::Con(Constant::Char(c)));
pdl.push(Addr::HeapCell(a1));
} else {
self.fail = true;
- },
+ },
(Addr::Con(Constant::EmptyList), Addr::Con(Constant::String(ref s)))
| (Addr::Con(Constant::String(ref s)), Addr::Con(Constant::EmptyList))
if self.flags.double_quotes.is_chars() => {
if c1 == c2 {
pdl.push(Addr::Con(Constant::String(s1.tail())));
pdl.push(Addr::Con(Constant::String(s2.tail())));
-
+
continue;
}
}
self.fail = true;
},
- (Addr::Con(ref c1), Addr::Con(ref c2)) =>
+ (Addr::Con(ref c1), Addr::Con(ref c2)) =>
if c1 != c2 {
self.fail = true;
},
Addr::StackCell(fr, sc) => {
self.and_stack[fr][sc] = Addr::Con(c.clone());
self.trail(Ref::StackCell(fr, sc));
- },
- Addr::Con(Constant::String(s)) =>
+ },
+ Addr::Con(Constant::String(s)) =>
self.fail = match c {
Constant::EmptyList
if self.flags.double_quotes.is_chars() =>
"-" => interms.push(a1 - a2),
"*" => interms.push(a1 * a2),
"/" => interms.push(self.div(a1, a2)?),
+ "^" => interms.push(self.pow(a1, a2)?),
"rdiv" => {
let r1 = self.get_rational(&ArithmeticTerm::Number(a1), &caller)?;
let r2 = self.get_rational(&ArithmeticTerm::Number(a2), &caller)?;
}
}
+ fn pow(&self, n1: Number, n2: Number) -> Result<Number, MachineStub>
+ {
+ match n1.pow(n2) {
+ Ok(result) => Ok(result),
+ Err(_) => {
+ let stub = MachineError::functor_stub(clause_name!("^"), 2);
+ Err(self.error_form(MachineError::evaluation_error(EvalError::NoRoots),
+ stub))
+ }
+ }
+ }
+
fn shr(&self, n1: Number, n2: Number) -> Result<Rc<BigInt>, MachineStub>
{
let stub = MachineError::functor_stub(clause_name!("(>>)"), 2);
}
pub(super) fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) {
- match instr {
+ 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.interms[t - 1] = n1 * n2;
self.p += 1;
},
+ &ArithmeticInstruction::Pow(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.interms[t - 1] = try_or_fail!(self, self.pow(n1, n2));
+ self.p += 1;
+ },
&ArithmeticInstruction::RDiv(ref a1, ref a2, t) => {
let stub = MachineError::functor_stub(clause_name!("(rdiv)"), 2);
self.interms[t - 1] = Number::Integer(try_or_fail!(self, self.idiv(n1, n2)));
self.p += 1;
},
+ &ArithmeticInstruction::Abs(ref a1, t) => {
+ let n1 = try_or_fail!(self, self.get_number(a1));
+
+ self.interms[t - 1] = n1.abs();
+ self.p += 1;
+ },
&ArithmeticInstruction::Neg(ref a1, t) => {
let n1 = try_or_fail!(self, self.get_number(a1));
if self.flags.double_quotes.is_chars() => {
if let Some(c) = s.head() {
let h = self.heap.h;
-
+
self.heap.push(HeapCellValue::Addr(Addr::Con(Constant::Char(c))));
self.heap.push(HeapCellValue::Addr(Addr::Con(Constant::String(s.tail()))));
Ordering::Less
},
(HeapCellValue::Addr(Addr::Con(Constant::String(ref s))),
- HeapCellValue::Addr(Addr::Con(Constant::EmptyList)))
+ HeapCellValue::Addr(Addr::Con(Constant::EmptyList)))
if self.flags.double_quotes.is_chars() => if s.is_empty() {
return Ordering::Equal;
} else {
return Ordering::Less;
} else {
return n.as_str().cmp(".");
- },
+ },
(HeapCellValue::NamedStr(..), _) =>
return Ordering::Greater,
(HeapCellValue::Addr(Addr::Lis(_)), _) =>
}
return true;
- },
+ },
(HeapCellValue::Addr(Addr::Con(Constant::String(ref s1))),
HeapCellValue::Addr(Addr::Con(Constant::String(ref s2)))) =>
match s1.head() {
HeapCellValue::Addr(Addr::Con(Constant::String(ref s))))
if self.flags.double_quotes.is_chars() => if !s.is_empty() {
return true;
- },
+ },
(HeapCellValue::NamedStr(ar1, n1, _), HeapCellValue::NamedStr(ar2, n2, _)) =>
if ar1 != ar2 || n1 != n2 {
return true;
use_default_cp: bool)
{
let mut default_call_policy: Box<CallPolicy> = Box::new(DefaultCallPolicy {});
- let call_policy = if use_default_cp {
+ let call_policy = if use_default_cp {
&mut default_call_policy
} else {
call_policy
};
self.last_call = lco;
-
+
match ct {
&ClauseType::BuiltIn(ref ct) =>
try_or_fail!(self, call_policy.call_builtin(self, ct, code_dirs)),
try_or_fail!(self, call_policy.context_call(self, name.clone(), arity, idx.clone(),
code_dirs)),
&ClauseType::System(ref ct) =>
- try_or_fail!(self, self.system_call(ct, code_dirs, call_policy, cut_policy))
+ try_or_fail!(self, self.system_call(ct, code_dirs, call_policy, cut_policy))
};
}
self.allocate(num_cells),
&ControlInstruction::CallClause(ref ct, arity, _, lco, use_default_cp) =>
self.handle_call_clause(code_dirs, call_policy, cut_policy, ct, arity, lco,
- use_default_cp),
+ use_default_cp),
&ControlInstruction::Deallocate => self.deallocate(),
&ControlInstruction::JmpBy(arity, offset, _, lco) => {
if !lco {
pub(super) fn execute_choice_instr(&mut self, instr: &ChoiceInstruction,
call_policy: &mut Box<CallPolicy>)
{
- match instr {
+ match instr {
&ChoiceInstruction::TryMeElse(offset) => {
let n = self.num_of_args;
let gi = self.next_global_index();
assert_prolog_success!(&mut wam, "?- f(5, Sum).", [["Sum = 33"]]);
assert_prolog_success!(&mut wam, "?- f(5, 33).");
assert_prolog_failure!(&mut wam, "?- f(5, 32).");
+
+ // exponentiation.
+
+ // the ~ operators tests whether |X - Y| <= 1/10000...
+ // or whatever degree of approximation used by Newton's method in rational_pow.
+ submit(&mut wam, ":- op(900, xfx, ~).");
+ submit(&mut wam, "X ~ Y :- abs(X - Y) =< 1 rdiv 10000.");
+
+ assert_prolog_success!(&mut wam, "?- X is 3 ^ 3.",
+ [["X = 27"]]);
+ assert_prolog_success!(&mut wam, "?- X is 3 ^ 0.",
+ [["X = 1"]]);
+ assert_prolog_success!(&mut wam, "?- X is 3 ^ -0.",
+ [["X = 1"]]);
+ assert_prolog_success!(&mut wam, "?- X is 3 ^ 1.",
+ [["X = 3"]]);
+ assert_prolog_success!(&mut wam, "?- X is 3 ^ -3.",
+ [["X = 1/27"]]);
+ assert_prolog_success!(&mut wam, "?- X is (-3) ^ 3.",
+ [["X = -27"]]);
+ assert_prolog_success!(&mut wam, "?- X is (-3) ^ 3.",
+ [["X = -27"]]);
+ assert_prolog_success!(&mut wam, "?- X is (-3) ^ 0.",
+ [["X = 1"]]);
+ assert_prolog_success!(&mut wam, "?- X is (-3) ^ -0.",
+ [["X = 1"]]);
+ assert_prolog_success!(&mut wam, "?- X is (-3) ^ 1.",
+ [["X = -3"]]);
+ assert_prolog_success!(&mut wam, "?- X is (-3) ^ -3.",
+ [["X = -1/27"]]);
+ assert_prolog_success!(&mut wam, "?- X is (1 rdiv 27) ^ -3, X ~ 19683.");
+ assert_prolog_success!(&mut wam, "?- X is (-1 rdiv 27) ^ -3, X ~ -19683.");
+
+ assert_prolog_success!(&mut wam, "?- X is 0.0 ^ 0.",
+ [["X = 1"]]);
+ assert_prolog_success!(&mut wam, "?- catch(_ is 0.0 ^ -2342, error(E, _), true).",
+ [["E = evaluation_error(no_roots)"]]);
+ assert_prolog_success!(&mut wam, "?- X is 0.0 ^ 2342.",
+ [["X = 0"]]);
+
+ assert_prolog_success!(&mut wam, "?- catch(_ is (-3) ^ (1 rdiv 2), error(E, _), true).",
+ [["E = evaluation_error(no_roots)"]]);
+ assert_prolog_success!(&mut wam, "?- catch(_ is (-3/2) ^ (1 rdiv 2), error(E, _), true).",
+ [["E = evaluation_error(no_roots)"]]);
+ assert_prolog_success!(&mut wam, "?- catch(_ is (-3 rdiv 2) ^ (1 rdiv 4), error(E, _), true).",
+ [["E = evaluation_error(no_roots)"]]);
+ assert_prolog_success!(&mut wam, "?- catch(_ is (-3 rdiv 2) ^ (-1 rdiv 4), error(E, _), true).",
+ [["E = evaluation_error(no_roots)"]]);
+ assert_prolog_success!(&mut wam, "?- catch(_ is 0 ^ (-5 rdiv 4), error(E, _), true).",
+ [["E = evaluation_error(no_roots)"]]);
+
+ assert_prolog_success!(&mut wam, "?- X is 3 ^ (1 rdiv 3), Y is X ^ 3, Y ~ 3.");
+ assert_prolog_success!(&mut wam, "?- X is (-3) ^ (1 rdiv 3), Y is X ^ 3, Y ~ -3.");
+ assert_prolog_failure!(&mut wam, "?- X is (-5) ^ (1 rdiv 3), Y is X ^ 3, Y ~ -3.");
+ assert_prolog_failure!(&mut wam, "?- X is 5 ^ (1 rdiv 3), Y is X ^ 3, Y ~ 3.");
+ assert_prolog_failure!(&mut wam, "?- X is (1 rdiv 3) ^ 0.5, Y is X ^ 2, X ~ Y.");
+ assert_prolog_success!(&mut wam, "?- X is (1 rdiv 3) ^ 0.5, Y is X ^ 2, 1 rdiv 3 ~ Y.");
+
+ assert_prolog_success!(&mut wam, "?- X is (-5) ^ (-1 rdiv 3), Y is X ^ 3, Y ~ -1 rdiv 5.");
+ assert_prolog_failure!(&mut wam, "?- X is (-5) ^ (-1 rdiv 3), Y is X ^ 3, Y ~ 1 rdiv 5.");
+
+ assert_prolog_success!(&mut wam, "?- X is (0 rdiv 5) ^ 5.",
+ [["X = 0"]]);
+ assert_prolog_success!(&mut wam, "?- X is (-0 rdiv 5) ^ 5.",
+ [["X = 0"]]);
+ assert_prolog_success!(&mut wam, "?- X is (0 rdiv 5) ^ 0.",
+ [["X = 1"]]);
+ assert_prolog_success!(&mut wam, "?- catch(_ is (0 rdiv 0) ^ 5, error(E, _), true).",
+ [["E = evaluation_error(zero_divisor)"]]);
}
#[test]
assert_prolog_failure!(&mut wam, "?- matcher(\"abcdef\", Y).");
submit(&mut wam, "?- set_prolog_flag(double_quotes, chars).");
-
+
assert_prolog_success!(&mut wam, "?- X = \"abc\", X = ['a' | Y], set_prolog_flag(double_quotes, atom).",
[["X = \"abc\"", "Y = \"bc\""]]);
}