add bitwise and modular operations.
This commit is contained in:
@@ -47,7 +47,8 @@ ideally, very fast) [Shen](http://shenlanguage.org) implementation.
|
|||||||
The following predicates are built-in to rusty-wam.
|
The following predicates are built-in to rusty-wam.
|
||||||
|
|
||||||
* Arithmetic support:
|
* 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, (div)/2, (/)/2, (rdiv)/2,
|
||||||
|
(xor)/2, (rem)/2, (mod)/2, (/\)/2, (\/)/2, (>>)/2, (<<)/2.
|
||||||
* atomic/1
|
* atomic/1
|
||||||
* call/N (1 <= N <= 63)
|
* call/N (1 <= N <= 63)
|
||||||
* catch/3
|
* catch/3
|
||||||
|
|||||||
@@ -783,6 +783,13 @@ mod tests {
|
|||||||
true);
|
true);
|
||||||
|
|
||||||
assert_eq!(submit(&mut wam, "?- X is (3 rdiv 4) / 2, Y is 3 rdiv 8, X = Y."), true);
|
assert_eq!(submit(&mut wam, "?- X is (3 rdiv 4) / 2, Y is 3 rdiv 8, X = Y."), true);
|
||||||
|
|
||||||
|
assert_eq!(submit(&mut wam, "?- X is 10 xor -4, X is -10."), true);
|
||||||
|
assert_eq!(submit(&mut wam, "?- X is 4 xor -7, X is -3."), true);
|
||||||
|
assert_eq!(submit(&mut wam, "?- X is 10 xor 5 + 55, X = 70."), true);
|
||||||
|
|
||||||
|
assert_eq!(submit(&mut wam, "?- X is 10 rem -3, X = 1."), true);
|
||||||
|
assert_eq!(submit(&mut wam, "?- X is 10 mod -3, X is -2."), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,9 +101,16 @@ impl<'a> ArithmeticEvaluator<'a> {
|
|||||||
"-" => Ok(ArithmeticInstruction::Sub(a1, a2, t)),
|
"-" => Ok(ArithmeticInstruction::Sub(a1, a2, t)),
|
||||||
"/" => Ok(ArithmeticInstruction::Div(a1, a2, t)),
|
"/" => Ok(ArithmeticInstruction::Div(a1, a2, t)),
|
||||||
"//" => Ok(ArithmeticInstruction::IDiv(a1, a2, t)),
|
"//" => Ok(ArithmeticInstruction::IDiv(a1, a2, t)),
|
||||||
"div" => Ok(ArithmeticInstruction::IDiv(a1, a2, t)),
|
"div" => Ok(ArithmeticInstruction::FIDiv(a1, a2, t)),
|
||||||
"rdiv" => Ok(ArithmeticInstruction::RDiv(a1, a2, t)),
|
"rdiv" => Ok(ArithmeticInstruction::RDiv(a1, a2, t)),
|
||||||
"*" => Ok(ArithmeticInstruction::Mul(a1, a2, t)),
|
"*" => Ok(ArithmeticInstruction::Mul(a1, a2, t)),
|
||||||
|
">>" => Ok(ArithmeticInstruction::Shr(a1, a2, t)),
|
||||||
|
"<<" => Ok(ArithmeticInstruction::Shl(a1, a2, t)),
|
||||||
|
"/\\" => Ok(ArithmeticInstruction::And(a1, a2, t)),
|
||||||
|
"\\/" => Ok(ArithmeticInstruction::Or(a1, a2, t)),
|
||||||
|
"xor" => Ok(ArithmeticInstruction::Xor(a1, a2, t)),
|
||||||
|
"mod" => Ok(ArithmeticInstruction::Mod(a1, a2, t)),
|
||||||
|
"rem" => Ok(ArithmeticInstruction::Rem(a1, a2, t)),
|
||||||
_ => Err(ArithmeticError::InvalidOp)
|
_ => Err(ArithmeticError::InvalidOp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -630,8 +630,16 @@ pub enum ArithmeticInstruction {
|
|||||||
Sub(ArithmeticTerm, ArithmeticTerm, usize),
|
Sub(ArithmeticTerm, ArithmeticTerm, usize),
|
||||||
Mul(ArithmeticTerm, ArithmeticTerm, usize),
|
Mul(ArithmeticTerm, ArithmeticTerm, usize),
|
||||||
IDiv(ArithmeticTerm, ArithmeticTerm, usize),
|
IDiv(ArithmeticTerm, ArithmeticTerm, usize),
|
||||||
|
FIDiv(ArithmeticTerm, ArithmeticTerm, usize),
|
||||||
RDiv(ArithmeticTerm, ArithmeticTerm, usize),
|
RDiv(ArithmeticTerm, ArithmeticTerm, usize),
|
||||||
Div(ArithmeticTerm, ArithmeticTerm, usize),
|
Div(ArithmeticTerm, ArithmeticTerm, usize),
|
||||||
|
Shl(ArithmeticTerm, ArithmeticTerm, usize),
|
||||||
|
Shr(ArithmeticTerm, ArithmeticTerm, usize),
|
||||||
|
Xor(ArithmeticTerm, ArithmeticTerm, usize),
|
||||||
|
And(ArithmeticTerm, ArithmeticTerm, usize),
|
||||||
|
Or(ArithmeticTerm, ArithmeticTerm, usize),
|
||||||
|
Mod(ArithmeticTerm, ArithmeticTerm, usize),
|
||||||
|
Rem(ArithmeticTerm, ArithmeticTerm, usize),
|
||||||
Neg(ArithmeticTerm, usize)
|
Neg(ArithmeticTerm, usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -127,12 +127,19 @@ pub fn build_code_dir() -> (Code, CodeDir, OpDir)
|
|||||||
op_dir.insert((String::from("is"), Fixity::In), (XFX, 700));
|
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, 500));
|
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, 500));
|
||||||
|
op_dir.insert((String::from("xor"), Fixity::In), (YFX, 500));
|
||||||
op_dir.insert((String::from("//"), Fixity::In), (YFX, 400));
|
op_dir.insert((String::from("//"), Fixity::In), (YFX, 400));
|
||||||
op_dir.insert((String::from("/"), Fixity::In), (YFX, 400));
|
op_dir.insert((String::from("/"), Fixity::In), (YFX, 400));
|
||||||
op_dir.insert((String::from("div"), 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::In), (YFX, 400));
|
||||||
op_dir.insert((String::from("-"), Fixity::Pre), (FY, 200));
|
op_dir.insert((String::from("-"), Fixity::Pre), (FY, 200));
|
||||||
op_dir.insert((String::from("rdiv"), Fixity::In), (YFX, 400));
|
op_dir.insert((String::from("rdiv"), Fixity::In), (YFX, 400));
|
||||||
|
op_dir.insert((String::from("<<"), Fixity::In), (YFX, 400));
|
||||||
|
op_dir.insert((String::from(">>"), Fixity::In), (YFX, 400));
|
||||||
|
op_dir.insert((String::from("mod"), Fixity::In), (YFX, 400));
|
||||||
|
op_dir.insert((String::from("rem"), Fixity::In), (YFX, 400));
|
||||||
|
|
||||||
// there are 63 registers in the VM, so call/N is defined for all 0 <= N <= 62
|
// 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)
|
// (an extra register is needed for the predicate name)
|
||||||
|
|||||||
@@ -216,8 +216,24 @@ impl fmt::Display for ArithmeticInstruction {
|
|||||||
write!(f, "div {}, {}, @{}", a1, a2, t),
|
write!(f, "div {}, {}, @{}", a1, a2, t),
|
||||||
&ArithmeticInstruction::IDiv(ref a1, ref a2, ref t) =>
|
&ArithmeticInstruction::IDiv(ref a1, ref a2, ref t) =>
|
||||||
write!(f, "idiv {}, {}, @{}", a1, a2, t),
|
write!(f, "idiv {}, {}, @{}", a1, a2, t),
|
||||||
|
&ArithmeticInstruction::FIDiv(ref a1, ref a2, ref t) =>
|
||||||
|
write!(f, "floored_idiv {}, {}, @{}", a1, a2, t),
|
||||||
&ArithmeticInstruction::RDiv(ref a1, ref a2, ref t) =>
|
&ArithmeticInstruction::RDiv(ref a1, ref a2, ref t) =>
|
||||||
write!(f, "rdiv {}, {}, @{}", a1, a2, t),
|
write!(f, "rdiv {}, {}, @{}", a1, a2, t),
|
||||||
|
&ArithmeticInstruction::Shl(ref a1, ref a2, ref t) =>
|
||||||
|
write!(f, "shl {}, {}, @{}", a1, a2, t),
|
||||||
|
&ArithmeticInstruction::Shr(ref a1, ref a2, ref t) =>
|
||||||
|
write!(f, "shr {}, {}, @{}", a1, a2, t),
|
||||||
|
&ArithmeticInstruction::Xor(ref a1, ref a2, ref t) =>
|
||||||
|
write!(f, "xor {}, {}, @{}", a1, a2, t),
|
||||||
|
&ArithmeticInstruction::And(ref a1, ref a2, ref t) =>
|
||||||
|
write!(f, "and {}, {}, @{}", a1, a2, t),
|
||||||
|
&ArithmeticInstruction::Or(ref a1, ref a2, ref t) =>
|
||||||
|
write!(f, "or {}, {}, @{}", a1, a2, t),
|
||||||
|
&ArithmeticInstruction::Mod(ref a1, ref a2, ref t) =>
|
||||||
|
write!(f, "mod {}, {}, @{}", a1, a2, t),
|
||||||
|
&ArithmeticInstruction::Rem(ref a1, ref a2, ref t) =>
|
||||||
|
write!(f, "rem {}, {}, @{}", a1, a2, t),
|
||||||
&ArithmeticInstruction::Neg(ref a, ref t) =>
|
&ArithmeticInstruction::Neg(ref a, ref t) =>
|
||||||
write!(f, "neg {}, @{}", a, t)
|
write!(f, "neg {}, @{}", a, t)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ use prolog::codegen::*;
|
|||||||
use prolog::copier::*;
|
use prolog::copier::*;
|
||||||
use prolog::heapview::*;
|
use prolog::heapview::*;
|
||||||
use prolog::and_stack::*;
|
use prolog::and_stack::*;
|
||||||
use prolog::num::Zero;
|
use prolog::num::{Integer, ToPrimitive, Zero};
|
||||||
use prolog::num::bigint::BigInt;
|
use prolog::num::bigint::{BigInt, BigUint};
|
||||||
use prolog::num::rational::Ratio;
|
use prolog::num::rational::Ratio;
|
||||||
use prolog::or_stack::*;
|
use prolog::or_stack::*;
|
||||||
use prolog::ordered_float::OrderedFloat;
|
use prolog::ordered_float::OrderedFloat;
|
||||||
@@ -881,6 +881,20 @@ impl MachineState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn signed_bitwise_op<Op>(&mut self, n1: BigInt, n2: BigInt, t: usize, f: Op)
|
||||||
|
where Op: FnOnce(BigUint, BigUint) -> BigUint
|
||||||
|
{
|
||||||
|
let n1_b = n1.to_signed_bytes_le();
|
||||||
|
let n2_b = n2.to_signed_bytes_le();
|
||||||
|
|
||||||
|
let u_n1 = BigUint::from_bytes_le(&n1_b);
|
||||||
|
let u_n2 = BigUint::from_bytes_le(&n2_b);
|
||||||
|
|
||||||
|
let result = BigInt::from_signed_bytes_le(&f(u_n1, u_n2).to_bytes_le());
|
||||||
|
|
||||||
|
self.interms[t - 1] = Number::Integer(result);
|
||||||
|
}
|
||||||
|
|
||||||
fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) {
|
fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) {
|
||||||
match instr {
|
match instr {
|
||||||
&ArithmeticInstruction::Add(ref a1, ref a2, t) => {
|
&ArithmeticInstruction::Add(ref a1, ref a2, t) => {
|
||||||
@@ -918,6 +932,30 @@ impl MachineState {
|
|||||||
self.interms[t - 1] = Number::Rational(r1 / r2);
|
self.interms[t - 1] = Number::Rational(r1 / r2);
|
||||||
self.p += 1;
|
self.p += 1;
|
||||||
},
|
},
|
||||||
|
&ArithmeticInstruction::FIDiv(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.interms[t - 1] = Number::Integer(n1.div_floor(&n2));
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
self.throw_exception(functor!("evaluation_error",
|
||||||
|
1,
|
||||||
|
[atom!("expected_integer_args")]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
&ArithmeticInstruction::IDiv(ref a1, ref a2, t) => {
|
&ArithmeticInstruction::IDiv(ref a1, ref a2, t) => {
|
||||||
let n1 = try_or_fail!(self, self.get_number(a1));
|
let n1 = try_or_fail!(self, self.get_number(a1));
|
||||||
let n2 = try_or_fail!(self, self.get_number(a2));
|
let n2 = try_or_fail!(self, self.get_number(a2));
|
||||||
@@ -960,6 +998,147 @@ impl MachineState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.interms[t - 1] = n1 / n2;
|
self.interms[t - 1] = n1 / n2;
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
&ArithmeticInstruction::Shr(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)) =>
|
||||||
|
match n2.to_usize() {
|
||||||
|
Some(n2) => self.interms[t - 1] = Number::Integer(n1 >> n2),
|
||||||
|
_ => self.interms[t - 1] = Number::Integer(n1 >> usize::max_value())
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
self.throw_exception(functor!("evaluation_error",
|
||||||
|
1,
|
||||||
|
[atom!("expected_integer_args")]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
&ArithmeticInstruction::Shl(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)) =>
|
||||||
|
match n2.to_usize() {
|
||||||
|
Some(n2) => self.interms[t - 1] = Number::Integer(n1 << n2),
|
||||||
|
_ => self.interms[t - 1] = Number::Integer(n1 << usize::max_value())
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
self.throw_exception(functor!("evaluation_error",
|
||||||
|
1,
|
||||||
|
[atom!("expected_integer_args")]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
&ArithmeticInstruction::Xor(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)) =>
|
||||||
|
self.signed_bitwise_op(n1, n2, t, |u_n1, u_n2| u_n1 ^ u_n2),
|
||||||
|
_ => {
|
||||||
|
self.throw_exception(functor!("evaluation_error",
|
||||||
|
1,
|
||||||
|
[atom!("expected_integer_args")]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
&ArithmeticInstruction::And(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)) =>
|
||||||
|
self.signed_bitwise_op(n1, n2, t, |u_n1, u_n2| u_n1 & u_n2),
|
||||||
|
_ => {
|
||||||
|
self.throw_exception(functor!("evaluation_error",
|
||||||
|
1,
|
||||||
|
[atom!("expected_integer_args")]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
&ArithmeticInstruction::Or(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)) =>
|
||||||
|
self.signed_bitwise_op(n1, n2, t, |u_n1, u_n2| u_n1 | u_n2),
|
||||||
|
_ => {
|
||||||
|
self.throw_exception(functor!("evaluation_error",
|
||||||
|
1,
|
||||||
|
[atom!("expected_integer_args")]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
&ArithmeticInstruction::Mod(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.interms[t - 1] = Number::Integer(n1.mod_floor(&n2));
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
self.throw_exception(functor!("evaluation_error",
|
||||||
|
1,
|
||||||
|
[atom!("expected_integer_args")]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.p += 1;
|
||||||
|
},
|
||||||
|
&ArithmeticInstruction::Rem(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.interms[t - 1] = Number::Integer(n1 % n2);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
self.throw_exception(functor!("evaluation_error",
|
||||||
|
1,
|
||||||
|
[atom!("expected_integer_args")]));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.p += 1;
|
self.p += 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user