fn code_dir(&mut self) -> &mut CodeDir {
&mut self.code_dir
- }
+ }
}
pub trait SubModuleUser {
// returns true on successful import.
fn import_decl(&mut self, name: ClauseName, arity: usize, submodule: &Module) -> bool {
let name = name.defrock_brackets();
-
+
if arity == 1 {
if let Some(op_data) = submodule.op_dir.get(&(name.clone(), Fixity::Pre)) {
self.op_dir().insert((name.clone(), Fixity::Pre), op_data.clone());
false
}
}
-
+
fn use_qualified_module(&mut self, submodule: &Module, exports: Vec<PredicateKey>) -> EvalSession
{
for (name, arity) in exports {
if !submodule.module_decl.exports.contains(&(name.clone(), arity)) {
continue;
}
-
+
if !self.import_decl(name, arity, submodule) {
return EvalSession::from(EvalError::ModuleDoesNotContainExport);
}
EvalSession::EntrySuccess
}
-
+
fn use_module(&mut self, submodule: &Module) -> EvalSession {
for (name, arity) in submodule.module_decl.exports.iter().cloned() {
if !self.import_decl(name, arity, submodule) {
return EvalSession::from(EvalError::ModuleDoesNotContainExport);
}
}
-
+
EvalSession::EntrySuccess
}
}
Throw,
}
-impl ClauseType {
- pub fn fixity(&self) -> Option<Fixity> {
- match self {
- &ClauseType::Compare | &ClauseType::CompareTerm(_)
- | &ClauseType::Inlined(InlinedClauseType::CompareNumber(_))
- | &ClauseType::NotEq | &ClauseType::Is | &ClauseType::Eq => Some(Fixity::In),
- &ClauseType::Op(_, fixity) => Some(fixity),
- _ => None
- }
- }
-}
-
#[derive(Clone)]
pub enum ClauseName {
BuiltIn(&'static str),
s
}
}
-
+
match self {
ClauseName::BuiltIn(s) =>
ClauseName::BuiltIn(defrock_brackets(s)),
}
impl ClauseType {
+ pub fn fixity(&self) -> Option<Fixity> {
+ match self {
+ &ClauseType::Compare | &ClauseType::CompareTerm(_)
+ | &ClauseType::Inlined(InlinedClauseType::CompareNumber(_))
+ | &ClauseType::NotEq | &ClauseType::Is | &ClauseType::Eq => Some(Fixity::In),
+ &ClauseType::Op(_, fixity) => Some(fixity),
+ _ => None
+ }
+ }
+
pub fn name(&self) -> ClauseName {
match self {
&ClauseType::Arg => clause_name!("arg"),
#[derive(Clone)]
pub enum ControlInstruction {
Allocate(usize), // num_frames.
- ArgCall,
- ArgExecute,
- Call(ClauseName, usize, usize), // name, arity, perm_vars after threshold.
- CallN(usize), // arity.
- CatchCall,
- CatchExecute,
+ CallClause(ClauseType, usize, usize, bool), // name, arity, perm_vars after threshold, last call.
CheckCpExecute,
- CompareCall,
- CompareExecute,
- CompareTermCall(CompareTermQT),
- CompareTermExecute(CompareTermQT),
- DisplayCall,
- DisplayExecute,
Deallocate,
- DuplicateTermCall,
- DuplicateTermExecute,
- DynamicIs,
- EqCall,
- EqExecute,
- Execute(ClauseName, usize),
- ExecuteN(usize),
- FunctorCall,
- FunctorExecute,
+ DynamicIs,
GetCleanerCall,
- GotoCall(usize, usize), // p, arity.
- GotoExecute(usize, usize), // p, arity.
- GroundCall,
- GroundExecute,
- IsCall(RegType, ArithmeticTerm),
- IsExecute(RegType, ArithmeticTerm),
- JmpByCall(usize, usize), // arity, global_offset.
- JmpByExecute(usize, usize),
- KeySortCall,
- KeySortExecute,
- NotEqCall,
- NotEqExecute,
- Proceed,
- SortCall,
- SortExecute,
- ThrowCall,
- ThrowExecute,
+ Goto(usize, usize, bool), // p, arity, last call.
+ IsClause(bool, RegType, ArithmeticTerm), // last call, register of var, term.
+ JmpBy(usize, usize, usize, bool), // arity, global_offset, perm_vars after threshold, last call.
+ Proceed
}
impl ControlInstruction {
pub fn is_jump_instr(&self) -> bool {
match self {
- &ControlInstruction::ArgCall => true,
- &ControlInstruction::ArgExecute => true,
- &ControlInstruction::Call(_, _, _) => true,
- &ControlInstruction::CatchCall => true,
- &ControlInstruction::CatchExecute => true,
- &ControlInstruction::CompareTermCall(..) => true,
- &ControlInstruction::CompareTermExecute(..) => true,
- &ControlInstruction::DisplayCall => true,
- &ControlInstruction::DisplayExecute => true,
- &ControlInstruction::DuplicateTermCall => true,
- &ControlInstruction::DuplicateTermExecute => true,
+ &ControlInstruction::CallClause(..) => true,
&ControlInstruction::DynamicIs => true,
- &ControlInstruction::EqCall => true,
- &ControlInstruction::EqExecute => true,
- &ControlInstruction::Execute(_, _) => true,
- &ControlInstruction::CallN(_) => true,
- &ControlInstruction::ExecuteN(_) => true,
- &ControlInstruction::FunctorCall => true,
- &ControlInstruction::FunctorExecute => true,
- &ControlInstruction::NotEqCall => true,
- &ControlInstruction::NotEqExecute => true,
- &ControlInstruction::ThrowCall => true,
- &ControlInstruction::ThrowExecute => true,
&ControlInstruction::GetCleanerCall => true,
- &ControlInstruction::GotoCall(..) => true,
- &ControlInstruction::GotoExecute(..) => true,
- &ControlInstruction::GroundCall => true,
- &ControlInstruction::GroundExecute => true,
- &ControlInstruction::IsCall(..) => true,
- &ControlInstruction::IsExecute(..) => true,
- &ControlInstruction::JmpByCall(..) => true,
- &ControlInstruction::JmpByExecute(..) => true,
- &ControlInstruction::CompareCall => true,
- &ControlInstruction::CompareExecute => true,
- &ControlInstruction::SortCall => true,
- &ControlInstruction::SortExecute => true,
- &ControlInstruction::KeySortCall => true,
- &ControlInstruction::KeySortExecute => true,
+ &ControlInstruction::Goto(..) => true,
+ &ControlInstruction::IsClause(..) => true,
+ &ControlInstruction::JmpBy(..) => true,
_ => false
}
}
TopLevel(usize, usize) // chunk_num, offset.
}
-impl CodePtr {
+impl CodePtr {
pub fn module_name(&self) -> ClauseName {
match self {
&CodePtr::DirEntry(_, ref name) => name.clone(),
put_unsafe_value!(4, 2),
put_value!(perm_v!(2), 3)],
deallocate!(),
- jmp_execute!(3, 1),
+ jmp_execute!(3, 1, 0),
try_me_else!(5), // 304.
is_var!(temp_v!(1)),
neck_cut!(),
remove_inference_counter!(perm_v!(3), temp_v!(2)),
query![put_value!(perm_v!(3), 1),
put_var!(perm_v!(2), 2)],
- jmp_call!(2, 5),
+ jmp_call!(2, 5, 0),
erase_ball!(),
query![put_value!(perm_v!(3), 1),
put_unsafe_value!(2, 2),
impl<'a> ConjunctInfo<'a>
{
- fn new(perm_vs: VariableFixtures<'a>, num_of_chunks: usize, has_deep_cut: bool) -> Self
- {
+ fn new(perm_vs: VariableFixtures<'a>, num_of_chunks: usize, has_deep_cut: bool) -> Self {
ConjunctInfo { perm_vs, num_of_chunks, has_deep_cut }
}
fn add_conditional_call(code: &mut Code, qt: &QueryTerm, pvs: usize)
{
match qt {
- &QueryTerm::Jump(ref vars) => {
- code.push(jmp_call!(vars.len(), 0));
- },
+ &QueryTerm::Jump(ref vars) =>
+ code.push(jmp_call!(vars.len(), 0, pvs)),
&QueryTerm::Clause(_, ref ct, ref terms) =>
- match ct {
- &ClauseType::CallWithInferenceLimit =>
- code.push(goto_call!(393, 3)),
- &ClauseType::SetupCallCleanup =>
- code.push(goto_call!(294, 3)),
- &ClauseType::Arg => {
- let call = ControlInstruction::ArgCall;
- code.push(Line::Control(call));
- },
- &ClauseType::CallN => {
- let call = ControlInstruction::CallN(terms.len());
- code.push(Line::Control(call));
- },
- &ClauseType::Catch =>
- code.push(Line::Control(ControlInstruction::CatchCall)),
- &ClauseType::Compare =>
- code.push(Line::Control(ControlInstruction::CompareCall)),
- &ClauseType::CompareTerm(qt) =>
- code.push(Line::Control(ControlInstruction::CompareTermCall(qt))),
- &ClauseType::Display =>
- code.push(Line::Control(ControlInstruction::DisplayCall)),
- &ClauseType::DuplicateTerm =>
- code.push(Line::Control(ControlInstruction::DuplicateTermCall)),
- &ClauseType::Eq =>
- code.push(Line::Control(ControlInstruction::EqCall)),
- &ClauseType::Ground =>
- code.push(Line::Control(ControlInstruction::GroundCall)),
- &ClauseType::Functor =>
- code.push(Line::Control(ControlInstruction::FunctorCall)),
- &ClauseType::Inlined(_) =>
- code.push(proceed!()),
- &ClauseType::KeySort =>
- code.push(keysort_call!()),
- &ClauseType::NotEq =>
- code.push(Line::Control(ControlInstruction::NotEqCall)),
- &ClauseType::Named(ref name) | &ClauseType::Op(ref name, _) => {
- let call = ControlInstruction::Call(name.clone(), terms.len(), pvs);
- code.push(Line::Control(call));
- },
- &ClauseType::Sort =>
- code.push(sort_call!()),
- &ClauseType::Throw =>
- code.push(Line::Control(ControlInstruction::ThrowCall)),
- _ => {}
- },
+ code.push(call_clause!(ct.clone(), terms.len(), pvs)),
_ => {}
}
}
match code.last_mut() {
Some(&mut Line::Control(ref mut ctrl)) =>
match ctrl.clone() {
- ControlInstruction::GotoCall(p, arity) =>
- *ctrl = ControlInstruction::GotoExecute(p, arity),
- ControlInstruction::ArgCall =>
- *ctrl = ControlInstruction::ArgExecute,
- ControlInstruction::Call(name, arity, _) =>
- *ctrl = ControlInstruction::Execute(name, arity),
- ControlInstruction::CallN(arity) =>
- *ctrl = ControlInstruction::ExecuteN(arity),
- ControlInstruction::CompareCall =>
- *ctrl = ControlInstruction::CompareExecute,
- ControlInstruction::CompareTermCall(qt) =>
- *ctrl = ControlInstruction::CompareTermExecute(qt),
- ControlInstruction::DisplayCall =>
- *ctrl = ControlInstruction::DisplayExecute,
- ControlInstruction::DuplicateTermCall =>
- *ctrl = ControlInstruction::DuplicateTermExecute,
- ControlInstruction::EqCall =>
- *ctrl = ControlInstruction::EqExecute,
- ControlInstruction::GroundCall =>
- *ctrl = ControlInstruction::GroundExecute,
- ControlInstruction::FunctorCall =>
- *ctrl = ControlInstruction::FunctorExecute,
- ControlInstruction::JmpByCall(arity, offset) =>
- *ctrl = ControlInstruction::JmpByExecute(arity, offset),
- ControlInstruction::NotEqCall =>
- *ctrl = ControlInstruction::NotEqExecute,
- ControlInstruction::CatchCall =>
- *ctrl = ControlInstruction::CatchExecute,
- ControlInstruction::KeySortCall =>
- *ctrl = ControlInstruction::KeySortExecute,
- ControlInstruction::SortCall =>
- *ctrl = ControlInstruction::SortExecute,
- ControlInstruction::ThrowCall =>
- *ctrl = ControlInstruction::ThrowExecute,
- ControlInstruction::IsCall(r, at) =>
- *ctrl = ControlInstruction::IsExecute(r, at),
+ ControlInstruction::CallClause(ct, arity, pvs, false) =>
+ *ctrl = ControlInstruction::CallClause(ct, arity, pvs, true),
+ ControlInstruction::Goto(p, arity, false) =>
+ *ctrl = ControlInstruction::Goto(p, arity, true),
+ ControlInstruction::JmpBy(arity, offset, pvs, false) =>
+ *ctrl = ControlInstruction::JmpBy(arity, offset, pvs, true),
+ ControlInstruction::IsClause(false, r, at) =>
+ *ctrl = ControlInstruction::IsClause(true, r, at),
ControlInstruction::Proceed => {},
_ => dealloc_index += 1 // = code.len()
},
fn is_in_use(&self, r: usize) -> bool {
let in_use_range = r <= self.arity && r >= self.arg_c;
- self.in_use.contains(&r) || in_use_range
+ in_use_range || self.in_use.contains(&r)
}
fn alloc_with_cr(&self, var: &Var) -> usize
match self {
&ControlInstruction::Allocate(num_cells) =>
write!(f, "allocate {}", num_cells),
- &ControlInstruction::ArgCall =>
- write!(f, "arg_call"),
- &ControlInstruction::ArgExecute =>
- write!(f, "arg_execute"),
- &ControlInstruction::Call(ref name, arity, pvs) =>
- write!(f, "call {}/{}, {}", name, arity, pvs),
- &ControlInstruction::CallN(arity) =>
- write!(f, "call_N {}", arity),
- &ControlInstruction::CatchCall =>
- write!(f, "catch_call"),
- &ControlInstruction::CatchExecute =>
- write!(f, "catch_execute"),
+ &ControlInstruction::CallClause(ref ct, arity, pvs, true) =>
+ write!(f, "execute {}/{}, {}", ct.name(), arity, pvs),
+ &ControlInstruction::CallClause(ref ct, arity, pvs, false) =>
+ write!(f, "call {}/{}, {}", ct.name(), arity, pvs),
&ControlInstruction::CheckCpExecute =>
write!(f, "check_cp_execute"),
- &ControlInstruction::CompareCall =>
- write!(f, "compare_call"),
- &ControlInstruction::CompareExecute =>
- write!(f, "compare_execute"),
- &ControlInstruction::CompareTermCall(qt) =>
- write!(f, "compare_term_call {}", qt),
- &ControlInstruction::CompareTermExecute(qt) =>
- write!(f, "compare_term_execute {}", qt),
- &ControlInstruction::DisplayCall =>
- write!(f, "display_call"),
- &ControlInstruction::DisplayExecute =>
- write!(f, "display_execute"),
- &ControlInstruction::DuplicateTermCall =>
- write!(f, "call_duplicate_term"),
- &ControlInstruction::DuplicateTermExecute =>
- write!(f, "execute_duplicate_term"),
- &ControlInstruction::EqCall =>
- write!(f, "eq_call"),
- &ControlInstruction::EqExecute =>
- write!(f, "eq_execute"),
- &ControlInstruction::ExecuteN(arity) =>
- write!(f, "execute_N {}", arity),
- &ControlInstruction::FunctorCall =>
- write!(f, "functor_call"),
- &ControlInstruction::FunctorExecute =>
- write!(f, "functor_execute"),
&ControlInstruction::Deallocate =>
write!(f, "deallocate"),
- &ControlInstruction::GroundCall =>
- write!(f, "ground_call"),
- &ControlInstruction::GroundExecute =>
- write!(f, "ground_execute"),
- &ControlInstruction::Execute(ref name, arity) =>
- write!(f, "execute {}/{}", name, arity),
&ControlInstruction::GetCleanerCall =>
write!(f, "get_cleaner_call"),
- &ControlInstruction::GotoCall(p, arity) =>
+ &ControlInstruction::Goto(p, arity, false) =>
write!(f, "goto_call {}/{}", p, arity),
- &ControlInstruction::GotoExecute(p, arity) =>
+ &ControlInstruction::Goto(p, arity, true) =>
write!(f, "goto_execute {}/{}", p, arity),
- &ControlInstruction::IsCall(r, ref at) =>
+ &ControlInstruction::IsClause(false, r, ref at) =>
write!(f, "is_call {}, {}", r, at),
- &ControlInstruction::IsExecute(r, ref at) =>
+ &ControlInstruction::IsClause(true, r, ref at) =>
write!(f, "is_execute {}, {}", r, at),
&ControlInstruction::DynamicIs =>
write!(f, "call_is"),
- &ControlInstruction::JmpByCall(arity, offset) =>
- write!(f, "jmp_by_call {}/{}", offset, arity),
- &ControlInstruction::JmpByExecute(arity, offset) =>
- write!(f, "jmp_by_execute {}/{}", offset, arity),
- &ControlInstruction::KeySortCall =>
- write!(f, "keysort_call"),
- &ControlInstruction::KeySortExecute =>
- write!(f, "keysort_execute"),
- &ControlInstruction::NotEqCall =>
- write!(f, "neq_call"),
- &ControlInstruction::NotEqExecute =>
- write!(f, "neq_execute"),
+ &ControlInstruction::JmpBy(arity, offset, pvs, false) =>
+ write!(f, "jmp_by_call {}/{}, {}", offset, arity, pvs),
+ &ControlInstruction::JmpBy(arity, offset, pvs, true) =>
+ write!(f, "jmp_by_execute {}/{}, {}", offset, arity, pvs),
&ControlInstruction::Proceed =>
write!(f, "proceed"),
- &ControlInstruction::SortCall =>
- write!(f, "call_sort"),
- &ControlInstruction::SortExecute =>
- write!(f, "execute_sort"),
- &ControlInstruction::ThrowCall =>
- write!(f, "call_throw"),
- &ControlInstruction::ThrowExecute =>
- write!(f, "execute_throw"),
}
}
}
for (idx, line) in code.iter_mut().enumerate() {
match line {
- &mut Line::Control(ControlInstruction::JmpByExecute(_, ref mut offset))
- | &mut Line::Control(ControlInstruction::JmpByCall(_, ref mut offset)) if *offset == 0 => {
- *offset = code_len - idx;
- break;
+ &mut Line::Control(ControlInstruction::JmpBy(_, ref mut offset, ..)) if *offset == 0 => {
+ *offset = code_len - idx;
+ break;
},
_ => {}
};
use prolog::copier::*;
use prolog::num::{BigInt, BigUint, Zero, One};
use prolog::or_stack::*;
+use prolog::heap_print::*;
use prolog::tabled_rc::*;
use downcast::Any;
+use std::cmp::Ordering;
use std::collections::HashMap;
use std::mem::swap;
use std::ops::{Index, IndexMut};
pub(crate) fn get(&self, name: ClauseName, arity: usize, p: &CodePtr)
-> Option<(usize, ClauseName)>
{
- let code_dir = self.get_current_code_dir(p);
+ let code_dir = self.get_current_code_dir(p);
code_dir.get(&(name, arity)).cloned()
}
}
},
None => machine_st.fail = true
};
-
+
Ok(())
}
Ok(())
}
+
+ fn try_call_clause<'a>(&mut self, machine_st: &mut MachineState, code_dirs: CodeDirs<'a>,
+ ct: &ClauseType, arity: usize, lco: bool)
+ -> CallResult
+ {
+ match ct {
+ &ClauseType::Arg => {
+ if !lco {
+ machine_st.cp = machine_st.p.clone() + 1;
+ }
+
+ machine_st.num_of_args = 3;
+ machine_st.b0 = machine_st.b;
+ machine_st.p = CodePtr::DirEntry(150, clause_name!("builtin"));
+
+ Ok(())
+ },
+ &ClauseType::Catch => {
+ if !lco {
+ machine_st.cp = machine_st.p.clone() + 1;
+ }
+
+ machine_st.num_of_args = 3;
+ machine_st.b0 = machine_st.b;
+ machine_st.p = CodePtr::DirEntry(5, clause_name!("builtin"));
+
+ Ok(())
+ },
+ &ClauseType::CallN =>
+ if let Some((name, arity)) = machine_st.setup_call_n(arity) {
+ if lco {
+ self.try_execute(machine_st, code_dirs, name, arity)
+ } else {
+ self.try_call(machine_st, code_dirs, name, arity)
+ }
+ } else {
+ Ok(())
+ },
+ &ClauseType::Compare => {
+ let a1 = machine_st[temp_v!(1)].clone();
+ let a2 = machine_st[temp_v!(2)].clone();
+ let a3 = machine_st[temp_v!(3)].clone();
+
+ let c = Addr::Con(match machine_st.compare_term_test(&a2, &a3) {
+ Ordering::Greater => atom!(">", machine_st.atom_tbl),
+ Ordering::Equal => atom!("=", machine_st.atom_tbl),
+ Ordering::Less => atom!("<", machine_st.atom_tbl)
+ });
+
+ machine_st.unify(a1, c);
+ return_from_clause!(lco, machine_st)
+ },
+ &ClauseType::CompareTerm(qt) => {
+ match qt {
+ CompareTermQT::Equal =>
+ machine_st.fail = machine_st.structural_eq_test(),
+ CompareTermQT::NotEqual =>
+ machine_st.fail = !machine_st.structural_eq_test(),
+ _ => machine_st.compare_term(qt)
+ };
+
+ return_from_clause!(lco, machine_st)
+ },
+ &ClauseType::Display => {
+ let output = machine_st.print_term(machine_st[temp_v!(1)].clone(),
+ DisplayFormatter {},
+ PrinterOutputter::new());
+
+ println!("{}", output.result());
+ return_from_clause!(lco, machine_st)
+ },
+ &ClauseType::DuplicateTerm => {
+ machine_st.duplicate_term();
+ return_from_clause!(lco, machine_st)
+ },
+ &ClauseType::Eq => {
+ machine_st.fail = machine_st.eq_test();
+ return_from_clause!(lco, machine_st)
+ },
+ &ClauseType::Ground => {
+ machine_st.fail = machine_st.ground_test();
+ return_from_clause!(lco, machine_st)
+ },
+ &ClauseType::Functor => {
+ machine_st.try_functor()?;
+ return_from_clause!(lco, machine_st)
+ },
+ &ClauseType::NotEq => {
+ machine_st.fail = !machine_st.eq_test();
+ return_from_clause!(lco, machine_st)
+ },
+ &ClauseType::Sort => {
+ let mut list = machine_st.try_from_list(temp_v!(1))?;
+
+ list.sort_unstable_by(|a1, a2| machine_st.compare_term_test(a1, a2));
+ machine_st.term_dedup(&mut list);
+
+ let heap_addr = Addr::HeapCell(machine_st.to_list(list.into_iter()));
+
+ let r2 = machine_st[temp_v!(2)].clone();
+ machine_st.unify(r2, heap_addr);
+
+ return_from_clause!(lco, machine_st)
+ },
+ &ClauseType::KeySort => {
+ let mut list = machine_st.try_from_list(temp_v!(1))?;
+ let mut key_pairs = Vec::new();
+
+ for val in list {
+ let key = machine_st.project_onto_key(val.clone())?;
+ key_pairs.push((key, val.clone()));
+ }
+
+ key_pairs.sort_by(|a1, a2| machine_st.compare_term_test(&a1.0, &a2.0));
+
+ let key_pairs = key_pairs.into_iter().map(|kp| kp.1);
+ let heap_addr = Addr::HeapCell(machine_st.to_list(key_pairs));
+
+ let r2 = machine_st[temp_v!(2)].clone();
+ machine_st.unify(r2, heap_addr);
+
+ return_from_clause!(lco, machine_st)
+ },
+ &ClauseType::Throw => {
+ if !lco {
+ machine_st.cp = machine_st.p.clone() + 1;
+ }
+
+ machine_st.goto_throw();
+ Ok(())
+ },
+ &ClauseType::Named(ref name) | &ClauseType::Op(ref name, _) =>
+ if lco {
+ self.try_execute(machine_st, code_dirs, name.clone(), arity)
+ } else {
+ self.try_call(machine_st, code_dirs, name.clone(), arity)
+ },
+ &ClauseType::CallWithInferenceLimit => {
+ machine_st.goto_ptr(CodePtr::DirEntry(393, clause_name!("builtin")), 3, lco);
+ Ok(())
+ },
+ &ClauseType::SetupCallCleanup => {
+ machine_st.goto_ptr(CodePtr::DirEntry(294, clause_name!("builtin")), 3, lco);
+ Ok(())
+ },
+ _ => panic!("(is)/2 or an inlined command: should have been superseded by previous clause.")
+ }
+ }
}
downcast!(CallPolicy);
self.prev_policy.trust(machine_st, offset)?;
self.increment()
}
+
+ fn try_call_clause<'a>(&mut self, machine_st: &mut MachineState, code_dirs: CodeDirs<'a>,
+ ct: &ClauseType, arity: usize, lco: bool)
+ -> CallResult
+ {
+ self.prev_policy.try_call_clause(machine_st, code_dirs, ct, arity, lco)?;
+ self.increment()
+ }
}
pub(crate) trait CutPolicy: Any {
self.trail(r1);
}
- pub(super) fn print_term<Fmt, Outputter>(&self, a: Addr, fmt: Fmt, output: Outputter)
- -> Outputter
+ pub(super) fn print_term<Fmt, Outputter>(&self, a: Addr, fmt: Fmt, output: Outputter) -> Outputter
where Fmt: HeapCellValueFormatter, Outputter: HeapCellValueOutputter
{
let iter = HeapCellPreOrderIterator::new(&self, a);
printer.print()
}
- fn unify(&mut self, a1: Addr, a2: Addr) {
+ pub(super) fn unify(&mut self, a1: Addr, a2: Addr) {
let mut pdl = vec![a1, a2];
self.fail = false;
}
}
- fn goto_throw(&mut self) {
+ pub(super) fn goto_throw(&mut self) {
self.num_of_args = 1;
self.b0 = self.b;
self.p = CodePtr::DirEntry(59, clause_name!("builtin"));
self.goto_throw();
}
- fn setup_call_n(&mut self, arity: usize) -> Option<PredicateKey>
+ pub(super) fn setup_call_n(&mut self, arity: usize) -> Option<PredicateKey>
{
let addr = self.store(self.deref(self.registers[arity].clone()));
self.p += 1;
}
- fn compare_term(&mut self, qt: CompareTermQT) {
+ pub(super) fn compare_term(&mut self, qt: CompareTermQT) {
let a1 = self[temp_v!(1)].clone();
let a2 = self[temp_v!(2)].clone();
};
}
- fn compare_term_test(&self, a1: &Addr, a2: &Addr) -> Ordering {
+ pub(super) fn compare_term_test(&self, a1: &Addr, a2: &Addr) -> Ordering {
let iter = self.zipped_acyclic_pre_order_iter(a1.clone(), a2.clone());
for (v1, v2) in iter {
};
}
- fn try_functor(&mut self) -> Result<(), Vec<HeapCellValue>> {
+ pub(super) fn try_functor(&mut self) -> Result<(), Vec<HeapCellValue>> {
let a1 = self.store(self.deref(self[temp_v!(1)].clone()));
match a1.clone() {
Ok(())
}
- fn term_dedup(&self, list: &mut Vec<Addr>) {
+ pub(super) fn term_dedup(&self, list: &mut Vec<Addr>) {
let mut result = vec![];
for a2 in list.iter().cloned() {
result.push(a2);
}
-
+
*list = result;
}
-
- fn to_list<Iter: Iterator<Item=Addr>>(&mut self, values: Iter) -> usize {
+
+ pub(super) fn to_list<Iter: Iterator<Item=Addr>>(&mut self, values: Iter) -> usize {
let head_addr = self.heap.h;
for value in values {
head_addr
}
- fn try_from_list(&self, r: RegType) -> Result<Vec<Addr>, Vec<HeapCellValue>>
+ pub(super) fn try_from_list(&self, r: RegType) -> Result<Vec<Addr>, Vec<HeapCellValue>>
{
let a1 = self.store(self.deref(self[r].clone()));
result.push(self.heap[l].as_addr(l));
l += 1;
-
+
loop {
match self.heap[l].clone() {
HeapCellValue::Addr(Addr::Lis(hcp)) => {
}
}
- fn project_onto_key(&self, a: Addr) -> Result<Addr, Vec<HeapCellValue>> {
+ pub(super) fn project_onto_key(&self, a: Addr) -> Result<Addr, Vec<HeapCellValue>> {
match self.store(self.deref(a)) {
Addr::Str(s) =>
match self.heap[s].clone() {
a => Err(functor!("type_error", 2, [heap_atom!("callable"), HeapCellValue::Addr(a)]))
}
}
-
- fn duplicate_term(&mut self) {
+
+ pub(super) fn duplicate_term(&mut self) {
let old_h = self.heap.h;
let a1 = self[temp_v!(1)].clone();
}
// returns true on failure.
- fn eq_test(&self) -> bool
+ pub(super) fn eq_test(&self) -> bool
{
let a1 = self[temp_v!(1)].clone();
let a2 = self[temp_v!(2)].clone();
}
// returns true on failure.
- fn structural_eq_test(&self) -> bool
+ pub(super) fn structural_eq_test(&self) -> bool
{
let a1 = self[temp_v!(1)].clone();
let a2 = self[temp_v!(2)].clone();
}
// returns true on failure.
- fn ground_test(&self) -> bool
+ pub(super) fn ground_test(&self) -> bool
{
let a = self.store(self.deref(self[temp_v!(1)].clone()));
self.and_stack.push(gi, self.e, self.cp.clone(), num_cells);
self.e = self.and_stack.len() - 1;
},
- &ControlInstruction::ArgCall => {
- self.cp = self.p.clone() + 1;
- self.num_of_args = 3;
- self.b0 = self.b;
- self.p = CodePtr::DirEntry(150, clause_name!("builtin"));
- },
- &ControlInstruction::ArgExecute => {
- self.num_of_args = 3;
- self.b0 = self.b;
- self.p = CodePtr::DirEntry(150, clause_name!("builtin"));
- },
- &ControlInstruction::Call(ref name, arity, _) =>
- try_or_fail!(self, call_policy.try_call(self, code_dirs, name.clone(), arity)),
- &ControlInstruction::CatchCall => {
- self.cp = self.p.clone() + 1;
- self.num_of_args = 3;
- self.b0 = self.b;
- self.p = CodePtr::DirEntry(5, clause_name!("builtin"));
- },
- &ControlInstruction::CatchExecute => {
- self.num_of_args = 3;
- self.b0 = self.b;
- self.p = CodePtr::DirEntry(5, clause_name!("builtin"));
- },
- &ControlInstruction::CallN(arity) =>
- if let Some((name, arity)) = self.setup_call_n(arity) {
- try_or_fail!(self, call_policy.try_call(self, code_dirs, name, arity))
- },
+ &ControlInstruction::CallClause(ref ct, arity, _, lco) =>
+ try_or_fail!(self, call_policy.try_call_clause(self, code_dirs, ct, arity, lco)),
&ControlInstruction::CheckCpExecute => {
let a = self.store(self.deref(self[temp_v!(2)].clone()));
}
};
},
- &ControlInstruction::CompareCall => {
- let a1 = self[temp_v!(1)].clone();
- let a2 = self[temp_v!(2)].clone();
- let a3 = self[temp_v!(3)].clone();
-
- let c = Addr::Con(match self.compare_term_test(&a2, &a3) {
- Ordering::Greater => atom!(">", self.atom_tbl),
- Ordering::Equal => atom!("=", self.atom_tbl),
- Ordering::Less => atom!("<", self.atom_tbl)
- });
-
- self.unify(a1, c);
-
- self.p += 1;
- },
- &ControlInstruction::CompareExecute => {
- let a1 = self[temp_v!(1)].clone();
- let a2 = self[temp_v!(2)].clone();
- let a3 = self[temp_v!(3)].clone();
-
- let c = Addr::Con(match self.compare_term_test(&a2, &a3) {
- Ordering::Greater => atom!(">", self.atom_tbl),
- Ordering::Equal => atom!("=", self.atom_tbl),
- Ordering::Less => atom!("<", self.atom_tbl)
- });
-
- self.unify(a1, c);
-
- self.p = self.cp.clone();
- },
- &ControlInstruction::CompareTermCall(qt) => {
- match qt {
- CompareTermQT::Equal =>
- self.fail = self.structural_eq_test(),
- CompareTermQT::NotEqual =>
- self.fail = !self.structural_eq_test(),
- _ => self.compare_term(qt)
- };
-
- self.p += 1;
- },
- &ControlInstruction::CompareTermExecute(qt) => {
- match qt {
- CompareTermQT::Equal =>
- self.fail = self.structural_eq_test(),
- CompareTermQT::NotEqual =>
- self.fail = !self.structural_eq_test(),
- _ => self.compare_term(qt)
- };
-
- self.p = self.cp.clone();
- },
&ControlInstruction::Deallocate => {
let e = self.e;
self.p += 1;
},
- &ControlInstruction::DisplayCall => {
- let output = self.print_term(self[temp_v!(1)].clone(),
- DisplayFormatter {},
- PrinterOutputter::new());
-
- println!("{}", output.result());
-
- self.p += 1;
- },
- &ControlInstruction::DisplayExecute => {
- let output = self.print_term(self[temp_v!(1)].clone(),
- DisplayFormatter {},
- PrinterOutputter::new());
-
- println!("{}", output.result());
-
- self.p = self.cp.clone();
- },
- &ControlInstruction::DuplicateTermCall => {
- self.duplicate_term();
- self.p += 1;
- },
- &ControlInstruction::DuplicateTermExecute => {
- self.duplicate_term();
- self.p = self.cp.clone();
- },
&ControlInstruction::DynamicIs => {
let a = self[temp_v!(1)].clone();
let result = try_or_fail!(self, self.arith_eval_by_metacall(temp_v!(2)));
self.unify(a, Addr::Con(Constant::Number(result)));
self.p += 1;
},
- &ControlInstruction::EqCall => {
- self.fail = self.eq_test();
- self.p += 1;
- },
- &ControlInstruction::EqExecute => {
- self.fail = self.eq_test();
- self.p = self.cp.clone();
- },
- &ControlInstruction::GroundCall => {
- self.fail = self.ground_test();
- self.p += 1;
- },
- &ControlInstruction::GroundExecute => {
- self.fail = self.ground_test();
- self.p = self.cp.clone();
- },
- &ControlInstruction::Execute(ref name, arity) =>
- try_or_fail!(self, call_policy.try_execute(self, code_dirs, name.clone(), arity)),
- &ControlInstruction::ExecuteN(arity) =>
- if let Some((name, arity)) = self.setup_call_n(arity) {
- try_or_fail!(self, call_policy.try_execute(self, code_dirs, name, arity))
- },
- &ControlInstruction::FunctorCall =>
- try_or_fail!(self, {
- let val = self.try_functor();
- self.p += 1;
- val
- }),
- &ControlInstruction::FunctorExecute =>
- try_or_fail!(self, {
- let val = self.try_functor();
- self.p = self.cp.clone();
- val
- }),
&ControlInstruction::GetCleanerCall => {
let dest = self[temp_v!(1)].clone();
self.fail = true;
},
- &ControlInstruction::GotoCall(p, arity) => {
- self.cp = self.p.clone() + 1;
- self.num_of_args = arity;
- self.b0 = self.b;
- self.p = CodePtr::DirEntry(p, clause_name!("builtin"));
- },
- &ControlInstruction::GotoExecute(p, arity) => {
- self.num_of_args = arity;
- self.b0 = self.b;
- self.p = CodePtr::DirEntry(p, clause_name!("builtin"));
- },
- &ControlInstruction::IsCall(r, ref at) => {
+ &ControlInstruction::Goto(p, arity, lco) =>
+ self.goto_ptr(CodePtr::DirEntry(p, clause_name!("builtin")), arity, lco),
+ &ControlInstruction::IsClause(lco, r, ref at) => {
let a1 = self[r].clone();
let a2 = try_or_fail!(self, self.get_number(at));
self.unify(a1, Addr::Con(Constant::Number(a2)));
- self.p += 1;
+ try_or_fail!(self, return_from_clause!(lco, self));
},
- &ControlInstruction::IsExecute(r, ref at) => {
- let a1 = self[r].clone();
- let a2 = try_or_fail!(self, self.get_number(at));
+ &ControlInstruction::JmpBy(arity, offset, _, lco) => {
+ if !lco {
+ self.cp = self.p.clone() + 1;
+ }
- self.unify(a1, Addr::Con(Constant::Number(a2)));
- self.p = self.cp.clone();
- },
- &ControlInstruction::JmpByCall(arity, offset) => {
- self.cp = self.p.clone() + 1;
self.num_of_args = arity;
self.b0 = self.b;
self.p += offset;
},
- &ControlInstruction::JmpByExecute(arity, offset) => {
- self.num_of_args = arity;
- self.b0 = self.b;
- self.p += offset;
- },
- &ControlInstruction::NotEqCall => {
- self.fail = !self.eq_test();
- self.p += 1;
- },
- &ControlInstruction::NotEqExecute => {
- self.fail = !self.eq_test();
- self.p = self.cp.clone();
- },
&ControlInstruction::Proceed =>
self.p = self.cp.clone(),
- &ControlInstruction::SortCall => {
- let mut list = try_or_fail!(self, {
- let val = self.try_from_list(temp_v!(1));
- self.p += 1;
- val
- });
-
- list.sort_unstable_by(|a1, a2| self.compare_term_test(a1, a2));
- self.term_dedup(&mut list);
-
- let heap_addr = Addr::HeapCell(self.to_list(list.into_iter()));
-
- let r2 = self[temp_v!(2)].clone();
- self.unify(r2, heap_addr);
- },
- &ControlInstruction::SortExecute => {
- let mut list = try_or_fail!(self, {
- let val = self.try_from_list(temp_v!(1));
- self.p = self.cp.clone();
- val
- });
-
- list.sort_unstable_by(|a1, a2| self.compare_term_test(a1, a2));
- self.term_dedup(&mut list);
-
- let heap_addr = Addr::HeapCell(self.to_list(list.into_iter()));
-
- let r2 = self[temp_v!(2)].clone();
- self.unify(r2, heap_addr);
- },
- &ControlInstruction::KeySortCall => {
- let mut list = try_or_fail!(self, {
- let val = self.try_from_list(temp_v!(1));
- self.p += 1;
- val
- });
-
- let mut key_pairs = Vec::new();
-
- for val in list {
- let key = try_or_fail!(self, self.project_onto_key(val.clone()));
- key_pairs.push((key, val.clone()));
- }
-
- key_pairs.sort_by(|a1, a2| self.compare_term_test(&a1.0, &a2.0));
-
- let key_pairs = key_pairs.into_iter().map(|kp| kp.1);
- let heap_addr = Addr::HeapCell(self.to_list(key_pairs));
-
- let r2 = self[temp_v!(2)].clone();
- self.unify(r2, heap_addr);
- },
- &ControlInstruction::KeySortExecute => {
- let mut list = try_or_fail!(self, {
- let val = self.try_from_list(temp_v!(1));
- self.p = self.cp.clone();
- val
- });
-
- let mut key_pairs = Vec::new();
-
- for val in list {
- let key = try_or_fail!(self, self.project_onto_key(val.clone()));
- key_pairs.push((key, val.clone()));
- }
-
- key_pairs.sort_by(|a1, a2| self.compare_term_test(&a1.0, &a2.0));
-
- let key_pairs = key_pairs.into_iter().map(|kp| kp.1);
- let heap_addr = Addr::HeapCell(self.to_list(key_pairs));
-
- let r2 = self[temp_v!(2)].clone();
- self.unify(r2, heap_addr);
- },
- &ControlInstruction::ThrowCall => {
- self.cp = self.p.clone() + 1;
- self.goto_throw();
- },
- &ControlInstruction::ThrowExecute => {
- self.goto_throw();
- },
};
}
+ pub(super) fn goto_ptr(&mut self, p: CodePtr, arity: usize, lco:bool) {
+ if !lco {
+ self.cp = self.p.clone() + 1;
+ }
+
+ self.num_of_args = arity;
+ self.b0 = self.b;
+ self.p = p;
+ }
+
pub(super) fn execute_indexed_choice_instr(&mut self, instr: &IndexedChoiceInstruction,
call_policy: &mut Box<CallPolicy>)
{
)
}
+macro_rules! call_clause {
+ ($ct:expr, $arity:expr, $pvs:expr) => (
+ Line::Control(ControlInstruction::CallClause($ct, $arity, $pvs, false))
+ )
+}
+
macro_rules! call_n {
($arity:expr) => (
- Line::Control(ControlInstruction::CallN($arity))
+ Line::Control(ControlInstruction::CallClause(ClauseType::CallN, $arity, 0, false))
)
}
macro_rules! execute_n {
($arity:expr) => (
- Line::Control(ControlInstruction::ExecuteN($arity))
+ Line::Control(ControlInstruction::CallClause(ClauseType::CallN, $arity, 0, true))
)
}
macro_rules! goto_call {
($line:expr, $arity:expr) => (
- Line::Control(ControlInstruction::GotoCall($line, $arity))
+ Line::Control(ControlInstruction::Goto($line, $arity, false))
)
}
macro_rules! goto_execute {
($line:expr, $arity:expr) => (
- Line::Control(ControlInstruction::GotoExecute($line, $arity))
+ Line::Control(ControlInstruction::Goto($line, $arity, true))
)
}
macro_rules! is_call {
($r:expr, $at:expr) => (
- Line::Control(ControlInstruction::IsCall($r, $at))
+ Line::Control(ControlInstruction::IsClause(false, $r, $at))
+
)
}
macro_rules! duplicate_term {
() => (
- Line::Control(ControlInstruction::DuplicateTermCall)
+ Line::Control(ControlInstruction::CallClause(ClauseType::DuplicateTerm, 2, 0, false))
)
}
macro_rules! functor_call {
() => (
- Line::Control(ControlInstruction::FunctorCall)
+ Line::Control(ControlInstruction::CallClause(ClauseType::Functor, 3, 0, false))
)
}
macro_rules! functor_execute {
() => (
- Line::Control(ControlInstruction::FunctorExecute)
+ Line::Control(ControlInstruction::CallClause(ClauseType::Functor, 3, 0, true))
)
}
macro_rules! display {
() => (
- Line::Control(ControlInstruction::DisplayCall)
+ Line::Control(ControlInstruction::CallClause(ClauseType::Display, 1, 0, false))
)
}
}
macro_rules! jmp_call {
- ($arity:expr, $offset:expr) => (
- Line::Control(ControlInstruction::JmpByCall($arity, $offset))
+ ($arity:expr, $offset:expr, $pvs:expr) => (
+ Line::Control(ControlInstruction::JmpBy($arity, $offset, $pvs, false))
)
}
macro_rules! jmp_execute {
- ($arity:expr, $offset:expr) => (
- Line::Control(ControlInstruction::JmpByExecute($arity, $offset))
+ ($arity:expr, $offset:expr, $pvs:expr) => (
+ Line::Control(ControlInstruction::JmpBy($arity, $offset, $pvs, true))
)
}
macro_rules! ground_execute {
() => (
- Line::Control(ControlInstruction::GroundExecute)
+ Line::Control(ControlInstruction::CallClause(ClauseType::Ground, 1, 0, true))
)
}
macro_rules! eq_execute {
() => (
- Line::Control(ControlInstruction::EqExecute)
+ Line::Control(ControlInstruction::CallClause(ClauseType::Eq, 2, 0, true))
)
}
macro_rules! not_eq_execute {
() => (
- Line::Control(ControlInstruction::NotEqExecute)
+ Line::Control(ControlInstruction::CallClause(ClauseType::NotEq, 2, 0, true))
)
}
macro_rules! compare_term_execute {
($qt:expr) => (
- Line::Control(ControlInstruction::CompareTermExecute($qt))
+ Line::Control(ControlInstruction::CallClause(ClauseType::CompareTerm($qt), 2, 0, true))
)
}
macro_rules! compare_execute {
() => (
- Line::Control(ControlInstruction::CompareExecute)
+ Line::Control(ControlInstruction::CallClause(ClauseType::Compare, 2, 0, true))
)
}
)
}
-macro_rules! sort_call {
+macro_rules! sort_execute {
() => (
- Line::Control(ControlInstruction::SortCall)
+ Line::Control(ControlInstruction::CallClause(ClauseType::Sort, 2, 0, true))
)
}
-macro_rules! keysort_call {
+macro_rules! keysort_execute {
() => (
- Line::Control(ControlInstruction::KeySortCall)
+ Line::Control(ControlInstruction::CallClause(ClauseType::KeySort, 2, 0, true))
)
}
-macro_rules! sort_execute {
- () => (
- Line::Control(ControlInstruction::SortExecute)
- )
-}
+macro_rules! return_from_clause {
+ ($lco:expr, $machine_st:expr) => {{
+ if $lco {
+ $machine_st.p = $machine_st.cp.clone();
+ } else {
+ $machine_st.p += 1;
+ }
-macro_rules! keysort_execute {
- () => (
- Line::Control(ControlInstruction::KeySortExecute)
- )
+ Ok(())
+ }}
}
submit(&mut wam, "f(X) :- call_with_inference_limit(g(X), 5, _).");
- assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X), 6, R).",
+ assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X), 7, R).",
[["R = true", "X = 1"],
["R = true", "X = 2"],
["R = true", "X = 3"],
["R = true", "X = 4"],
["R = !", "X = 5"]]);
- assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X), 5, R).",
+ assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X), 6, R).",
[["R = true", "X = 1"],
["R = true", "X = 2"],
["R = true", "X = 3"],
["R = true", "X = 4"],
["R = inference_limit_exceeded", "X = _1"]]);
- assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X), 3, R).",
+ assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X), 4, R).",
[["R = true", "X = 1"],
["R = true", "X = 2"],
["R = inference_limit_exceeded", "X = _1"]]);
- assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X), 2, R).",
+ assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X), 3, R).",
[["R = true", "X = 1"],
["R = inference_limit_exceeded", "X = _1"]]);
- assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X), 1, R).",
+ assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X), 2, R).",
[["R = inference_limit_exceeded", "X = _1"]]);
submit(&mut wam, "e(X) :- call_with_inference_limit(f(X), 10, _).");
- assert_prolog_success!(&mut wam, "?- call_with_inference_limit(e(X), 7, R).",
+ assert_prolog_success!(&mut wam, "?- call_with_inference_limit(e(X), 10, R).",
[["R = true", "X = 1"],
["R = true", "X = 2"],
["R = true", "X = 3"],
["R = true", "X = 4"],
["R = !", "X = 5"]]);
- assert_prolog_success!(&mut wam, "?- call_with_inference_limit(e(X), 6, R).",
+ assert_prolog_success!(&mut wam, "?- call_with_inference_limit(e(X), 8, R).",
[["R = true", "X = 1"],
["R = true", "X = 2"],
["R = true", "X = 3"],
["R = true", "X = 4"],
["R = inference_limit_exceeded", "X = _1"]]);
- assert_prolog_success!(&mut wam, "?- call_with_inference_limit(e(X), 4, R).",
+ assert_prolog_success!(&mut wam, "?- call_with_inference_limit(e(X), 6, R).",
[["R = true", "X = 1"],
["R = true", "X = 2"],
["R = inference_limit_exceeded", "X = _1"]]);
- assert_prolog_success!(&mut wam, "?- call_with_inference_limit(e(X), 3, R).",
+ assert_prolog_success!(&mut wam, "?- call_with_inference_limit(e(X), 5, R).",
[["R = true", "X = 1"],
["R = inference_limit_exceeded", "X = _1"]]);
- assert_prolog_success!(&mut wam, "?- call_with_inference_limit(e(X), 2, R).",
+ assert_prolog_success!(&mut wam, "?- call_with_inference_limit(e(X), 4, R).",
[["R = inference_limit_exceeded", "X = _1"]]);
submit(&mut wam, "f(X, R) :- call_with_inference_limit(g(X), 5, R).");
- assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X, R), 3, S).",
+ assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X, R), 4, S).",
[["S = true", "X = 1", "R = true"],
["S = true", "X = 2", "R = true"],
["S = inference_limit_exceeded", "X = _1", "R = _2"]]);
- assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X, R), 7, R).",
+ assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X, R), 8, R).",
[["R = true", "X = 1"],
["R = true", "X = 2"],
["R = true", "X = 3"],
submit(&mut wam, "g(1). g(2). g(3). g(4). g(5). g(6).");
- assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X, R), 7, S).",
+ assert_prolog_success!(&mut wam, "?- call_with_inference_limit(f(X, R), 8, S).",
[["R = true", "X = 1", "S = true"],
["R = true", "X = 2", "S = true"],
["R = true", "X = 3", "S = true"],