Merge branch 'master' into library-use-case
# Conflicts: # Cargo.lock # Cargo.toml # src/bin/scryer-prolog.rs # src/loader.pl # src/machine/mock_wam.rs # src/machine/mod.rs # src/machine/system_calls.rs
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
use dashu::base::Abs;
|
||||
use dashu::base::Gcd;
|
||||
use dashu::integer::IBig;
|
||||
use divrem::*;
|
||||
|
||||
use crate::arena::*;
|
||||
@@ -8,7 +11,7 @@ use crate::heap_iter::*;
|
||||
use crate::machine::machine_errors::*;
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::rug::{Integer, Rational};
|
||||
use crate::parser::dashu::{Integer, Rational};
|
||||
use crate::types::*;
|
||||
|
||||
use crate::fixnum;
|
||||
@@ -159,7 +162,7 @@ pub(crate) fn add(lhs: Number, rhs: Number, arena: &mut Arena) -> Result<Number,
|
||||
Ok(Number::Float(add_f(float_fn_to_f(n1.get_num())?, n2)?))
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
Ok(Number::arena_from(Integer::from(&*n1) + &*n2, arena)) // add_i
|
||||
Ok(Number::arena_from(&*n1 + &*n2, arena)) // add_i
|
||||
}
|
||||
(Number::Integer(n1), Number::Float(OrderedFloat(n2)))
|
||||
| (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
|
||||
@@ -167,7 +170,7 @@ pub(crate) fn add(lhs: Number, rhs: Number, arena: &mut Arena) -> Result<Number,
|
||||
}
|
||||
(Number::Integer(n1), Number::Rational(n2))
|
||||
| (Number::Rational(n2), Number::Integer(n1)) => {
|
||||
Ok(Number::arena_from(Rational::from(&*n1) + &*n2, arena))
|
||||
Ok(Number::arena_from(&*n1 + &*n2, arena))
|
||||
}
|
||||
(Number::Rational(n1), Number::Float(OrderedFloat(n2)))
|
||||
| (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
|
||||
@@ -177,7 +180,7 @@ pub(crate) fn add(lhs: Number, rhs: Number, arena: &mut Arena) -> Result<Number,
|
||||
Ok(Number::Float(add_f(f1, f2)?))
|
||||
}
|
||||
(Number::Rational(r1), Number::Rational(r2)) => {
|
||||
Ok(Number::arena_from(Rational::from(&*r1) + &*r2, arena))
|
||||
Ok(Number::arena_from(&*r1 + &*r2, arena))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,9 +194,15 @@ pub(crate) fn neg(n: Number, arena: &mut Arena) -> Number {
|
||||
Number::arena_from(-Integer::from(n.get_num()), arena)
|
||||
}
|
||||
}
|
||||
Number::Integer(n) => Number::arena_from(-Integer::from(&*n), arena),
|
||||
Number::Integer(n) => {
|
||||
let n_clone: Integer = (*n).clone();
|
||||
Number::arena_from(-Integer::from(n_clone), arena)
|
||||
},
|
||||
Number::Float(OrderedFloat(f)) => Number::Float(OrderedFloat(-f)),
|
||||
Number::Rational(r) => Number::arena_from(-Rational::from(&*r), arena),
|
||||
Number::Rational(r) => {
|
||||
let r_clone: Rational = (*r).clone();
|
||||
Number::arena_from(-Rational::from(r_clone), arena)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,12 +212,19 @@ pub(crate) fn abs(n: Number, arena: &mut Arena) -> Number {
|
||||
if let Some(n) = n.get_num().checked_abs() {
|
||||
fixnum!(Number, n, arena)
|
||||
} else {
|
||||
Number::arena_from(Integer::from(n.get_num()).abs(), arena)
|
||||
let arena_int = Integer::from(n.get_num());
|
||||
Number::arena_from(arena_int.abs(), arena)
|
||||
}
|
||||
}
|
||||
Number::Integer(n) => Number::arena_from(Integer::from(n.abs_ref()), arena),
|
||||
Number::Integer(n) => {
|
||||
let n_clone: Integer = (*n).clone();
|
||||
Number::arena_from(Integer::from(n_clone.abs()), arena)
|
||||
},
|
||||
Number::Float(f) => Number::Float(f.abs()),
|
||||
Number::Rational(r) => Number::arena_from(Rational::from(r.abs_ref()), arena),
|
||||
Number::Rational(r) => {
|
||||
let r_clone: Rational = (*r).clone();
|
||||
Number::arena_from(Rational::from(r_clone.abs()), arena)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +263,8 @@ pub(crate) fn mul(lhs: Number, rhs: Number, arena: &mut Arena) -> Result<Number,
|
||||
Ok(Number::Float(mul_f(float_fn_to_f(n1.get_num())?, n2)?))
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
Ok(Number::arena_from(Integer::from(&*n1) * &*n2, arena)) // mul_i
|
||||
let n1_clone: Integer = (*n1).clone();
|
||||
Ok(Number::arena_from(Integer::from(n1_clone) * &*n2, arena)) // mul_i
|
||||
}
|
||||
(Number::Integer(n1), Number::Float(OrderedFloat(n2)))
|
||||
| (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
|
||||
@@ -255,7 +272,8 @@ pub(crate) fn mul(lhs: Number, rhs: Number, arena: &mut Arena) -> Result<Number,
|
||||
}
|
||||
(Number::Integer(n1), Number::Rational(n2))
|
||||
| (Number::Rational(n2), Number::Integer(n1)) => {
|
||||
Ok(Number::arena_from(Rational::from(&*n1) * &*n2, arena))
|
||||
let n1_clone: Integer = (*n1).clone();
|
||||
Ok(Number::arena_from(Rational::from(n1_clone) * &*n2, arena))
|
||||
}
|
||||
(Number::Rational(n1), Number::Float(OrderedFloat(n2)))
|
||||
| (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
|
||||
@@ -265,7 +283,8 @@ pub(crate) fn mul(lhs: Number, rhs: Number, arena: &mut Arena) -> Result<Number,
|
||||
Ok(Number::Float(mul_f(f1, f2)?))
|
||||
}
|
||||
(Number::Rational(r1), Number::Rational(r2)) => {
|
||||
Ok(Number::arena_from(Rational::from(&*r1) * &*r2, arena))
|
||||
let r1_clone: Rational = (*r1).clone();
|
||||
Ok(Number::arena_from(Rational::from(r1_clone) * &*r2, arena))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,7 +357,7 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result<Numbe
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
let n1_i = n1.get_num();
|
||||
|
||||
if !(n1_i == 1 || n1_i == 0 || n1_i == -1) && &*n2 < &0 {
|
||||
if !(n1_i == 1 || n1_i == 0 || n1_i == -1) && &*n2 < &Integer::from(0) {
|
||||
let n = Number::Fixnum(n1);
|
||||
Err(numerical_type_error(ValidType::Float, n, stub_gen))
|
||||
} else {
|
||||
@@ -349,7 +368,7 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result<Numbe
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => {
|
||||
let n2_i = n2.get_num();
|
||||
|
||||
if !(&*n1 == &1 || &*n1 == &0 || &*n1 == &-1) && n2_i < 0 {
|
||||
if !(&*n1 == &Integer::from(1) || &*n1 == &Integer::from(0) || &*n1 == &Integer::from(-1)) && n2_i < 0 {
|
||||
let n = Number::Integer(n1);
|
||||
Err(numerical_type_error(ValidType::Float, n, stub_gen))
|
||||
} else {
|
||||
@@ -358,7 +377,7 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result<Numbe
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
if !(&*n1 == &1 || &*n1 == &0 || &*n1 == &-1) && &*n2 < &0 {
|
||||
if !(&*n1 == &Integer::from(1) || &*n1 == &Integer::from(0) || &*n1 == &Integer::from(-1)) && &*n2 < &Integer::from(0) {
|
||||
let n = Number::Integer(n1);
|
||||
Err(numerical_type_error(ValidType::Float, n, stub_gen))
|
||||
} else {
|
||||
@@ -521,7 +540,7 @@ pub fn rational_from_number(
|
||||
match n {
|
||||
Number::Fixnum(n) => Ok(arena_alloc!(Rational::from(n.get_num()), arena)),
|
||||
Number::Rational(r) => Ok(r),
|
||||
Number::Float(OrderedFloat(f)) => match Rational::from_f64(f) {
|
||||
Number::Float(OrderedFloat(f)) => match Rational::simplest_from_f64(f) {
|
||||
Some(r) => Ok(arena_alloc!(r, arena)),
|
||||
None => Err(Box::new(move |machine_st| {
|
||||
let instantiation_error = machine_st.instantiation_error();
|
||||
@@ -530,7 +549,10 @@ pub fn rational_from_number(
|
||||
machine_st.error_form(instantiation_error, stub)
|
||||
})),
|
||||
},
|
||||
Number::Integer(n) => Ok(arena_alloc!(Rational::from(&*n), arena)),
|
||||
Number::Integer(n) => {
|
||||
let n_clone: Integer = (*n).clone();
|
||||
Ok(arena_alloc!(Rational::from(n_clone), arena))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,7 +612,7 @@ pub(crate) fn idiv(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number,
|
||||
Err(zero_divisor_eval_error(stub_gen))
|
||||
} else {
|
||||
Ok(Number::arena_from(
|
||||
<(Integer, Integer)>::from(n1.div_rem_ref(&*n2)).0,
|
||||
<(Integer, Integer)>::from(n1.div_rem_floor_ref(&*n2)).0,
|
||||
arena,
|
||||
))
|
||||
}
|
||||
@@ -624,6 +646,10 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
||||
functor_stub(shr_atom, 2)
|
||||
};
|
||||
|
||||
if n2.is_integer() && n2.is_negative() {
|
||||
return shl(n1, neg(n2, arena), arena);
|
||||
}
|
||||
|
||||
match (n1, n2) {
|
||||
(Number::Fixnum(n1), Number::Fixnum(n2)) => {
|
||||
let n1_i = n1.get_num();
|
||||
@@ -631,33 +657,33 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
||||
|
||||
let n1 = Integer::from(n1_i);
|
||||
|
||||
if let Ok(n2) = u32::try_from(n2_i) {
|
||||
if let Ok(n2) = usize::try_from(n2_i) {
|
||||
return Ok(Number::arena_from(n1 >> n2, arena));
|
||||
} else {
|
||||
return Ok(Number::arena_from(n1 >> u32::max_value(), arena));
|
||||
} else {
|
||||
return Ok(Number::arena_from(n1 >> usize::max_value(), arena));
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
let n1 = Integer::from(n1.get_num());
|
||||
|
||||
match n2.to_u32() {
|
||||
match n2.to_usize() {
|
||||
Some(n2) => Ok(Number::arena_from(n1 >> n2, arena)),
|
||||
_ => Ok(Number::arena_from(n1 >> u32::max_value(), arena)),
|
||||
_ => {
|
||||
Ok(Number::arena_from(n1 >> usize::max_value(), arena))
|
||||
},
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => match u32::try_from(n2.get_num()) {
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) {
|
||||
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
|
||||
_ => Ok(Number::arena_from(
|
||||
Integer::from(&*n1 >> u32::max_value()),
|
||||
arena,
|
||||
)),
|
||||
_ => {
|
||||
Ok(Number::arena_from(Integer::from(&*n1 >> usize::max_value()),arena))
|
||||
},
|
||||
},
|
||||
(Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() {
|
||||
(Number::Integer(n1), Number::Integer(n2)) => match n2.to_usize() {
|
||||
Some(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
|
||||
_ => Ok(Number::arena_from(
|
||||
Integer::from(&*n1 >> u32::max_value()),
|
||||
arena,
|
||||
)),
|
||||
_ => {
|
||||
Ok(Number::arena_from(Integer::from(&*n1 >> usize::max_value()), arena))
|
||||
},
|
||||
},
|
||||
(Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||
(Number::Fixnum(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||
@@ -667,10 +693,14 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
||||
|
||||
pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
|
||||
let stub_gen = || {
|
||||
let shl_atom = atom!(">>");
|
||||
let shl_atom = atom!("<<");
|
||||
functor_stub(shl_atom, 2)
|
||||
};
|
||||
|
||||
if n2.is_integer() && n2.is_negative() {
|
||||
return shr(n1, neg(n2, arena), arena);
|
||||
}
|
||||
|
||||
match (n1, n2) {
|
||||
(Number::Fixnum(n1), Number::Fixnum(n2)) => {
|
||||
let n1_i = n1.get_num();
|
||||
@@ -678,33 +708,33 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
||||
|
||||
let n1 = Integer::from(n1_i);
|
||||
|
||||
if let Ok(n2) = u32::try_from(n2_i) {
|
||||
if let Ok(n2) = usize::try_from(n2_i) {
|
||||
return Ok(Number::arena_from(n1 << n2, arena));
|
||||
} else {
|
||||
return Ok(Number::arena_from(n1 << u32::max_value(), arena));
|
||||
} else {
|
||||
return Ok(Number::arena_from(n1 << usize::max_value(), arena));
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
let n1 = Integer::from(n1.get_num());
|
||||
|
||||
match n2.to_u32() {
|
||||
Some(n2) => Ok(Number::arena_from(n1 << n2, arena)),
|
||||
_ => Ok(Number::arena_from(n1 << u32::max_value(), arena)),
|
||||
Some(n2) => Ok(Number::arena_from(n1.to_u64().unwrap() << n2, arena)),
|
||||
_ => {
|
||||
Ok(Number::arena_from(n1 << usize::max_value(), arena))
|
||||
}
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => match u32::try_from(n2.get_num()) {
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) {
|
||||
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
|
||||
_ => Ok(Number::arena_from(
|
||||
Integer::from(&*n1 << u32::max_value()),
|
||||
arena,
|
||||
)),
|
||||
_ => {
|
||||
Ok(Number::arena_from(Integer::from(&*n1 << usize::max_value()),arena))
|
||||
}
|
||||
},
|
||||
(Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() {
|
||||
Some(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
|
||||
_ => Ok(Number::arena_from(
|
||||
Integer::from(&*n1 << u32::max_value()),
|
||||
arena,
|
||||
)),
|
||||
Some(n2) => Ok(Number::arena_from(Integer::from(n1.to_u64().unwrap() << n2), arena)),
|
||||
_ => {
|
||||
Ok(Number::arena_from(Integer::from(&*n1 << usize::max_value()),arena))
|
||||
}
|
||||
},
|
||||
(Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||
(Number::Fixnum(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||
@@ -918,18 +948,21 @@ pub(crate) fn gcd(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
||||
if let Some(result) = isize_gcd(n1_i, n2_i) {
|
||||
Ok(Number::arena_from(result, arena))
|
||||
} else {
|
||||
let value: IBig = Integer::from(n1_i).gcd(&Integer::from(n2_i)).into();
|
||||
Ok(Number::arena_from(
|
||||
Integer::from(n1_i).gcd(&Integer::from(n2_i)),
|
||||
value,
|
||||
arena,
|
||||
))
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) | (Number::Integer(n2), Number::Fixnum(n1)) => {
|
||||
let n1 = Integer::from(n1.get_num());
|
||||
Ok(Number::arena_from(Integer::from(n2.gcd_ref(&n1)), arena))
|
||||
let n2_clone: Integer = (*n2).clone();
|
||||
Ok(Number::arena_from(Integer::from(n2_clone.gcd(&n1)), arena))
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
Ok(Number::arena_from(Integer::from(n1.gcd_ref(&n2)), arena))
|
||||
let n1_clone: Integer = (*n1).clone();
|
||||
Ok(Number::arena_from(Integer::from(n1_clone.gcd(&Integer::from(n2.to_isize().unwrap()))) as IBig, arena))
|
||||
}
|
||||
(Number::Float(f), _) | (_, Number::Float(f)) => {
|
||||
let n = Number::Float(f);
|
||||
@@ -998,6 +1031,63 @@ pub(crate) fn atan(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.atan())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn asinh(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.asinh())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn acosh(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.acosh())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn atanh(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
let stub_gen = || {
|
||||
let is_atom = atom!("is");
|
||||
functor_stub(is_atom, 2)
|
||||
};
|
||||
|
||||
let f1 = try_numeric_result!(result_f(&n1), stub_gen)?;
|
||||
|
||||
try_numeric_result!(if f1 == 1.0 || f1 == -1.0 {
|
||||
Err(EvalError::Undefined)
|
||||
} else {
|
||||
result_f(&Number::Float(OrderedFloat(f1.atanh())))
|
||||
},
|
||||
stub_gen)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn sinh(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.sinh())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn cosh(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.cosh())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn tanh(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.tanh())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn log10(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.log(10f64))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn float_fractional_part(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.fract())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn float_integer_part(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.trunc())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn sqrt(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
if n1.is_negative() {
|
||||
@@ -1017,6 +1107,7 @@ pub(crate) fn floor(n1: Number, arena: &mut Arena) -> Number {
|
||||
rnd_i(&n1, arena)
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn ceiling(n1: Number, arena: &mut Arena) -> Number {
|
||||
let n1 = neg(n1, arena);
|
||||
@@ -1098,7 +1189,7 @@ impl MachineState {
|
||||
|
||||
pub(crate) fn arith_eval_by_metacall(&mut self, value: HeapCellValue) -> Result<Number, MachineStub> {
|
||||
let stub_gen = || functor_stub(atom!("is"), 2);
|
||||
let mut iter = stackful_post_order_iter(&mut self.heap, value);
|
||||
let mut iter = stackful_post_order_iter(&mut self.heap, &mut self.stack, value);
|
||||
|
||||
while let Some(value) = iter.next() {
|
||||
if value.get_forwarding_bit() {
|
||||
@@ -1115,7 +1206,7 @@ impl MachineState {
|
||||
HeapCellValueTag::PStrLoc) => {
|
||||
(atom!("."), 2)
|
||||
}
|
||||
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var) => {
|
||||
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
|
||||
let err = self.instantiation_error();
|
||||
return Err(self.error_form(err, stub_gen()));
|
||||
}
|
||||
@@ -1247,6 +1338,33 @@ impl MachineState {
|
||||
atom!("tan") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, tan(a1))
|
||||
))),
|
||||
atom!("cosh") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, cosh(a1))
|
||||
))),
|
||||
atom!("sinh") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, sinh(a1))
|
||||
))),
|
||||
atom!("tanh") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, tanh(a1))
|
||||
))),
|
||||
atom!("acosh") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, acosh(a1))
|
||||
))),
|
||||
atom!("asinh") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, asinh(a1))
|
||||
))),
|
||||
atom!("atanh") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, atanh(a1))
|
||||
))),
|
||||
atom!("log10") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, log10(a1))
|
||||
))),
|
||||
atom!("float_fractional_part") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, float_fractional_part(a1))
|
||||
))),
|
||||
atom!("float_integer_part") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, float_integer_part(a1))
|
||||
))),
|
||||
atom!("sqrt") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, sqrt(a1))
|
||||
))),
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
:- module('$atts', []).
|
||||
|
||||
|
||||
driver(Vars, Values) :-
|
||||
iterate(Vars, Values, ListOfListsOfGoalLists),
|
||||
!,
|
||||
call_goals(ListOfListsOfGoalLists),
|
||||
'$reset_attr_var_state',
|
||||
'$return_from_verify_attr'.
|
||||
|
||||
iterate([Var|VarBindings], [Value|ValueBindings], [ListOfGoalLists | ListsCubed]) :-
|
||||
|
||||
@@ -33,8 +33,8 @@ impl AttrVarInitializer {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn reset(&mut self) {
|
||||
self.attr_var_queue.clear();
|
||||
pub(super) fn reset(&mut self, len: usize) {
|
||||
self.attr_var_queue.truncate(len);
|
||||
self.bindings.clear();
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,7 @@ impl MachineState {
|
||||
self.cp = INSTALL_VERIFY_ATTR_INTERRUPT;
|
||||
}
|
||||
|
||||
debug_assert_eq!(self.heap[h].get_tag(), HeapCellValueTag::AttrVar);
|
||||
self.attr_var_init.bindings.push((h, addr));
|
||||
}
|
||||
|
||||
@@ -63,10 +64,9 @@ impl MachineState {
|
||||
.map(|(ref h, _)| attr_var_as_cell!(*h));
|
||||
|
||||
let var_list_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, iter));
|
||||
|
||||
let iter = self.attr_var_init.bindings.drain(0..).map(|(_, ref v)| *v);
|
||||
|
||||
let value_list_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, iter));
|
||||
|
||||
(var_list_addr, value_list_addr)
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ impl MachineState {
|
||||
let mut seen_set = IndexSet::new();
|
||||
let mut seen_vars = vec![];
|
||||
|
||||
let mut iter = stackful_preorder_iter(&mut self.heap, cell);
|
||||
let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, cell);
|
||||
|
||||
while let Some(value) = iter.next() {
|
||||
read_heap_cell!(value,
|
||||
@@ -147,6 +147,16 @@ impl MachineState {
|
||||
|
||||
let value = unmark_cell_bits!(value);
|
||||
|
||||
if h != iter.focus().value() as usize {
|
||||
let deref_value = heap_bound_store(iter.heap, heap_bound_deref(iter.heap, value));
|
||||
|
||||
if deref_value.is_compound(iter.heap) {
|
||||
// a cyclic structure is bound to the attributed variable at h.
|
||||
// it mustn't be included in seen_vars.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
seen_vars.push(value);
|
||||
seen_set.insert(h);
|
||||
|
||||
@@ -157,7 +167,7 @@ impl MachineState {
|
||||
loop {
|
||||
read_heap_cell!(iter.heap[l],
|
||||
(HeapCellValueTag::Lis) => {
|
||||
iter.push_stack(l);
|
||||
iter.push_stack(IterStackLoc::iterable_loc(l, HeapOrStackTag::Heap));
|
||||
// l = elem + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::instructions::*;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexSet;
|
||||
|
||||
fn capture_offset(line: &Instruction, index: usize, stack: &mut Vec<usize>) -> bool {
|
||||
@@ -7,38 +8,24 @@ fn capture_offset(line: &Instruction, index: usize, stack: &mut Vec<usize>) -> b
|
||||
&Instruction::TryMeElse(offset) if offset > 0 => {
|
||||
stack.push(index + offset);
|
||||
}
|
||||
&Instruction::DefaultRetryMeElse(offset) |
|
||||
&Instruction::RetryMeElse(offset)
|
||||
if offset > 0 =>
|
||||
{
|
||||
&Instruction::DefaultRetryMeElse(offset) | &Instruction::RetryMeElse(offset) if offset > 0 => {
|
||||
stack.push(index + offset);
|
||||
}
|
||||
&Instruction::DynamicElse(_, _, NextOrFail::Next(offset))
|
||||
if offset > 0 =>
|
||||
{
|
||||
&Instruction::DynamicElse(_, _, NextOrFail::Next(offset)) if offset > 0 => {
|
||||
stack.push(index + offset);
|
||||
}
|
||||
&Instruction::DynamicInternalElse(_, _, NextOrFail::Next(offset))
|
||||
if offset > 0 =>
|
||||
{
|
||||
&Instruction::DynamicInternalElse(_, _, NextOrFail::Next(offset)) if offset > 0 => {
|
||||
stack.push(index + offset);
|
||||
}
|
||||
&Instruction::JmpByCall(_, offset, _) => {
|
||||
stack.push(index + offset);
|
||||
}
|
||||
&Instruction::JmpByExecute(_, offset, _) => {
|
||||
stack.push(index + offset);
|
||||
return true;
|
||||
}
|
||||
&Instruction::Proceed => {
|
||||
&Instruction::Proceed | &Instruction::JmpByCall(_) => {
|
||||
return true;
|
||||
}
|
||||
&Instruction::RevJmpBy(offset) => {
|
||||
if offset > 0 {
|
||||
stack.push(index - offset);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
instr if instr.is_execute() => {
|
||||
return true;
|
||||
@@ -55,7 +42,7 @@ fn capture_offset(line: &Instruction, index: usize, stack: &mut Vec<usize>) -> b
|
||||
*/
|
||||
pub(crate) fn walk_code(code: &Code, p: usize, mut walker: impl FnMut(&Instruction)) {
|
||||
let mut stack = vec![p];
|
||||
let mut visited_indices = IndexSet::new();
|
||||
let mut visited_indices = IndexSet::with_hasher(FxBuildHasher::default());
|
||||
|
||||
while let Some(first_index) = stack.pop() {
|
||||
if visited_indices.contains(&first_index) {
|
||||
@@ -73,23 +60,3 @@ pub(crate) fn walk_code(code: &Code, p: usize, mut walker: impl FnMut(&Instructi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* A function for code walking that might result in modification to
|
||||
* the code. Otherwise identical to walk_code.
|
||||
*/
|
||||
/*
|
||||
pub(crate) fn walk_code_mut(code: &mut Code, p: usize, mut walker: impl FnMut(&mut Line))
|
||||
{
|
||||
let mut queue = VecDeque::from(vec![p]);
|
||||
|
||||
while let Some(first_idx) = queue.pop_front() {
|
||||
let mut last_idx = first_idx;
|
||||
|
||||
capture_next_range(code, &mut queue, &mut last_idx);
|
||||
|
||||
for instr in &mut code[first_idx .. last_idx + 1] {
|
||||
walker(instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -44,60 +44,6 @@ pub(super) fn bootstrapping_compile(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// throw errors if declaration or query found.
|
||||
pub(super) fn compile_relation(
|
||||
cg: &mut CodeGenerator,
|
||||
tl: &TopLevel,
|
||||
) -> Result<Code, CompilationError> {
|
||||
match tl {
|
||||
&TopLevel::Query(_) => Err(CompilationError::ExpectedRel),
|
||||
&TopLevel::Predicate(ref clauses) => cg.compile_predicate(&clauses),
|
||||
&TopLevel::Fact(ref fact, ..) => cg.compile_fact(fact),
|
||||
&TopLevel::Rule(ref rule, ..) => cg.compile_rule(rule),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn compile_appendix(
|
||||
code: &mut Code,
|
||||
mut queue: VecDeque<TopLevel>,
|
||||
jmp_by_locs: Vec<usize>,
|
||||
non_counted_bt: bool,
|
||||
atom_tbl: &mut AtomTable,
|
||||
) -> Result<(), CompilationError> {
|
||||
let mut jmp_by_locs = VecDeque::from(jmp_by_locs);
|
||||
|
||||
while let Some(jmp_by_offset) = jmp_by_locs.pop_front() {
|
||||
let code_len = code.len();
|
||||
|
||||
match &mut code[jmp_by_offset] {
|
||||
&mut Instruction::JmpByCall(_, ref mut offset, ..) |
|
||||
&mut Instruction::JmpByExecute(_, ref mut offset, ..) => {
|
||||
*offset = code_len - jmp_by_offset;
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
// false because the inner predicate is a one-off, hence not extensible.
|
||||
let settings = CodeGenSettings {
|
||||
global_clock_tick: None,
|
||||
is_extensible: false,
|
||||
non_counted_bt,
|
||||
};
|
||||
|
||||
let mut cg = CodeGenerator::new(atom_tbl, settings);
|
||||
|
||||
let tl = queue.pop_front().unwrap();
|
||||
let decl_code = compile_relation(&mut cg, &tl)?;
|
||||
|
||||
jmp_by_locs.extend(cg.jmp_by_locs.into_iter().map(|offset| offset + code.len()));
|
||||
code.extend(decl_code.into_iter());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lower_bound_of_target_clause(skeleton: &PredicateSkeleton, target_pos: usize) -> usize {
|
||||
if target_pos == 0 {
|
||||
return 0;
|
||||
@@ -1342,22 +1288,14 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
let mut preprocessor = Preprocessor::new(settings);
|
||||
|
||||
let clause = self.try_term_to_tl(term, &mut preprocessor)?;
|
||||
let queue = preprocessor.parse_queue(self)?;
|
||||
// let queue = preprocessor.parse_queue(self)?;
|
||||
|
||||
let mut cg = CodeGenerator::new(
|
||||
&mut LS::machine_st(&mut self.payload).atom_tbl,
|
||||
settings,
|
||||
);
|
||||
|
||||
let mut clause_code = cg.compile_predicate(&vec![clause])?;
|
||||
|
||||
compile_appendix(
|
||||
&mut clause_code,
|
||||
queue,
|
||||
cg.jmp_by_locs,
|
||||
settings.non_counted_bt,
|
||||
cg.atom_tbl,
|
||||
)?;
|
||||
let clause_code = cg.compile_predicate(vec![clause])?;
|
||||
|
||||
Ok(StandaloneCompileResult {
|
||||
clause_code,
|
||||
@@ -1385,22 +1323,12 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
clauses.push(self.try_term_to_tl(term, &mut preprocessor)?);
|
||||
}
|
||||
|
||||
let queue = preprocessor.parse_queue(self)?;
|
||||
|
||||
let mut cg = CodeGenerator::new(
|
||||
&mut LS::machine_st(&mut self.payload).atom_tbl,
|
||||
settings,
|
||||
);
|
||||
|
||||
let mut code = cg.compile_predicate(&clauses)?;
|
||||
|
||||
compile_appendix(
|
||||
&mut code,
|
||||
queue,
|
||||
cg.jmp_by_locs,
|
||||
settings.non_counted_bt,
|
||||
cg.atom_tbl,
|
||||
)?;
|
||||
let mut code = cg.compile_predicate(clauses)?;
|
||||
|
||||
if settings.is_extensible {
|
||||
let mut clause_clause_locs = VecDeque::new();
|
||||
@@ -1869,7 +1797,13 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
skeleton.clauses[target_pos + 1].clause_start =
|
||||
skeleton.clauses[target_pos].clause_start;
|
||||
|
||||
let index_ptr_opt = if target_pos == 0 {
|
||||
let update_code_index = target_pos == 0 &&
|
||||
skeleton.clauses[target_pos + 1]
|
||||
.opt_arg_index_key
|
||||
.switch_on_term_loc()
|
||||
.is_none();
|
||||
|
||||
let index_ptr_opt = if update_code_index {
|
||||
Some(IndexPtr::index(clause_loc))
|
||||
} else {
|
||||
None
|
||||
@@ -2274,14 +2208,17 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
.ok_or(SessionError::NamelessEntry)?;
|
||||
|
||||
let listing_src_file_name = self.listing_src_file_name();
|
||||
let payload_compilation_target = self.payload.compilation_target;
|
||||
|
||||
let mut predicate_info = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton(&self.payload.predicates.compilation_target, &key)
|
||||
.map(|skeleton| skeleton.predicate_info())
|
||||
.unwrap_or_default();
|
||||
// payload_compilation_target describes the compilation context,
|
||||
// e.g. compiling
|
||||
//
|
||||
// table_wrapper:tabled(get_node(A), b).
|
||||
//
|
||||
// without a module declaration means self.payload.compilation_target
|
||||
// is CompilationTarget::User while self.payload.predicates.compilation_target
|
||||
// is CompilationTarget::Module(atom!("table_wrapper")).
|
||||
|
||||
let payload_compilation_target = self.payload.compilation_target;
|
||||
|
||||
let local_predicate_info = self
|
||||
.wam_prelude
|
||||
@@ -2295,34 +2232,37 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
.map(|skeleton| skeleton.predicate_info())
|
||||
.unwrap_or_default();
|
||||
|
||||
if local_predicate_info.must_retract_local_clauses() {
|
||||
let mut predicate_info = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton(&self.payload.predicates.compilation_target, &key)
|
||||
.map(|skeleton| skeleton.predicate_info())
|
||||
.unwrap_or_default();
|
||||
|
||||
let is_cross_module_clause =
|
||||
payload_compilation_target != self.payload.predicates.compilation_target;
|
||||
|
||||
if local_predicate_info.must_retract_local_clauses(is_cross_module_clause) {
|
||||
self.retract_local_clauses(&key, predicate_info.is_dynamic);
|
||||
}
|
||||
|
||||
let do_incremental_compile =
|
||||
if payload_compilation_target == self.payload.predicates.compilation_target {
|
||||
predicate_info.compile_incrementally()
|
||||
} else {
|
||||
local_predicate_info.is_multifile && predicate_info.compile_incrementally()
|
||||
};
|
||||
|
||||
let predicates_len = self.payload.predicates.len();
|
||||
let non_counted_bt = self.payload.non_counted_bt_preds.contains(&key);
|
||||
|
||||
if do_incremental_compile {
|
||||
if predicate_info.compile_incrementally() {
|
||||
let predicates = self.payload.predicates.take();
|
||||
|
||||
for term in predicates.predicates {
|
||||
self.incremental_compile_clause(
|
||||
key,
|
||||
term,
|
||||
payload_compilation_target,
|
||||
self.payload.predicates.compilation_target,
|
||||
non_counted_bt,
|
||||
AppendOrPrepend::Append,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
if payload_compilation_target != self.payload.predicates.compilation_target {
|
||||
if is_cross_module_clause {
|
||||
if !local_predicate_info.is_extensible {
|
||||
if predicate_info.is_multifile {
|
||||
println!(
|
||||
@@ -2337,9 +2277,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
.indices
|
||||
.remove_predicate_skeleton(&self.payload.predicates.compilation_target, &key)
|
||||
{
|
||||
let compilation_target = self.payload.predicates.compilation_target;
|
||||
|
||||
if predicate_info.is_dynamic {
|
||||
let clause_clause_compilation_target =
|
||||
match self.payload.predicates.compilation_target {
|
||||
match compilation_target {
|
||||
CompilationTarget::User => {
|
||||
CompilationTarget::Module(atom!("builtins"))
|
||||
}
|
||||
@@ -2358,7 +2300,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::RemovedSkeleton(
|
||||
payload_compilation_target,
|
||||
compilation_target,
|
||||
key,
|
||||
skeleton,
|
||||
),
|
||||
@@ -2409,9 +2351,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
.clause_clauses.drain(0..std::cmp::min(predicates_len, clause_clauses_len))
|
||||
.collect();
|
||||
|
||||
let compilation_target = self.payload.predicates.compilation_target;
|
||||
|
||||
self.compile_clause_clauses(
|
||||
key,
|
||||
payload_compilation_target,
|
||||
compilation_target,
|
||||
clauses_vec.into_iter(),
|
||||
AppendOrPrepend::Append,
|
||||
)?;
|
||||
|
||||
@@ -28,7 +28,10 @@ pub(crate) fn copy_term<T: CopierTarget>(
|
||||
attr_var_policy: AttrVarPolicy,
|
||||
) {
|
||||
let mut copy_term_state = CopyTermState::new(target, attr_var_policy);
|
||||
|
||||
copy_term_state.copy_term_impl(addr);
|
||||
copy_term_state.copy_attr_var_lists();
|
||||
copy_term_state.unwind_trail();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -38,6 +41,7 @@ struct CopyTermState<T: CopierTarget> {
|
||||
old_h: usize,
|
||||
target: T,
|
||||
attr_var_policy: AttrVarPolicy,
|
||||
attr_var_list_locs: Vec<(usize, HeapCellValue)>,
|
||||
}
|
||||
|
||||
impl<T: CopierTarget> CopyTermState<T> {
|
||||
@@ -48,6 +52,7 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
old_h: target.threshold(),
|
||||
target,
|
||||
attr_var_policy,
|
||||
attr_var_list_locs: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,16 +91,12 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
self.target.push(hcv);
|
||||
}
|
||||
|
||||
let cdr = self
|
||||
.target
|
||||
.store(self.target.deref(heap_loc_as_cell!(addr + 1)));
|
||||
let cdr = self.target.store(self.target.deref(heap_loc_as_cell!(addr + 1)));
|
||||
|
||||
if !cdr.is_var() {
|
||||
self.trail_list_cell(addr + 1, threshold);
|
||||
} else {
|
||||
let car = self
|
||||
.target
|
||||
.store(self.target.deref(heap_loc_as_cell!(addr)));
|
||||
let car = self.target.store(self.target.deref(heap_loc_as_cell!(addr)));
|
||||
|
||||
if !car.is_var() {
|
||||
self.trail_list_cell(addr, threshold);
|
||||
@@ -167,6 +168,51 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
self.trail.push((Ref::heap_cell(pstr_loc), trail_item));
|
||||
}
|
||||
|
||||
fn copy_attr_var_lists(&mut self) {
|
||||
while !self.attr_var_list_locs.is_empty() {
|
||||
let iter = mem::replace(&mut self.attr_var_list_locs, vec![]);
|
||||
|
||||
for (threshold, list_loc) in iter {
|
||||
self.target[threshold] = list_loc_as_cell!(self.target.threshold());
|
||||
self.copy_attr_var_list(list_loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Attributed variable attribute lists adhere to a particular
|
||||
* structure which is ensured by this function and not at all by
|
||||
* the vanilla copier.
|
||||
*/
|
||||
fn copy_attr_var_list(&mut self, mut list_addr: HeapCellValue) {
|
||||
while let HeapCellValueTag::Lis = list_addr.get_tag() {
|
||||
let threshold = self.target.threshold();
|
||||
let heap_loc = list_addr.get_value();
|
||||
let str_loc = self.target[heap_loc].get_value();
|
||||
|
||||
self.target.push(heap_loc_as_cell!(threshold+2));
|
||||
self.target.push(heap_loc_as_cell!(threshold+1));
|
||||
|
||||
read_heap_cell!(self.target[str_loc],
|
||||
(HeapCellValueTag::Atom) => {
|
||||
self.target.push(self.target[str_loc]);
|
||||
}
|
||||
(HeapCellValueTag::Str) => {
|
||||
self.copy_term_impl(self.target[str_loc]);
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
);
|
||||
|
||||
list_addr = self.target[heap_loc + 1];
|
||||
|
||||
if HeapCellValueTag::Lis == list_addr.get_tag() {
|
||||
self.target[threshold + 1] = list_loc_as_cell!(self.target.threshold());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reinstantiate_var(&mut self, addr: HeapCellValue, frontier: usize) {
|
||||
read_heap_cell!(addr,
|
||||
(HeapCellValueTag::Var, h) => {
|
||||
@@ -195,9 +241,15 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
|
||||
if let AttrVarPolicy::DeepCopy = self.attr_var_policy {
|
||||
self.target.push(attr_var_as_cell!(threshold));
|
||||
self.target.push(heap_loc_as_cell!(threshold + 1));
|
||||
|
||||
let list_val = self.target[h + 1];
|
||||
self.target.push(list_val);
|
||||
let old_list_link = self.target[h + 1];
|
||||
self.trail.push((Ref::heap_cell(h + 1), old_list_link));
|
||||
self.target[h + 1] = heap_loc_as_cell!(threshold + 1);
|
||||
|
||||
if old_list_link.get_tag() == HeapCellValueTag::Lis {
|
||||
self.attr_var_list_locs.push((threshold + 1, old_list_link));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@@ -298,8 +350,6 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
self.unwind_trail();
|
||||
}
|
||||
|
||||
fn unwind_trail(&mut self) {
|
||||
|
||||
837
src/machine/disjuncts.rs
Normal file
837
src/machine/disjuncts.rs
Normal file
@@ -0,0 +1,837 @@
|
||||
use crate::atom_table::*;
|
||||
use crate::forms::*;
|
||||
use crate::instructions::*;
|
||||
use crate::iterators::*;
|
||||
use crate::machine::loader::*;
|
||||
use crate::machine::machine_errors::CompilationError;
|
||||
use crate::machine::preprocessor::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::dashu::Rational;
|
||||
use crate::variable_records::*;
|
||||
|
||||
use dashu::Integer;
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::VecDeque;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
#[derive(Debug, Clone)] //, PartialOrd, PartialEq, Eq, Hash)]
|
||||
pub struct BranchNumber {
|
||||
branch_num: Rational,
|
||||
delta: Rational,
|
||||
}
|
||||
|
||||
impl Default for BranchNumber {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
branch_num: Rational::from(1usize << 63),
|
||||
delta: Rational::from(1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<BranchNumber> for BranchNumber {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &BranchNumber) -> bool {
|
||||
self.branch_num == rhs.branch_num
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for BranchNumber {}
|
||||
|
||||
impl Hash for BranchNumber {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
self.branch_num.hash(hasher)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<BranchNumber> for BranchNumber {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, rhs: &BranchNumber) -> Option<Ordering> {
|
||||
self.branch_num.partial_cmp(&rhs.branch_num)
|
||||
}
|
||||
}
|
||||
|
||||
impl BranchNumber {
|
||||
fn split(&self) -> BranchNumber {
|
||||
BranchNumber {
|
||||
branch_num: self.branch_num.clone() + &self.delta / Rational::from(2),
|
||||
delta: &self.delta / Rational::from(4),
|
||||
}
|
||||
}
|
||||
|
||||
fn incr_by_delta(&self) -> BranchNumber {
|
||||
BranchNumber {
|
||||
branch_num: self.branch_num.clone() + &self.delta,
|
||||
delta: self.delta.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn halve_delta(&self) -> BranchNumber {
|
||||
BranchNumber {
|
||||
branch_num: self.branch_num.clone(),
|
||||
delta : &self.delta / Rational::from(2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct VarInfo {
|
||||
var_ptr: VarPtr,
|
||||
chunk_type: ChunkType,
|
||||
classify_info: ClassifyInfo,
|
||||
lvl: Level,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ChunkInfo {
|
||||
chunk_num: usize,
|
||||
term_loc: GenContext,
|
||||
// pointer to incidence, term occurrence arity.
|
||||
vars: Vec<VarInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BranchArm {
|
||||
pub arm_terms: Vec<QueryTerm>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct BranchInfo {
|
||||
branch_num: BranchNumber,
|
||||
chunks: Vec<ChunkInfo>,
|
||||
}
|
||||
|
||||
impl BranchInfo {
|
||||
fn new(branch_num: BranchNumber) -> Self {
|
||||
Self { branch_num, chunks: vec![] }
|
||||
}
|
||||
}
|
||||
|
||||
type BranchMapInt = IndexMap<VarPtr, Vec<BranchInfo>>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BranchMap(BranchMapInt);
|
||||
|
||||
impl Deref for BranchMap {
|
||||
type Target = BranchMapInt;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &BranchMapInt {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for BranchMap {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut BranchMapInt {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
type RootSet = IndexSet<BranchNumber>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ClassifyInfo {
|
||||
arg_c: usize,
|
||||
arity: usize,
|
||||
}
|
||||
|
||||
enum TraversalState {
|
||||
// construct a QueryTerm::Branch with number of disjuncts, reset
|
||||
// the chunk type to that of the chunk preceding the disjunct and the chunk_num.
|
||||
BuildDisjunct(usize),
|
||||
// add the last disjunct to a QueryTerm::Branch, continuing from
|
||||
// where it leaves off.
|
||||
BuildFinalDisjunct(usize),
|
||||
Fail,
|
||||
GetCutPoint{ var_num: usize, prev_b: bool },
|
||||
Cut { var_num: usize, is_global: bool },
|
||||
ResetCallPolicy(CallPolicy),
|
||||
Term(Term),
|
||||
RemoveBranchNum, // pop the current_branch_num and from the root set.
|
||||
AddBranchNum(BranchNumber), // set current_branch_num, add it to the root set
|
||||
RepBranchNum(BranchNumber), // replace current_branch_num and the latest in the root set
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VariableClassifier {
|
||||
call_policy: CallPolicy,
|
||||
current_branch_num: BranchNumber,
|
||||
current_chunk_num: usize,
|
||||
current_chunk_type: ChunkType,
|
||||
branch_map: BranchMap,
|
||||
var_num: usize,
|
||||
root_set: RootSet,
|
||||
global_cut_var_num: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct VarData {
|
||||
pub records: VariableRecords,
|
||||
pub global_cut_var_num: Option<usize>,
|
||||
pub allocates: bool,
|
||||
}
|
||||
|
||||
impl VarData {
|
||||
fn emit_initial_get_level(&mut self, build_stack: &mut ChunkedTermVec) {
|
||||
let global_cut_var_num =
|
||||
if let &Some(global_cut_var_num) = &self.global_cut_var_num {
|
||||
match &self.records[global_cut_var_num].allocation {
|
||||
VarAlloc::Perm(..) => Some(global_cut_var_num),
|
||||
VarAlloc::Temp { term_loc, .. } if term_loc.chunk_num() > 0 => {
|
||||
Some(global_cut_var_num)
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(global_cut_var_num) = global_cut_var_num {
|
||||
let term = QueryTerm::GetLevel(global_cut_var_num);
|
||||
self.records[global_cut_var_num].allocation = VarAlloc::Perm(0, PermVarAllocation::Pending);
|
||||
|
||||
match build_stack.front_mut() {
|
||||
Some(ChunkedTerms::Branch(_)) => {
|
||||
build_stack.push_front(ChunkedTerms::Chunk(VecDeque::from(vec![term])));
|
||||
}
|
||||
Some(ChunkedTerms::Chunk(chunk)) => {
|
||||
chunk.push_front(term);
|
||||
}
|
||||
None => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type ClassifyFactResult = (Term, VarData);
|
||||
pub type ClassifyRuleResult = (Term, ChunkedTermVec, VarData);
|
||||
|
||||
fn merge_branch_seq(branches: impl Iterator<Item = BranchInfo>) -> BranchInfo {
|
||||
let mut branch_info = BranchInfo::new(BranchNumber::default());
|
||||
|
||||
for mut branch in branches {
|
||||
branch_info.branch_num = branch.branch_num;
|
||||
branch_info.chunks.extend(branch.chunks.drain(..));
|
||||
}
|
||||
|
||||
branch_info.branch_num.delta = branch_info.branch_num.delta * Integer::from(2);
|
||||
branch_info.branch_num.branch_num -= &branch_info.branch_num.delta;
|
||||
|
||||
branch_info
|
||||
}
|
||||
|
||||
fn flatten_into_disjunct(build_stack: &mut ChunkedTermVec, preceding_len: usize) {
|
||||
let branch_vec = build_stack.drain(preceding_len + 1 ..).collect();
|
||||
|
||||
if let ChunkedTerms::Branch(ref mut disjuncts) = &mut build_stack[preceding_len] {
|
||||
disjuncts.push(branch_vec);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
impl VariableClassifier {
|
||||
pub fn new(call_policy: CallPolicy) -> Self {
|
||||
Self {
|
||||
call_policy,
|
||||
current_branch_num: BranchNumber::default(),
|
||||
current_chunk_num: 0,
|
||||
current_chunk_type: ChunkType::Head,
|
||||
branch_map: BranchMap(BranchMapInt::new()),
|
||||
root_set: RootSet::new(),
|
||||
var_num: 0,
|
||||
global_cut_var_num: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn classify_fact(mut self, term: Term) -> Result<ClassifyFactResult, CompilationError> {
|
||||
self.classify_head_variables(&term)?;
|
||||
Ok((term, self.branch_map.separate_and_classify_variables(
|
||||
self.var_num,
|
||||
self.global_cut_var_num,
|
||||
self.current_chunk_num,
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn classify_rule<'a, LS: LoadState<'a>>(
|
||||
mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
head: Term,
|
||||
body: Term,
|
||||
) -> Result<ClassifyRuleResult, CompilationError> {
|
||||
self.classify_head_variables(&head)?;
|
||||
self.root_set.insert(self.current_branch_num.clone());
|
||||
|
||||
let mut query_terms = self.classify_body_variables(loader, body)?;
|
||||
|
||||
self.merge_branches();
|
||||
|
||||
let mut var_data = self.branch_map.separate_and_classify_variables(
|
||||
self.var_num,
|
||||
self.global_cut_var_num,
|
||||
self.current_chunk_num,
|
||||
);
|
||||
|
||||
var_data.emit_initial_get_level(&mut query_terms);
|
||||
|
||||
Ok((head, query_terms, var_data))
|
||||
}
|
||||
|
||||
fn merge_branches(&mut self) {
|
||||
for branches in self.branch_map.values_mut() {
|
||||
let mut old_branches = std::mem::replace(branches, vec![]);
|
||||
|
||||
while let Some(last_branch_num) = old_branches.last().map(|bi| &bi.branch_num) {
|
||||
let mut old_branches_len = old_branches.len();
|
||||
|
||||
for (rev_idx, bi) in old_branches.iter().rev().enumerate() {
|
||||
if &bi.branch_num > last_branch_num {
|
||||
old_branches_len = old_branches.len() - rev_idx;
|
||||
}
|
||||
}
|
||||
|
||||
let iter = old_branches.drain(old_branches_len - 1 ..);
|
||||
branches.push(merge_branch_seq(iter));
|
||||
}
|
||||
|
||||
branches.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
fn try_set_chunk_at_inlined_boundary(&mut self) -> bool {
|
||||
if self.current_chunk_type.is_last() {
|
||||
self.current_chunk_type = ChunkType::Mid;
|
||||
self.current_chunk_num += 1;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn try_set_chunk_at_call_boundary(&mut self) -> bool {
|
||||
if self.current_chunk_type.is_last() {
|
||||
self.current_chunk_num += 1;
|
||||
true
|
||||
} else {
|
||||
self.current_chunk_type = ChunkType::Last;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn probe_body_term(&mut self, arg_c: usize, arity: usize, term: &Term) {
|
||||
let classify_info = ClassifyInfo { arg_c, arity };
|
||||
|
||||
// second arg is true to iterate the root, which may be a variable
|
||||
for term_ref in breadth_first_iter(term, RootIterationPolicy::Iterated) {
|
||||
if let TermRef::Var(lvl, _, var_ptr) = term_ref {
|
||||
// root terms are shallow here (since we're iterating a
|
||||
// body term) so take the child level.
|
||||
let lvl = lvl.child_level();
|
||||
self.probe_body_var(VarInfo {
|
||||
var_ptr,
|
||||
lvl,
|
||||
classify_info,
|
||||
chunk_type: self.current_chunk_type,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn probe_body_var(&mut self, var_info: VarInfo) {
|
||||
let term_loc = self.current_chunk_type.to_gen_context(self.current_chunk_num);
|
||||
|
||||
let branch_info_v = self.branch_map.entry(var_info.var_ptr.clone())
|
||||
.or_insert_with(|| vec![]);
|
||||
|
||||
let needs_new_branch = if let Some(last_bi) = branch_info_v.last() {
|
||||
!self.root_set.contains(&last_bi.branch_num)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if needs_new_branch {
|
||||
branch_info_v.push(BranchInfo::new(self.current_branch_num.clone()));
|
||||
}
|
||||
|
||||
let branch_info = branch_info_v.last_mut().unwrap();
|
||||
|
||||
let needs_new_chunk = if let Some(last_ci) = branch_info.chunks.last() {
|
||||
last_ci.chunk_num != self.current_chunk_num
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if needs_new_chunk {
|
||||
branch_info.chunks.push(ChunkInfo {
|
||||
chunk_num: self.current_chunk_num,
|
||||
term_loc,
|
||||
vars: vec![],
|
||||
});
|
||||
}
|
||||
|
||||
let chunk_info = branch_info.chunks.last_mut().unwrap();
|
||||
chunk_info.vars.push(var_info);
|
||||
}
|
||||
|
||||
fn probe_in_situ_var(&mut self, var_num: usize) {
|
||||
let classify_info = ClassifyInfo { arg_c: 1, arity: 1 };
|
||||
|
||||
let var_info = VarInfo {
|
||||
var_ptr: VarPtr::from(Var::InSitu(var_num)),
|
||||
classify_info,
|
||||
chunk_type: self.current_chunk_type,
|
||||
lvl: Level::Shallow,
|
||||
};
|
||||
|
||||
self.probe_body_var(var_info);
|
||||
}
|
||||
|
||||
fn classify_head_variables(&mut self, term: &Term) -> Result<(), CompilationError> {
|
||||
match term {
|
||||
Term::Clause(..) | Term::Literal(_, Literal::Atom(_)) => {
|
||||
}
|
||||
_ => return Err(CompilationError::InvalidRuleHead),
|
||||
}
|
||||
|
||||
let mut classify_info = ClassifyInfo { arg_c: 1, arity: term.arity() };
|
||||
|
||||
match term {
|
||||
Term::Clause(_, _, terms) => {
|
||||
for term in terms.into_iter() {
|
||||
for term_ref in breadth_first_iter(term, RootIterationPolicy::Iterated) {
|
||||
if let TermRef::Var(lvl, _, var_ptr) = term_ref {
|
||||
// a body term, so we need the child level here.
|
||||
let lvl = lvl.child_level();
|
||||
|
||||
// the body of the if let here is an inlined
|
||||
// "probe_head_var". note the difference between it
|
||||
// and "probe_body_var".
|
||||
let branch_info_v = self.branch_map.entry(var_ptr.clone())
|
||||
.or_insert_with(|| vec![]);
|
||||
|
||||
let needs_new_branch = branch_info_v.is_empty();
|
||||
|
||||
if needs_new_branch {
|
||||
branch_info_v.push(BranchInfo::new(self.current_branch_num.clone()));
|
||||
}
|
||||
|
||||
let branch_info = branch_info_v.last_mut().unwrap();
|
||||
let needs_new_chunk = branch_info.chunks.is_empty();
|
||||
|
||||
if needs_new_chunk {
|
||||
branch_info.chunks.push(ChunkInfo {
|
||||
chunk_num: self.current_chunk_num,
|
||||
term_loc: GenContext::Head,
|
||||
vars: vec![],
|
||||
});
|
||||
}
|
||||
|
||||
let chunk_info = branch_info.chunks.last_mut().unwrap();
|
||||
let var_info = VarInfo {
|
||||
var_ptr,
|
||||
classify_info,
|
||||
chunk_type: self.current_chunk_type,
|
||||
lvl,
|
||||
};
|
||||
|
||||
chunk_info.vars.push(var_info);
|
||||
}
|
||||
}
|
||||
|
||||
classify_info.arg_c += 1;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn classify_body_variables<'a, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
term: Term,
|
||||
) -> Result<ChunkedTermVec, CompilationError> {
|
||||
let mut state_stack = vec![TraversalState::Term(term)];
|
||||
let mut build_stack = ChunkedTermVec::new();
|
||||
|
||||
self.current_chunk_type = ChunkType::Mid;
|
||||
|
||||
while let Some(traversal_st) = state_stack.pop() {
|
||||
match traversal_st {
|
||||
TraversalState::AddBranchNum(branch_num) => {
|
||||
self.root_set.insert(branch_num.clone());
|
||||
self.current_branch_num = branch_num;
|
||||
}
|
||||
TraversalState::RemoveBranchNum => {
|
||||
self.root_set.pop();
|
||||
}
|
||||
TraversalState::RepBranchNum(branch_num) => {
|
||||
self.root_set.pop();
|
||||
self.root_set.insert(branch_num.clone());
|
||||
self.current_branch_num = branch_num;
|
||||
}
|
||||
TraversalState::ResetCallPolicy(call_policy) => {
|
||||
self.call_policy = call_policy;
|
||||
}
|
||||
TraversalState::BuildDisjunct(preceding_len) => {
|
||||
flatten_into_disjunct(&mut build_stack, preceding_len);
|
||||
|
||||
self.current_chunk_type = ChunkType::Mid;
|
||||
self.current_chunk_num += 1;
|
||||
}
|
||||
TraversalState::BuildFinalDisjunct(preceding_len) => {
|
||||
flatten_into_disjunct(&mut build_stack, preceding_len);
|
||||
|
||||
self.current_chunk_type = ChunkType::Mid;
|
||||
self.current_chunk_num += 1;
|
||||
}
|
||||
TraversalState::GetCutPoint { var_num, prev_b } => {
|
||||
if self.try_set_chunk_at_inlined_boundary() {
|
||||
build_stack.add_chunk();
|
||||
}
|
||||
|
||||
self.probe_in_situ_var(var_num);
|
||||
build_stack.push_chunk_term(QueryTerm::GetCutPoint { var_num, prev_b });
|
||||
}
|
||||
TraversalState::Cut { var_num, is_global } => {
|
||||
if self.try_set_chunk_at_inlined_boundary() {
|
||||
build_stack.add_chunk();
|
||||
}
|
||||
|
||||
self.probe_in_situ_var(var_num);
|
||||
|
||||
build_stack.push_chunk_term(
|
||||
if is_global {
|
||||
QueryTerm::GlobalCut(var_num)
|
||||
} else {
|
||||
QueryTerm::LocalCut(var_num)
|
||||
}
|
||||
);
|
||||
}
|
||||
TraversalState::Fail => {
|
||||
build_stack.push_chunk_term(QueryTerm::Fail);
|
||||
}
|
||||
TraversalState::Term(term) => {
|
||||
// return true iff new chunk should be added.
|
||||
let update_chunk_data = |classifier: &mut Self, predicate_name, arity| {
|
||||
if ClauseType::is_inlined(predicate_name, arity) {
|
||||
classifier.try_set_chunk_at_inlined_boundary()
|
||||
} else {
|
||||
classifier.try_set_chunk_at_call_boundary()
|
||||
}
|
||||
};
|
||||
|
||||
match term {
|
||||
Term::Clause(_, atom!(","), mut terms) if terms.len() == 2 => {
|
||||
let tail = terms.pop().unwrap();
|
||||
let head = terms.pop().unwrap();
|
||||
|
||||
let iter = unfold_by_str(tail, atom!(","))
|
||||
.into_iter()
|
||||
.rev()
|
||||
.chain(std::iter::once(head))
|
||||
.map(TraversalState::Term);
|
||||
|
||||
state_stack.extend(iter);
|
||||
}
|
||||
Term::Clause(_, atom!(";"), mut terms) if terms.len() == 2 => {
|
||||
let tail = terms.pop().unwrap();
|
||||
let head = terms.pop().unwrap();
|
||||
|
||||
let first_branch_num = self.current_branch_num.split();
|
||||
let branches: Vec<_> = std::iter::once(head)
|
||||
.chain(unfold_by_str(tail, atom!(";")).into_iter())
|
||||
.collect();
|
||||
|
||||
let mut branch_numbers = vec![first_branch_num];
|
||||
|
||||
for idx in 1 .. branches.len() {
|
||||
let succ_branch_number = branch_numbers[idx - 1].incr_by_delta();
|
||||
|
||||
branch_numbers.push(if idx + 1 < branches.len() {
|
||||
succ_branch_number.split()
|
||||
} else {
|
||||
succ_branch_number
|
||||
});
|
||||
}
|
||||
|
||||
let build_stack_len = build_stack.len();
|
||||
build_stack.reserve_branch(branches.len());
|
||||
|
||||
state_stack.push(TraversalState::RepBranchNum(
|
||||
self.current_branch_num.halve_delta(),
|
||||
));
|
||||
|
||||
let iter = branches.into_iter().zip(branch_numbers.into_iter());
|
||||
let final_disjunct_loc = state_stack.len();
|
||||
|
||||
for (term, branch_num) in iter.rev() {
|
||||
state_stack.push(TraversalState::BuildDisjunct(build_stack_len));
|
||||
state_stack.push(TraversalState::RemoveBranchNum);
|
||||
state_stack.push(TraversalState::Term(term));
|
||||
state_stack.push(TraversalState::AddBranchNum(branch_num));
|
||||
}
|
||||
|
||||
if let TraversalState::BuildDisjunct(build_stack_len) = state_stack[final_disjunct_loc] {
|
||||
state_stack[final_disjunct_loc] = TraversalState::BuildFinalDisjunct(build_stack_len);
|
||||
}
|
||||
|
||||
self.current_chunk_type = ChunkType::Mid;
|
||||
self.current_chunk_num += 1;
|
||||
}
|
||||
Term::Clause(_, atom!("->"), mut terms) if terms.len() == 2 => {
|
||||
let then_term = terms.pop().unwrap();
|
||||
let if_term = terms.pop().unwrap();
|
||||
|
||||
let prev_b = if matches!(state_stack.last(), Some(TraversalState::RemoveBranchNum)) {
|
||||
// check if the second-to-last element is a regular BuildDisjunct, as we don't
|
||||
// want to add GetPrevLevel in case of a TrustMe.
|
||||
matches!(state_stack.iter().rev().nth(1), Some(TraversalState::BuildDisjunct(..)))
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
state_stack.push(TraversalState::Term(then_term));
|
||||
state_stack.push(TraversalState::Cut { var_num: self.var_num, is_global: false });
|
||||
state_stack.push(TraversalState::Term(if_term));
|
||||
state_stack.push(TraversalState::GetCutPoint { var_num: self.var_num, prev_b });
|
||||
|
||||
self.var_num += 1;
|
||||
}
|
||||
Term::Clause(_, atom!("\\+"), mut terms) if terms.len() == 1 => {
|
||||
let not_term = terms.pop().unwrap();
|
||||
let build_stack_len = build_stack.len();
|
||||
|
||||
build_stack.reserve_branch(2);
|
||||
|
||||
state_stack.push(TraversalState::BuildFinalDisjunct(build_stack_len));
|
||||
state_stack.push(TraversalState::Term(Term::Clause(Cell::default(), atom!("$succeed"), vec![])));
|
||||
state_stack.push(TraversalState::BuildDisjunct(build_stack_len));
|
||||
state_stack.push(TraversalState::Fail);
|
||||
state_stack.push(TraversalState::Cut { var_num: self.var_num, is_global: false });
|
||||
state_stack.push(TraversalState::Term(not_term));
|
||||
state_stack.push(TraversalState::GetCutPoint { var_num: self.var_num, prev_b: true });
|
||||
|
||||
self.current_chunk_type = ChunkType::Mid;
|
||||
self.current_chunk_num += 1;
|
||||
|
||||
self.var_num += 1;
|
||||
}
|
||||
Term::Clause(_, atom!(":"), mut terms) if terms.len() == 2 => {
|
||||
let predicate_name = terms.pop().unwrap();
|
||||
let module_name = terms.pop().unwrap();
|
||||
|
||||
match (module_name, predicate_name) {
|
||||
(
|
||||
Term::Literal(_, Literal::Atom(module_name)),
|
||||
Term::Literal(_, Literal::Atom(predicate_name)),
|
||||
) => {
|
||||
if update_chunk_data(self, predicate_name, 0) {
|
||||
build_stack.add_chunk();
|
||||
}
|
||||
|
||||
build_stack.push_chunk_term(
|
||||
qualified_clause_to_query_term(
|
||||
loader,
|
||||
module_name,
|
||||
predicate_name,
|
||||
vec![],
|
||||
self.call_policy,
|
||||
),
|
||||
);
|
||||
}
|
||||
(
|
||||
Term::Literal(_, Literal::Atom(module_name)),
|
||||
Term::Clause(_, name, terms),
|
||||
) => {
|
||||
if update_chunk_data(self, name, terms.len()) {
|
||||
build_stack.add_chunk();
|
||||
}
|
||||
|
||||
for (arg_c, term) in terms.iter().enumerate() {
|
||||
self.probe_body_term(arg_c + 1, terms.len(), term);
|
||||
}
|
||||
|
||||
build_stack.push_chunk_term(
|
||||
qualified_clause_to_query_term(
|
||||
loader,
|
||||
module_name,
|
||||
name,
|
||||
terms,
|
||||
self.call_policy,
|
||||
),
|
||||
);
|
||||
}
|
||||
(module_name, predicate_name) => {
|
||||
if update_chunk_data(self, atom!("call"), 2) {
|
||||
build_stack.add_chunk();
|
||||
}
|
||||
|
||||
self.probe_body_term(1, 0, &module_name);
|
||||
self.probe_body_term(2, 0, &predicate_name);
|
||||
|
||||
terms.push(module_name);
|
||||
terms.push(predicate_name);
|
||||
|
||||
build_stack.push_chunk_term(
|
||||
clause_to_query_term(
|
||||
loader,
|
||||
atom!("call"),
|
||||
vec![Term::Clause(Cell::default(), atom!(":"), terms)],
|
||||
self.call_policy,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Term::Clause(_, atom!("$call_with_inference_counting"), mut terms) if terms.len() == 1 => {
|
||||
state_stack.push(TraversalState::ResetCallPolicy(self.call_policy));
|
||||
state_stack.push(TraversalState::Term(terms.pop().unwrap()));
|
||||
|
||||
self.call_policy = CallPolicy::Counted;
|
||||
}
|
||||
Term::Clause(_, name, terms) => {
|
||||
if update_chunk_data(self, name, terms.len()) {
|
||||
build_stack.add_chunk();
|
||||
}
|
||||
|
||||
for (arg_c, term) in terms.iter().enumerate() {
|
||||
self.probe_body_term(arg_c + 1, terms.len(), term);
|
||||
}
|
||||
|
||||
build_stack.push_chunk_term(
|
||||
clause_to_query_term(
|
||||
loader,
|
||||
name,
|
||||
terms,
|
||||
self.call_policy,
|
||||
),
|
||||
);
|
||||
}
|
||||
var @ Term::Var(..) => {
|
||||
if update_chunk_data(self, atom!("call"), 1) {
|
||||
build_stack.add_chunk();
|
||||
}
|
||||
|
||||
self.probe_body_term(1, 1, &var);
|
||||
|
||||
build_stack.push_chunk_term(
|
||||
clause_to_query_term(
|
||||
loader,
|
||||
atom!("call"),
|
||||
vec![var],
|
||||
self.call_policy,
|
||||
),
|
||||
);
|
||||
}
|
||||
Term::Literal(_, Literal::Atom(atom!("!")) | Literal::Char('!')) => {
|
||||
if self.global_cut_var_num.is_none() {
|
||||
self.global_cut_var_num = Some(self.var_num);
|
||||
self.var_num += 1;
|
||||
}
|
||||
|
||||
self.probe_in_situ_var(self.global_cut_var_num.unwrap());
|
||||
|
||||
state_stack.push(TraversalState::Cut {
|
||||
var_num: self.global_cut_var_num.unwrap(),
|
||||
is_global: true,
|
||||
});
|
||||
}
|
||||
Term::Literal(_, Literal::Atom(name)) => {
|
||||
if update_chunk_data(self, name, 0) {
|
||||
build_stack.add_chunk();
|
||||
}
|
||||
|
||||
build_stack.push_chunk_term(
|
||||
clause_to_query_term(
|
||||
loader,
|
||||
name,
|
||||
vec![],
|
||||
self.call_policy,
|
||||
),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
return Err(CompilationError::InadmissibleQueryTerm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(build_stack)
|
||||
}
|
||||
}
|
||||
|
||||
impl BranchMap {
|
||||
pub fn separate_and_classify_variables(
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
global_cut_var_num: Option<usize>,
|
||||
current_chunk_num: usize,
|
||||
) -> VarData {
|
||||
let mut var_data = VarData {
|
||||
records: VariableRecords::new(var_num),
|
||||
global_cut_var_num,
|
||||
allocates: current_chunk_num > 0,
|
||||
};
|
||||
|
||||
for (var, branches) in self.iter_mut() {
|
||||
let (mut var_num, var_num_incr) =
|
||||
if let Var::InSitu(var_num) = *var.borrow() {
|
||||
(var_num, false)
|
||||
} else {
|
||||
(var_data.records.len(), true)
|
||||
};
|
||||
|
||||
for branch in branches.iter_mut() {
|
||||
if var_num_incr {
|
||||
var_num = var_data.records.len();
|
||||
var_data.records.push(VariableRecord::default());
|
||||
}
|
||||
|
||||
if branch.chunks.len() <= 1 { // true iff var is a temporary variable.
|
||||
debug_assert_eq!(branch.chunks.len(), 1);
|
||||
|
||||
let chunk = &mut branch.chunks[0];
|
||||
let mut temp_var_data = TempVarData::new();
|
||||
|
||||
for var_info in chunk.vars.iter_mut() {
|
||||
if var_info.lvl == Level::Shallow {
|
||||
let term_loc = var_info.chunk_type.to_gen_context(chunk.chunk_num);
|
||||
temp_var_data.use_set.insert((term_loc, var_info.classify_info.arg_c));
|
||||
}
|
||||
}
|
||||
|
||||
var_data.records[var_num].allocation = VarAlloc::Temp {
|
||||
term_loc: chunk.term_loc,
|
||||
temp_reg: 0,
|
||||
temp_var_data,
|
||||
safety: VarSafetyStatus::Needed,
|
||||
to_perm_var_num: None,
|
||||
};
|
||||
} // else VarAlloc is already a Perm variant, as it's the default.
|
||||
|
||||
for chunk in branch.chunks.iter_mut() {
|
||||
var_data.records[var_num].num_occurrences += chunk.vars.len();
|
||||
|
||||
for var_info in chunk.vars.iter_mut() {
|
||||
var_info.var_ptr.set(Var::Generated(var_num));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var_data.records.populate_restricting_sets();
|
||||
var_data
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ use crate::machine::heap::*;
|
||||
use crate::types::*;
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::heap_iter::FocusedHeapIter;
|
||||
use crate::heap_iter::{IterStackLoc, FocusedHeapIter, HeapOrStackTag};
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
@@ -75,8 +75,8 @@ pub(crate) struct StacklessPreOrderHeapIter<'a, UMP: UnmarkPolicy> {
|
||||
#[cfg(test)]
|
||||
impl<'a> FocusedHeapIter for StacklessPreOrderHeapIter<'a, IteratorUMP> {
|
||||
#[inline]
|
||||
fn focus(&self) -> usize {
|
||||
self.current
|
||||
fn focus(&self) -> IterStackLoc {
|
||||
IterStackLoc::iterable_loc(self.current, HeapOrStackTag::Heap)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::machine::partial_string::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::types::*;
|
||||
|
||||
use crate::parser::rug::{Integer, Rational};
|
||||
use crate::parser::dashu::{Integer, Rational};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ pub(super) fn import_module_exports<'a, LS: LoadState<'a>>(
|
||||
meta_predicates.insert(key, meta_specs.clone());
|
||||
}
|
||||
|
||||
if let Some(src_code_index) = imported_module.code_dir.get(&key) {
|
||||
if let Some(src_code_index) = imported_module.code_dir.get(&key).cloned() {
|
||||
let arena = &mut LS::machine_st(payload).arena;
|
||||
|
||||
let target_code_index = code_dir
|
||||
@@ -148,6 +148,10 @@ pub(super) fn import_module_exports<'a, LS: LoadState<'a>>(
|
||||
target_code_index,
|
||||
src_code_index.get(),
|
||||
);
|
||||
|
||||
if src_code_index.is_dynamic_undefined() {
|
||||
code_dir.insert(key, src_code_index);
|
||||
}
|
||||
} else {
|
||||
return Err(SessionError::ModuleDoesNotContainExport(
|
||||
imported_module.module_decl.name,
|
||||
@@ -441,13 +445,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
term: Term,
|
||||
preprocessor: &mut Preprocessor,
|
||||
) -> Result<PredicateClause, SessionError> {
|
||||
let tl = preprocessor.try_term_to_tl(self, term, CutContext::BlocksCuts)?;
|
||||
let tl = preprocessor.try_term_to_tl(self, term)?;
|
||||
|
||||
Ok(match tl {
|
||||
TopLevel::Fact(fact) => PredicateClause::Fact(fact),
|
||||
TopLevel::Rule(rule) => PredicateClause::Rule(rule),
|
||||
TopLevel::Query(_) => return Err(SessionError::QueryCannotBeDefinedAsFact),
|
||||
_ => unreachable!(),
|
||||
TopLevel::Fact(fact, var_data) => PredicateClause::Fact(fact, var_data),
|
||||
TopLevel::Rule(rule, var_data) => PredicateClause::Rule(rule, var_data),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::Rc;
|
||||
|
||||
/*
|
||||
* The loader compiles Prolog terms read from a TermStream instance,
|
||||
@@ -329,6 +328,10 @@ impl<'a> LoadState<'a> for LiveLoadAndMachineState<'a> {
|
||||
loader: &Loader<'a, Self>,
|
||||
key: PredicateKey,
|
||||
) -> Result<(), SessionError> {
|
||||
if ClauseType::is_inbuilt(key.0, key.1) {
|
||||
return Err(SessionError::CannotOverwriteBuiltIn(key));
|
||||
}
|
||||
|
||||
if let Some(builtins) = loader.wam_prelude.indices.modules.get(&atom!("builtins")) {
|
||||
if builtins.module_decl.exports.contains(&ModuleExport::PredicateKey(key)) {
|
||||
return Err(SessionError::CannotOverwriteBuiltIn(key));
|
||||
@@ -465,6 +468,13 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn read_term_from_heap(&mut self, r: RegType) -> Result<Term, SessionError> {
|
||||
let machine_st = LS::machine_st(&mut self.payload);
|
||||
let cell = machine_st[r];
|
||||
|
||||
machine_st.read_term_from_heap(cell)
|
||||
}
|
||||
|
||||
pub(crate) fn load(mut self) -> Result<LS::Evacuable, SessionError> {
|
||||
while let Some(decl) = self.dequeue_terms()? {
|
||||
self.load_decl(decl)?;
|
||||
@@ -531,106 +541,6 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn read_term_from_heap(&mut self, heap_term_loc: RegType) -> Result<Term, SessionError> {
|
||||
let machine_st = LS::machine_st(&mut self.payload);
|
||||
let term_addr = machine_st[heap_term_loc];
|
||||
|
||||
let mut term_stack = vec![];
|
||||
let mut iter = stackful_post_order_iter(&mut machine_st.heap, term_addr);
|
||||
|
||||
while let Some(addr) = iter.next() {
|
||||
let addr = unmark_cell_bits!(addr);
|
||||
|
||||
read_heap_cell!(addr,
|
||||
(HeapCellValueTag::Lis) => {
|
||||
use crate::parser::parser::as_partial_string;
|
||||
|
||||
let tail = term_stack.pop().unwrap();
|
||||
let head = term_stack.pop().unwrap();
|
||||
|
||||
match as_partial_string(head, tail) {
|
||||
Ok((string, Some(tail))) => {
|
||||
term_stack.push(Term::PartialString(Cell::default(), string, tail));
|
||||
}
|
||||
Ok((string, None)) => {
|
||||
let atom = machine_st.atom_tbl.build_with(&string);
|
||||
term_stack.push(Term::CompleteString(Cell::default(), atom));
|
||||
}
|
||||
Err(cons_term) => term_stack.push(cons_term),
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar, h) => {
|
||||
let offset_string = format!("_{}", h);
|
||||
term_stack.push(Term::Var(Cell::default(), Rc::new(offset_string)));
|
||||
}
|
||||
(HeapCellValueTag::Cons | HeapCellValueTag::CStr | HeapCellValueTag::Fixnum |
|
||||
HeapCellValueTag::Char | HeapCellValueTag::F64) => {
|
||||
term_stack.push(Term::Literal(Cell::default(), Literal::try_from(addr).unwrap()));
|
||||
}
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
let h = iter.focus();
|
||||
let mut arity = arity;
|
||||
|
||||
if iter.heap.len() > h + arity + 1 {
|
||||
let value = iter.heap[h + arity + 1];
|
||||
|
||||
if let Some(idx) = get_structure_index(value) {
|
||||
// in the second condition, arity == 0,
|
||||
// meaning idx cannot pertain to this atom
|
||||
// if it is the direct subterm of a larger
|
||||
// structure.
|
||||
if arity > 0 || !iter.direct_subterm_of_str(h) {
|
||||
term_stack.push(
|
||||
Term::Literal(Cell::default(), Literal::CodeIndex(idx))
|
||||
);
|
||||
|
||||
arity += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if arity == 0 {
|
||||
term_stack.push(Term::Literal(Cell::default(), Literal::Atom(name)));
|
||||
} else {
|
||||
let subterms = term_stack
|
||||
.drain(term_stack.len() - arity ..)
|
||||
.collect();
|
||||
|
||||
term_stack.push(Term::Clause(Cell::default(), name, subterms));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::PStr, atom) => {
|
||||
let tail = term_stack.pop().unwrap();
|
||||
|
||||
if let Term::Literal(_, Literal::Atom(atom!("[]"))) = &tail {
|
||||
term_stack.push(Term::CompleteString(Cell::default(), atom));
|
||||
} else {
|
||||
term_stack.push(Term::PartialString(
|
||||
Cell::default(),
|
||||
atom.as_str().to_owned(),
|
||||
Box::new(tail),
|
||||
));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::PStrLoc, h) => {
|
||||
let atom = cell_as_atom_cell!(iter.heap[h]).get_name();
|
||||
let tail = term_stack.pop().unwrap();
|
||||
|
||||
term_stack.push(Term::PartialString(
|
||||
Cell::default(),
|
||||
atom.as_str().to_owned(),
|
||||
Box::new(tail),
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
debug_assert!(term_stack.len() == 1);
|
||||
Ok(term_stack.pop().unwrap())
|
||||
}
|
||||
|
||||
fn reset_machine(&mut self) {
|
||||
while let Some(record) = self.payload.retraction_info.records.pop() {
|
||||
match record {
|
||||
@@ -1143,7 +1053,10 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
&mut self,
|
||||
r: RegType,
|
||||
) -> Result<IndexSet<ModuleExport>, SessionError> {
|
||||
let export_list = self.read_term_from_heap(r)?;
|
||||
let machine_st = LS::machine_st(&mut self.payload);
|
||||
let cell = machine_st[r];
|
||||
|
||||
let export_list = machine_st.read_term_from_heap(cell)?;
|
||||
let atom_tbl = &mut LS::machine_st(&mut self.payload).atom_tbl;
|
||||
let export_list = setup_module_export_list(export_list, atom_tbl)?;
|
||||
|
||||
@@ -1493,6 +1406,104 @@ impl<'a> MachinePreludeView<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl MachineState {
|
||||
pub(super) fn read_term_from_heap(&mut self, term_addr: HeapCellValue) -> Result<Term, SessionError> {
|
||||
let mut term_stack = vec![];
|
||||
let mut iter = stackful_post_order_iter(&mut self.heap, &mut self.stack, term_addr);
|
||||
|
||||
while let Some(addr) = iter.next() {
|
||||
let addr = unmark_cell_bits!(addr);
|
||||
|
||||
read_heap_cell!(addr,
|
||||
(HeapCellValueTag::Lis) => {
|
||||
use crate::parser::parser::as_partial_string;
|
||||
|
||||
let tail = term_stack.pop().unwrap();
|
||||
let head = term_stack.pop().unwrap();
|
||||
|
||||
match as_partial_string(head, tail) {
|
||||
Ok((string, Some(tail))) => {
|
||||
term_stack.push(Term::PartialString(Cell::default(), string, tail));
|
||||
}
|
||||
Ok((string, None)) => {
|
||||
let atom = self.atom_tbl.build_with(&string);
|
||||
term_stack.push(Term::CompleteString(Cell::default(), atom));
|
||||
}
|
||||
Err(cons_term) => term_stack.push(cons_term),
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar, h) => {
|
||||
term_stack.push(Term::Var(Cell::default(), VarPtr::from(format!("_{}", h))));
|
||||
}
|
||||
(HeapCellValueTag::Cons | HeapCellValueTag::CStr | HeapCellValueTag::Fixnum |
|
||||
HeapCellValueTag::Char | HeapCellValueTag::F64) => {
|
||||
term_stack.push(Term::Literal(Cell::default(), Literal::try_from(addr).unwrap()));
|
||||
}
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
let h = iter.focus().value() as usize;
|
||||
let mut arity = arity;
|
||||
|
||||
if iter.heap.len() > h + arity + 1 {
|
||||
let value = iter.heap[h + arity + 1];
|
||||
|
||||
if let Some(idx) = get_structure_index(value) {
|
||||
// in the second condition, arity == 0,
|
||||
// meaning idx cannot pertain to this atom
|
||||
// if it is the direct subterm of a larger
|
||||
// structure.
|
||||
if arity > 0 || !iter.direct_subterm_of_str(h) {
|
||||
term_stack.push(
|
||||
Term::Literal(Cell::default(), Literal::CodeIndex(idx))
|
||||
);
|
||||
|
||||
arity += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if arity == 0 {
|
||||
term_stack.push(Term::Literal(Cell::default(), Literal::Atom(name)));
|
||||
} else {
|
||||
let subterms = term_stack
|
||||
.drain(term_stack.len() - arity ..)
|
||||
.collect();
|
||||
|
||||
term_stack.push(Term::Clause(Cell::default(), name, subterms));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::PStr, atom) => {
|
||||
let tail = term_stack.pop().unwrap();
|
||||
|
||||
if let Term::Literal(_, Literal::Atom(atom!("[]"))) = &tail {
|
||||
term_stack.push(Term::CompleteString(Cell::default(), atom));
|
||||
} else {
|
||||
term_stack.push(Term::PartialString(
|
||||
Cell::default(),
|
||||
atom.as_str().to_owned(),
|
||||
Box::new(tail),
|
||||
));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::PStrLoc, h) => {
|
||||
let atom = cell_as_atom_cell!(iter.heap[h]).get_name();
|
||||
let tail = term_stack.pop().unwrap();
|
||||
|
||||
term_stack.push(Term::PartialString(
|
||||
Cell::default(),
|
||||
atom.as_str().to_owned(),
|
||||
Box::new(tail),
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
debug_assert!(term_stack.len() == 1);
|
||||
Ok(term_stack.pop().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
pub(crate) fn use_module(&mut self) -> CallResult {
|
||||
let subevacuable_addr = self
|
||||
@@ -1620,25 +1631,18 @@ impl Machine {
|
||||
usize,
|
||||
) -> Result<(), SessionError>,
|
||||
) -> CallResult {
|
||||
let module_name = cell_as_atom!(
|
||||
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
|
||||
);
|
||||
let module_name = cell_as_atom!(self.deref_register(1));
|
||||
|
||||
let compilation_target = match module_name {
|
||||
atom!("user") => CompilationTarget::User,
|
||||
_ => CompilationTarget::Module(module_name),
|
||||
};
|
||||
|
||||
let predicate_name = cell_as_atom!(
|
||||
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2]))
|
||||
);
|
||||
|
||||
let arity = self
|
||||
.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st.registers[3]));
|
||||
let predicate_name = cell_as_atom!(self.deref_register(2));
|
||||
|
||||
let arity = self.deref_register(3);
|
||||
let arity = match Number::try_from(arity) {
|
||||
Ok(Number::Integer(n)) if &*n >= &0 && &*n <= &MAX_ARITY => Ok(n.to_usize().unwrap()),
|
||||
Ok(Number::Integer(n)) if &*n >= &Integer::from(0) && &*n <= &Integer::from(MAX_ARITY) => Ok(n.to_usize().unwrap()),
|
||||
Ok(Number::Fixnum(n)) if n.get_num() >= 0 && n.get_num() <= MAX_ARITY as i64 => {
|
||||
Ok(usize::try_from(n.get_num()).unwrap())
|
||||
}
|
||||
@@ -1692,6 +1696,21 @@ impl Machine {
|
||||
let add_clause = || {
|
||||
let term = loader.read_term_from_heap(temp_v!(2))?;
|
||||
|
||||
let indexing_arg = match term.name() {
|
||||
Some(atom!(":-")) => term.first_arg().and_then(Term::first_arg),
|
||||
Some(_) => term.first_arg(),
|
||||
None => None,
|
||||
};
|
||||
|
||||
if let Some(indexing_term) = indexing_arg {
|
||||
if let Some(indexing_name) = indexing_term.name() {
|
||||
loader.wam_prelude
|
||||
.indices
|
||||
.goal_expansion_indices
|
||||
.insert((indexing_name, indexing_term.arity()));
|
||||
}
|
||||
}
|
||||
|
||||
loader.incremental_compile_clause(
|
||||
(atom!("goal_expansion"), 2),
|
||||
term,
|
||||
@@ -1962,11 +1981,8 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compile_assert(&mut self, append_or_prepend: AppendOrPrepend) -> CallResult
|
||||
{
|
||||
let module_name = cell_as_atom!(
|
||||
self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
|
||||
);
|
||||
pub(crate) fn compile_assert(&mut self, append_or_prepend: AppendOrPrepend) -> CallResult {
|
||||
let module_name = cell_as_atom!(self.deref_register(1));
|
||||
|
||||
let compilation_target = match module_name {
|
||||
atom!("user") => CompilationTarget::User,
|
||||
@@ -1980,13 +1996,20 @@ impl Machine {
|
||||
}
|
||||
};
|
||||
|
||||
let head = self.deref_register(2);
|
||||
|
||||
if head.is_var() {
|
||||
let err = self.machine_st.instantiation_error();
|
||||
return Err(self.machine_st.error_form(err, stub_gen()));
|
||||
}
|
||||
|
||||
let mut compile_assert = || {
|
||||
let mut loader: Loader<'_, LiveLoadAndMachineState<'_>> =
|
||||
Loader::new(self, LiveTermStream::new(ListingSource::User));
|
||||
|
||||
loader.payload.compilation_target = compilation_target;
|
||||
|
||||
let head = loader.read_term_from_heap(temp_v!(2))?;
|
||||
let head = LiveLoadAndMachineState::machine_st(&mut loader.payload).read_term_from_heap(head)?;
|
||||
|
||||
let name = if let Some(name) = head.name() {
|
||||
name
|
||||
@@ -1995,6 +2018,7 @@ impl Machine {
|
||||
};
|
||||
|
||||
let arity = head.arity();
|
||||
let is_builtin = loader.wam_prelude.indices.builtin_property((name, arity));
|
||||
|
||||
let is_dynamic_predicate = loader
|
||||
.wam_prelude
|
||||
@@ -2005,7 +2029,7 @@ impl Machine {
|
||||
);
|
||||
|
||||
let no_such_predicate =
|
||||
if !is_dynamic_predicate && !ClauseType::is_inbuilt(name, arity) {
|
||||
if !is_dynamic_predicate && !is_builtin {
|
||||
let idx_tag = loader
|
||||
.wam_prelude
|
||||
.indices
|
||||
@@ -2017,8 +2041,9 @@ impl Machine {
|
||||
.map(|code_idx| code_idx.get_tag())
|
||||
.unwrap_or(IndexPtrTag::DynamicUndefined);
|
||||
|
||||
idx_tag == IndexPtrTag::DynamicUndefined ||
|
||||
idx_tag == IndexPtrTag::Undefined
|
||||
idx_tag == IndexPtrTag::DynamicUndefined || idx_tag == IndexPtrTag::Undefined
|
||||
} else if is_builtin {
|
||||
return Err(SessionError::CannotOverwriteBuiltIn((name, arity)));
|
||||
} else {
|
||||
is_dynamic_predicate
|
||||
};
|
||||
@@ -2445,21 +2470,6 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn builtin_property(&mut self) {
|
||||
let (name, arity) = self
|
||||
.machine_st
|
||||
.read_predicate_key(self.machine_st.registers[1], self.machine_st.registers[2]);
|
||||
|
||||
if !ClauseType::is_inbuilt(name, arity) { // ClauseType::from(key.0, key.1, &mut self.machine_st.arena) {
|
||||
if let Some(module) = self.indices.modules.get(&(atom!("builtins"))) {
|
||||
self.machine_st.fail = !module.code_dir.contains_key(&(name, arity));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.machine_st.fail = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Loader<'a, LiveLoadAndMachineState<'a>> {
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::parser::ast::*;
|
||||
|
||||
use crate::ffi::FFIError;
|
||||
use crate::forms::*;
|
||||
use crate::machine::heap::*;
|
||||
use crate::machine::loader::CompilationTarget;
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::machine::streams::*;
|
||||
use crate::machine::system_calls::BrentAlgState;
|
||||
use crate::types::*;
|
||||
|
||||
@@ -157,9 +160,29 @@ impl PermissionError for HeapCellValue {
|
||||
index_atom: Atom,
|
||||
perm: Permission,
|
||||
) -> MachineError {
|
||||
let cell = read_heap_cell!(self,
|
||||
(HeapCellValueTag::Cons, ptr) => {
|
||||
match_untyped_arena_ptr!(ptr,
|
||||
(ArenaHeaderTag::Stream, stream) => {
|
||||
if let Some(alias) = stream.options().get_alias() {
|
||||
atom_as_cell!(alias)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self
|
||||
}
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
self
|
||||
}
|
||||
);
|
||||
|
||||
let stub = functor!(
|
||||
atom!("permission_error"),
|
||||
[atom(perm.as_atom()), atom(index_atom), cell(self)]
|
||||
[atom(perm.as_atom()), atom(index_atom), cell(cell)]
|
||||
);
|
||||
|
||||
MachineError {
|
||||
@@ -419,7 +442,7 @@ impl MachineState {
|
||||
// SessionError::CannotOverwriteImport(pred_atom) => {
|
||||
self.permission_error(
|
||||
Permission::Modify,
|
||||
atom!("private_procedure"),
|
||||
atom!("static_procedure"),
|
||||
functor_stub(key.0, key.1).into_iter().collect::<MachineStub>(),
|
||||
)
|
||||
}
|
||||
@@ -515,6 +538,24 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn ffi_error(&mut self, err: FFIError) -> MachineError {
|
||||
let error_atom = match err {
|
||||
FFIError::ValueCast => atom!("value_cast"),
|
||||
FFIError::ValueDontFit => atom!("value_dont_fit"),
|
||||
FFIError::InvalidFFIType => atom!("invalid_ffi_type"),
|
||||
FFIError::InvalidStructName => atom!("invalid_struct_name"),
|
||||
FFIError::FunctionNotFound => atom!("function_not_found"),
|
||||
FFIError::StructNotFound => atom!("struct_not_found"),
|
||||
};
|
||||
let stub = functor!(atom!("ffi_error"),[atom(error_atom)]);
|
||||
|
||||
MachineError {
|
||||
stub,
|
||||
location: None,
|
||||
from: ErrorProvenance::Constructed,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn error_form(&mut self, err: MachineError, src: FunctorStub) -> MachineStub {
|
||||
let h = self.heap.len();
|
||||
let location = err.location;
|
||||
@@ -661,7 +702,6 @@ impl CompilationError {
|
||||
functor!(atom!("no_such_module"), [atom(module_name)])
|
||||
}
|
||||
&CompilationError::InvalidRuleHead => {
|
||||
|
||||
functor!(atom!("invalid_head_of_rule")) // TODO: type_error(callable, _).
|
||||
}
|
||||
&CompilationError::InvalidUseModuleDecl => {
|
||||
@@ -780,7 +820,7 @@ pub enum CycleSearchResult {
|
||||
NotList(usize, HeapCellValue), // the list length until the second argument in the heap
|
||||
PartialList(usize, Ref), // the list length (up to max), and an offset into the heap.
|
||||
ProperList(usize), // the list length.
|
||||
PStrLocation(usize, usize), // list length (up to max), the heap address of the PStrOffset
|
||||
PStrLocation(usize, usize, usize), // list length (up to max), the heap address of the PStr, the offset
|
||||
UntouchedList(usize, usize), // list length (up to max), the address of an uniterated Addr::Lis(address).
|
||||
UntouchedCStr(Atom, usize),
|
||||
}
|
||||
|
||||
@@ -2,21 +2,20 @@ use crate::parser::ast::*;
|
||||
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::fixtures::*;
|
||||
use crate::forms::*;
|
||||
use crate::machine::ClauseType;
|
||||
use crate::machine::loader::*;
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::machine::streams::Stream;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexMap;
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use modular_bitfield::{BitfieldSpecifier, bitfield};
|
||||
use modular_bitfield::specifiers::*;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeSet;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::types::*;
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
@@ -228,8 +227,32 @@ impl CodeIndex {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type HeapVarDict = IndexMap<Rc<String>, HeapCellValue, FxBuildHasher>;
|
||||
pub(crate) type AllocVarDict = IndexMap<Rc<String>, VarData, FxBuildHasher>;
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum VarKey {
|
||||
AnonVar(usize),
|
||||
VarPtr(VarPtr),
|
||||
}
|
||||
|
||||
impl VarKey {
|
||||
#[inline]
|
||||
pub(crate) fn to_string(&self) -> String {
|
||||
match self {
|
||||
VarKey::AnonVar(h) => format!("_{}", h),
|
||||
VarKey::VarPtr(var) => var.borrow().to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_anon(&self) -> bool {
|
||||
if let VarKey::AnonVar(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type HeapVarDict = IndexMap<VarKey, HeapCellValue, FxBuildHasher>;
|
||||
|
||||
pub(crate) type GlobalVarDir = IndexMap<Atom, (Ball, Option<HeapCellValue>), FxBuildHasher>;
|
||||
|
||||
@@ -245,12 +268,15 @@ pub(crate) type LocalExtensiblePredicates =
|
||||
|
||||
pub(crate) type CodeDir = IndexMap<PredicateKey, CodeIndex, FxBuildHasher>;
|
||||
|
||||
pub(crate) type GoalExpansionIndices = IndexSet<PredicateKey, FxBuildHasher>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IndexStore {
|
||||
pub(super) code_dir: CodeDir,
|
||||
pub(super) extensible_predicates: ExtensiblePredicates,
|
||||
pub(super) local_extensible_predicates: LocalExtensiblePredicates,
|
||||
pub(super) global_variables: GlobalVarDir,
|
||||
pub(super) goal_expansion_indices: GoalExpansionIndices,
|
||||
pub(super) meta_predicates: MetaPredicateDir,
|
||||
pub(super) modules: ModuleDir,
|
||||
pub(super) op_dir: OpDir,
|
||||
@@ -259,6 +285,23 @@ pub struct IndexStore {
|
||||
}
|
||||
|
||||
impl IndexStore {
|
||||
pub(crate) fn builtin_property(&self, key: PredicateKey) -> bool {
|
||||
let (name, arity) = key;
|
||||
|
||||
if !ClauseType::is_inbuilt(name, arity) {
|
||||
self.modules.get(&(atom!("builtins")))
|
||||
.map(|module| module.code_dir.contains_key(&(name, arity)))
|
||||
.unwrap_or(false)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn goal_expansion_defined(&self, key: PredicateKey) -> bool {
|
||||
self.goal_expansion_indices.contains(&key)
|
||||
}
|
||||
|
||||
pub(crate) fn get_predicate_skeleton_mut(
|
||||
&mut self,
|
||||
compilation_target: &CompilationTarget,
|
||||
@@ -371,22 +414,11 @@ impl IndexStore {
|
||||
module: Atom,
|
||||
) -> Option<CodeIndex> {
|
||||
if module == atom!("user") {
|
||||
/*match ClauseType::from(name, arity) {
|
||||
ClauseType::Named(arity, name, _) => */
|
||||
self.code_dir.get(&(name, arity)).cloned()
|
||||
/* _ => None,
|
||||
}*/
|
||||
} else {
|
||||
self.modules
|
||||
.get(&module)
|
||||
.and_then(|module|/* |module| match ClauseType::from(name, arity) {
|
||||
ClauseType::Named(arity, name, _) => { */
|
||||
module.code_dir.get(&(name, arity)).cloned()
|
||||
/*
|
||||
}
|
||||
_ => None,
|
||||
} */
|
||||
)
|
||||
.and_then(|module| module.code_dir.get(&(name, arity)).cloned())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,16 +12,16 @@ use crate::machine::machine_indices::*;
|
||||
use crate::machine::stack::*;
|
||||
use crate::machine::streams::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::read::TermWriteResult;
|
||||
use crate::types::*;
|
||||
|
||||
use crate::parser::rug::Integer;
|
||||
use crate::parser::dashu::Integer;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub(crate) type Registers = [HeapCellValue; MAX_ARITY + 1];
|
||||
|
||||
@@ -50,6 +50,12 @@ pub enum FirstOrNext {
|
||||
Next,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OnEOF {
|
||||
Return,
|
||||
Continue,
|
||||
}
|
||||
|
||||
pub struct MachineState {
|
||||
pub atom_tbl: AtomTable,
|
||||
pub arena: Arena,
|
||||
@@ -74,11 +80,12 @@ pub struct MachineState {
|
||||
pub(super) tr: usize,
|
||||
pub(super) hb: usize,
|
||||
pub(super) block: usize, // an offset into the OR stack.
|
||||
pub(super) scc_block: usize, // an offset into the OR stack for setup_call_cleanup/3.
|
||||
pub(super) ball: Ball,
|
||||
pub(super) ball_stack: Vec<Ball>, // save current ball before jumping via, e.g., verify_attr interrupt.
|
||||
pub(super) lifted_heap: Heap,
|
||||
pub(super) interms: Vec<Number>, // intermediate numbers.
|
||||
// locations of cleaners, cut points, the previous block. for setup_call_cleanup.
|
||||
// locations of cleaners, cut points, the previous scc_block. for setup_call_cleanup/3.
|
||||
pub(super) cont_pts: Vec<(HeapCellValue, usize, usize)>,
|
||||
pub(super) cwil: CWIL,
|
||||
pub(crate) flags: MachineFlags,
|
||||
@@ -113,6 +120,7 @@ impl fmt::Debug for MachineState {
|
||||
.field("tr", &self.tr)
|
||||
.field("hb", &self.hb)
|
||||
.field("block", &self.block)
|
||||
.field("scc_block", &self.scc_block)
|
||||
.field("ball", &self.ball)
|
||||
.field("ball_stack", &self.ball_stack)
|
||||
.field("lifted_heap", &self.lifted_heap)
|
||||
@@ -192,6 +200,27 @@ pub fn pstr_loc_and_offset(heap: &[HeapCellValue], index: usize) -> (usize, Fixn
|
||||
)
|
||||
}
|
||||
|
||||
fn push_var_eq_functors<'a>(
|
||||
heap: &mut Heap,
|
||||
iter: impl Iterator<Item = (&'a VarKey, &'a HeapCellValue)>,
|
||||
atom_tbl: &mut AtomTable,
|
||||
) -> Vec<HeapCellValue> {
|
||||
let mut list_of_var_eqs = vec![];
|
||||
|
||||
for (var, binding) in iter {
|
||||
let var_atom = atom_tbl.build_with(&var.to_string());
|
||||
let h = heap.len();
|
||||
|
||||
heap.push(atom_as_cell!(atom!("="), 2));
|
||||
heap.push(atom_as_cell!(var_atom));
|
||||
heap.push(*binding);
|
||||
|
||||
list_of_var_eqs.push(str_loc_as_cell!(h));
|
||||
}
|
||||
|
||||
list_of_var_eqs
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ball {
|
||||
pub(super) boundary: usize,
|
||||
@@ -481,6 +510,133 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_read_term_options(
|
||||
&mut self,
|
||||
mut var_list: Vec<(VarKey, HeapCellValue, usize)>,
|
||||
singleton_var_list: Vec<HeapCellValue>,
|
||||
) -> CallResult {
|
||||
var_list.sort_by(|(_,_,idx_1),(_,_,idx_2)| idx_1.cmp(idx_2));
|
||||
|
||||
let list_of_var_eqs = push_var_eq_functors(
|
||||
&mut self.heap,
|
||||
var_list.iter().filter_map(|(var_name, var,_)| if var_name.is_anon() { None } else { Some((var_name,var)) }),
|
||||
&mut self.atom_tbl,
|
||||
);
|
||||
|
||||
let singleton_addr = self.registers[3];
|
||||
let singletons_offset = heap_loc_as_cell!(
|
||||
iter_to_heap_list(&mut self.heap, singleton_var_list.into_iter())
|
||||
);
|
||||
|
||||
unify_fn!(*self, singletons_offset, singleton_addr);
|
||||
|
||||
if self.fail {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let vars_addr = self.registers[4];
|
||||
let vars_offset = heap_loc_as_cell!(
|
||||
iter_to_heap_list(&mut self.heap, var_list.into_iter().map(|(_,cell,_)| cell))
|
||||
);
|
||||
|
||||
unify_fn!(*self, vars_offset, vars_addr);
|
||||
|
||||
if self.fail {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let var_names_addr = self.registers[5];
|
||||
let var_names_offset = heap_loc_as_cell!(
|
||||
iter_to_heap_list(&mut self.heap, list_of_var_eqs.into_iter())
|
||||
);
|
||||
|
||||
Ok(unify_fn!(*self, var_names_offset, var_names_addr))
|
||||
}
|
||||
|
||||
pub fn read_term_body(&mut self, mut term_write_result: TermWriteResult) -> CallResult {
|
||||
let heap_loc = read_heap_cell!(self.heap[term_write_result.heap_loc],
|
||||
(HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset) => {
|
||||
pstr_loc_as_cell!(term_write_result.heap_loc)
|
||||
}
|
||||
_ => {
|
||||
heap_loc_as_cell!(term_write_result.heap_loc)
|
||||
}
|
||||
);
|
||||
|
||||
unify_fn!(*self, heap_loc, self.registers[2]);
|
||||
|
||||
if self.fail {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for var in term_write_result.var_dict.values_mut() {
|
||||
*var = heap_bound_deref(&self.heap, *var);
|
||||
}
|
||||
|
||||
let mut singleton_var_set: IndexMap<Ref, bool> = IndexMap::new();
|
||||
|
||||
for cell in stackful_preorder_iter(&mut self.heap, &mut self.stack, heap_loc) {
|
||||
let cell = unmark_cell_bits!(cell);
|
||||
|
||||
if let Some(var) = cell.as_var() {
|
||||
if !singleton_var_set.contains_key(&var) {
|
||||
singleton_var_set.insert(var, true);
|
||||
} else {
|
||||
singleton_var_set.insert(var, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let singleton_var_list = push_var_eq_functors(
|
||||
&mut self.heap,
|
||||
term_write_result.var_dict.iter().filter(|(var_name, binding)| {
|
||||
if var_name.is_anon() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(r) = binding.as_var() {
|
||||
*singleton_var_set.get(&r).unwrap_or(&false)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}),
|
||||
&mut self.atom_tbl,
|
||||
);
|
||||
|
||||
for var in term_write_result.var_dict.values_mut() {
|
||||
*var = heap_bound_deref(&self.heap, *var);
|
||||
}
|
||||
|
||||
let mut var_list = Vec::with_capacity(singleton_var_set.len());
|
||||
|
||||
for (var_name, addr) in term_write_result.var_dict {
|
||||
if let Some(var) = addr.as_var() {
|
||||
if let Some(idx) = singleton_var_set.get_index_of(&var) {
|
||||
var_list.push((var_name, addr, idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.write_read_term_options(var_list, singleton_var_list)
|
||||
}
|
||||
|
||||
pub fn read_term_from_user_input_eof_handler(&mut self, stream: Stream) -> Result<OnEOF, MachineStub> {
|
||||
self.eof_action(
|
||||
self.registers[2],
|
||||
stream,
|
||||
atom!("read_term"),
|
||||
3,
|
||||
)?;
|
||||
|
||||
if stream.options().eof_action() == EOFAction::Reset {
|
||||
if self.fail == false {
|
||||
return Ok(OnEOF::Continue);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(OnEOF::Return)
|
||||
}
|
||||
|
||||
// Safety: the atom_tbl lives for the lifetime of the machine, as does the helper, so the ptr
|
||||
// will always be valid.
|
||||
pub fn read_term_from_user_input(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult {
|
||||
@@ -490,40 +646,54 @@ impl MachineState {
|
||||
unsafe {
|
||||
let readline = ptr.as_ptr().as_mut().unwrap();
|
||||
readline.set_atoms_for_completion(atoms_ptr);
|
||||
let ret = self.read_term(stream, indices);
|
||||
return ret
|
||||
return self.read_term(
|
||||
stream,
|
||||
indices,
|
||||
MachineState::read_term_from_user_input_eof_handler,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Stream::Byte(_) = stream {
|
||||
return self.read_term(stream, indices)
|
||||
return self.read_term(
|
||||
stream,
|
||||
indices,
|
||||
MachineState::read_term_from_user_input_eof_handler
|
||||
)
|
||||
}
|
||||
|
||||
unreachable!("Stream must be a Stream::Readline(_)")
|
||||
}
|
||||
|
||||
pub fn read_term(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult {
|
||||
fn push_var_eq_functors<'a>(
|
||||
heap: &mut Heap,
|
||||
iter: impl Iterator<Item = (&'a Rc<String>, &'a HeapCellValue)>,
|
||||
atom_tbl: &mut AtomTable,
|
||||
) -> Vec<HeapCellValue> {
|
||||
let mut list_of_var_eqs = vec![];
|
||||
pub fn read_term_eof_handler(&mut self, mut stream: Stream) -> Result<OnEOF, MachineStub> {
|
||||
if stream.at_end_of_stream() {
|
||||
unify!(self, self.registers[2], atom_as_cell!(atom!("end_of_file")));
|
||||
stream.set_past_end_of_stream(true);
|
||||
return Ok(OnEOF::Return);
|
||||
} else if stream.past_end_of_stream() {
|
||||
self.eof_action(
|
||||
self.registers[2],
|
||||
stream,
|
||||
atom!("read_term"),
|
||||
3,
|
||||
)?;
|
||||
|
||||
for (var, binding) in iter {
|
||||
let var_atom = atom_tbl.build_with(&var);
|
||||
let h = heap.len();
|
||||
|
||||
heap.push(atom_as_cell!(atom!("="), 2));
|
||||
heap.push(atom_as_cell!(var_atom));
|
||||
heap.push(*binding);
|
||||
|
||||
list_of_var_eqs.push(str_loc_as_cell!(h));
|
||||
if stream.options().eof_action() == EOFAction::Reset {
|
||||
if self.fail == false {
|
||||
return Ok(OnEOF::Continue);
|
||||
}
|
||||
}
|
||||
|
||||
list_of_var_eqs
|
||||
}
|
||||
|
||||
Ok(OnEOF::Return)
|
||||
}
|
||||
|
||||
pub fn read_term(
|
||||
&mut self,
|
||||
stream: Stream,
|
||||
indices: &mut IndexStore,
|
||||
eof_handler: impl Fn(&mut Self, Stream) -> Result<OnEOF, MachineStub>,
|
||||
) -> CallResult {
|
||||
self.check_stream_properties(
|
||||
stream,
|
||||
StreamType::Text,
|
||||
@@ -542,116 +712,16 @@ impl MachineState {
|
||||
|
||||
loop {
|
||||
match self.read(stream, &indices.op_dir) {
|
||||
Ok(mut term_write_result) => {
|
||||
let heap_loc = read_heap_cell!(self.heap[term_write_result.heap_loc],
|
||||
(HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset) => {
|
||||
pstr_loc_as_cell!(term_write_result.heap_loc)
|
||||
}
|
||||
_ => {
|
||||
heap_loc_as_cell!(term_write_result.heap_loc)
|
||||
}
|
||||
);
|
||||
|
||||
let term = self.registers[2];
|
||||
unify_fn!(*self, heap_loc, term);
|
||||
let term = heap_loc;
|
||||
|
||||
if self.fail {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut singleton_var_set: IndexMap<Ref, bool> = IndexMap::new();
|
||||
|
||||
for addr in stackful_preorder_iter(&mut self.heap, term) {
|
||||
let addr = unmark_cell_bits!(addr);
|
||||
|
||||
if let Some(var) = addr.as_var() {
|
||||
if !singleton_var_set.contains_key(&var) {
|
||||
singleton_var_set.insert(var, true);
|
||||
} else {
|
||||
singleton_var_set.insert(var, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for var in term_write_result.var_dict.values_mut() {
|
||||
*var = heap_bound_deref(&self.heap, *var);
|
||||
}
|
||||
|
||||
let singleton_var_list = push_var_eq_functors(
|
||||
&mut self.heap,
|
||||
term_write_result.var_dict.iter().filter(|(_, binding)| {
|
||||
if let Some(r) = binding.as_var() {
|
||||
*singleton_var_set.get(&r).unwrap_or(&false)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}),
|
||||
&mut self.atom_tbl,
|
||||
);
|
||||
|
||||
let mut var_list = Vec::with_capacity(singleton_var_set.len());
|
||||
|
||||
for (var_name, addr) in term_write_result.var_dict {
|
||||
if let Some(var) = addr.as_var() {
|
||||
let idx = singleton_var_set.get_index_of(&var).unwrap();
|
||||
var_list.push((var_name, addr, idx));
|
||||
}
|
||||
}
|
||||
|
||||
var_list.sort_by(|(_,_,idx_1),(_,_,idx_2)| idx_1.cmp(idx_2));
|
||||
|
||||
let list_of_var_eqs = push_var_eq_functors(
|
||||
&mut self.heap,
|
||||
var_list.iter().map(|(var_name, var,_)| (var_name,var)),
|
||||
&mut self.atom_tbl,
|
||||
);
|
||||
|
||||
let singleton_addr = self.registers[3];
|
||||
let singletons_offset = heap_loc_as_cell!(
|
||||
iter_to_heap_list(&mut self.heap, singleton_var_list.into_iter())
|
||||
);
|
||||
|
||||
unify_fn!(*self, singletons_offset, singleton_addr);
|
||||
|
||||
if self.fail {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let vars_addr = self.registers[4];
|
||||
let vars_offset = heap_loc_as_cell!(
|
||||
iter_to_heap_list(&mut self.heap, var_list.into_iter().map(|(_,cell,_)| cell))
|
||||
);
|
||||
|
||||
unify_fn!(*self, vars_offset, vars_addr);
|
||||
|
||||
if self.fail {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let var_names_addr = self.registers[5];
|
||||
let var_names_offset = heap_loc_as_cell!(
|
||||
iter_to_heap_list(&mut self.heap, list_of_var_eqs.into_iter())
|
||||
);
|
||||
|
||||
return Ok(unify_fn!(*self, var_names_offset, var_names_addr));
|
||||
}
|
||||
Ok(term_write_result) => return self.read_term_body(term_write_result),
|
||||
Err(err) => {
|
||||
if let CompilationError::ParserError(ParserError::UnexpectedEOF) = err {
|
||||
self.eof_action(
|
||||
self.registers[2],
|
||||
stream,
|
||||
atom!("read_term"),
|
||||
3,
|
||||
)?;
|
||||
|
||||
if stream.options().eof_action() == EOFAction::Reset {
|
||||
if self.fail == false {
|
||||
continue;
|
||||
match &err {
|
||||
CompilationError::ParserError(e) if e.is_unexpected_eof() => {
|
||||
match eof_handler(self, stream)? {
|
||||
OnEOF::Return => return self.write_read_term_options(vec![], vec![]),
|
||||
OnEOF::Continue => continue,
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let stub = functor_stub(atom!("read_term"), 3);
|
||||
@@ -671,13 +741,14 @@ impl MachineState {
|
||||
let numbervars = self.store(self.deref(self.registers[4]));
|
||||
let quoted = self.store(self.deref(self.registers[5]));
|
||||
let max_depth = self.store(self.deref(self.registers[7]));
|
||||
let double_quotes = self.store(self.deref(self.registers[8]));
|
||||
|
||||
let term_to_be_printed = self.store(self.deref(self.registers[2]));
|
||||
let stub_gen = || functor_stub(atom!("write_term"), 2);
|
||||
|
||||
let printer = match self.try_from_list(self.registers[6], stub_gen) {
|
||||
Ok(addrs) => {
|
||||
let mut var_names: IndexMap<HeapCellValue, Rc<String>> = IndexMap::new();
|
||||
let mut var_names: IndexMap<HeapCellValue, VarPtr> = IndexMap::new();
|
||||
|
||||
for addr in addrs {
|
||||
read_heap_cell!(addr,
|
||||
@@ -695,18 +766,18 @@ impl MachineState {
|
||||
|
||||
read_heap_cell!(atom,
|
||||
(HeapCellValueTag::Char, c) => {
|
||||
var_names.insert(var, Rc::new(c.to_string()));
|
||||
var_names.insert(var, VarPtr::from(c.to_string()));
|
||||
}
|
||||
(HeapCellValueTag::Atom, (name, _arity)) => {
|
||||
debug_assert_eq!(_arity, 0);
|
||||
var_names.insert(var, Rc::new(name.as_str().to_owned()));
|
||||
var_names.insert(var, VarPtr::from(name.as_str()));
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
debug_assert_eq!(arity, 0);
|
||||
var_names.insert(var, Rc::new(name.as_str().to_owned()));
|
||||
var_names.insert(var, VarPtr::from(name.as_str()));
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
@@ -752,7 +823,25 @@ impl MachineState {
|
||||
);
|
||||
|
||||
let quoted = read_heap_cell!(quoted,
|
||||
(HeapCellValueTag::Atom, (name, _arity)) => {
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
debug_assert_eq!(arity, 0);
|
||||
name == atom!("true")
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
debug_assert_eq!(arity, 0);
|
||||
name == atom!("true")
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
);
|
||||
|
||||
let double_quotes = read_heap_cell!(double_quotes,
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
debug_assert_eq!(arity, 0);
|
||||
name == atom!("true")
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
@@ -769,6 +858,8 @@ impl MachineState {
|
||||
|
||||
let mut printer = HCPrinter::new(
|
||||
&mut self.heap,
|
||||
&mut self.atom_tbl,
|
||||
&mut self.stack,
|
||||
op_dir,
|
||||
PrinterOutputter::new(),
|
||||
term_to_be_printed,
|
||||
@@ -777,6 +868,7 @@ impl MachineState {
|
||||
printer.ignore_ops = ignore_ops;
|
||||
printer.numbervars = numbervars;
|
||||
printer.quoted = quoted;
|
||||
printer.double_quotes = double_quotes;
|
||||
|
||||
match Number::try_from(max_depth) {
|
||||
Ok(Number::Fixnum(n)) => {
|
||||
@@ -824,7 +916,7 @@ impl MachineState {
|
||||
let b = self.b;
|
||||
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::Fixnum, b0) => {
|
||||
(HeapCellValueTag::CutPoint, b0) => {
|
||||
let b0 = b0.get_num() as usize;
|
||||
|
||||
if b > b0 {
|
||||
@@ -836,63 +928,6 @@ impl MachineState {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn try_me_else(&mut self, offset: usize) {
|
||||
let n = self.num_of_args;
|
||||
let b = self.stack.allocate_or_frame(n);
|
||||
let or_frame = self.stack.index_or_frame_mut(b);
|
||||
|
||||
or_frame.prelude.univ_prelude.num_cells = n;
|
||||
or_frame.prelude.e = self.e;
|
||||
or_frame.prelude.cp = self.cp;
|
||||
or_frame.prelude.b = self.b;
|
||||
or_frame.prelude.bp = self.p + offset;
|
||||
or_frame.prelude.boip = 0;
|
||||
or_frame.prelude.biip = 0;
|
||||
or_frame.prelude.tr = self.tr;
|
||||
or_frame.prelude.h = self.heap.len();
|
||||
or_frame.prelude.b0 = self.b0;
|
||||
|
||||
self.b = b;
|
||||
|
||||
for i in 0..n {
|
||||
or_frame[i] = self.registers[i+1];
|
||||
}
|
||||
|
||||
self.hb = self.heap.len();
|
||||
self.p += 1;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn indexed_try(&mut self, offset: usize) {
|
||||
let n = self.num_of_args;
|
||||
let b = self.stack.allocate_or_frame(n);
|
||||
let or_frame = self.stack.index_or_frame_mut(b);
|
||||
|
||||
or_frame.prelude.univ_prelude.num_cells = n;
|
||||
or_frame.prelude.e = self.e;
|
||||
or_frame.prelude.cp = self.cp;
|
||||
or_frame.prelude.b = self.b;
|
||||
or_frame.prelude.bp = self.p; // + 1; in self.iip now!
|
||||
or_frame.prelude.boip = self.oip;
|
||||
or_frame.prelude.biip = self.iip + 1;
|
||||
or_frame.prelude.tr = self.tr;
|
||||
or_frame.prelude.h = self.heap.len();
|
||||
or_frame.prelude.b0 = self.b0;
|
||||
|
||||
self.b = b;
|
||||
|
||||
for i in 0..n {
|
||||
or_frame[i] = self.registers[i+1];
|
||||
}
|
||||
|
||||
self.hb = self.heap.len();
|
||||
self.p = self.p + offset;
|
||||
|
||||
self.oip = 0;
|
||||
self.iip = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -61,6 +61,8 @@ impl MockWAM {
|
||||
|
||||
let mut printer = HCPrinter::new(
|
||||
&mut self.machine_st.heap,
|
||||
&mut self.machine_st.atom_tbl,
|
||||
&mut self.machine_st.stack,
|
||||
&self.op_dir,
|
||||
PrinterOutputter::new(),
|
||||
heap_loc_as_cell!(term_write_result.heap_loc),
|
||||
@@ -69,7 +71,12 @@ impl MockWAM {
|
||||
printer.var_names = term_write_result
|
||||
.var_dict
|
||||
.into_iter()
|
||||
.map(|(var, cell)| (cell, var))
|
||||
.map(|(var, cell)| {
|
||||
match var {
|
||||
VarKey::VarPtr(var) => (cell, var.clone()),
|
||||
VarKey::AnonVar(_) => (cell, VarPtr::from(var.to_string()))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(printer.print().result())
|
||||
|
||||
@@ -19,16 +19,19 @@ pub mod machine_state_impl;
|
||||
pub mod mock_wam;
|
||||
pub mod parsed_results;
|
||||
pub mod partial_string;
|
||||
pub mod disjuncts;
|
||||
pub mod preprocessor;
|
||||
pub mod stack;
|
||||
pub mod streams;
|
||||
pub mod system_calls;
|
||||
pub mod term_stream;
|
||||
pub mod unify;
|
||||
|
||||
use crate::arena::*;
|
||||
use crate::arithmetic::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::forms::*;
|
||||
use crate::ffi::ForeignFunctionTable;
|
||||
use crate::instructions::*;
|
||||
use crate::machine::args::*;
|
||||
use crate::machine::compile::*;
|
||||
@@ -41,7 +44,7 @@ use crate::machine::machine_state::*;
|
||||
use crate::machine::stack::*;
|
||||
use crate::machine::streams::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::rug::{Integer, Rational};
|
||||
use crate::parser::dashu::{Integer, Rational};
|
||||
use crate::types::*;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
@@ -70,6 +73,7 @@ pub struct Machine {
|
||||
pub(super) user_output: Stream,
|
||||
pub(super) user_error: Stream,
|
||||
pub(super) load_contexts: Vec<LoadContext>,
|
||||
pub(super) foreign_function_table: ForeignFunctionTable,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -205,7 +209,7 @@ impl Machine {
|
||||
self.machine_st.throw_exception(err);
|
||||
}
|
||||
|
||||
fn run_module_predicate(&mut self, module_name: Atom, key: PredicateKey) {
|
||||
fn run_module_predicate(&mut self, module_name: Atom, key: PredicateKey) -> std::process::ExitCode {
|
||||
if let Some(module) = self.indices.modules.get(&module_name) {
|
||||
if let Some(ref code_index) = module.code_dir.get(&key) {
|
||||
let p = code_index.local().unwrap();
|
||||
@@ -255,31 +259,22 @@ impl Machine {
|
||||
let mut path_buf = current_dir();
|
||||
path_buf.push("machine/attributed_variables.pl");
|
||||
|
||||
bootstrapping_compile(
|
||||
Stream::from_static_string(
|
||||
include_str!("attributed_variables.pl"),
|
||||
&mut self.machine_st.arena,
|
||||
),
|
||||
self,
|
||||
ListingSource::from_file_and_path(
|
||||
atom!("attributed_variables"),
|
||||
path_buf,
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
let stream = Stream::from_static_string(
|
||||
include_str!("attributed_variables.pl"),
|
||||
&mut self.machine_st.arena,
|
||||
);
|
||||
|
||||
self.load_file(path_buf.to_str().unwrap(), stream);
|
||||
|
||||
let mut path_buf = current_dir();
|
||||
path_buf.push("machine/project_attributes.pl");
|
||||
|
||||
bootstrapping_compile(
|
||||
Stream::from_static_string(
|
||||
include_str!("project_attributes.pl"),
|
||||
&mut self.machine_st.arena,
|
||||
),
|
||||
self,
|
||||
ListingSource::from_file_and_path(atom!("project_attributes"), path_buf),
|
||||
)
|
||||
.unwrap();
|
||||
let stream = Stream::from_static_string(
|
||||
include_str!("project_attributes.pl"),
|
||||
&mut self.machine_st.arena,
|
||||
);
|
||||
|
||||
self.load_file(path_buf.to_str().unwrap(), stream);
|
||||
|
||||
if let Some(module) = self.indices.modules.get(&atom!("$atts")) {
|
||||
if let Some(code_index) = module.code_dir.get(&(atom!("driver"), 2)) {
|
||||
@@ -288,7 +283,7 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_top_level(&mut self, module_name: Atom, key: PredicateKey) {
|
||||
pub fn run_top_level(&mut self, module_name: Atom, key: PredicateKey) -> std::process::ExitCode {
|
||||
let mut arg_pstrs = vec![];
|
||||
|
||||
for arg in env::args() {
|
||||
@@ -304,7 +299,7 @@ impl Machine {
|
||||
arg_pstrs.into_iter()
|
||||
));
|
||||
|
||||
self.run_module_predicate(module_name, key);
|
||||
self.run_module_predicate(module_name, key)
|
||||
}
|
||||
|
||||
pub fn set_user_input(&mut self, input: String) {
|
||||
@@ -380,46 +375,45 @@ impl Machine {
|
||||
Instruction::BreakFromDispatchLoop,
|
||||
Instruction::InstallVerifyAttr,
|
||||
Instruction::VerifyAttrInterrupt,
|
||||
Instruction::ExecuteTermGreaterThan(0),
|
||||
Instruction::ExecuteTermLessThan(0),
|
||||
Instruction::ExecuteTermGreaterThanOrEqual(0),
|
||||
Instruction::ExecuteTermLessThanOrEqual(0),
|
||||
Instruction::ExecuteTermEqual(0),
|
||||
Instruction::ExecuteTermNotEqual(0),
|
||||
Instruction::ExecuteNumberGreaterThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2)), 0),
|
||||
Instruction::ExecuteNumberLessThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2)), 0),
|
||||
Instruction::ExecuteNumberGreaterThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2)), 0),
|
||||
Instruction::ExecuteNumberLessThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2)), 0),
|
||||
Instruction::ExecuteNumberEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2)), 0),
|
||||
Instruction::ExecuteNumberNotEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2)), 0),
|
||||
Instruction::ExecuteIs(temp_v!(1), ar_reg!(temp_v!(2)), 0),
|
||||
Instruction::ExecuteAcyclicTerm(0),
|
||||
Instruction::ExecuteArg(0),
|
||||
Instruction::ExecuteCompare(0),
|
||||
Instruction::ExecuteCopyTerm(0),
|
||||
Instruction::ExecuteFunctor(0),
|
||||
Instruction::ExecuteGround(0),
|
||||
Instruction::ExecuteKeySort(0),
|
||||
Instruction::ExecuteRead(0),
|
||||
Instruction::ExecuteSort(0),
|
||||
Instruction::ExecuteN(1, 0),
|
||||
Instruction::ExecuteN(2, 0),
|
||||
Instruction::ExecuteN(3, 0),
|
||||
Instruction::ExecuteN(4, 0),
|
||||
Instruction::ExecuteN(5, 0),
|
||||
Instruction::ExecuteN(6, 0),
|
||||
Instruction::ExecuteN(7, 0),
|
||||
Instruction::ExecuteN(8, 0),
|
||||
Instruction::ExecuteN(9, 0),
|
||||
Instruction::ExecuteIsAtom(temp_v!(1), 0),
|
||||
Instruction::ExecuteIsAtomic(temp_v!(1), 0),
|
||||
Instruction::ExecuteIsCompound(temp_v!(1), 0),
|
||||
Instruction::ExecuteIsInteger(temp_v!(1), 0),
|
||||
Instruction::ExecuteIsNumber(temp_v!(1), 0),
|
||||
Instruction::ExecuteIsRational(temp_v!(1), 0),
|
||||
Instruction::ExecuteIsFloat(temp_v!(1), 0),
|
||||
Instruction::ExecuteIsNonVar(temp_v!(1), 0),
|
||||
Instruction::ExecuteIsVar(temp_v!(1), 0)
|
||||
Instruction::ExecuteTermGreaterThan,
|
||||
Instruction::ExecuteTermLessThan,
|
||||
Instruction::ExecuteTermGreaterThanOrEqual,
|
||||
Instruction::ExecuteTermLessThanOrEqual,
|
||||
Instruction::ExecuteTermEqual,
|
||||
Instruction::ExecuteTermNotEqual,
|
||||
Instruction::ExecuteNumberGreaterThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteNumberLessThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteNumberGreaterThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteNumberLessThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteNumberEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteNumberNotEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteIs(temp_v!(1), ar_reg!(temp_v!(2))),
|
||||
Instruction::ExecuteAcyclicTerm,
|
||||
Instruction::ExecuteArg,
|
||||
Instruction::ExecuteCompare,
|
||||
Instruction::ExecuteCopyTerm,
|
||||
Instruction::ExecuteFunctor,
|
||||
Instruction::ExecuteGround,
|
||||
Instruction::ExecuteKeySort,
|
||||
Instruction::ExecuteSort,
|
||||
Instruction::ExecuteN(1),
|
||||
Instruction::ExecuteN(2),
|
||||
Instruction::ExecuteN(3),
|
||||
Instruction::ExecuteN(4),
|
||||
Instruction::ExecuteN(5),
|
||||
Instruction::ExecuteN(6),
|
||||
Instruction::ExecuteN(7),
|
||||
Instruction::ExecuteN(8),
|
||||
Instruction::ExecuteN(9),
|
||||
Instruction::ExecuteIsAtom(temp_v!(1)),
|
||||
Instruction::ExecuteIsAtomic(temp_v!(1)),
|
||||
Instruction::ExecuteIsCompound(temp_v!(1)),
|
||||
Instruction::ExecuteIsInteger(temp_v!(1)),
|
||||
Instruction::ExecuteIsNumber(temp_v!(1)),
|
||||
Instruction::ExecuteIsRational(temp_v!(1)),
|
||||
Instruction::ExecuteIsFloat(temp_v!(1)),
|
||||
Instruction::ExecuteIsNonVar(temp_v!(1)),
|
||||
Instruction::ExecuteIsVar(temp_v!(1))
|
||||
].into_iter());
|
||||
|
||||
for (p, instr) in self.code[impls_offset ..].iter().enumerate() {
|
||||
@@ -458,6 +452,7 @@ impl Machine {
|
||||
user_output,
|
||||
user_error,
|
||||
load_contexts: vec![],
|
||||
foreign_function_table: Default::default(),
|
||||
};
|
||||
|
||||
let mut lib_path = current_dir();
|
||||
@@ -566,103 +561,436 @@ impl Machine {
|
||||
self.machine_st.verify_attr_interrupt(p, arity);
|
||||
}
|
||||
|
||||
fn next_clause_applicable(&mut self, mut offset: usize) -> bool {
|
||||
loop {
|
||||
match &self.code[offset] {
|
||||
Instruction::IndexingCode(indexing_lines) => {
|
||||
let mut oip = 0;
|
||||
let mut cell = empty_list_as_cell!();
|
||||
|
||||
loop {
|
||||
let indexing_code_ptr = match &indexing_lines[oip] {
|
||||
&IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(arg, v, c, l, s)) => {
|
||||
cell = self.deref_register(arg);
|
||||
self.machine_st.select_switch_on_term_index(cell, v, c, l, s)
|
||||
}
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(hm)) => {
|
||||
let lit = self.machine_st.constant_to_literal(cell);
|
||||
hm.get(&lit).cloned().unwrap_or(IndexingCodePtr::Fail)
|
||||
}
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(hm)) => {
|
||||
self.machine_st.select_switch_on_structure_index(cell, hm)
|
||||
}
|
||||
_ => {
|
||||
offset += 1;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
match indexing_code_ptr {
|
||||
IndexingCodePtr::External(_) | IndexingCodePtr::DynamicExternal(_) => {
|
||||
offset += 1;
|
||||
break;
|
||||
}
|
||||
IndexingCodePtr::Internal(i) => oip += i,
|
||||
IndexingCodePtr::Fail => return false,
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::GetConstant(Level::Shallow, lit, RegType::Temp(t)) => {
|
||||
let cell = self.deref_register(t);
|
||||
|
||||
if cell.is_var() {
|
||||
offset += 1;
|
||||
} else if lit.get_tag() == HeapCellValueTag::CStr {
|
||||
read_heap_cell!(cell,
|
||||
(HeapCellValueTag::CStr) => {
|
||||
if cell == lit {
|
||||
offset += 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => {
|
||||
offset += 1;
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
if name == atom!(".") && arity == 2 {
|
||||
offset += 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
self.machine_st.write_literal_to_var(cell, lit);
|
||||
|
||||
if self.machine_st.fail {
|
||||
self.machine_st.fail = false;
|
||||
return false;
|
||||
} else {
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
&Instruction::GetList(Level::Shallow, RegType::Temp(t)) => {
|
||||
let cell = self.deref_register(t);
|
||||
|
||||
read_heap_cell!(cell,
|
||||
(HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc | HeapCellValueTag::CStr) => {
|
||||
offset += 1;
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity();
|
||||
|
||||
if name == atom!(".") && arity == 2 {
|
||||
offset += 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
|
||||
offset += 1;
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
&Instruction::GetStructure(Level::Shallow, name, arity, RegType::Temp(t)) => {
|
||||
let cell = self.deref_register(t);
|
||||
|
||||
read_heap_cell!(cell,
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
if (name, arity) == cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity() {
|
||||
offset += 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
|
||||
offset += 1;
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
&Instruction::GetPartialString(Level::Shallow, string, RegType::Temp(t), has_tail) => {
|
||||
let cell = self.deref_register(t);
|
||||
|
||||
read_heap_cell!(cell,
|
||||
(HeapCellValueTag::CStr, cstr) => {
|
||||
if !has_tail && string != cstr {
|
||||
return false;
|
||||
}
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
(HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => {
|
||||
offset += 1;
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity();
|
||||
|
||||
if name == atom!(".") && arity == 2 {
|
||||
offset += 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
|
||||
offset += 1;
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
Instruction::GetConstant(..) |
|
||||
Instruction::GetList(..) |
|
||||
Instruction::GetStructure(..) |
|
||||
Instruction::GetPartialString(..) |
|
||||
&Instruction::UnifyVoid(..) |
|
||||
&Instruction::UnifyConstant(..) |
|
||||
&Instruction::GetVariable(..) |
|
||||
&Instruction::GetValue(..) |
|
||||
&Instruction::UnifyVariable(..) |
|
||||
&Instruction::UnifyValue(..) |
|
||||
&Instruction::UnifyLocalValue(..) => {
|
||||
offset += 1;
|
||||
}
|
||||
_ => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn next_applicable_clause(&mut self, mut offset: usize) -> Option<usize> {
|
||||
while !self.next_clause_applicable(self.machine_st.p + offset + 1) {
|
||||
match &self.code[self.machine_st.p + offset] {
|
||||
&Instruction::DefaultRetryMeElse(o) | &Instruction::RetryMeElse(o) |
|
||||
&Instruction::DynamicElse(.., NextOrFail::Next(o)) |
|
||||
&Instruction::DynamicInternalElse(.., NextOrFail::Next(o)) => offset += o,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(offset)
|
||||
}
|
||||
|
||||
fn next_inner_applicable_clause(&mut self) -> Option<u32> {
|
||||
let mut inner_offset = 1u32;
|
||||
|
||||
loop {
|
||||
match &self.code[self.machine_st.p] {
|
||||
Instruction::IndexingCode(indexing_lines) => {
|
||||
match &indexing_lines[self.machine_st.oip as usize] {
|
||||
IndexingLine::IndexedChoice(indexed_choice) => {
|
||||
match &indexed_choice[(self.machine_st.iip + inner_offset) as usize] {
|
||||
&IndexedChoiceInstruction::Retry(o) => {
|
||||
if self.next_clause_applicable(self.machine_st.p + o) {
|
||||
return Some(inner_offset);
|
||||
}
|
||||
|
||||
inner_offset += 1;
|
||||
}
|
||||
&IndexedChoiceInstruction::Trust(o) => {
|
||||
return if self.next_clause_applicable(self.machine_st.p + o) {
|
||||
Some(inner_offset)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
IndexingLine::DynamicIndexedChoice(indexed_choice) => {
|
||||
let idx = (self.machine_st.iip + inner_offset) as usize;
|
||||
let o = indexed_choice[idx];
|
||||
|
||||
if idx + 1 == indexed_choice.len() {
|
||||
return if self.next_clause_applicable(self.machine_st.p + o) {
|
||||
Some(inner_offset)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
} else {
|
||||
if self.next_clause_applicable(self.machine_st.p + o) {
|
||||
return Some(inner_offset);
|
||||
}
|
||||
|
||||
inner_offset += 1;
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn try_me_else(&mut self, offset: usize) {
|
||||
if let Some(offset) = self.next_applicable_clause(offset) {
|
||||
let n = self.machine_st.num_of_args;
|
||||
let b = self.machine_st.stack.allocate_or_frame(n);
|
||||
let or_frame = self.machine_st.stack.index_or_frame_mut(b);
|
||||
|
||||
or_frame.prelude.num_cells = n;
|
||||
or_frame.prelude.e = self.machine_st.e;
|
||||
or_frame.prelude.cp = self.machine_st.cp;
|
||||
or_frame.prelude.b = self.machine_st.b;
|
||||
or_frame.prelude.bp = self.machine_st.p + offset;
|
||||
or_frame.prelude.boip = 0;
|
||||
or_frame.prelude.biip = 0;
|
||||
or_frame.prelude.tr = self.machine_st.tr;
|
||||
or_frame.prelude.h = self.machine_st.heap.len();
|
||||
or_frame.prelude.b0 = self.machine_st.b0;
|
||||
or_frame.prelude.attr_var_queue_len = self.machine_st.attr_var_init.attr_var_queue.len();
|
||||
|
||||
self.machine_st.b = b;
|
||||
|
||||
for i in 0..n {
|
||||
or_frame[i] = self.machine_st.registers[i+1];
|
||||
}
|
||||
|
||||
self.machine_st.hb = self.machine_st.heap.len();
|
||||
}
|
||||
|
||||
self.machine_st.p += 1;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn indexed_try(&mut self, offset: usize) {
|
||||
if let Some(iip_offset) = self.next_inner_applicable_clause() {
|
||||
let n = self.machine_st.num_of_args;
|
||||
let b = self.machine_st.stack.allocate_or_frame(n);
|
||||
let or_frame = self.machine_st.stack.index_or_frame_mut(b);
|
||||
|
||||
or_frame.prelude.num_cells = n;
|
||||
or_frame.prelude.e = self.machine_st.e;
|
||||
or_frame.prelude.cp = self.machine_st.cp;
|
||||
or_frame.prelude.b = self.machine_st.b;
|
||||
or_frame.prelude.bp = self.machine_st.p;
|
||||
or_frame.prelude.boip = self.machine_st.oip;
|
||||
or_frame.prelude.biip = self.machine_st.iip + iip_offset; // 1
|
||||
or_frame.prelude.tr = self.machine_st.tr;
|
||||
or_frame.prelude.h = self.machine_st.heap.len();
|
||||
or_frame.prelude.b0 = self.machine_st.b0;
|
||||
or_frame.prelude.attr_var_queue_len = self.machine_st.attr_var_init.attr_var_queue.len();
|
||||
|
||||
self.machine_st.b = b;
|
||||
|
||||
for i in 0..n {
|
||||
or_frame[i] = self.machine_st.registers[i+1];
|
||||
}
|
||||
|
||||
self.machine_st.hb = self.machine_st.heap.len();
|
||||
|
||||
self.machine_st.oip = 0;
|
||||
self.machine_st.iip = 0;
|
||||
}
|
||||
|
||||
self.machine_st.p += offset;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn retry_me_else(&mut self, offset: usize) {
|
||||
let b = self.machine_st.b;
|
||||
let or_frame = self.machine_st.stack.index_or_frame_mut(b);
|
||||
let n = or_frame.prelude.univ_prelude.num_cells;
|
||||
let n = or_frame.prelude.num_cells;
|
||||
|
||||
let old_tr = or_frame.prelude.tr;
|
||||
let curr_tr = self.machine_st.tr;
|
||||
|
||||
for i in 0..n {
|
||||
self.machine_st.registers[i + 1] = or_frame[i];
|
||||
}
|
||||
|
||||
self.machine_st.num_of_args = n;
|
||||
self.machine_st.e = or_frame.prelude.e;
|
||||
self.machine_st.cp = or_frame.prelude.cp;
|
||||
|
||||
or_frame.prelude.bp = self.machine_st.p + offset;
|
||||
|
||||
let old_tr = or_frame.prelude.tr;
|
||||
let curr_tr = self.machine_st.tr;
|
||||
let target_h = or_frame.prelude.h;
|
||||
|
||||
self.machine_st.tr = or_frame.prelude.tr;
|
||||
|
||||
self.reset_attr_var_state();
|
||||
self.machine_st.hb = target_h;
|
||||
|
||||
self.unwind_trail(old_tr, curr_tr);
|
||||
|
||||
self.machine_st.trail.truncate(self.machine_st.tr);
|
||||
self.machine_st.heap.truncate(target_h);
|
||||
if let Some(offset) = self.next_applicable_clause(offset) {
|
||||
let or_frame = self.machine_st.stack.index_or_frame_mut(b);
|
||||
|
||||
self.machine_st.p += 1;
|
||||
self.machine_st.num_of_args = n;
|
||||
self.machine_st.e = or_frame.prelude.e;
|
||||
self.machine_st.cp = or_frame.prelude.cp;
|
||||
|
||||
or_frame.prelude.bp = self.machine_st.p + offset;
|
||||
|
||||
let target_h = or_frame.prelude.h;
|
||||
let attr_var_queue_len = or_frame.prelude.attr_var_queue_len;
|
||||
|
||||
self.machine_st.tr = or_frame.prelude.tr;
|
||||
self.reset_attr_var_state(attr_var_queue_len);
|
||||
|
||||
self.machine_st.hb = target_h;
|
||||
|
||||
self.machine_st.trail.truncate(self.machine_st.tr);
|
||||
self.machine_st.heap.truncate(target_h);
|
||||
|
||||
self.machine_st.p += 1;
|
||||
} else {
|
||||
self.trust_me_epilogue();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn retry(&mut self, offset: usize) {
|
||||
let b = self.machine_st.b;
|
||||
let or_frame = self.machine_st.stack.index_or_frame_mut(b);
|
||||
let n = or_frame.prelude.univ_prelude.num_cells;
|
||||
let n = or_frame.prelude.num_cells;
|
||||
|
||||
let old_tr = or_frame.prelude.tr;
|
||||
let curr_tr = self.machine_st.tr;
|
||||
|
||||
for i in 0..n {
|
||||
self.machine_st.registers[i+1] = or_frame[i];
|
||||
}
|
||||
|
||||
self.machine_st.num_of_args = n;
|
||||
self.machine_st.e = or_frame.prelude.e;
|
||||
self.machine_st.cp = or_frame.prelude.cp;
|
||||
|
||||
or_frame.prelude.biip += 1;
|
||||
|
||||
let old_tr = or_frame.prelude.tr;
|
||||
let curr_tr = self.machine_st.tr;
|
||||
let target_h = or_frame.prelude.h;
|
||||
|
||||
self.machine_st.tr = or_frame.prelude.tr;
|
||||
self.reset_attr_var_state();
|
||||
|
||||
self.machine_st.hb = target_h;
|
||||
self.machine_st.p = self.machine_st.p + offset;
|
||||
|
||||
self.unwind_trail(old_tr, curr_tr);
|
||||
|
||||
self.machine_st.trail.truncate(self.machine_st.tr);
|
||||
self.machine_st.heap.truncate(target_h);
|
||||
if let Some(iip_offset) = self.next_inner_applicable_clause() {
|
||||
let or_frame = self.machine_st.stack.index_or_frame_mut(b);
|
||||
|
||||
self.machine_st.oip = 0;
|
||||
self.machine_st.iip = 0;
|
||||
self.machine_st.num_of_args = n;
|
||||
self.machine_st.e = or_frame.prelude.e;
|
||||
self.machine_st.cp = or_frame.prelude.cp;
|
||||
|
||||
or_frame.prelude.biip += iip_offset;
|
||||
|
||||
let target_h = or_frame.prelude.h;
|
||||
let attr_var_queue_len = or_frame.prelude.attr_var_queue_len;
|
||||
|
||||
self.machine_st.tr = or_frame.prelude.tr;
|
||||
self.machine_st.trail.truncate(self.machine_st.tr);
|
||||
|
||||
self.reset_attr_var_state(attr_var_queue_len);
|
||||
|
||||
self.machine_st.hb = target_h;
|
||||
self.machine_st.p += offset;
|
||||
|
||||
self.machine_st.heap.truncate(target_h);
|
||||
|
||||
self.machine_st.oip = 0;
|
||||
self.machine_st.iip = 0;
|
||||
} else {
|
||||
self.trust_epilogue(offset);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn trust(&mut self, offset: usize) {
|
||||
let b = self.machine_st.b;
|
||||
let or_frame = self.machine_st.stack.index_or_frame(b);
|
||||
let n = or_frame.prelude.univ_prelude.num_cells;
|
||||
let n = or_frame.prelude.num_cells;
|
||||
|
||||
let old_tr = or_frame.prelude.tr;
|
||||
let curr_tr = self.machine_st.tr;
|
||||
|
||||
for i in 0..n {
|
||||
self.machine_st.registers[i+1] = or_frame[i];
|
||||
}
|
||||
|
||||
self.unwind_trail(old_tr, curr_tr);
|
||||
self.trust_epilogue(offset);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn trust_epilogue(&mut self, offset: usize) {
|
||||
let b = self.machine_st.b;
|
||||
let or_frame = self.machine_st.stack.index_or_frame(b);
|
||||
let n = or_frame.prelude.num_cells;
|
||||
|
||||
self.machine_st.num_of_args = n;
|
||||
self.machine_st.e = or_frame.prelude.e;
|
||||
self.machine_st.cp = or_frame.prelude.cp;
|
||||
|
||||
let old_tr = or_frame.prelude.tr;
|
||||
let curr_tr = self.machine_st.tr;
|
||||
let target_h = or_frame.prelude.h;
|
||||
|
||||
self.machine_st.tr = or_frame.prelude.tr;
|
||||
self.machine_st.trail.truncate(self.machine_st.tr);
|
||||
|
||||
self.machine_st.b = or_frame.prelude.b;
|
||||
|
||||
self.reset_attr_var_state();
|
||||
self.reset_attr_var_state(or_frame.prelude.attr_var_queue_len);
|
||||
|
||||
self.machine_st.hb = target_h;
|
||||
self.machine_st.p = self.machine_st.p + offset;
|
||||
|
||||
self.unwind_trail(old_tr, curr_tr);
|
||||
|
||||
self.machine_st.trail.truncate(self.machine_st.tr);
|
||||
self.machine_st.stack.truncate(b);
|
||||
self.machine_st.heap.truncate(target_h);
|
||||
|
||||
@@ -674,35 +1002,63 @@ impl Machine {
|
||||
fn trust_me(&mut self) {
|
||||
let b = self.machine_st.b;
|
||||
let or_frame = self.machine_st.stack.index_or_frame(b);
|
||||
let n = or_frame.prelude.univ_prelude.num_cells;
|
||||
let n = or_frame.prelude.num_cells;
|
||||
|
||||
for i in 0..n {
|
||||
self.machine_st.registers[i+1] = or_frame[i];
|
||||
}
|
||||
|
||||
let old_tr = or_frame.prelude.tr;
|
||||
let curr_tr = self.machine_st.tr;
|
||||
|
||||
self.unwind_trail(old_tr, curr_tr);
|
||||
|
||||
self.trust_me_epilogue();
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn trust_me_epilogue(&mut self) {
|
||||
let b = self.machine_st.b;
|
||||
let or_frame = self.machine_st.stack.index_or_frame(b);
|
||||
let n = or_frame.prelude.num_cells;
|
||||
|
||||
self.machine_st.num_of_args = n;
|
||||
self.machine_st.e = or_frame.prelude.e;
|
||||
self.machine_st.cp = or_frame.prelude.cp;
|
||||
|
||||
let old_tr = or_frame.prelude.tr;
|
||||
let curr_tr = self.machine_st.tr;
|
||||
let target_h = or_frame.prelude.h;
|
||||
|
||||
self.machine_st.tr = or_frame.prelude.tr;
|
||||
self.machine_st.b = or_frame.prelude.b;
|
||||
|
||||
self.reset_attr_var_state();
|
||||
self.reset_attr_var_state(or_frame.prelude.attr_var_queue_len);
|
||||
|
||||
self.machine_st.hb = target_h;
|
||||
self.machine_st.p += 1;
|
||||
|
||||
self.unwind_trail(old_tr, curr_tr);
|
||||
|
||||
self.machine_st.trail.truncate(self.machine_st.tr);
|
||||
self.machine_st.stack.truncate(b);
|
||||
self.machine_st.heap.truncate(target_h);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn undefined_procedure(&mut self, name: Atom, arity: usize) -> CallResult {
|
||||
match self.machine_st.flags.unknown {
|
||||
Unknown::Error => {
|
||||
Err(self.machine_st.throw_undefined_error(name, arity))
|
||||
}
|
||||
Unknown::Fail => {
|
||||
self.machine_st.fail = true;
|
||||
Ok(())
|
||||
}
|
||||
Unknown::Warn => {
|
||||
println!("warning: predicate {}/{} is undefined", name.as_str(), arity);
|
||||
self.machine_st.fail = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_call(&mut self, name: Atom, arity: usize, idx: IndexPtr) -> CallResult {
|
||||
let compiled_tl_index = idx.p() as usize;
|
||||
@@ -712,7 +1068,7 @@ impl Machine {
|
||||
self.machine_st.fail = true;
|
||||
}
|
||||
IndexPtrTag::Undefined => {
|
||||
return Err(self.machine_st.throw_undefined_error(name, arity));
|
||||
return self.undefined_procedure(name, arity);
|
||||
}
|
||||
IndexPtrTag::DynamicIndex => {
|
||||
self.machine_st.dynamic_mode = FirstOrNext::First;
|
||||
@@ -735,7 +1091,7 @@ impl Machine {
|
||||
self.machine_st.fail = true;
|
||||
}
|
||||
IndexPtrTag::Undefined => {
|
||||
return Err(self.machine_st.throw_undefined_error(name, arity));
|
||||
return self.undefined_procedure(name, arity);
|
||||
}
|
||||
IndexPtrTag::DynamicIndex => {
|
||||
self.machine_st.dynamic_mode = FirstOrNext::First;
|
||||
@@ -764,7 +1120,7 @@ impl Machine {
|
||||
if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() {
|
||||
self.try_call(name, arity, idx.get())
|
||||
} else {
|
||||
Err(self.machine_st.throw_undefined_error(name, arity))
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
} else {
|
||||
let stub = functor_stub(name, arity);
|
||||
@@ -783,14 +1139,14 @@ impl Machine {
|
||||
if let Some(idx) = self.indices.code_dir.get(&(name, arity)).cloned() {
|
||||
self.try_execute(name, arity, idx.get())
|
||||
} else {
|
||||
Err(self.machine_st.throw_undefined_error(name, arity))
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
} else {
|
||||
if let Some(module) = self.indices.modules.get(&module_name) {
|
||||
if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() {
|
||||
self.try_execute(name, arity, idx.get())
|
||||
} else {
|
||||
Err(self.machine_st.throw_undefined_error(name, arity))
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
} else {
|
||||
let stub = functor_stub(name, arity);
|
||||
@@ -841,7 +1197,7 @@ impl Machine {
|
||||
|
||||
if let Some(&(_, b_cutoff, prev_block)) = self.machine_st.cont_pts.last() {
|
||||
if self.machine_st.b < b_cutoff {
|
||||
let (idx, arity) = if self.machine_st.block > prev_block {
|
||||
let (idx, arity) = if self.machine_st.effective_block() > prev_block {
|
||||
(r_c_w_h, 0)
|
||||
} else {
|
||||
self.machine_st.registers[1] = fixnum_as_cell!(
|
||||
@@ -876,14 +1232,22 @@ impl Machine {
|
||||
TrailEntryTag::TrailedAttrVar => {
|
||||
self.machine_st.heap[h] = attr_var_as_cell!(h);
|
||||
}
|
||||
TrailEntryTag::TrailedAttrVarHeapLink => {
|
||||
self.machine_st.heap[h] = heap_loc_as_cell!(h);
|
||||
}
|
||||
TrailEntryTag::TrailedAttrVarListLink => {
|
||||
let l = self.machine_st.trail[i + 1].get_value() as usize;
|
||||
|
||||
if l < self.machine_st.hb {
|
||||
self.machine_st.heap[h] = list_loc_as_cell!(l);
|
||||
if h == l {
|
||||
self.machine_st.heap[h] = heap_loc_as_cell!(h);
|
||||
} else {
|
||||
read_heap_cell!(self.machine_st.heap[l],
|
||||
(HeapCellValueTag::Var) => {
|
||||
self.machine_st.heap[h] = list_loc_as_cell!(l);
|
||||
}
|
||||
_ => {
|
||||
self.machine_st.heap[h] = heap_loc_as_cell!(l);
|
||||
}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.machine_st.heap[h] = heap_loc_as_cell!(h);
|
||||
}
|
||||
@@ -910,4 +1274,4 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::atom_table::*;
|
||||
use ordered_float::OrderedFloat;
|
||||
use rug::*;
|
||||
use dashu::*;
|
||||
use std::collections::BTreeMap;
|
||||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -181,7 +181,7 @@ impl<'a> HeapPStrIter<'a> {
|
||||
self.brent_st.hare = result.focus;
|
||||
} else {
|
||||
read_heap_cell!(self.heap[result.focus],
|
||||
(HeapCellValueTag::Lis | HeapCellValueTag::Str) => {
|
||||
(HeapCellValueTag::Lis | HeapCellValueTag::Str | HeapCellValueTag::PStr) => {
|
||||
self.focus = self.heap[self.brent_st.hare];
|
||||
}
|
||||
_ => {
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::atom_table::*;
|
||||
use crate::codegen::CodeGenSettings;
|
||||
use crate::forms::*;
|
||||
use crate::instructions::*;
|
||||
use crate::iterators::*;
|
||||
use crate::machine::disjuncts::*;
|
||||
use crate::machine::loader::*;
|
||||
use crate::machine::machine_errors::*;
|
||||
use crate::parser::ast::*;
|
||||
@@ -10,35 +10,7 @@ use crate::parser::ast::*;
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::convert::TryFrom;
|
||||
use std::rc::Rc;
|
||||
|
||||
/*
|
||||
* The preprocessor fabricates if-then-else ( .. -> ... ; ...)
|
||||
* clauses into nameless standalone predicates, which it queues for
|
||||
* later preprocessing and compilation. Fabricated predicates inherit
|
||||
* explicit "cut variables" from the handwritten predicate
|
||||
* surrounding their source if-then-else. They must be specially
|
||||
* handled.
|
||||
*/
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) enum CutContext {
|
||||
BlocksCuts,
|
||||
HasCutVariable,
|
||||
}
|
||||
|
||||
pub(crate) fn fold_by_str<I>(terms: I, mut term: Term, sym: Atom) -> Term
|
||||
where
|
||||
I: DoubleEndedIterator<Item = Term>,
|
||||
{
|
||||
for prec in terms.rev() {
|
||||
term = Term::Clause(Cell::default(), sym, vec![prec, term]);
|
||||
}
|
||||
|
||||
term
|
||||
}
|
||||
|
||||
pub(crate) fn to_op_decl(
|
||||
prec: u16,
|
||||
@@ -132,6 +104,13 @@ fn setup_module_export(
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn build_rule_body(vars: &[Term], body_term: Term) -> Term {
|
||||
let head_term = Term::Clause(Cell::default(), atom!(""), vars.iter().cloned().collect());
|
||||
let rule = vec![head_term, body_term];
|
||||
|
||||
Term::Clause(Cell::default(), atom!(":-"), rule)
|
||||
}
|
||||
|
||||
pub(super) fn setup_module_export_list(
|
||||
mut export_list: Term,
|
||||
atom_tbl: &mut AtomTable,
|
||||
@@ -325,110 +304,6 @@ fn setup_meta_predicate<'a, LS: LoadState<'a>>(
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_clauses(tls: &mut VecDeque<TopLevel>) -> Result<TopLevel, CompilationError> {
|
||||
let mut clauses = vec![];
|
||||
|
||||
while let Some(tl) = tls.pop_front() {
|
||||
match tl {
|
||||
TopLevel::Query(_) if clauses.is_empty() && tls.is_empty() => {
|
||||
return Ok(tl);
|
||||
}
|
||||
TopLevel::Query(_) => {
|
||||
return Err(CompilationError::InconsistentEntry);
|
||||
}
|
||||
TopLevel::Fact(fact) => {
|
||||
let clause = PredicateClause::Fact(fact);
|
||||
clauses.push(clause);
|
||||
}
|
||||
TopLevel::Rule(rule) => {
|
||||
let clause = PredicateClause::Rule(rule);
|
||||
clauses.push(clause);
|
||||
}
|
||||
TopLevel::Predicate(predicate) => clauses.extend(predicate.into_iter()),
|
||||
}
|
||||
}
|
||||
|
||||
if clauses.is_empty() {
|
||||
Err(CompilationError::InconsistentEntry)
|
||||
} else {
|
||||
Ok(TopLevel::Predicate(clauses))
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_cut_variables_as(terms: &mut Vec<Term>, name: Atom) {
|
||||
for term in terms.iter_mut() {
|
||||
match term {
|
||||
&mut Term::Literal(_, Literal::Atom(ref mut var)) if *var == atom!("!") => {
|
||||
*var = name;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_cut_variable(term: &mut Term) -> bool {
|
||||
let cut_var_found = match term {
|
||||
&mut Term::Literal(_, Literal::Atom(ref var)) if *var == atom!("!") => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if cut_var_found {
|
||||
*term = Term::Var(Cell::default(), Rc::new(String::from("!")));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_cut_variables(terms: &mut Vec<Term>) -> bool {
|
||||
let mut found_cut_var = false;
|
||||
|
||||
for item in terms.iter_mut() {
|
||||
found_cut_var = mark_cut_variable(item) || found_cut_var;
|
||||
}
|
||||
|
||||
found_cut_var
|
||||
}
|
||||
|
||||
// terms is a list of goals composing one clause in a (;) functor. it
|
||||
// checks that the first (and only) of these clauses is a ->. if so,
|
||||
// it expands its terms using a blocked_!.
|
||||
fn check_for_internal_if_then(terms: &mut Vec<Term>) {
|
||||
if terms.len() != 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(Term::Clause(_, name, ref subterms)) = terms.last() {
|
||||
if *name != atom!("->") || subterms.len() != 2 {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(Term::Clause(_, _, mut subterms)) = terms.pop() {
|
||||
let mut conq_terms = VecDeque::from(unfold_by_str(subterms.pop().unwrap(), atom!(",")));
|
||||
let mut pre_cut_terms = VecDeque::from(unfold_by_str(subterms.pop().unwrap(), atom!(",")));
|
||||
|
||||
conq_terms.push_front(Term::Literal(
|
||||
Cell::default(),
|
||||
Literal::Atom(atom!("blocked_!")),
|
||||
));
|
||||
|
||||
while let Some(term) = pre_cut_terms.pop_back() {
|
||||
conq_terms.push_front(term);
|
||||
}
|
||||
|
||||
let tail_term = conq_terms.pop_back().unwrap();
|
||||
|
||||
terms.push(fold_by_str(
|
||||
conq_terms.into_iter(),
|
||||
tail_term,
|
||||
atom!(","),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn setup_declaration<'a, LS: LoadState<'a>>(
|
||||
loader: &mut Loader<'a, LS>,
|
||||
mut terms: Vec<Term>,
|
||||
@@ -570,7 +445,7 @@ fn build_meta_predicate_clause<'a, LS: LoadState<'a>>(
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clause_to_query_term<'a, LS: LoadState<'a>>(
|
||||
pub(super) fn clause_to_query_term<'a, LS: LoadState<'a>>(
|
||||
loader: &mut Loader<'a, LS>,
|
||||
name: Atom,
|
||||
mut terms: Vec<Term>,
|
||||
@@ -609,7 +484,7 @@ fn clause_to_query_term<'a, LS: LoadState<'a>>(
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn qualified_clause_to_query_term<'a, LS: LoadState<'a>>(
|
||||
pub(super) fn qualified_clause_to_query_term<'a, LS: LoadState<'a>>(
|
||||
loader: &mut Loader<'a, LS>,
|
||||
module_name: Atom,
|
||||
name: Atom,
|
||||
@@ -647,308 +522,58 @@ fn qualified_clause_to_query_term<'a, LS: LoadState<'a>>(
|
||||
QueryTerm::Clause(Cell::default(), ct, terms, call_policy)
|
||||
}
|
||||
|
||||
fn compute_head(term: &Term) -> Vec<Term> {
|
||||
let mut vars = IndexSet::new();
|
||||
|
||||
for term in post_order_iter(term) {
|
||||
if let TermRef::Var(_, _, v) = term {
|
||||
vars.insert(v.clone());
|
||||
}
|
||||
}
|
||||
|
||||
vars.insert(Rc::new(String::from("!")));
|
||||
vars.into_iter()
|
||||
.map(|v| Term::Var(Cell::default(), v))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn build_rule_body(vars: &[Term], body_term: Term) -> Term {
|
||||
let head_term = Term::Clause(Cell::default(), atom!(""), vars.iter().cloned().collect());
|
||||
let rule = vec![head_term, body_term];
|
||||
|
||||
Term::Clause(Cell::default(), atom!(":-"), rule)
|
||||
}
|
||||
|
||||
// the terms form the body of the rule. We create a head, by
|
||||
// gathering variables from the body of terms and recording them
|
||||
// in the head clause.
|
||||
fn build_rule(body_term: Term) -> (JumpStub, VecDeque<Term>) {
|
||||
// collect the vars of body_term into a head, return the num_vars
|
||||
// (the arity) as well.
|
||||
let vars = compute_head(&body_term);
|
||||
let rule = build_rule_body(&vars, body_term);
|
||||
|
||||
(vars, VecDeque::from(vec![rule]))
|
||||
}
|
||||
|
||||
fn build_disjunct(body_term: Term) -> (JumpStub, VecDeque<Term>) {
|
||||
let vars = compute_head(&body_term);
|
||||
let results = unfold_by_str(body_term, atom!(";"))
|
||||
.into_iter()
|
||||
.map(|term| {
|
||||
let mut subterms = unfold_by_str(term, atom!(","));
|
||||
mark_cut_variables(&mut subterms);
|
||||
|
||||
check_for_internal_if_then(&mut subterms);
|
||||
|
||||
let term = subterms.pop().unwrap();
|
||||
let clause = fold_by_str(subterms.into_iter(), term, atom!(","));
|
||||
|
||||
build_rule_body(&vars, clause)
|
||||
})
|
||||
.collect();
|
||||
|
||||
(vars, results)
|
||||
}
|
||||
|
||||
fn build_if_then(prec: Term, conq: Term) -> (JumpStub, VecDeque<Term>) {
|
||||
let mut prec_seq = unfold_by_str(prec, atom!(","));
|
||||
let comma_sym = atom!(",");
|
||||
let cut_sym = Literal::Atom(atom!("!"));
|
||||
|
||||
prec_seq.push(Term::Literal(Cell::default(), cut_sym));
|
||||
|
||||
mark_cut_variables_as(&mut prec_seq, atom!("blocked_!"));
|
||||
|
||||
let mut conq_seq = unfold_by_str(conq, atom!(","));
|
||||
|
||||
mark_cut_variables(&mut conq_seq);
|
||||
prec_seq.extend(conq_seq.into_iter());
|
||||
|
||||
let back_term = prec_seq.pop().unwrap();
|
||||
let front_term = prec_seq.pop().unwrap();
|
||||
|
||||
let body_term = Term::Clause(
|
||||
Cell::default(),
|
||||
comma_sym,
|
||||
vec![front_term, back_term],
|
||||
);
|
||||
|
||||
build_rule(fold_by_str(prec_seq.into_iter(), body_term, comma_sym))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Preprocessor {
|
||||
queue: VecDeque<VecDeque<Term>>,
|
||||
settings: CodeGenSettings,
|
||||
}
|
||||
|
||||
impl Preprocessor {
|
||||
pub(super) fn new(settings: CodeGenSettings) -> Self {
|
||||
Preprocessor {
|
||||
queue: VecDeque::new(),
|
||||
settings,
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_fact(&mut self, term: Term) -> Result<Term, CompilationError> {
|
||||
fn setup_fact(&mut self, term: Term) -> Result<(Fact, VarData), CompilationError> {
|
||||
match term {
|
||||
Term::Clause(..) | Term::Literal(_, Literal::Atom(..)) => Ok(term),
|
||||
Term::Clause(..) | Term::Literal(_, Literal::Atom(..)) => {
|
||||
let classifier = VariableClassifier::new(
|
||||
self.settings.default_call_policy(),
|
||||
);
|
||||
|
||||
let (head, var_data) = classifier.classify_fact(term)?;
|
||||
Ok((Fact { head }, var_data))
|
||||
}
|
||||
_ => Err(CompilationError::InadmissibleFact),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_query_term<'a, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
term: Term,
|
||||
) -> Result<QueryTerm, CompilationError> {
|
||||
match term {
|
||||
Term::Literal(_, Literal::Atom(name)) => {
|
||||
if name == atom!("!") || name == atom!("blocked_!") {
|
||||
Ok(QueryTerm::BlockedCut)
|
||||
} else {
|
||||
Ok(clause_to_query_term(
|
||||
loader,
|
||||
name,
|
||||
vec![],
|
||||
self.settings.default_call_policy(),
|
||||
))
|
||||
}
|
||||
}
|
||||
Term::Literal(_, Literal::Char('!')) => Ok(QueryTerm::BlockedCut),
|
||||
Term::Var(_, ref v) if v.as_str() == "!" => {
|
||||
Ok(QueryTerm::UnblockedCut(Cell::default()))
|
||||
}
|
||||
Term::Clause(r, name, mut terms) => match (name, terms.len()) {
|
||||
(atom!(";"), 2) => {
|
||||
let term = Term::Clause(r, name, terms);
|
||||
|
||||
let (stub, clauses) = build_disjunct(term);
|
||||
self.queue.push_back(clauses);
|
||||
|
||||
Ok(QueryTerm::Jump(stub))
|
||||
}
|
||||
(atom!("->"), 2) => {
|
||||
let conq = terms.pop().unwrap();
|
||||
let prec = terms.pop().unwrap();
|
||||
|
||||
let (stub, clauses) = build_if_then(prec, conq);
|
||||
self.queue.push_back(clauses);
|
||||
|
||||
Ok(QueryTerm::Jump(stub))
|
||||
}
|
||||
(atom!("\\+"), 1) => {
|
||||
terms.push(Term::Literal(
|
||||
Cell::default(),
|
||||
Literal::Atom(atom!("$fail")),
|
||||
));
|
||||
|
||||
let conq = Term::Literal(Cell::default(), Literal::Atom(atom!("true")));
|
||||
|
||||
let prec = Term::Clause(Cell::default(), atom!("->"), terms);
|
||||
let terms = vec![prec, conq];
|
||||
|
||||
let term = Term::Clause(Cell::default(), atom!(";"), terms);
|
||||
let (stub, clauses) = build_disjunct(term);
|
||||
|
||||
debug_assert!(clauses.len() > 0);
|
||||
self.queue.push_back(clauses);
|
||||
|
||||
Ok(QueryTerm::Jump(stub))
|
||||
}
|
||||
(atom!("$get_level"), 1) => {
|
||||
if let Term::Var(_, ref var) = &terms[0] {
|
||||
Ok(QueryTerm::GetLevelAndUnify(Cell::default(), var.clone()))
|
||||
} else {
|
||||
Err(CompilationError::InadmissibleQueryTerm)
|
||||
}
|
||||
}
|
||||
(atom!(":"), 2) => {
|
||||
let predicate_name = terms.pop().unwrap();
|
||||
let module_name = terms.pop().unwrap();
|
||||
|
||||
match (module_name, predicate_name) {
|
||||
(
|
||||
Term::Literal(_, Literal::Atom(module_name)),
|
||||
Term::Literal(_, Literal::Atom(predicate_name)),
|
||||
) => Ok(qualified_clause_to_query_term(
|
||||
loader,
|
||||
module_name,
|
||||
predicate_name,
|
||||
vec![],
|
||||
self.settings.default_call_policy(),
|
||||
)),
|
||||
(
|
||||
Term::Literal(_, Literal::Atom(module_name)),
|
||||
Term::Clause(_, name, terms),
|
||||
) => Ok(qualified_clause_to_query_term(
|
||||
loader,
|
||||
module_name,
|
||||
name,
|
||||
terms,
|
||||
self.settings.default_call_policy()
|
||||
)),
|
||||
(module_name, predicate_name) => {
|
||||
terms.push(module_name);
|
||||
terms.push(predicate_name);
|
||||
|
||||
Ok(clause_to_query_term(
|
||||
loader,
|
||||
atom!("call"),
|
||||
vec![Term::Clause(r, name, terms)],
|
||||
self.settings.default_call_policy(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => Ok(clause_to_query_term(loader, name, terms,
|
||||
self.settings.default_call_policy())),
|
||||
},
|
||||
Term::Var(..) => Ok(QueryTerm::Clause(
|
||||
Cell::default(),
|
||||
ClauseType::CallN(1),
|
||||
vec![term],
|
||||
self.settings.default_call_policy(),
|
||||
)),
|
||||
_ => Err(CompilationError::InadmissibleQueryTerm),
|
||||
}
|
||||
}
|
||||
|
||||
fn pre_query_term<'a, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
term: Term,
|
||||
) -> Result<QueryTerm, CompilationError> {
|
||||
match term {
|
||||
Term::Clause(r, name, mut subterms) => {
|
||||
if subterms.len() == 1 && name == atom!("$call_with_inference_counting") {
|
||||
self.to_query_term(loader, subterms.pop().unwrap())
|
||||
.map(|mut query_term| {
|
||||
query_term.set_call_policy(CallPolicy::Counted);
|
||||
query_term
|
||||
})
|
||||
} else {
|
||||
let clause = Term::Clause(r, name, subterms);
|
||||
self.to_query_term(loader, clause)
|
||||
}
|
||||
}
|
||||
_ => self.to_query_term(loader, term),
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_query<'a, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
terms: Vec<Term>,
|
||||
cut_context: CutContext,
|
||||
) -> Result<Vec<QueryTerm>, CompilationError> {
|
||||
let mut query_terms = vec![];
|
||||
let mut work_queue = VecDeque::from(terms);
|
||||
|
||||
while let Some(term) = work_queue.pop_front() {
|
||||
let mut term = term;
|
||||
|
||||
if let Term::Clause(cell, name, terms) = term {
|
||||
if name == atom!(",") && terms.len() == 2 {
|
||||
let term = Term::Clause(cell, name, terms);
|
||||
let mut subterms = unfold_by_str(term, atom!(","));
|
||||
|
||||
while let Some(subterm) = subterms.pop() {
|
||||
work_queue.push_front(subterm);
|
||||
}
|
||||
|
||||
continue;
|
||||
} else {
|
||||
term = Term::Clause(cell, name, terms);
|
||||
}
|
||||
}
|
||||
|
||||
if let CutContext::HasCutVariable = cut_context {
|
||||
mark_cut_variable(&mut term);
|
||||
}
|
||||
|
||||
query_terms.push(self.pre_query_term(loader, term)?);
|
||||
}
|
||||
|
||||
Ok(query_terms)
|
||||
}
|
||||
|
||||
fn setup_rule<'a, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
mut terms: Vec<Term>,
|
||||
cut_context: CutContext,
|
||||
) -> Result<Rule, CompilationError> {
|
||||
let post_head_terms: Vec<_> = terms.drain(1..).collect();
|
||||
let mut query_terms = self.setup_query(loader, post_head_terms, cut_context)?;
|
||||
head: Term,
|
||||
body: Term,
|
||||
) -> Result<(Rule, VarData), CompilationError> {
|
||||
let classifier = VariableClassifier::new(
|
||||
self.settings.default_call_policy(),
|
||||
);
|
||||
|
||||
let clauses = query_terms.drain(1..).collect();
|
||||
let qt = query_terms.pop().unwrap();
|
||||
let (head, clauses, var_data) = classifier.classify_rule(loader, head, body)?;
|
||||
|
||||
match terms.pop().unwrap() {
|
||||
Term::Clause(_, name, terms) => Ok(Rule {
|
||||
head: (name, terms, qt),
|
||||
match head {
|
||||
Term::Clause(_, name, terms) => Ok((Rule {
|
||||
head: (name, terms),
|
||||
clauses,
|
||||
}),
|
||||
Term::Literal(_, Literal::Atom(name)) => Ok(Rule {
|
||||
head: (name, vec![], qt),
|
||||
}, var_data)),
|
||||
Term::Literal(_, Literal::Atom(name)) => Ok((Rule {
|
||||
head: (name, vec![]),
|
||||
clauses,
|
||||
}),
|
||||
}, var_data)),
|
||||
_ => Err(CompilationError::InvalidRuleHead),
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn try_term_to_query<'a, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
@@ -961,63 +586,49 @@ impl Preprocessor {
|
||||
cut_context,
|
||||
)?))
|
||||
}
|
||||
*/
|
||||
|
||||
pub(super) fn try_term_to_tl<'a, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
term: Term,
|
||||
cut_context: CutContext,
|
||||
) -> Result<TopLevel, CompilationError> {
|
||||
match term {
|
||||
Term::Clause(r, name, terms) => {
|
||||
if name == atom!("?-") {
|
||||
self.try_term_to_query(loader, terms, cut_context)
|
||||
} else if name == atom!(":-") && terms.len() == 2 {
|
||||
Ok(TopLevel::Rule(self.setup_rule(
|
||||
loader,
|
||||
terms,
|
||||
cut_context,
|
||||
)?))
|
||||
Term::Clause(r, name, mut terms) => {
|
||||
let is_rule = name == atom!(":-") && terms.len() == 2;
|
||||
|
||||
if is_rule {
|
||||
let tail = terms.pop().unwrap();
|
||||
let head = terms.pop().unwrap();
|
||||
|
||||
let (rule, var_data) = self.setup_rule(loader, head, tail)?;
|
||||
Ok(TopLevel::Rule(rule, var_data))
|
||||
} else {
|
||||
let term = Term::Clause(r, name, terms);
|
||||
Ok(TopLevel::Fact(self.setup_fact(term)?))
|
||||
let (fact, var_data) = self.setup_fact(term)?;
|
||||
Ok(TopLevel::Fact(fact, var_data))
|
||||
}
|
||||
}
|
||||
term => Ok(TopLevel::Fact(self.setup_fact(term)?)),
|
||||
term => {
|
||||
let (fact, var_data) = self.setup_fact(term)?;
|
||||
Ok(TopLevel::Fact(fact, var_data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn try_terms_to_tls<'a, I: IntoIterator<Item = Term>, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
terms: I,
|
||||
cut_context: CutContext,
|
||||
) -> Result<VecDeque<TopLevel>, CompilationError> {
|
||||
let mut results = VecDeque::new();
|
||||
|
||||
for term in terms.into_iter() {
|
||||
results.push_back(self.try_term_to_tl(loader, term, cut_context)?);
|
||||
results.push_back(self.try_term_to_tl(loader, term)?);
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub(super) fn parse_queue<'a, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
) -> Result<VecDeque<TopLevel>, CompilationError> {
|
||||
let mut queue = VecDeque::new();
|
||||
|
||||
while let Some(terms) = self.queue.pop_front() {
|
||||
let clauses = merge_clauses(&mut self.try_terms_to_tls(
|
||||
loader,
|
||||
terms,
|
||||
CutContext::HasCutVariable,
|
||||
)?)?;
|
||||
|
||||
queue.push_back(clauses);
|
||||
}
|
||||
|
||||
Ok(queue)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
:- module('$project_atts', [copy_term/3]).
|
||||
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(error), [can_be/2]).
|
||||
:- use_module(library(lambda)).
|
||||
:- use_module(library(lists), [foldl/4, maplist/2]).
|
||||
|
||||
project_attributes(QueryVars, AttrVars) :-
|
||||
gather_attr_modules(AttrVars, Modules0),
|
||||
phrase(gather_attr_modules(AttrVars), Modules0),
|
||||
sort(Modules0, Modules),
|
||||
call_project_attributes(Modules, QueryVars, AttrVars).
|
||||
|
||||
@@ -17,19 +22,14 @@ project_attributes(QueryVars, AttrVars) :-
|
||||
call_project_attributes([], _, _).
|
||||
call_project_attributes([Module|Modules], QueryVars, AttrVars) :-
|
||||
( catch(Module:project_attributes(QueryVars, AttrVars),
|
||||
E,
|
||||
'$project_atts':'$print_project_attributes_exception'(Module, E)
|
||||
)
|
||||
E,
|
||||
'$project_atts':'$print_project_attributes_exception'(Module, E)
|
||||
)
|
||||
-> true
|
||||
; true
|
||||
),
|
||||
call_project_attributes(Modules, QueryVars, AttrVars).
|
||||
|
||||
call_attribute_goals([], _, _).
|
||||
call_attribute_goals([Module|Modules], GoalCaller, AttrVars) :-
|
||||
call(GoalCaller, AttrVars, Module, Goals),
|
||||
call_attribute_goals(Modules, GoalCaller, AttrVars).
|
||||
|
||||
'$print_attribute_goals_exception'(Module, E) :-
|
||||
( E = error(evaluation_error((Module:attribute_goals)/3), attribute_goals/3)
|
||||
; E = error(existence_error(procedure, attribute_goals/3), attribute_goals/3)
|
||||
@@ -38,20 +38,6 @@ call_attribute_goals([Module|Modules], GoalCaller, AttrVars) :-
|
||||
nl
|
||||
).
|
||||
|
||||
call_query_var_goals([], _, []).
|
||||
call_query_var_goals([AttrVar|AttrVars], Module, Goals) :-
|
||||
( catch(( Module:attribute_goals(AttrVar, Goals, RGoals0),
|
||||
atts:'$default_attr_list'(Module, AttrVar, RGoals0, RGoals)
|
||||
),
|
||||
E,
|
||||
( '$project_atts':'$print_attribute_goals_exception'(Module, E),
|
||||
atts:'$default_attr_list'(Module, AttrVar, Goals, RGoals)
|
||||
))
|
||||
-> true
|
||||
; atts:'$default_attr_list'(Module, AttrVar, Goals, RGoals)
|
||||
),
|
||||
call_query_var_goals(AttrVars, Module, RGoals).
|
||||
|
||||
call_attr_var_goals([], _, []).
|
||||
call_attr_var_goals([AttrVar|AttrVars], Module, Goals) :-
|
||||
( catch(Module:attribute_goals(AttrVar, Goals, RGoals),
|
||||
@@ -77,25 +63,52 @@ call_attribute_goals_with_module_prefix([Module | Modules], GoalCaller, AttrVars
|
||||
module_prefixed_goals(Goals0, Module, Goals, Gs),
|
||||
call_attribute_goals_with_module_prefix(Modules, GoalCaller, AttrVars, Gs).
|
||||
|
||||
gather_attr_modules([]) --> [].
|
||||
gather_attr_modules([AttrVar|AttrVars]) -->
|
||||
{ '$get_attr_list'(AttrVar, Attrs) },
|
||||
copy_attribute_modules(Attrs),
|
||||
gather_attr_modules(AttrVars).
|
||||
|
||||
gather_attr_modules([], []).
|
||||
gather_attr_modules([AttrVar|AttrVars], Modules) :-
|
||||
'$get_attr_list'(AttrVar, Attrs),
|
||||
copy_attribute_modules(Attrs, Modules, Modules0),
|
||||
gather_attr_modules(AttrVars, Modules0).
|
||||
copy_attribute_modules(Attrs) -->
|
||||
{ var(Attrs) },
|
||||
!.
|
||||
copy_attribute_modules([Module:_|Attrs]) -->
|
||||
[Module],
|
||||
copy_attribute_modules(Attrs).
|
||||
|
||||
copy_attribute_modules(Attrs, Ls, Ls) :-
|
||||
var(Attrs), !.
|
||||
copy_attribute_modules([Module:_|Attrs], [Module|Modules0], Modules1) :-
|
||||
copy_attribute_modules(Attrs, Modules0, Modules1).
|
||||
gather_residual_goals_(M, V, V0, V1) :-
|
||||
( catch(M:attribute_goals(V, V0, V1),
|
||||
E,
|
||||
('$project_atts':'$print_attribute_goals_exception'(M, E),
|
||||
V0 = V1)
|
||||
) ->
|
||||
true
|
||||
; V0 = V1
|
||||
).
|
||||
|
||||
gather_residual_goals(M, V) -->
|
||||
gather_residual_goals_(M, V),
|
||||
atts:'$default_attr_list'(M, V).
|
||||
|
||||
copy_term(Source, Dest, Goals) :-
|
||||
'$term_attributed_variables'(Source, AttrVars),
|
||||
gather_attr_modules(AttrVars, Modules0),
|
||||
sort(Modules0, Modules),
|
||||
call_attribute_goals_with_module_prefix(Modules, '$project_atts':call_query_var_goals,
|
||||
AttrVars, Goals0),
|
||||
sort(Goals0, Goals1),
|
||||
!,
|
||||
'$copy_term_without_attr_vars'([Source | Goals1], [Dest | Goals]).
|
||||
gather_residual_goals([]) --> [].
|
||||
gather_residual_goals([V|Vs]) -->
|
||||
{ '$get_attr_list'(V, Attrs),
|
||||
phrase(copy_attribute_modules(Attrs), Modules0),
|
||||
sort(Modules0, Modules) },
|
||||
foldl(V+\M^gather_residual_goals(M, V), Modules),
|
||||
gather_residual_goals(Vs).
|
||||
|
||||
delete_all_attributes_from_var(V) :- '$delete_all_attributes_from_var'(V).
|
||||
|
||||
copy_term(Term, Copy, Gs) :-
|
||||
can_be(list, Gs),
|
||||
findall(Term-Rs, term_residual_goals(Term,Rs), [Copy-Gs]),
|
||||
( var(Gs) ->
|
||||
Gs = []
|
||||
; true
|
||||
).
|
||||
|
||||
term_residual_goals(Term,Rs) :-
|
||||
'$term_attributed_variables'(Term, Vs),
|
||||
phrase(gather_residual_goals(Vs), Rs),
|
||||
maplist(delete_all_attributes_from_var, Vs).
|
||||
|
||||
@@ -36,14 +36,9 @@ impl Drop for Stack {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct FramePrelude {
|
||||
pub(crate) num_cells: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AndFramePrelude {
|
||||
pub(crate) univ_prelude: FramePrelude,
|
||||
pub(crate) num_cells: usize,
|
||||
pub(crate) e: usize,
|
||||
pub(crate) cp: usize,
|
||||
}
|
||||
@@ -113,7 +108,7 @@ impl IndexMut<usize> for Stack {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct OrFramePrelude {
|
||||
pub(crate) univ_prelude: FramePrelude,
|
||||
pub(crate) num_cells: usize,
|
||||
pub(crate) e: usize,
|
||||
pub(crate) cp: usize,
|
||||
pub(crate) b: usize,
|
||||
@@ -123,6 +118,7 @@ pub(crate) struct OrFramePrelude {
|
||||
pub(crate) tr: usize,
|
||||
pub(crate) h: usize,
|
||||
pub(crate) b0: usize,
|
||||
pub(crate) attr_var_queue_len: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -206,8 +202,8 @@ impl Stack {
|
||||
offset += mem::size_of::<HeapCellValue>();
|
||||
}
|
||||
|
||||
let and_frame = &mut *(new_ptr as *mut AndFrame);
|
||||
and_frame.prelude.univ_prelude.num_cells = num_cells;
|
||||
let and_frame = self.index_and_frame_mut(e);
|
||||
and_frame.prelude.num_cells = num_cells;
|
||||
|
||||
e
|
||||
}
|
||||
@@ -230,8 +226,8 @@ impl Stack {
|
||||
offset += mem::size_of::<HeapCellValue>();
|
||||
}
|
||||
|
||||
let or_frame = &mut *(new_ptr as *mut OrFrame);
|
||||
or_frame.prelude.univ_prelude.num_cells = num_cells;
|
||||
let or_frame = self.index_or_frame_mut(b);
|
||||
or_frame.prelude.num_cells = num_cells;
|
||||
|
||||
b
|
||||
}
|
||||
@@ -297,7 +293,7 @@ mod tests {
|
||||
0// 10 * mem::size_of::<HeapCellValue>() + prelude_size::<AndFrame>()
|
||||
);
|
||||
|
||||
assert_eq!(and_frame.prelude.univ_prelude.num_cells, 10);
|
||||
assert_eq!(and_frame.prelude.num_cells, 10);
|
||||
|
||||
for idx in 0..10 {
|
||||
assert_eq!(and_frame[idx + 1], stack_loc_as_cell!(AndFrame, e, idx + 1));
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::machine::machine_errors::*;
|
||||
use crate::machine::machine_indices::*;
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::types::*;
|
||||
use crate::http::HttpResponse;
|
||||
|
||||
pub use modular_bitfield::prelude::*;
|
||||
|
||||
@@ -26,7 +27,6 @@ use std::ops::{Deref, DerefMut};
|
||||
use std::ptr;
|
||||
|
||||
use native_tls::TlsStream;
|
||||
use hyper::body::{Bytes, Sender};
|
||||
|
||||
#[derive(Debug, BitfieldSpecifier, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[bits = 1]
|
||||
@@ -102,6 +102,13 @@ impl EOFAction {
|
||||
#[derive(Debug)]
|
||||
pub struct ByteStream(Cursor<Vec<u8>>);
|
||||
|
||||
impl ByteStream {
|
||||
#[inline(always)]
|
||||
pub fn from_string(string: String) -> Self {
|
||||
ByteStream(Cursor::new(string.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for ByteStream {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
@@ -269,28 +276,42 @@ impl Read for HttpReadStream {
|
||||
}
|
||||
|
||||
pub struct HttpWriteStream {
|
||||
body_writer: Sender,
|
||||
status_code: u16,
|
||||
headers: hyper::HeaderMap,
|
||||
response: TypedArenaPtr<HttpResponse>,
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Debug for HttpWriteStream {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Http Write Stream")
|
||||
write!(f, "Http Write Stream")
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for HttpWriteStream {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
let bytes = Bytes::copy_from_slice(buf);
|
||||
let len = bytes.len();
|
||||
match self.body_writer.try_send_data(bytes) {
|
||||
Ok(()) => Ok(len),
|
||||
Err(_) => Err(std::io::Error::from(ErrorKind::Interrupted))
|
||||
}
|
||||
self.buffer.extend_from_slice(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
let (ready, response, cvar) = &**self.response;
|
||||
|
||||
let mut ready = ready.lock().unwrap();
|
||||
{
|
||||
let mut response = response.lock().unwrap();
|
||||
|
||||
let bytes = bytes::Bytes::copy_from_slice(&self.buffer);
|
||||
let mut response_ = hyper::Response::builder()
|
||||
.status(self.status_code);
|
||||
*response_.headers_mut().unwrap() = self.headers.clone();
|
||||
*response = Some(response_.body(http_body_util::Full::new(bytes)).unwrap());
|
||||
}
|
||||
*ready = true;
|
||||
cvar.notify_one();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -505,7 +526,7 @@ impl Stream {
|
||||
ArenaHeaderTag::NamedTcpStream => Stream::NamedTcp(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::NamedTlsStream => Stream::NamedTls(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::HttpReadStream => Stream::HttpRead(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::HttpWriteStream => Stream::HttpWrite(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::HttpWriteStream => Stream::HttpWrite(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::ReadlineStream => Stream::Readline(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::StaticStringStream => {
|
||||
Stream::StaticString(TypedArenaPtr::new(ptr as *mut _))
|
||||
@@ -559,7 +580,7 @@ impl Stream {
|
||||
Stream::NamedTcp(ptr) => ptr.header_ptr(),
|
||||
Stream::NamedTls(ptr) => ptr.header_ptr(),
|
||||
Stream::HttpRead(ptr) => ptr.header_ptr(),
|
||||
Stream::HttpWrite(ptr) => ptr.header_ptr(),
|
||||
Stream::HttpWrite(ptr) => ptr.header_ptr(),
|
||||
Stream::Null(_) => ptr::null(),
|
||||
Stream::Readline(ptr) => ptr.header_ptr(),
|
||||
Stream::StandardOutput(ptr) => ptr.header_ptr(),
|
||||
@@ -576,7 +597,7 @@ impl Stream {
|
||||
Stream::NamedTcp(ref ptr) => &ptr.options,
|
||||
Stream::NamedTls(ref ptr) => &ptr.options,
|
||||
Stream::HttpRead(ref ptr) => &ptr.options,
|
||||
Stream::HttpWrite(ref ptr) => &ptr.options,
|
||||
Stream::HttpWrite(ref ptr) => &ptr.options,
|
||||
Stream::Null(ref options) => options,
|
||||
Stream::Readline(ref ptr) => &ptr.options,
|
||||
Stream::StandardOutput(ref ptr) => &ptr.options,
|
||||
@@ -593,7 +614,7 @@ impl Stream {
|
||||
Stream::NamedTcp(ref mut ptr) => &mut ptr.options,
|
||||
Stream::NamedTls(ref mut ptr) => &mut ptr.options,
|
||||
Stream::HttpRead(ref mut ptr) => &mut ptr.options,
|
||||
Stream::HttpWrite(ref mut ptr) => &mut ptr.options,
|
||||
Stream::HttpWrite(ref mut ptr) => &mut ptr.options,
|
||||
Stream::Null(ref mut options) => options,
|
||||
Stream::Readline(ref mut ptr) => &mut ptr.options,
|
||||
Stream::StandardOutput(ref mut ptr) => &mut ptr.options,
|
||||
@@ -611,7 +632,7 @@ impl Stream {
|
||||
Stream::NamedTcp(ptr) => ptr.lines_read += incr_num_lines_read,
|
||||
Stream::NamedTls(ptr) => ptr.lines_read += incr_num_lines_read,
|
||||
Stream::HttpRead(ptr) => ptr.lines_read += incr_num_lines_read,
|
||||
Stream::HttpWrite(_) => {}
|
||||
Stream::HttpWrite(_) => {}
|
||||
Stream::Null(_) => {}
|
||||
Stream::Readline(ptr) => ptr.lines_read += incr_num_lines_read,
|
||||
Stream::StandardOutput(ptr) => ptr.lines_read += incr_num_lines_read,
|
||||
@@ -629,7 +650,7 @@ impl Stream {
|
||||
Stream::NamedTcp(ptr) => ptr.lines_read = value,
|
||||
Stream::NamedTls(ptr) => ptr.lines_read = value,
|
||||
Stream::HttpRead(ptr) => ptr.lines_read = value,
|
||||
Stream::HttpWrite(_) => {}
|
||||
Stream::HttpWrite(_) => {}
|
||||
Stream::Null(_) => {}
|
||||
Stream::Readline(ptr) => ptr.lines_read = value,
|
||||
Stream::StandardOutput(ptr) => ptr.lines_read = value,
|
||||
@@ -647,7 +668,7 @@ impl Stream {
|
||||
Stream::NamedTcp(ptr) => ptr.lines_read,
|
||||
Stream::NamedTls(ptr) => ptr.lines_read,
|
||||
Stream::HttpRead(ptr) => ptr.lines_read,
|
||||
Stream::HttpWrite(_) => 0,
|
||||
Stream::HttpWrite(_) => 0,
|
||||
Stream::Null(_) => 0,
|
||||
Stream::Readline(ptr) => ptr.lines_read,
|
||||
Stream::StandardOutput(ptr) => ptr.lines_read,
|
||||
@@ -669,7 +690,7 @@ impl CharRead for Stream {
|
||||
Stream::OutputFile(_) |
|
||||
Stream::StandardError(_) |
|
||||
Stream::StandardOutput(_) |
|
||||
Stream::HttpWrite(_) |
|
||||
Stream::HttpWrite(_) |
|
||||
Stream::Null(_) => Some(Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::ReadFromOutputStream,
|
||||
@@ -689,7 +710,7 @@ impl CharRead for Stream {
|
||||
Stream::OutputFile(_) |
|
||||
Stream::StandardError(_) |
|
||||
Stream::StandardOutput(_) |
|
||||
Stream::HttpWrite(_) |
|
||||
Stream::HttpWrite(_) |
|
||||
Stream::Null(_) => Some(Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::ReadFromOutputStream,
|
||||
@@ -709,7 +730,7 @@ impl CharRead for Stream {
|
||||
Stream::OutputFile(_) |
|
||||
Stream::StandardError(_) |
|
||||
Stream::StandardOutput(_) |
|
||||
Stream::HttpWrite(_) |
|
||||
Stream::HttpWrite(_) |
|
||||
Stream::Null(_) => {}
|
||||
}
|
||||
}
|
||||
@@ -726,7 +747,7 @@ impl CharRead for Stream {
|
||||
Stream::OutputFile(_) |
|
||||
Stream::StandardError(_) |
|
||||
Stream::StandardOutput(_) |
|
||||
Stream::HttpWrite(_) |
|
||||
Stream::HttpWrite(_) |
|
||||
Stream::Null(_) => {}
|
||||
}
|
||||
}
|
||||
@@ -744,13 +765,13 @@ impl Read for Stream {
|
||||
Stream::StaticString(src) => (*src).read(buf),
|
||||
Stream::Byte(cursor) => (*cursor).read(buf),
|
||||
Stream::OutputFile(_)
|
||||
| Stream::StandardError(_)
|
||||
| Stream::StandardOutput(_)
|
||||
| Stream::HttpWrite(_)
|
||||
| Stream::Null(_) => Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::ReadFromOutputStream,
|
||||
)),
|
||||
| Stream::StandardError(_)
|
||||
| Stream::StandardOutput(_)
|
||||
| Stream::HttpWrite(_)
|
||||
| Stream::Null(_) => Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::ReadFromOutputStream,
|
||||
)),
|
||||
};
|
||||
|
||||
bytes_read
|
||||
@@ -766,7 +787,7 @@ impl Write for Stream {
|
||||
Stream::Byte(ref mut cursor) => cursor.get_mut().write(buf),
|
||||
Stream::StandardOutput(stream) => stream.write(buf),
|
||||
Stream::StandardError(stream) => stream.write(buf),
|
||||
Stream::HttpWrite(ref mut stream) => stream.get_mut().write(buf),
|
||||
Stream::HttpWrite(ref mut stream) => stream.get_mut().write(buf),
|
||||
Stream::HttpRead(_) |
|
||||
Stream::StaticString(_) |
|
||||
Stream::Readline(_) |
|
||||
@@ -786,7 +807,7 @@ impl Write for Stream {
|
||||
Stream::Byte(ref mut cursor) => cursor.stream.get_mut().flush(),
|
||||
Stream::StandardError(stream) => stream.stream.flush(),
|
||||
Stream::StandardOutput(stream) => stream.stream.flush(),
|
||||
Stream::HttpWrite(ref mut stream) => stream.stream.get_mut().flush(),
|
||||
Stream::HttpWrite(ref mut stream) => stream.stream.get_mut().flush(),
|
||||
Stream::HttpRead(_) |
|
||||
Stream::StaticString(_) |
|
||||
Stream::Readline(_) |
|
||||
@@ -863,19 +884,38 @@ impl PartialEq for Stream {
|
||||
|
||||
impl Eq for Stream {}
|
||||
|
||||
fn cursor_position<T>(past_end_of_stream: &mut bool, cursor: &Cursor<T>, cursor_len: u64) -> AtEndOfStream {
|
||||
let position = cursor.position();
|
||||
|
||||
let at_end_of_stream = match position.cmp(&cursor_len) {
|
||||
Ordering::Equal => AtEndOfStream::At,
|
||||
Ordering::Greater => {
|
||||
*past_end_of_stream = true;
|
||||
AtEndOfStream::Past
|
||||
}
|
||||
Ordering::Less => AtEndOfStream::Not,
|
||||
};
|
||||
|
||||
at_end_of_stream
|
||||
}
|
||||
|
||||
impl Stream {
|
||||
#[inline]
|
||||
pub(crate) fn position(&mut self) -> Option<(u64, usize)> {
|
||||
// returns lines_read, position.
|
||||
let result = match self {
|
||||
Stream::Byte(byte_stream_layout) => {
|
||||
Some(byte_stream_layout.stream.get_ref().0.position())
|
||||
}
|
||||
Stream::StaticString(string_stream_layout) => {
|
||||
Some(string_stream_layout.stream.stream.position())
|
||||
}
|
||||
Stream::InputFile(file_stream) => {
|
||||
file_stream.position()
|
||||
}
|
||||
Stream::NamedTcp(..)
|
||||
| Stream::NamedTls(..)
|
||||
| Stream::Readline(..)
|
||||
| Stream::StaticString(..)
|
||||
| Stream::Byte(..) => Some(0),
|
||||
Stream::NamedTcp(..) | Stream::NamedTls(..) | Stream::Readline(..) => {
|
||||
Some(0)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
@@ -913,7 +953,7 @@ impl Stream {
|
||||
Stream::NamedTcp(stream) => stream.past_end_of_stream,
|
||||
Stream::NamedTls(stream) => stream.past_end_of_stream,
|
||||
Stream::HttpRead(stream) => stream.past_end_of_stream,
|
||||
Stream::HttpWrite(stream) => stream.past_end_of_stream,
|
||||
Stream::HttpWrite(stream) => stream.past_end_of_stream,
|
||||
Stream::Null(_) => false,
|
||||
Stream::Readline(stream) => stream.past_end_of_stream,
|
||||
Stream::StandardOutput(stream) => stream.past_end_of_stream,
|
||||
@@ -936,7 +976,7 @@ impl Stream {
|
||||
Stream::NamedTcp(stream) => stream.past_end_of_stream = value,
|
||||
Stream::NamedTls(stream) => stream.past_end_of_stream = value,
|
||||
Stream::HttpRead(stream) => stream.past_end_of_stream = value,
|
||||
Stream::HttpWrite(stream) => stream.past_end_of_stream = value,
|
||||
Stream::HttpWrite(stream) => stream.past_end_of_stream = value,
|
||||
Stream::Null(_) => {}
|
||||
Stream::Readline(stream) => stream.past_end_of_stream = value,
|
||||
Stream::StandardOutput(stream) => stream.past_end_of_stream = value,
|
||||
@@ -950,38 +990,61 @@ impl Stream {
|
||||
return AtEndOfStream::Past;
|
||||
}
|
||||
|
||||
if let Stream::InputFile(stream_layout) = self {
|
||||
let position = stream_layout.position();
|
||||
match self {
|
||||
Stream::Byte(stream_layout) => {
|
||||
let StreamLayout {
|
||||
past_end_of_stream,
|
||||
stream,
|
||||
..
|
||||
} = &mut **stream_layout;
|
||||
|
||||
let StreamLayout {
|
||||
past_end_of_stream,
|
||||
stream,
|
||||
..
|
||||
} = &mut **stream_layout;
|
||||
let cursor_len = stream.get_ref().0.get_ref().len() as u64;
|
||||
cursor_position(past_end_of_stream, &stream.get_ref().0, cursor_len)
|
||||
}
|
||||
Stream::StaticString(stream_layout) => {
|
||||
let StreamLayout {
|
||||
past_end_of_stream,
|
||||
stream,
|
||||
..
|
||||
} = &mut **stream_layout;
|
||||
|
||||
match stream.get_ref().file.metadata() {
|
||||
Ok(metadata) => {
|
||||
if let Some(position) = position {
|
||||
return match position.cmp(&metadata.len()) {
|
||||
Ordering::Equal => AtEndOfStream::At,
|
||||
Ordering::Less => AtEndOfStream::Not,
|
||||
Ordering::Greater => {
|
||||
*past_end_of_stream = true;
|
||||
AtEndOfStream::Past
|
||||
let cursor_len = stream.stream.get_ref().len() as u64;
|
||||
cursor_position(past_end_of_stream, &stream.stream, cursor_len)
|
||||
}
|
||||
Stream::InputFile(stream_layout) => {
|
||||
let position = stream_layout.position();
|
||||
|
||||
let StreamLayout {
|
||||
past_end_of_stream,
|
||||
stream,
|
||||
..
|
||||
} = &mut **stream_layout;
|
||||
|
||||
match stream.get_ref().file.metadata() {
|
||||
Ok(metadata) => {
|
||||
if let Some(position) = position {
|
||||
match position.cmp(&metadata.len()) {
|
||||
Ordering::Equal => AtEndOfStream::At,
|
||||
Ordering::Less => AtEndOfStream::Not,
|
||||
Ordering::Greater => {
|
||||
*past_end_of_stream = true;
|
||||
AtEndOfStream::Past
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
} else {
|
||||
*past_end_of_stream = true;
|
||||
AtEndOfStream::Past
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
*past_end_of_stream = true;
|
||||
AtEndOfStream::Past
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
*past_end_of_stream = true;
|
||||
AtEndOfStream::Past
|
||||
}
|
||||
}
|
||||
} else {
|
||||
AtEndOfStream::Not
|
||||
_ => {
|
||||
AtEndOfStream::Not
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1000,10 +1063,10 @@ impl Stream {
|
||||
pub(crate) fn mode(&self) -> Atom {
|
||||
match self {
|
||||
Stream::Byte(_)
|
||||
| Stream::Readline(_)
|
||||
| Stream::StaticString(_)
|
||||
| Stream::HttpRead(_)
|
||||
| Stream::InputFile(..) => atom!("read"),
|
||||
| Stream::Readline(_)
|
||||
| Stream::StaticString(_)
|
||||
| Stream::HttpRead(_)
|
||||
| Stream::InputFile(..) => atom!("read"),
|
||||
Stream::NamedTcp(..) | Stream::NamedTls(..) => atom!("read_append"),
|
||||
Stream::OutputFile(file) if file.is_append => atom!("append"),
|
||||
Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) | Stream::HttpWrite(_) => atom!("write"),
|
||||
@@ -1077,12 +1140,17 @@ impl Stream {
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn from_http_sender(
|
||||
body_writer: Sender,
|
||||
response: TypedArenaPtr<HttpResponse>,
|
||||
status_code: u16,
|
||||
headers: hyper::HeaderMap,
|
||||
arena: &mut Arena,
|
||||
) -> Self {
|
||||
Stream::HttpWrite(arena_alloc!(
|
||||
StreamLayout::new(CharReader::new(HttpWriteStream {
|
||||
body_writer
|
||||
response,
|
||||
status_code,
|
||||
headers,
|
||||
buffer: Vec::new(),
|
||||
})),
|
||||
arena
|
||||
))
|
||||
@@ -1135,11 +1203,11 @@ impl Stream {
|
||||
Stream::HttpWrite(ref mut http_stream) => {
|
||||
unsafe {
|
||||
http_stream.set_tag(ArenaHeaderTag::Dropped);
|
||||
std::ptr::drop_in_place(&mut http_stream.inner_mut().body_writer as *mut _);
|
||||
std::ptr::drop_in_place(&mut http_stream.inner_mut().buffer as *mut _);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Stream::InputFile(mut file_stream) => {
|
||||
// close the stream by dropping the inner File.
|
||||
unsafe {
|
||||
@@ -1175,12 +1243,12 @@ impl Stream {
|
||||
pub(crate) fn is_input_stream(&self) -> bool {
|
||||
match self {
|
||||
Stream::NamedTcp(..)
|
||||
| Stream::NamedTls(..)
|
||||
| Stream::HttpRead(..)
|
||||
| Stream::Byte(_)
|
||||
| Stream::Readline(_)
|
||||
| Stream::StaticString(_)
|
||||
| Stream::InputFile(..) => true,
|
||||
| Stream::NamedTls(..)
|
||||
| Stream::HttpRead(..)
|
||||
| Stream::Byte(_)
|
||||
| Stream::Readline(_)
|
||||
| Stream::StaticString(_)
|
||||
| Stream::InputFile(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -1189,12 +1257,12 @@ impl Stream {
|
||||
pub(crate) fn is_output_stream(&self) -> bool {
|
||||
match self {
|
||||
Stream::StandardError(_)
|
||||
| Stream::StandardOutput(_)
|
||||
| Stream::NamedTcp(..)
|
||||
| Stream::NamedTls(..)
|
||||
| Stream::HttpWrite(..)
|
||||
| Stream::Byte(_)
|
||||
| Stream::OutputFile(..) => true,
|
||||
| Stream::StandardOutput(_)
|
||||
| Stream::NamedTcp(..)
|
||||
| Stream::NamedTls(..)
|
||||
| Stream::HttpWrite(..)
|
||||
| Stream::Byte(_)
|
||||
| Stream::OutputFile(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -1242,12 +1310,9 @@ impl Stream {
|
||||
}
|
||||
}
|
||||
Stream::InputFile(ref mut file) => {
|
||||
let mut b = [0u8; 1];
|
||||
|
||||
match file.read(&mut b)? {
|
||||
1 => {
|
||||
file.stream.get_mut().file.seek(SeekFrom::Current(-1))?;
|
||||
Ok(b[0])
|
||||
match file.peek_byte() {
|
||||
Some(result) => {
|
||||
Ok(result?)
|
||||
}
|
||||
_ => Err(std::io::Error::new(
|
||||
ErrorKind::UnexpectedEof,
|
||||
@@ -1283,7 +1348,7 @@ impl MachineState {
|
||||
match eof_action {
|
||||
EOFAction::Error => {
|
||||
stream.set_past_end_of_stream(true);
|
||||
return Err(self.open_past_eos_error(stream, caller, arity));
|
||||
Err(self.open_past_eos_error(stream, caller, arity))
|
||||
}
|
||||
EOFAction::EOFCode => {
|
||||
let end_of_stream = if stream.options().stream_type() == StreamType::Binary {
|
||||
@@ -1313,101 +1378,101 @@ impl MachineState {
|
||||
stream_type: HeapCellValue,
|
||||
) -> StreamOptions {
|
||||
let alias = read_heap_cell!(self.store(MachineState::deref(self, alias)),
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
debug_assert_eq!(arity, 0);
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
debug_assert_eq!(arity, 0);
|
||||
|
||||
if name != atom!("[]") {
|
||||
Some(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
if name != atom!("[]") {
|
||||
Some(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
debug_assert_eq!(arity, 0);
|
||||
debug_assert_eq!(arity, 0);
|
||||
|
||||
if name != atom!("[]") {
|
||||
Some(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
if name != atom!("[]") {
|
||||
Some(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
);
|
||||
|
||||
let eof_action = read_heap_cell!(self.store(MachineState::deref(self, eof_action)),
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
debug_assert_eq!(arity, 0);
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
debug_assert_eq!(arity, 0);
|
||||
|
||||
match name {
|
||||
atom!("eof_code") => EOFAction::EOFCode,
|
||||
atom!("error") => EOFAction::Error,
|
||||
atom!("reset") => EOFAction::Reset,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
match name {
|
||||
atom!("eof_code") => EOFAction::EOFCode,
|
||||
atom!("error") => EOFAction::Error,
|
||||
atom!("reset") => EOFAction::Reset,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
debug_assert_eq!(arity, 0);
|
||||
debug_assert_eq!(arity, 0);
|
||||
|
||||
match name {
|
||||
atom!("eof_code") => EOFAction::EOFCode,
|
||||
atom!("error") => EOFAction::Error,
|
||||
atom!("reset") => EOFAction::Reset,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
match name {
|
||||
atom!("eof_code") => EOFAction::EOFCode,
|
||||
atom!("error") => EOFAction::Error,
|
||||
atom!("reset") => EOFAction::Reset,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
);
|
||||
|
||||
let reposition = read_heap_cell!(self.store(MachineState::deref(self, reposition)),
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
debug_assert_eq!(arity, 0);
|
||||
name == atom!("true")
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
debug_assert_eq!(arity, 0);
|
||||
name == atom!("true")
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
debug_assert_eq!(arity, 0);
|
||||
name == atom!("true")
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
debug_assert_eq!(arity, 0);
|
||||
name == atom!("true")
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
);
|
||||
|
||||
let stream_type = read_heap_cell!(self.store(MachineState::deref(self, stream_type)),
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
debug_assert_eq!(arity, 0);
|
||||
match name {
|
||||
atom!("text") => StreamType::Text,
|
||||
atom!("binary") => StreamType::Binary,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
debug_assert_eq!(arity, 0);
|
||||
match name {
|
||||
atom!("text") => StreamType::Text,
|
||||
atom!("binary") => StreamType::Binary,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
debug_assert_eq!(arity, 0);
|
||||
match name {
|
||||
atom!("text") => StreamType::Text,
|
||||
atom!("binary") => StreamType::Binary,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
debug_assert_eq!(arity, 0);
|
||||
match name {
|
||||
atom!("text") => StreamType::Text,
|
||||
atom!("binary") => StreamType::Binary,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
);
|
||||
|
||||
let mut options = StreamOptions::default();
|
||||
@@ -1430,60 +1495,60 @@ impl MachineState {
|
||||
let addr = self.store(MachineState::deref(self, addr));
|
||||
|
||||
read_heap_cell!(addr,
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
debug_assert_eq!(arity, 0);
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
debug_assert_eq!(arity, 0);
|
||||
|
||||
return match stream_aliases.get(&name) {
|
||||
Some(stream) if !stream.is_null_stream() => Ok(*stream),
|
||||
_ => {
|
||||
let stub = functor_stub(caller, arity);
|
||||
let addr = atom_as_cell!(name);
|
||||
return match stream_aliases.get(&name) {
|
||||
Some(stream) if !stream.is_null_stream() => Ok(*stream),
|
||||
_ => {
|
||||
let stub = functor_stub(caller, arity);
|
||||
let addr = atom_as_cell!(name);
|
||||
|
||||
let existence_error = self.existence_error(ExistenceError::Stream(addr));
|
||||
let existence_error = self.existence_error(ExistenceError::Stream(addr));
|
||||
|
||||
Err(self.error_form(existence_error, stub))
|
||||
}
|
||||
};
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
Err(self.error_form(existence_error, stub))
|
||||
}
|
||||
};
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
debug_assert_eq!(arity, 0);
|
||||
debug_assert_eq!(arity, 0);
|
||||
|
||||
return match stream_aliases.get(&name) {
|
||||
Some(stream) if !stream.is_null_stream() => Ok(*stream),
|
||||
_ => {
|
||||
let stub = functor_stub(caller, arity);
|
||||
let addr = atom_as_cell!(name);
|
||||
return match stream_aliases.get(&name) {
|
||||
Some(stream) if !stream.is_null_stream() => Ok(*stream),
|
||||
_ => {
|
||||
let stub = functor_stub(caller, arity);
|
||||
let addr = atom_as_cell!(name);
|
||||
|
||||
let existence_error = self.existence_error(ExistenceError::Stream(addr));
|
||||
let existence_error = self.existence_error(ExistenceError::Stream(addr));
|
||||
|
||||
Err(self.error_form(existence_error, stub))
|
||||
}
|
||||
};
|
||||
}
|
||||
(HeapCellValueTag::Cons, ptr) => {
|
||||
match_untyped_arena_ptr!(ptr,
|
||||
(ArenaHeaderTag::Stream, stream) => {
|
||||
return if stream.is_null_stream() {
|
||||
Err(self.open_permission_error(stream_as_cell!(stream), caller, arity))
|
||||
} else {
|
||||
Ok(stream)
|
||||
};
|
||||
}
|
||||
(ArenaHeaderTag::Dropped, _value) => {
|
||||
let stub = functor_stub(caller, arity);
|
||||
let err = self.existence_error(ExistenceError::Stream(addr));
|
||||
Err(self.error_form(existence_error, stub))
|
||||
}
|
||||
};
|
||||
}
|
||||
(HeapCellValueTag::Cons, ptr) => {
|
||||
match_untyped_arena_ptr!(ptr,
|
||||
(ArenaHeaderTag::Stream, stream) => {
|
||||
return if stream.is_null_stream() {
|
||||
Err(self.open_permission_error(stream_as_cell!(stream), caller, arity))
|
||||
} else {
|
||||
Ok(stream)
|
||||
};
|
||||
}
|
||||
(ArenaHeaderTag::Dropped, _value) => {
|
||||
let stub = functor_stub(caller, arity);
|
||||
let err = self.existence_error(ExistenceError::Stream(addr));
|
||||
|
||||
return Err(self.error_form(err, stub));
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
return Err(self.error_form(err, stub));
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
);
|
||||
|
||||
let stub = functor_stub(caller, arity);
|
||||
@@ -1497,20 +1562,10 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn open_parsing_stream(
|
||||
&mut self,
|
||||
mut stream: Stream,
|
||||
stub_name: Atom,
|
||||
stub_arity: usize,
|
||||
) -> Result<Stream, MachineStub> {
|
||||
pub(crate) fn open_parsing_stream(&mut self, mut stream: Stream) -> Result<Stream, ParserError> {
|
||||
match stream.peek_char() {
|
||||
None => Ok(stream), // empty stream is handled gracefully by Lexer::eof
|
||||
Some(Err(e)) => {
|
||||
let err = self.session_error(SessionError::from(e));
|
||||
let stub = functor_stub(stub_name, stub_arity);
|
||||
|
||||
Err(self.error_form(err, stub))
|
||||
}
|
||||
Some(Err(e)) => Err(ParserError::IO(e)),
|
||||
Some(Ok(c)) => {
|
||||
if c == '\u{feff}' {
|
||||
// skip UTF-8 BOM
|
||||
@@ -1531,7 +1586,15 @@ impl MachineState {
|
||||
arity: usize,
|
||||
) -> MachineStub {
|
||||
let stub = functor_stub(caller, arity);
|
||||
let err = self.permission_error(perm, err_atom, stream_as_cell!(stream));
|
||||
let err = self.permission_error(
|
||||
perm,
|
||||
err_atom,
|
||||
if let Some(alias) = stream.options().get_alias() {
|
||||
atom_as_cell!(alias)
|
||||
} else {
|
||||
stream_as_cell!(stream)
|
||||
},
|
||||
);
|
||||
|
||||
self.error_form(err, stub)
|
||||
}
|
||||
@@ -1699,7 +1762,7 @@ impl MachineState {
|
||||
}
|
||||
ErrorKind::PermissionDenied => {
|
||||
// 8.11.5.3k)
|
||||
return Err(self.open_permission_error(self[temp_v!(1)], atom!("open"), 4));
|
||||
return Err(self.open_permission_error(self.registers[1], atom!("open"), 4));
|
||||
}
|
||||
_ => {
|
||||
let stub = functor_stub(atom!("open"), 4);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ use crate::machine::loader::*;
|
||||
use crate::machine::machine_errors::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::parser::*;
|
||||
use crate::read::devour_whitespace;
|
||||
|
||||
use crate::predicate_queue;
|
||||
|
||||
@@ -52,14 +53,14 @@ impl<'a> TermStream for BootstrappingTermStream<'a> {
|
||||
fn next(&mut self, op_dir: &CompositeOpDir) -> Result<Term, CompilationError> {
|
||||
self.parser.reset();
|
||||
self.parser
|
||||
.read_term(op_dir)
|
||||
.read_term(op_dir, Tokens::Default)
|
||||
.map_err(CompilationError::from)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn eof(&mut self) -> Result<bool, CompilationError> {
|
||||
self.parser.devour_whitespace()?; // eliminate dangling comments before checking for EOF.
|
||||
Ok(self.parser.eof()?)
|
||||
devour_whitespace(&mut self.parser) // eliminate dangling comments before checking for EOF.
|
||||
.map_err(CompilationError::from)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -111,7 +112,7 @@ impl TermStream for LiveTermStream {
|
||||
|
||||
#[inline]
|
||||
fn eof(&mut self) -> Result<bool, CompilationError> {
|
||||
return Ok(self.term_queue.is_empty());
|
||||
Ok(self.term_queue.is_empty())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -125,15 +126,15 @@ pub struct InlineTermStream {
|
||||
|
||||
impl TermStream for InlineTermStream {
|
||||
fn next(&mut self, _: &CompositeOpDir) -> Result<Term, CompilationError> {
|
||||
Err(CompilationError::from(ParserError::UnexpectedEOF))
|
||||
Err(CompilationError::from(ParserError::unexpected_eof()))
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> Result<bool, CompilationError> {
|
||||
Ok(true)
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn listing_src(&self) -> &ListingSource {
|
||||
&ListingSource::User
|
||||
&ListingSource::User
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
788
src/machine/unify.rs
Normal file
788
src/machine/unify.rs
Normal file
@@ -0,0 +1,788 @@
|
||||
use crate::arena::*;
|
||||
use crate::forms::*;
|
||||
use crate::heap_iter::stackful_preorder_iter;
|
||||
use crate::machine::*;
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::machine::partial_string::*;
|
||||
use crate::types::*;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use derive_deref::*;
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexSet;
|
||||
|
||||
pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
fn unify_structure(&mut self, s1: usize, value: HeapCellValue) {
|
||||
// s1 is the value of a STR cell.
|
||||
let (n1, a1) = cell_as_atom_cell!(self.heap[s1]).get_name_and_arity();
|
||||
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::Str, s2) => {
|
||||
let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
|
||||
.get_name_and_arity();
|
||||
|
||||
if n1 == n2 && a1 == a2 {
|
||||
for idx in (0..a1).rev() {
|
||||
self.pdl.push(heap_loc_as_cell!(s2+1+idx));
|
||||
self.pdl.push(heap_loc_as_cell!(s1+1+idx));
|
||||
}
|
||||
} else {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis, l2) => {
|
||||
if a1 == 2 && n1 == atom!(".") {
|
||||
for idx in (0..2).rev() {
|
||||
self.pdl.push(heap_loc_as_cell!(l2+1+idx));
|
||||
self.pdl.push(heap_loc_as_cell!(s1+1+idx));
|
||||
}
|
||||
} else {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Atom, (n2, a2)) => {
|
||||
self.fail = !(a1 == 0 && a2 == 0 && n1 == n2);
|
||||
}
|
||||
(HeapCellValueTag::AttrVar, h) => {
|
||||
Self::bind(self, Ref::attr_var(h), str_loc_as_cell!(s1));
|
||||
}
|
||||
(HeapCellValueTag::Var, h) => {
|
||||
Self::bind(self, Ref::heap_cell(h), str_loc_as_cell!(s1));
|
||||
}
|
||||
(HeapCellValueTag::StackVar, s) => {
|
||||
Self::bind(self, Ref::stack_cell(s), str_loc_as_cell!(s1));
|
||||
}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
fn unify_list(&mut self, l1: usize, value: HeapCellValue) {
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::Lis, l2) => {
|
||||
for idx in (0..2).rev() {
|
||||
self.pdl.push(heap_loc_as_cell!(l2 + idx));
|
||||
self.pdl.push(heap_loc_as_cell!(l1 + idx));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Str, s2) => {
|
||||
let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
|
||||
.get_name_and_arity();
|
||||
|
||||
if a2 == 2 && n2 == atom!(".") {
|
||||
for idx in (0..2).rev() {
|
||||
self.pdl.push(heap_loc_as_cell!(s2+1+idx));
|
||||
self.pdl.push(heap_loc_as_cell!(l1+idx));
|
||||
}
|
||||
} else {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::PStrLoc | HeapCellValueTag::CStr | HeapCellValueTag::PStr) => {
|
||||
Self::unify_partial_string(self, list_loc_as_cell!(l1), value)
|
||||
}
|
||||
(HeapCellValueTag::AttrVar, h) => {
|
||||
Self::bind(self, Ref::attr_var(h), list_loc_as_cell!(l1));
|
||||
}
|
||||
(HeapCellValueTag::Var, h) => {
|
||||
Self::bind(self, Ref::heap_cell(h), list_loc_as_cell!(l1));
|
||||
}
|
||||
(HeapCellValueTag::StackVar, s) => {
|
||||
Self::bind(self, Ref::stack_cell(s), list_loc_as_cell!(l1));
|
||||
}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
fn unify_complete_string(&mut self, atom: Atom, value: HeapCellValue) {
|
||||
if let Some(r) = value.as_var() {
|
||||
if atom == atom!("") {
|
||||
Self::bind(self, r, atom_as_cell!(atom!("[]")));
|
||||
} else {
|
||||
Self::bind(self, r, atom_as_cstr_cell!(atom));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::Atom, (cstr_atom, arity)) if atom == atom!("") => {
|
||||
debug_assert_eq!(arity, 0);
|
||||
self.fail = cstr_atom != atom!("[]");
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
if arity == 0 {
|
||||
self.fail = atom == atom!("") && name != atom!("[]");
|
||||
} else {
|
||||
// this is intentionally the same policy for
|
||||
// value.tag() == Lis and PStrLoc. they're not
|
||||
// grouped together to allow for arity == 0.
|
||||
Self::unify_partial_string(self, atom_as_cstr_cell!(atom), value);
|
||||
|
||||
if !self.pdl.is_empty() {
|
||||
Self::unify_internal(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::CStr, cstr_atom) => {
|
||||
self.fail = atom != cstr_atom;
|
||||
}
|
||||
(HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => {
|
||||
Self::unify_partial_string(self, atom_as_cstr_cell!(atom), value);
|
||||
|
||||
if !self.pdl.is_empty() {
|
||||
Self::unify_internal(self);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// the return value of unify_partial_string is interpreted as
|
||||
// follows:
|
||||
//
|
||||
// Some(None) -- the strings are equal, nothing to unify
|
||||
// Some(Some(f2,f1)) -- prefixes equal, try to unify focus values f2, f1
|
||||
// None -- prefixes not equal, unification fails
|
||||
//
|
||||
// d1's tag is assumed to be one of LIS, STR or PSTRLOC.
|
||||
fn unify_partial_string(&mut self, value_1: HeapCellValue, value_2: HeapCellValue) {
|
||||
if let Some(r) = value_2.as_var() {
|
||||
Self::bind(self, r, value_1);
|
||||
return;
|
||||
}
|
||||
|
||||
let machine_st = self.deref_mut();
|
||||
|
||||
let s1 = machine_st.heap.len();
|
||||
|
||||
machine_st.heap.push(value_1);
|
||||
machine_st.heap.push(value_2);
|
||||
|
||||
let mut pstr_iter1 = HeapPStrIter::new(&machine_st.heap, s1);
|
||||
let mut pstr_iter2 = HeapPStrIter::new(&machine_st.heap, s1 + 1);
|
||||
|
||||
match compare_pstr_prefixes(&mut pstr_iter1, &mut pstr_iter2) {
|
||||
PStrCmpResult::Ordered(Ordering::Equal) => {}
|
||||
PStrCmpResult::Ordered(Ordering::Less) => {
|
||||
if pstr_iter2.focus.as_var().is_none() {
|
||||
machine_st.fail = true;
|
||||
} else {
|
||||
machine_st.pdl.push(empty_list_as_cell!());
|
||||
machine_st.pdl.push(pstr_iter2.focus);
|
||||
}
|
||||
}
|
||||
PStrCmpResult::Ordered(Ordering::Greater) => {
|
||||
if pstr_iter1.focus.as_var().is_none() {
|
||||
machine_st.fail = true;
|
||||
} else {
|
||||
machine_st.pdl.push(empty_list_as_cell!());
|
||||
machine_st.pdl.push(pstr_iter1.focus);
|
||||
}
|
||||
}
|
||||
continuable @ PStrCmpResult::FirstIterContinuable(iteratee) |
|
||||
continuable @ PStrCmpResult::SecondIterContinuable(iteratee) => {
|
||||
if continuable.is_second_iter() {
|
||||
std::mem::swap(&mut pstr_iter1, &mut pstr_iter2);
|
||||
}
|
||||
|
||||
let mut chars_iter = PStrCharsIter {
|
||||
iter: pstr_iter1,
|
||||
item: Some(iteratee),
|
||||
};
|
||||
|
||||
let mut focus = pstr_iter2.focus;
|
||||
|
||||
'outer: loop {
|
||||
while let Some(c) = chars_iter.peek() {
|
||||
read_heap_cell!(focus,
|
||||
(HeapCellValueTag::Lis, l) => {
|
||||
let val = pstr_iter2.heap[l];
|
||||
|
||||
machine_st.pdl.push(val);
|
||||
machine_st.pdl.push(char_as_cell!(c));
|
||||
|
||||
focus = pstr_iter2.heap[l+1];
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(pstr_iter2.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
if name == atom!(".") && arity == 2 {
|
||||
machine_st.pdl.push(pstr_iter2.heap[s+1]);
|
||||
machine_st.pdl.push(char_as_cell!(c));
|
||||
|
||||
focus = pstr_iter2.heap[s+2];
|
||||
} else {
|
||||
machine_st.fail = true;
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
|
||||
match chars_iter.item.unwrap() {
|
||||
PStrIteratee::Char(focus, _) => {
|
||||
machine_st.pdl.push(machine_st.heap[focus]);
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
}
|
||||
PStrIteratee::PStrSegment(focus, _, n) => {
|
||||
read_heap_cell!(machine_st.heap[focus],
|
||||
(HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => {
|
||||
if focus < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
let target_cell = match machine_st.heap[focus].get_tag() {
|
||||
HeapCellValueTag::CStr => {
|
||||
atom_as_cstr_cell!(pstr_atom)
|
||||
}
|
||||
HeapCellValueTag::PStr => {
|
||||
pstr_loc_as_cell!(focus)
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
||||
machine_st.pdl.push(target_cell);
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
} else {
|
||||
let h_len = machine_st.heap.len();
|
||||
|
||||
machine_st.heap.push(pstr_offset_as_cell!(focus));
|
||||
machine_st.heap.push(fixnum_as_cell!(
|
||||
Fixnum::build_with(n as i64)
|
||||
));
|
||||
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(h_len));
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
(HeapCellValueTag::PStrOffset, pstr_loc) => {
|
||||
let n0 = cell_as_fixnum!(machine_st.heap[focus+1])
|
||||
.get_num() as usize;
|
||||
|
||||
if pstr_loc < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
if n == n0 {
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(focus));
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
} else {
|
||||
let h_len = machine_st.heap.len();
|
||||
|
||||
machine_st.heap.push(pstr_offset_as_cell!(pstr_loc));
|
||||
machine_st.heap.push(fixnum_as_cell!(
|
||||
Fixnum::build_with(n as i64)
|
||||
));
|
||||
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(h_len));
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
);
|
||||
|
||||
if focus < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
machine_st.pdl.push(machine_st.heap[focus]);
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
break 'outer;
|
||||
}
|
||||
_ => {
|
||||
machine_st.fail = true;
|
||||
break 'outer;
|
||||
}
|
||||
);
|
||||
|
||||
chars_iter.next();
|
||||
}
|
||||
|
||||
chars_iter.iter.next();
|
||||
|
||||
machine_st.pdl.push(focus);
|
||||
machine_st.pdl.push(chars_iter.iter.focus);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
PStrCmpResult::Unordered => {
|
||||
machine_st.pdl.push(pstr_iter1.focus);
|
||||
machine_st.pdl.push(pstr_iter2.focus);
|
||||
}
|
||||
}
|
||||
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
fn unify_atom(&mut self, atom: Atom, value: HeapCellValue) {
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
self.fail = !(arity == 0 && name == atom);
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
self.fail = !(arity == 0 && name == atom);
|
||||
}
|
||||
(HeapCellValueTag::CStr, cstr_atom) if atom == atom!("[]") => {
|
||||
self.fail = cstr_atom != atom!("");
|
||||
}
|
||||
(HeapCellValueTag::Char, c1) => {
|
||||
if let Some(c2) = atom.as_char() {
|
||||
self.fail = c1 != c2;
|
||||
} else {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::AttrVar, h) => {
|
||||
Self::bind(self, Ref::attr_var(h), atom_as_cell!(atom));
|
||||
}
|
||||
(HeapCellValueTag::Var, h) => {
|
||||
Self::bind(self, Ref::heap_cell(h), atom_as_cell!(atom));
|
||||
}
|
||||
(HeapCellValueTag::StackVar, s) => {
|
||||
Self::bind(self, Ref::stack_cell(s), atom_as_cell!(atom));
|
||||
}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
fn unify_char(&mut self, c: char, value: HeapCellValue) {
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
if let Some(c2) = name.as_char() {
|
||||
self.fail = !(c == c2 && arity == 0);
|
||||
} else {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
if let Some(c2) = name.as_char() {
|
||||
self.fail = !(c == c2 && arity == 0);
|
||||
} else {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Char, c2) => {
|
||||
if c != c2 {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::AttrVar, h) => {
|
||||
Self::bind(self, Ref::attr_var(h), char_as_cell!(c));
|
||||
}
|
||||
(HeapCellValueTag::Var, h) => {
|
||||
Self::bind(self, Ref::heap_cell(h), char_as_cell!(c));
|
||||
}
|
||||
(HeapCellValueTag::StackVar, s) => {
|
||||
Self::bind(self, Ref::stack_cell(s), char_as_cell!(c));
|
||||
}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
fn unify_fixnum(&mut self, n1: Fixnum, value: HeapCellValue) {
|
||||
if let Some(r) = value.as_var() {
|
||||
Self::bind(self, r, fixnum_as_cell!(n1));
|
||||
return;
|
||||
}
|
||||
|
||||
match Number::try_from(value) {
|
||||
Ok(n2) => match n2 {
|
||||
Number::Fixnum(n2) if n1.get_num() == n2.get_num() => {}
|
||||
Number::Integer(n2) if n1.get_num() == *n2 => {}
|
||||
Number::Rational(n2) if n1.get_num() == *n2 => {}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_big_num<N>(&mut self, n1: TypedArenaPtr<N>, value: HeapCellValue)
|
||||
where N: PartialEq<Rational>
|
||||
+ PartialEq<Integer>
|
||||
+ PartialEq<i64>
|
||||
+ ArenaAllocated
|
||||
{
|
||||
if let Some(r) = value.as_var() {
|
||||
Self::bind(self, r, typed_arena_ptr_as_cell!(n1));
|
||||
return;
|
||||
}
|
||||
|
||||
match Number::try_from(value) {
|
||||
Ok(n2) => match n2 {
|
||||
Number::Fixnum(n2) if *n1 == n2.get_num() => {}
|
||||
Number::Integer(n2) if *n1 == *n2 => {}
|
||||
Number::Rational(n2) if *n1 == *n2 => {}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_f64(&mut self, f1: F64Ptr, value: HeapCellValue) {
|
||||
if let Some(r) = value.as_var() {
|
||||
Self::bind(self, r, HeapCellValue::from(f1));
|
||||
return;
|
||||
}
|
||||
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::F64, f2) => {
|
||||
self.fail = **f1 != **f2;
|
||||
}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
fn unify_constant(&mut self, ptr: UntypedArenaPtr, value: HeapCellValue) {
|
||||
if let Some(ptr2) = value.to_untyped_arena_ptr() {
|
||||
if ptr.get_ptr() == ptr2.get_ptr() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
match_untyped_arena_ptr!(ptr,
|
||||
(ArenaHeaderTag::Integer, int_ptr) => {
|
||||
Self::unify_big_num(self, int_ptr, value);
|
||||
}
|
||||
(ArenaHeaderTag::Rational, rat_ptr) => {
|
||||
Self::unify_big_num(self, rat_ptr, value);
|
||||
}
|
||||
(ArenaHeaderTag::Stream, stream) => {
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
|
||||
Self::bind(self, value.as_var().unwrap(), untyped_arena_ptr_as_cell!(ptr));
|
||||
}
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
if arity > 0 {
|
||||
self.fail = true;
|
||||
} else {
|
||||
let stream_options = stream.options();
|
||||
|
||||
if let Some(alias) = stream_options.get_alias() {
|
||||
self.fail = name != alias;
|
||||
} else {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
if let Some(r) = value.as_var() {
|
||||
Self::bind(self, r, untyped_arena_ptr_as_cell!(ptr));
|
||||
} else {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
fn unify_internal(&mut self) {
|
||||
let mut tabu_list = IndexSet::with_hasher(FxBuildHasher::default());
|
||||
|
||||
while !(self.pdl.is_empty() || self.fail) {
|
||||
let s1 = self.pdl.pop().unwrap();
|
||||
let s1 = (self.deref() as &MachineState).deref(s1);
|
||||
|
||||
let s2 = self.pdl.pop().unwrap();
|
||||
let s2 = (self.deref() as &MachineState).deref(s2);
|
||||
|
||||
if s1 != s2 {
|
||||
let d1 = self.store(s1);
|
||||
let d2 = self.store(s2);
|
||||
|
||||
read_heap_cell!(d1,
|
||||
(HeapCellValueTag::AttrVar, h) => {
|
||||
Self::bind(self, Ref::attr_var(h), d2);
|
||||
}
|
||||
(HeapCellValueTag::Var, h) => {
|
||||
Self::bind(self, Ref::heap_cell(h), d2);
|
||||
}
|
||||
(HeapCellValueTag::StackVar, s) => {
|
||||
Self::bind(self, Ref::stack_cell(s), d2);
|
||||
}
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
debug_assert_eq!(arity, 0);
|
||||
Self::unify_atom(self, name, d2);
|
||||
}
|
||||
(HeapCellValueTag::Str, s1) => {
|
||||
if tabu_list.contains(&(d1, d2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Self::unify_structure(self, s1, d2);
|
||||
|
||||
if !self.fail {
|
||||
let d2 = self.store(d2);
|
||||
tabu_list.insert((d1, d2));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis, l1) => {
|
||||
if d2.is_ref() {
|
||||
if tabu_list.contains(&(d1, d2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Self::unify_list(self, l1, d2);
|
||||
|
||||
if !self.fail {
|
||||
let d2 = self.store(d2);
|
||||
tabu_list.insert((d1, d2));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::PStrLoc) => {
|
||||
read_heap_cell!(d2,
|
||||
(HeapCellValueTag::PStrLoc |
|
||||
HeapCellValueTag::Lis |
|
||||
HeapCellValueTag::Str) => {
|
||||
if tabu_list.contains(&(d1, d2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::CStr |
|
||||
HeapCellValueTag::AttrVar |
|
||||
HeapCellValueTag::Var |
|
||||
HeapCellValueTag::StackVar) => {
|
||||
}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
break;
|
||||
}
|
||||
);
|
||||
|
||||
Self::unify_partial_string(self, d1, d2);
|
||||
|
||||
if !self.fail && !d2.is_constant() {
|
||||
let d2 = self.store(d2);
|
||||
tabu_list.insert((d1, d2));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::CStr) => {
|
||||
read_heap_cell!(d2,
|
||||
(HeapCellValueTag::AttrVar, h) => {
|
||||
Self::bind(self, Ref::attr_var(h), d1);
|
||||
continue;
|
||||
}
|
||||
(HeapCellValueTag::Var, h) => {
|
||||
Self::bind(self, Ref::heap_cell(h), d1);
|
||||
continue;
|
||||
}
|
||||
(HeapCellValueTag::StackVar, s) => {
|
||||
Self::bind(self, Ref::stack_cell(s), d1);
|
||||
continue;
|
||||
}
|
||||
(HeapCellValueTag::Str |
|
||||
HeapCellValueTag::Lis |
|
||||
HeapCellValueTag::PStrLoc) => {
|
||||
}
|
||||
(HeapCellValueTag::CStr) => {
|
||||
self.fail = d1 != d2;
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
return;
|
||||
}
|
||||
);
|
||||
|
||||
Self::unify_partial_string(self, d2, d1);
|
||||
}
|
||||
(HeapCellValueTag::F64, f1) => {
|
||||
Self::unify_f64(self, f1, d2);
|
||||
}
|
||||
(HeapCellValueTag::Fixnum, n1) => {
|
||||
Self::unify_fixnum(self, n1, d2);
|
||||
}
|
||||
(HeapCellValueTag::Char, c1) => {
|
||||
Self::unify_char(self, c1, d2);
|
||||
}
|
||||
(HeapCellValueTag::Cons, ptr_1) => {
|
||||
Self::unify_constant(self, ptr_1, d2);
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn bind(&mut self, r: Ref, value: HeapCellValue);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bind_with_occurs_check<U: Unifier>(unifier: &mut U, r: Ref, value: HeapCellValue) -> bool {
|
||||
if let RefTag::StackCell = r.get_tag() {
|
||||
// local variable optimization -- r cannot occur in the
|
||||
// heap structure bound to value, so don't bother
|
||||
// traversing value.
|
||||
U::bind(unifier, r, value);
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut occurs_triggered = false;
|
||||
|
||||
if !value.is_constant() {
|
||||
let machine_st: &mut MachineState = unifier.deref_mut();
|
||||
|
||||
for cell in stackful_preorder_iter(&mut machine_st.heap, &mut machine_st.stack, value) {
|
||||
let cell = unmark_cell_bits!(cell);
|
||||
|
||||
if let Some(inner_r) = cell.as_var() {
|
||||
if r == inner_r {
|
||||
occurs_triggered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if occurs_triggered {
|
||||
unifier.fail = true;
|
||||
} else {
|
||||
U::bind(unifier, r, value);
|
||||
}
|
||||
|
||||
return occurs_triggered;
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub(crate) struct DefaultUnifier<'a> {
|
||||
machine_st: &'a mut MachineState,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut MachineState> for DefaultUnifier<'a> {
|
||||
#[inline(always)]
|
||||
fn from(machine_st: &'a mut MachineState) -> Self {
|
||||
Self { machine_st }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Unifier for DefaultUnifier<'a> {
|
||||
fn bind(&mut self, r: Ref, value: HeapCellValue) {
|
||||
self.machine_st.bind(r, value);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct CompositeUnifierForOccursCheck<U> {
|
||||
unifier: U,
|
||||
}
|
||||
|
||||
impl<U: Unifier> Deref for CompositeUnifierForOccursCheck<U> {
|
||||
type Target = MachineState;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.unifier.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<U: Unifier> DerefMut for CompositeUnifierForOccursCheck<U> {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.unifier.deref_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<U: Unifier> From<U> for CompositeUnifierForOccursCheck<U> {
|
||||
#[inline(always)]
|
||||
fn from(unifier: U) -> Self {
|
||||
Self { unifier }
|
||||
}
|
||||
}
|
||||
|
||||
impl<U: Unifier> Unifier for CompositeUnifierForOccursCheck<U> {
|
||||
fn bind(&mut self, r: Ref, value: HeapCellValue) {
|
||||
bind_with_occurs_check(&mut self.unifier, r, value);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct CompositeUnifierForOccursCheckWithError<U: Unifier> {
|
||||
unifier: U,
|
||||
}
|
||||
|
||||
impl<U: Unifier> Deref for CompositeUnifierForOccursCheckWithError<U> {
|
||||
type Target = MachineState;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.unifier.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<U: Unifier> DerefMut for CompositeUnifierForOccursCheckWithError<U> {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.unifier.deref_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<U: Unifier> From<U> for CompositeUnifierForOccursCheckWithError<U> {
|
||||
#[inline(always)]
|
||||
fn from(unifier: U) -> Self {
|
||||
Self { unifier }
|
||||
}
|
||||
}
|
||||
|
||||
impl<U: Unifier> Unifier for CompositeUnifierForOccursCheckWithError<U> {
|
||||
fn bind(&mut self, r: Ref, value: HeapCellValue) {
|
||||
if bind_with_occurs_check(&mut self.unifier, r, value) {
|
||||
let err = self.representation_error(RepFlag::Term);
|
||||
let stub = functor_stub(atom!("unify_with_occurs_check"), 2);
|
||||
let err = self.error_form(err, stub);
|
||||
|
||||
self.throw_exception(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user