From 495025dafbae5fe27e8fcbda53a2ab6a3df288ad Mon Sep 17 00:00:00 2001 From: Paulo Moura Date: Mon, 27 Sep 2021 12:27:09 +0100 Subject: [PATCH 001/361] Add support for the float_integer_part/1 and float_fractional_part/1 standard arithmetic functions --- src/arithmetic.rs | 2 ++ src/instructions.rs | 4 ++++ src/machine/machine_state_impl.rs | 12 ++++++++++++ src/write.rs | 2 ++ 4 files changed, 20 insertions(+) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index f5a3beeb..2edd90fd 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -165,6 +165,8 @@ impl<'a> ArithmeticEvaluator<'a> { "round" => Ok(ArithmeticInstruction::Round(a1, t)), "ceiling" => Ok(ArithmeticInstruction::Ceiling(a1, t)), "floor" => Ok(ArithmeticInstruction::Floor(a1, t)), + "float_integer_part" => Ok(ArithmeticInstruction::FloatIntegerPart(a1, t)), + "float_fractional_part" => Ok(ArithmeticInstruction::FloatFractionalPart(a1, t)), "sign" => Ok(ArithmeticInstruction::Sign(a1, t)), "\\" => Ok(ArithmeticInstruction::BitwiseComplement(a1, t)), _ => Err(ArithmeticError::NonEvaluableFunctor( diff --git a/src/instructions.rs b/src/instructions.rs index 90cbeeab..ba9909fd 100644 --- a/src/instructions.rs +++ b/src/instructions.rs @@ -373,6 +373,8 @@ pub(crate) enum ArithmeticInstruction { Round(ArithmeticTerm, usize), Ceiling(ArithmeticTerm, usize), Floor(ArithmeticTerm, usize), + FloatIntegerPart(ArithmeticTerm, usize), + FloatFractionalPart(ArithmeticTerm, usize), Neg(ArithmeticTerm, usize), Plus(ArithmeticTerm, usize), BitwiseComplement(ArithmeticTerm, usize), @@ -495,6 +497,8 @@ impl ArithmeticInstruction { &ArithmeticInstruction::Floor(ref at, t) => { arith_instr_unary_functor(h, "floor", at, t) } + &ArithmeticInstruction::FloatIntegerPart(ref at, t) => arith_instr_unary_functor(h, "trunc", at, t), + &ArithmeticInstruction::FloatFractionalPart(ref at, t) => arith_instr_unary_functor(h, "fract", at, t), &ArithmeticInstruction::Neg(ref at, t) => arith_instr_unary_functor(h, "-", at, t), &ArithmeticInstruction::Plus(ref at, t) => arith_instr_unary_functor(h, "+", at, t), &ArithmeticInstruction::BitwiseComplement(ref at, t) => { diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index c4929d2d..60db86dd 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -1175,6 +1175,18 @@ impl MachineState { self.interms[t - 1] = self.floor(n1); self.p += 1; } + &ArithmeticInstruction::FloatIntegerPart(ref a1, t) => { + let n1 = try_or_fail!(self, self.get_number(a1)); + + self.interms[t - 1] = self.trunc(n1); + self.p += 1; + } + &ArithmeticInstruction::FloatFractionalPart(ref a1, t) => { + let n1 = try_or_fail!(self, self.get_number(a1)); + + self.interms[t - 1] = self.fract(n1); + self.p += 1; + } &ArithmeticInstruction::Plus(ref a1, t) => { let n1 = try_or_fail!(self, self.get_number(a1)); diff --git a/src/write.rs b/src/write.rs index 1c1fc3dc..1de2f53b 100644 --- a/src/write.rs +++ b/src/write.rs @@ -649,6 +649,8 @@ impl fmt::Display for ArithmeticInstruction { &ArithmeticInstruction::Ceiling(ref a, ref t) => write!(f, "ceiling {}, @{}", a, t), &ArithmeticInstruction::Floor(ref a, ref t) => write!(f, "floor {}, @{}", a, t), &ArithmeticInstruction::Float(ref a, ref t) => write!(f, "float {}, @{}", a, t), + &ArithmeticInstruction::FloatIntegerPart(ref a, ref t) => write!(f, "float_integer_part {}, @{}", a, t), + &ArithmeticInstruction::FloatFractionalPart(ref a, ref t) => write!(f, "float_fractional_part {}, @{}", a, t), } } } From c90dd80ece7d2073a5b0d4906dbee77420d93c19 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 13 Nov 2022 20:51:40 -0700 Subject: [PATCH 002/361] revise UnsafeVarMarker (#1545) --- src/codegen.rs | 47 +--------------- src/fixtures.rs | 147 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 124 insertions(+), 70 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index ec69f277..6abed278 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -49,48 +49,6 @@ impl<'a> ConjunctInfo<'a> { fn perm_var_offset(&self) -> usize { self.has_deep_cut as usize } - - fn mark_unsafe_vars(&self, mut unsafe_var_marker: UnsafeVarMarker, code: &mut Code) { - if code.is_empty() { - return; - } - - let mut code_index = 0; - - for phase in 0.. { - while code[code_index].is_query_instr() { - let query_instr = &mut code[code_index]; - - if !unsafe_var_marker.mark_safe_vars(query_instr) { - unsafe_var_marker.mark_phase(query_instr, phase); - } - - code_index += 1; - } - - if code_index + 1 < code.len() { - code_index += 1; - } else { - break; - } - } - - code_index = 0; - - for phase in 0.. { - while code[code_index].is_query_instr() { - let query_instr = &mut code[code_index]; - unsafe_var_marker.mark_unsafe_vars(query_instr, phase); - code_index += 1; - } - - if code_index + 1 < code.len() { - code_index += 1; - } else { - break; - } - } - } } #[derive(Clone, Copy, Debug)] @@ -989,7 +947,8 @@ impl<'b> CodeGenerator<'b> { let iter = ChunkedIterator::from_rule_body(p1, clauses); self.compile_seq(iter, &conjunct_info, &mut code, false)?; - conjunct_info.mark_unsafe_vars(unsafe_var_marker, &mut code); + unsafe_var_marker.mark_unsafe_instrs(&mut code); + self.compile_cleanup(&mut code, &conjunct_info, clauses.last().unwrap_or(p1)); Ok(code) @@ -1013,7 +972,7 @@ impl<'b> CodeGenerator<'b> { } } - UnsafeVarMarker::from_safe_vars(safe_vars) + UnsafeVarMarker::from_fact_vars(safe_vars) } pub(crate) fn compile_fact(&mut self, term: &Term) -> Result { diff --git a/src/fixtures.rs b/src/fixtures.rs index 43c3ace8..3857d294 100644 --- a/src/fixtures.rs +++ b/src/fixtures.rs @@ -253,68 +253,163 @@ impl<'a> VariableFixtures<'a> { #[derive(Debug)] pub(crate) struct UnsafeVarMarker { - pub(crate) unsafe_vars: IndexMap, - pub(crate) safe_vars: IndexSet, + pub(crate) unsafe_perm_vars: IndexMap, + pub(crate) unsafe_temp_vars: IndexSet, + pub(crate) safe_perm_vars: IndexSet, + pub(crate) safe_temp_vars: IndexSet, } impl UnsafeVarMarker { pub(crate) fn new() -> Self { UnsafeVarMarker { - unsafe_vars: IndexMap::new(), - safe_vars: IndexSet::new(), + unsafe_perm_vars: IndexMap::new(), + unsafe_temp_vars: IndexSet::new(), + safe_perm_vars: IndexSet::new(), + safe_temp_vars: IndexSet::new(), } } - pub(crate) fn from_safe_vars(safe_vars: IndexSet) -> Self { - UnsafeVarMarker { - unsafe_vars: IndexMap::new(), - safe_vars, + pub(crate) fn from_fact_vars(safe_vars: IndexSet) -> Self { + let mut unsafe_var_marker = Self::new(); + + for r in safe_vars { + unsafe_var_marker.mark_var_as_safe(r); + } + + unsafe_var_marker + } + + fn mark_var_as_safe(&mut self, r: RegType) { + match r { + RegType::Temp(t) => { + self.safe_temp_vars.insert(t); + } + RegType::Perm(p) => { + self.safe_perm_vars.insert(p); + } + }; + } + + fn mark_var_as_unsafe(&mut self, r: RegType, phase: usize) { + match r { + RegType::Temp(t) => { + self.unsafe_temp_vars.insert(t); + } + RegType::Perm(p) => { + self.unsafe_perm_vars.insert(p, phase); + } } } - pub(crate) fn mark_safe_vars(&mut self, query_instr: &Instruction) -> bool { + fn mark_safe_vars(&mut self, query_instr: &Instruction) -> bool { match query_instr { &Instruction::PutVariable(r @ RegType::Temp(_), _) | &Instruction::SetVariable(r) => { - self.safe_vars.insert(r); + self.mark_var_as_safe(r); true } _ => false, } } - pub(crate) fn mark_phase(&mut self, query_instr: &Instruction, phase: usize) { + fn mark_phase(&mut self, query_instr: &Instruction, phase: usize) { match query_instr { &Instruction::PutValue(r @ RegType::Perm(_), _) | &Instruction::SetValue(r) => { - let p = self.unsafe_vars.entry(r).or_insert(0); - *p = phase; + self.mark_var_as_unsafe(r, phase); } _ => {} } } - pub(crate) fn mark_unsafe_vars(&mut self, query_instr: &mut Instruction, phase: usize) { + fn mark_unsafe_perm_vars(&mut self, query_instr: &mut Instruction, phase: usize) { match query_instr { - &mut Instruction::PutValue(RegType::Perm(i), arg) => { - if let Some(p) = self.unsafe_vars.swap_remove(&RegType::Perm(i)) { - if p == phase { - *query_instr = Instruction::PutUnsafeValue(i, arg); - self.safe_vars.insert(RegType::Perm(i)); + &mut Instruction::PutValue(RegType::Perm(p), arg) => { + if let Some(ph) = self.unsafe_perm_vars.swap_remove(&p) { + if ph == phase { + *query_instr = Instruction::PutUnsafeValue(p, arg); + self.safe_perm_vars.insert(p); } else { - self.unsafe_vars.insert(RegType::Perm(i), p); + self.unsafe_perm_vars.insert(p, ph); } } } - &mut Instruction::SetValue(r) => { - if !self.safe_vars.contains(&r) { - *query_instr = Instruction::SetLocalValue(r); + &mut Instruction::SetValue(r @ RegType::Perm(p)) if !self.safe_perm_vars.contains(&p) => { + *query_instr = Instruction::SetLocalValue(r); - self.safe_vars.insert(r); - self.unsafe_vars.remove(&r); - } + self.safe_perm_vars.insert(p); + self.unsafe_perm_vars.remove(&p); } _ => {} } } + + fn mark_unsafe_temp_vars(&mut self, query_instr: &mut Instruction) { + match query_instr { + &mut Instruction::SetValue(r @ RegType::Temp(t)) if !self.safe_temp_vars.contains(&t) => { + *query_instr = Instruction::SetLocalValue(r); + + self.safe_temp_vars.insert(t); + self.unsafe_temp_vars.remove(&t); + } + _ => { + } + } + } + + fn clear_temp_vars(&mut self) { + self.safe_temp_vars.clear(); + self.unsafe_temp_vars.clear(); + } + + pub(crate) fn mark_unsafe_instrs(&mut self, code: &mut Code) { + if code.is_empty() { + return; + } + + let mut code_index = 0; + + for phase in 0.. { + while code[code_index].is_query_instr() { + let query_instr = &mut code[code_index]; + + if !self.mark_safe_vars(query_instr) { + self.mark_phase(query_instr, phase); + self.mark_unsafe_temp_vars(query_instr); + } + + code_index += 1; + } + + while code_index < code.len() && !code[code_index].is_query_instr() { + code_index += 1; + } + + self.clear_temp_vars(); + + if code_index >= code.len() { + break; + } + } + + code_index = 0; + + for phase in 0.. { + while code[code_index].is_query_instr() { + let query_instr = &mut code[code_index]; + self.mark_unsafe_perm_vars(query_instr, phase); + code_index += 1; + } + + // ensure phase->instruction assignments match those of + // the previous for loop. + while code_index < code.len() && !code[code_index].is_query_instr() { + code_index += 1; + } + + if code_index >= code.len() { + break; + } + } + } } From d19a8d6b984c5543b0032fa1fb2645815fa3256e Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 13 Nov 2022 21:43:14 -0700 Subject: [PATCH 003/361] begin to mark registers as safe from built-in predicates like is/2 (#1545) --- src/fixtures.rs | 56 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/src/fixtures.rs b/src/fixtures.rs index 3857d294..65340da0 100644 --- a/src/fixtures.rs +++ b/src/fixtures.rs @@ -257,6 +257,7 @@ pub(crate) struct UnsafeVarMarker { pub(crate) unsafe_temp_vars: IndexSet, pub(crate) safe_perm_vars: IndexSet, pub(crate) safe_temp_vars: IndexSet, + pub(crate) temp_vars_to_perm_vars: IndexMap, } impl UnsafeVarMarker { @@ -266,6 +267,7 @@ impl UnsafeVarMarker { unsafe_temp_vars: IndexSet::new(), safe_perm_vars: IndexSet::new(), safe_temp_vars: IndexSet::new(), + temp_vars_to_perm_vars: IndexMap::new(), } } @@ -301,6 +303,8 @@ impl UnsafeVarMarker { } } + // returns true if the instruction at *query_instr cannot be + // changed by mark_unsafe_vars. fn mark_safe_vars(&mut self, query_instr: &Instruction) -> bool { match query_instr { &Instruction::PutVariable(r @ RegType::Temp(_), _) | @@ -308,6 +312,17 @@ impl UnsafeVarMarker { self.mark_var_as_safe(r); true } + &Instruction::PutVariable(RegType::Perm(p), t) => { + self.temp_vars_to_perm_vars.insert(t, p); + true + } + &Instruction::CallIs(RegType::Temp(t), ..) => { + if let Some(p) = self.temp_vars_to_perm_vars.get(&t) { + self.mark_var_as_safe(RegType::Perm(*p)); + } + + true + } _ => false, } } @@ -324,34 +339,37 @@ impl UnsafeVarMarker { fn mark_unsafe_perm_vars(&mut self, query_instr: &mut Instruction, phase: usize) { match query_instr { - &mut Instruction::PutValue(RegType::Perm(p), arg) => { - if let Some(ph) = self.unsafe_perm_vars.swap_remove(&p) { - if ph == phase { - *query_instr = Instruction::PutUnsafeValue(p, arg); - self.safe_perm_vars.insert(p); - } else { - self.unsafe_perm_vars.insert(p, ph); + &mut Instruction::PutValue(RegType::Perm(p), arg) + if !self.safe_perm_vars.contains(&p) => { + if let Some(ph) = self.unsafe_perm_vars.swap_remove(&p) { + if ph == phase { + *query_instr = Instruction::PutUnsafeValue(p, arg); + self.safe_perm_vars.insert(p); + } else { + self.unsafe_perm_vars.insert(p, ph); + } } } - } - &mut Instruction::SetValue(r @ RegType::Perm(p)) if !self.safe_perm_vars.contains(&p) => { - *query_instr = Instruction::SetLocalValue(r); + &mut Instruction::SetValue(r @ RegType::Perm(p)) + if !self.safe_perm_vars.contains(&p) => { + *query_instr = Instruction::SetLocalValue(r); - self.safe_perm_vars.insert(p); - self.unsafe_perm_vars.remove(&p); - } + self.safe_perm_vars.insert(p); + self.unsafe_perm_vars.remove(&p); + } _ => {} } } fn mark_unsafe_temp_vars(&mut self, query_instr: &mut Instruction) { match query_instr { - &mut Instruction::SetValue(r @ RegType::Temp(t)) if !self.safe_temp_vars.contains(&t) => { - *query_instr = Instruction::SetLocalValue(r); + &mut Instruction::SetValue(r @ RegType::Temp(t)) + if !self.safe_temp_vars.contains(&t) => { + *query_instr = Instruction::SetLocalValue(r); - self.safe_temp_vars.insert(t); - self.unsafe_temp_vars.remove(&t); - } + self.safe_temp_vars.insert(t); + self.unsafe_temp_vars.remove(&t); + } _ => { } } @@ -360,6 +378,7 @@ impl UnsafeVarMarker { fn clear_temp_vars(&mut self) { self.safe_temp_vars.clear(); self.unsafe_temp_vars.clear(); + self.temp_vars_to_perm_vars.clear(); } pub(crate) fn mark_unsafe_instrs(&mut self, code: &mut Code) { @@ -382,6 +401,7 @@ impl UnsafeVarMarker { } while code_index < code.len() && !code[code_index].is_query_instr() { + self.mark_safe_vars(&code[code_index]); code_index += 1; } From d16312a3141d3508cd27d1977ad6ff40b2a5bacc Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 13 Nov 2022 22:58:00 -0700 Subject: [PATCH 004/361] use existing bindings in compile_is (#1545) --- src/arithmetic.rs | 36 +++++++++++++----------------------- src/codegen.rs | 6 +++--- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 172af5bb..0cbb1eab 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -67,19 +67,6 @@ impl<'a> ArithInstructionIterator<'a> { Term::Clause(cell, name, terms) => { TermIterState::Clause(Level::Shallow, 0, cell, *name, terms) } - /* match ClauseType::from(*name, terms.len()) { - ct @ ClauseType::Named(..) => { - Ok(TermIterState::Clause(Level::Shallow, 0, cell, ct, terms)) - } - ct @ ClauseType::Inlined(InlinedClauseType::IsFloat(_)) => { - // let ct = ClauseType::Named(1, atom!("float"), CodeIndex::default()); - Ok(TermIterState::Clause(Level::Shallow, 0, cell, ct, terms)) - } - _ => Err(ArithmeticError::NonEvaluableFunctor( - Literal::Atom(*name), - terms.len(), - )), - }?,*/ Term::Literal(cell, cons) => TermIterState::Literal(Level::Shallow, cell, cons), Term::Cons(..) | Term::PartialString(..) | Term::CompleteString(..) => { return Err(ArithmeticError::NonEvaluableFunctor( @@ -320,8 +307,7 @@ impl<'a> ArithmeticEvaluator<'a> { src: &'a Term, term_loc: GenContext, arg: usize, - ) -> Result - { + ) -> Result { let mut code = vec![]; let mut iter = src.iter()?; @@ -338,15 +324,19 @@ impl<'a> ArithmeticEvaluator<'a> { &mut code, ) } else if term_loc.is_last() || cell.get().norm().reg_num() == 0 { - self.marker.mark_var::( - name.clone(), - lvl, - cell, - term_loc, - &mut code, - ); + if let Some(r) = self.marker.get_binding(&name) { + r + } else { + self.marker.mark_var::( + name.clone(), + lvl, + cell, + term_loc, + &mut code, + ); - self.marker.get_binding(&name).unwrap() + self.marker.get_binding(&name).unwrap() + } } else { cell.get().norm() }; diff --git a/src/codegen.rs b/src/codegen.rs index 6abed278..6f722336 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -747,7 +747,7 @@ impl<'b> CodeGenerator<'b> { ) -> Result<(), CompilationError> { macro_rules! compile_expr { ($self:expr, $terms:expr, $term_loc:expr, $code:expr) => ({ - let (acode, at) = $self.compile_arith_expr(&$terms[1], 1, $term_loc, 2)?; + let (acode, at) = $self.compile_arith_expr($terms, 1, $term_loc, 2)?; $code.extend(acode.into_iter()); at }); @@ -765,7 +765,7 @@ impl<'b> CodeGenerator<'b> { code, ); - compile_expr!(self, terms, term_loc, code) + compile_expr!(self, &terms[1], term_loc, code) } &Term::Literal(_, c @ Literal::Integer(_) | c @ Literal::Float(_) | @@ -775,7 +775,7 @@ impl<'b> CodeGenerator<'b> { code.push(instr!("put_constant", Level::Shallow, v, temp_v!(1))); self.marker.advance_arg(); - compile_expr!(self, terms, term_loc, code) + compile_expr!(self, &terms[1], term_loc, code) } _ => { code.push(instr!("$fail", 0)); From b491a06c6d4b4df4afa421c495441d78433d3b1a Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Mon, 14 Nov 2022 21:13:52 -0700 Subject: [PATCH 005/361] README rustc version bump --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6b2fbad..0e1fa17a 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ light.exe scryer-prolog.wixobj ``` It will generate a very basic MSI file which installs the main executable and a shortcut in the Start Menu. It can be installed with a double-click. To uninstall, go to the Control Panel and uninstall as usual. -Scryer Prolog must be built with **Rust 1.57 and up**. +Scryer Prolog must be built with **Rust 1.61 and up**. ### Docker Install From 9fd1bf5574fc8fe303ab6350f485121a6cb4b5e8 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Mon, 14 Nov 2022 21:16:46 -0700 Subject: [PATCH 006/361] mention #scryer in README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0e1fa17a..66f5cb82 100644 --- a/README.md +++ b/README.md @@ -679,4 +679,5 @@ If Scryer Prolog crashes or yields unexpected errors, consider filing an [issue](https://github.com/mthom/scryer-prolog/issues). To get in touch with the Scryer Prolog community, participate in -[discussions](https://github.com/mthom/scryer-prolog/discussions)! +[discussions](https://github.com/mthom/scryer-prolog/discussions) +or visit our #scryer IRC channel on [Libera](https://libera.chat)! From 68b3c480c9662a7161c57d4bb99e59c810315da0 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Mon, 14 Nov 2022 21:18:03 -0700 Subject: [PATCH 007/361] update Dockerfile to use rustc 1.61 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 501a2dce..661ff5d0 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # See https://github.com/LukeMathWalker/cargo-chef -ARG RUST_VERSION=1.60-buster +ARG RUST_VERSION=1.61-buster FROM rust:${RUST_VERSION} as planner WORKDIR /scryer-prolog RUN cargo install cargo-chef From b3df81c1433c428c175dd7b1dd9dc2c47282cddb Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Mon, 14 Nov 2022 23:19:07 +0100 Subject: [PATCH 008/361] Adjust Github Action: Docker Publish 1. Not only publish Docker images when new release tags are created but on every push to master, since release frequency is so low. 2. Use newer versions of the various actions (setup-buildx-action, login-action, metadata-action, ...) to suppress some deprecation warnings see: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/ --- .github/workflows/docker-publish.yml | 31 +++++++++++++++------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index b31829e7..dffe1cd7 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -2,10 +2,10 @@ name: Docker Publish on: push: - tags: [ 'v*.*.*' ] - -env: - IMAGE_NAME: mjt128/scryer-prolog + branches: + - 'master' + tags: + - 'v*.*.*' jobs: build: @@ -18,33 +18,36 @@ jobs: # Workaround: https://github.com/docker/build-push-action/issues/461 - name: Setup Docker buildx - uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf + # https://github.com/docker/setup-buildx-action + uses: docker/setup-buildx-action@v2.2.1 # Login against Docker registry - # https://github.com/docker/login-action - name: Log into registry - uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c + # https://github.com/docker/login-action + uses: docker/login-action@v2.1.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} # Extract Docker image tag from git tag. E.g. if git tag is "v0.19.1" then use - # Docker image tag "0.19.1". Tag "latest" is automatically synced with newest - # version. - # https://github.com/docker/metadata-action + # Docker image tag "0.19.1". The "latest" tag reflects the most recent build on + # master. - name: Extract Docker metadata id: meta - uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + # https://github.com/docker/metadata-action + uses: docker/metadata-action@v4.1.1 with: - images: docker.io/${{ env.IMAGE_NAME }} + images: docker.io/${{ secrets.DOCKERHUB_USERNAME }}/scryer-prolog tags: | type=semver,pattern={{version}} + type=raw,value=latest,enable={{is_default_branch}} + # type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }} # Build and push Docker image with Buildx - # https://github.com/docker/build-push-action - name: Build and push Docker image id: build-and-push - uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc + # https://github.com/docker/build-push-action + uses: docker/build-push-action@v3.2.0 with: context: . push: true From 3bdcc3aba9eeae4ef32140c47be34f5022916d93 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Fri, 18 Nov 2022 18:19:29 -0700 Subject: [PATCH 009/361] return -1 from get_code to indicate end of file (#1622) --- src/machine/system_calls.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index b154e266..f249f655 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -3058,11 +3058,10 @@ impl Machine { } if stream.at_end_of_stream() { - let end_of_file = atom!("end_of_file"); stream.set_past_end_of_stream(true); - self.machine_st.unify_atom( - end_of_file, + self.machine_st.unify_fixnum( + Fixnum::build_with(-1), self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), ); From 76d24fe4e307032214beba551311a5705c92d6fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Fri, 2 Dec 2022 23:05:03 +0100 Subject: [PATCH 010/361] Compatible Doclog docs for library(assoc) --- src/lib/assoc.pl | 125 +++++++++++++++++++++++------------------------ 1 file changed, 61 insertions(+), 64 deletions(-) diff --git a/src/lib/assoc.pl b/src/lib/assoc.pl index 79e174e6..80edb005 100644 --- a/src/lib/assoc.pl +++ b/src/lib/assoc.pl @@ -54,28 +54,27 @@ :- use_module(library(lists)). -/** Binary associations +/** Binary associations Assocs are Key-Value associations implemented as a balanced binary tree (AVL tree). -@see library(pairs), library(rbtrees) -@author R.A.O'Keefe, L.Damas, V.S.Costa and Jan Wielemaker +Authors: R.A.O'Keefe, L.Damas, V.S.Costa and Jan Wielemaker */ :- meta_predicate map_assoc(1, ?). :- meta_predicate map_assoc(2, ?, ?). -%! empty_assoc(?Assoc) is semidet. +%% empty_assoc(?Assoc) is semidet. % -% Is true if Assoc is the empty association list. +% Is true if Assoc is the empty association list. empty_assoc(t). -%! assoc_to_list(+Assoc, -Pairs) is det. +%% assoc_to_list(+Assoc, -Pairs) is det. % -% Translate Assoc to a list Pairs of Key-Value pairs. The keys -% in Pairs are sorted in ascending order. +% Translate Assoc to a list Pairs of Key-Value pairs. The keys +% in Pairs are sorted in ascending order. assoc_to_list(Assoc, List) :- assoc_to_list(Assoc, List, []). @@ -86,10 +85,10 @@ assoc_to_list(t(Key,Val,_,L,R), List, Rest) :- assoc_to_list(t, List, List). -%! assoc_to_keys(+Assoc, -Keys) is det. +%% assoc_to_keys(+Assoc, -Keys) is det. % -% True if Keys is the list of keys in Assoc. The keys are sorted -% in ascending order. +% True if Keys is the list of keys in Assoc. The keys are sorted +% in ascending order. assoc_to_keys(Assoc, List) :- assoc_to_keys(Assoc, List, []). @@ -100,11 +99,11 @@ assoc_to_keys(t(Key,_,_,L,R), List, Rest) :- assoc_to_keys(t, List, List). -%! assoc_to_values(+Assoc, -Values) is det. +%% assoc_to_values(+Assoc, -Values) is det. % -% True if Values is the list of values in Assoc. Values are -% ordered in ascending order of the key to which they were -% associated. Values may contain duplicates. +% True if Values is the list of values in Assoc. Values are +% ordered in ascending order of the key to which they were +% associated. Values may contain duplicates. assoc_to_values(Assoc, List) :- assoc_to_values(Assoc, List, []). @@ -114,12 +113,12 @@ assoc_to_values(t(_,Value,_,L,R), List, Rest) :- assoc_to_values(R, More, Rest). assoc_to_values(t, List, List). -%! is_assoc(+Assoc) is semidet. +%% is_assoc(+Assoc) is semidet. % -% True if Assoc is an association list. This predicate checks -% that the structure is valid, elements are in order, and tree -% is balanced to the extent guaranteed by AVL trees. I.e., -% branches of each subtree differ in depth by at most 1. +% True if Assoc is an association list. This predicate checks +% that the structure is valid, elements are in order, and tree +% is balanced to the extent guaranteed by AVL trees. I.e., +% branches of each subtree differ in depth by at most 1. is_assoc(Assoc) :- is_assoc(Assoc, _Min, _Max, _Depth). @@ -151,12 +150,10 @@ balance(=,-). balance(<,<). balance(>,>). -%! gen_assoc(?Key, +Assoc, ?Value) is nondet. +%% gen_assoc(?Key, +Assoc, ?Value) is nondet. % -% True if Key-Value is an association in Assoc. Enumerates keys in -% ascending order on backtracking. -% -% @see get_assoc/3. +% True if Key-Value is an association in Assoc. Enumerates keys in +% ascending order on backtracking. gen_assoc(Key, Assoc, Value) :- ( ground(Key) @@ -171,11 +168,11 @@ gen_assoc_(Key, t(_,_,_,_,R), Val) :- gen_assoc_(Key, R, Val). -%! get_assoc(+Key, +Assoc, -Value) is semidet. +%% get_assoc(+Key, +Assoc, -Value) is semidet. % -% True if Key-Value is an association in Assoc. +% True if Key-Value is an association in Assoc. % -% @error type_error(assoc, Assoc) if Assoc is not an association list. +% Throws error: type_error(assoc, Assoc) if Assoc is not an association list. get_assoc(Key, Assoc, Val) :- must_be(assoc, Assoc), @@ -201,9 +198,9 @@ get_assoc(>, Key, _, _, Tree, Val) :- % :- endif. -%! get_assoc(+Key, +Assoc0, ?Val0, ?Assoc, ?Val) is semidet. +%% get_assoc(+Key, +Assoc0, ?Val0, ?Assoc, ?Val) is semidet. % -% True if Key-Val0 is in Assoc0 and Key-Val is in Assoc. +% True if Key-Val0 is in Assoc0 and Key-Val is in Assoc. get_assoc(Key, t(K,V,B,L,R), Val, t(K,NV,B,NL,NR), NVal) :- compare(Rel, Key, K), @@ -216,12 +213,12 @@ get_assoc(>, Key, V, L, R, Val, V, L, NR, NVal) :- get_assoc(Key, R, Val, NR, NVal). -%! list_to_assoc(+Pairs, -Assoc) is det. +%% list_to_assoc(+Pairs, -Assoc) is det. % -% Create an association from a list Pairs of Key-Value pairs. List -% must not contain duplicate keys. +% Create an association from a list Pairs of Key-Value pairs. List +% must not contain duplicate keys. % -% @error domain_error(unique_key_pairs, List) if List contains duplicate keys +% Throws error: domain_error(unique_key_pairs, List) if List contains duplicate keys list_to_assoc(List, Assoc) :- ( List = [] -> Assoc = t @@ -246,13 +243,13 @@ list_to_assoc(N, List, More, Depth, t(K,V,Balance,L,R)) :- compare(B, RDepth, LDepth), balance(B, Balance). -%! ord_list_to_assoc(+Pairs, -Assoc) is det. +%% ord_list_to_assoc(+Pairs, -Assoc) is det. % -% Assoc is created from an ordered list Pairs of Key-Value -% pairs. The pairs must occur in strictly ascending order of -% their keys. +% Assoc is created from an ordered list Pairs of Key-Value +% pairs. The pairs must occur in strictly ascending order of +% their keys. % -% @error domain_error(key_ordered_pairs, List) if pairs are not ordered. +% Throws error: domain_error(key_ordered_pairs, List) if pairs are not ordered. ord_list_to_assoc(Sorted, Assoc) :- ( Sorted = [] -> Assoc = t @@ -263,9 +260,9 @@ ord_list_to_assoc(Sorted, Assoc) :- ) ). -%! ord_pairs(+Pairs) is semidet +%% ord_pairs(+Pairs) is semidet % -% True if Pairs is a list of Key-Val pairs strictly ordered by key. +% True if Pairs is a list of Key-Val pairs strictly ordered by key. ord_pairs([K-_V|Rest]) :- ord_pairs(Rest, K). @@ -274,9 +271,9 @@ ord_pairs([K-_V|Rest], K0) :- K0 @< K, ord_pairs(Rest, K). -%! map_assoc(:Pred, +Assoc) is semidet. +%% map_assoc(:Pred, +Assoc) is semidet. % -% True if Pred(Value) is true for all values in Assoc. +% True if Pred(Value) is true for all values in Assoc. map_assoc(Pred, T) :- map_assoc_(T, Pred). @@ -287,10 +284,10 @@ map_assoc_(t(_,Val,_,L,R), Pred) :- call(Pred, Val), map_assoc_(R, Pred). -%! map_assoc(:Pred, +Assoc0, ?Assoc) is semidet. +%% map_assoc(:Pred, +Assoc0, ?Assoc) is semidet. % -% Map corresponding values. True if Assoc is Assoc0 with Pred -% applied to all corresponding pairs of of values. +% Map corresponding values. True if Assoc is Assoc0 with Pred +% applied to all corresponding pairs of of values. map_assoc(Pred, T0, T) :- map_assoc_(T0, Pred, T). @@ -302,9 +299,9 @@ map_assoc_(t(Key,Val,B,L0,R0), Pred, t(Key,Ans,B,L1,R1)) :- map_assoc_(R0, Pred, R1). -%! max_assoc(+Assoc, -Key, -Value) is semidet. +%% max_assoc(+Assoc, -Key, -Value) is semidet. % -% True if Key-Value is in Assoc and Key is the largest key. +% True if Key-Value is in Assoc and Key is the largest key. max_assoc(t(K,V,_,_,R), Key, Val) :- max_assoc(R, K, V, Key, Val). @@ -314,9 +311,9 @@ max_assoc(t(K,V,_,_,R), _, _, Key, Val) :- max_assoc(R, K, V, Key, Val). -%! min_assoc(+Assoc, -Key, -Value) is semidet. +%% min_assoc(+Assoc, -Key, -Value) is semidet. % -% True if Key-Value is in assoc and Key is the smallest key. +% True if Key-Value is in assoc and Key is the smallest key. min_assoc(t(K,V,_,L,_), Key, Val) :- min_assoc(L, K, V, Key, Val). @@ -326,10 +323,10 @@ min_assoc(t(K,V,_,L,_), _, _, Key, Val) :- min_assoc(L, K, V, Key, Val). -%! put_assoc(+Key, +Assoc0, +Value, -Assoc) is det. +%% put_assoc(+Key, +Assoc0, +Value, -Assoc) is det. % -% Assoc is Assoc0, except that Key is associated with -% Value. This can be used to insert and change associations. +% Assoc is Assoc0, except that Key is associated with +% Value. This can be used to insert and change associations. put_assoc(Key, A0, Value, A) :- insert(A0, Key, Value, A, _). @@ -361,11 +358,11 @@ table(< , right , - , no , no ) :- !. table(> , left , - , no , no ) :- !. table(> , right , - , no , yes ) :- !. -%! del_min_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet. +%% del_min_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet. % -% True if Key-Value is in Assoc0 and Key is the smallest key. -% Assoc is Assoc0 with Key-Value removed. Warning: This will -% succeed with _no_ bindings for Key or Val if Assoc0 is empty. +% True if Key-Value is in Assoc0 and Key is the smallest key. +% Assoc is Assoc0 with Key-Value removed. Warning: This will +% succeed with _no_ bindings for Key or Val if Assoc0 is empty. del_min_assoc(Tree, Key, Val, NewTree) :- del_min_assoc(Tree, Key, Val, NewTree, _DepthChanged). @@ -375,11 +372,11 @@ del_min_assoc(t(K,V,B,L,R), Key, Val, NewTree, Changed) :- del_min_assoc(L, Key, Val, NewL, LeftChanged), deladjust(LeftChanged, t(K,V,B,NewL,R), left, NewTree, Changed). -%! del_max_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet. +%% del_max_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet. % -% True if Key-Value is in Assoc0 and Key is the greatest key. -% Assoc is Assoc0 with Key-Value removed. Warning: This will -% succeed with _no_ bindings for Key or Val if Assoc0 is empty. +% True if Key-Value is in Assoc0 and Key is the greatest key. +% Assoc is Assoc0 with Key-Value removed. Warning: This will +% succeed with _no_ bindings for Key or Val if Assoc0 is empty. del_max_assoc(Tree, Key, Val, NewTree) :- del_max_assoc(Tree, Key, Val, NewTree, _DepthChanged). @@ -389,10 +386,10 @@ del_max_assoc(t(K,V,B,L,R), Key, Val, NewTree, Changed) :- del_max_assoc(R, Key, Val, NewR, RightChanged), deladjust(RightChanged, t(K,V,B,L,NewR), right, NewTree, Changed). -%! del_assoc(+Key, +Assoc0, ?Value, -Assoc) is semidet. +%% del_assoc(+Key, +Assoc0, ?Value, -Assoc) is semidet. % -% True if Key-Value is in Assoc0. Assoc is Assoc0 with -% Key-Value removed. +% True if Key-Value is in Assoc0. Assoc is Assoc0 with +% Key-Value removed. del_assoc(Key, A0, Value, A) :- delete(A0, Key, Value, A, _). From 8bafd7adb15782ce84b6636ed038e2bcd715c410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Fri, 2 Dec 2022 23:23:16 +0100 Subject: [PATCH 011/361] Compatible Doclog docs for library(uuid) --- src/lib/uuid.pl | 48 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/lib/uuid.pl b/src/lib/uuid.pl index 3f89db4b..b72f9620 100644 --- a/src/lib/uuid.pl +++ b/src/lib/uuid.pl @@ -1,25 +1,30 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written in February 2021 by Adrián Arroyo (adrian.arroyocalle@gmail.com) Part of Scryer-Prolog - This library provides reasoning about UUID (only version 4 right now). - There are three predicates: - * uuidv4/1, to generate a new UUIDv4 - * uuidv4_string/1, to generate a new UUIDv4 in string hex representation - * uuid_string/2, to converte between UUID list of bytes and UUID hex representation - - Examples: - ?- uuidv4(X). - X = [42,147,248,242,117,196,79,2,129,159|...]. - ?- uuidv4_string(X). - X = "428499fc-76e3-4240- ...". - ?- uuidv4(X), uuid_string(X, S). - X = [173,12,244,152,139,118,64,139,137,4|...], S = "ad0cf498-8b76-408b- ...". - ?- uuid_string(X, "61ae692e-eaf6-4199-8dd3-9f01db70a20b"). - X = [97,174,105,46,234,246,65,153,141,211|...]. - I place this code in the public domain. Use it in any way you want. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/** +This library provides reasoning and working with [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) +(only version 4 right now). + +There are three predicates: + * uuidv4/1, to generate a new UUIDv4 + * uuidv4\_string/1, to generate a new UUIDv4 in string hex representation + * uuid\_string/2, to converte between UUID list of bytes and UUID hex representation + +Examples: + + ?- uuidv4(X). + X = [42,147,248,242,117,196,79,2,129,159|...]. + ?- uuidv4_string(X). + X = "428499fc-76e3-4240- ...". + ?- uuidv4(X), uuid_string(X, S). + X = [173,12,244,152,139,118,64,139,137,4|...], S = "ad0cf498-8b76-408b- ...". + ?- uuid_string(X, "61ae692e-eaf6-4199-8dd3-9f01db70a20b"). + X = [97,174,105,46,234,246,65,153,141,211|...]. +*/ + :- module(uuid, [ uuidv4/1, uuidv4_string/1, @@ -39,6 +44,10 @@ clock_seq_hi_and_res_clock_seq_low - 2 node - 6 UUID v4 can be generated from a set of 16 random bytes: https://www.rfc-archive.org/getrfc.php?rfc=4122#gsc.tab=0 (section 4.4) */ + +%% uuidv4(-Uuid). +% +% Generates a new UUID v4 (random). It unifies with a list of bytes. uuidv4(Uuid) :- crypto_n_random_bytes(16, Bytes), Bytes = [B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16], @@ -52,8 +61,15 @@ uuidv4(Uuid) :- byte_bits(NewTimeHi, NewBitsTimeHi), Uuid = [B1, B2, B3, B4, B5, B6, NewTimeHi, B8, NewClockSeqHi0, B10, B11, B12, B13, B14, B15, B16]. +%% uuidv4_string(-UuidString). +% +% Generates a new UUID v4 (random). It unifies with a string representation of the UUID. +% It is equivalent of calling uuidv4/1 followed by uuid\_string/2. uuidv4_string(String) :- uuidv4(Uuid), uuid_string(Uuid, String). +%% uuid_string(?UuidBytes, ?UuidString). +% +% Translates between the bytes representation and the string representation of the same UUID. uuid_string(Uuid, String) :- Uuid = [B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16], phrase(uuid_([S1, S2, S3, S4, S5]), String), From b2a0d0c1c7375e85a39b009a39213c69bff97491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Fri, 2 Dec 2022 23:43:24 +0100 Subject: [PATCH 012/361] Compatible Doclog docs for library(random) --- src/lib/random.pl | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/lib/random.pl b/src/lib/random.pl index d678aff2..15c35f3c 100644 --- a/src/lib/random.pl +++ b/src/lib/random.pl @@ -1,24 +1,38 @@ -:- module(random, [maybe/0, random/1, random_integer/3, set_random/1]). +/** +This library provides probabilistic predicates and random number generators. -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - To retain desirable declarative properties, predicates that internally - use random numbers should be equipped with an argument that specifies - the random seed. This makes everything completely reproducible. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +To retain desirable declarative properties, predicates that internally +use random numbers should be equipped with an argument that specifies +the random seed. This makes everything completely reproducible. +*/ + +:- module(random, [maybe/0, random/1, random_integer/3, set_random/1]). :- use_module(library(error)). -% succeeds with probability 0.5. +%% maybe. +% +% Succeeds with probability 0.5. maybe :- '$maybe'. % The higher the precision, the slower it gets. random_number_precision(64). +%% random(-R). +% +% Generates a random floating number between 0 (inclusive) and 1 (exclusive). random(R) :- var(R), random_number_precision(N), rnd(N, R). +%% random_integer(+Lower, +Upper, -R). +% +% Generates a random integer number between Lower (inclusive) and Upper (exclusive). +% +% Throws instantiation\_error if Lower or Upper are variables. +% +% Throws type\_error if Lower or Upper aren't integers. random_integer(Lower, Upper, R) :- var(R), ( (var(Lower) ; var(Upper)) -> @@ -46,6 +60,10 @@ rnd_(N, R0, R) :- R1 is R0 + 1.0 / 2.0 ^ N, rnd_(N1, R1, R). +%% set_random(+Seed). +% +% Sets a seed that will be used for subsequent random generations in this library. +% It's necessary to set a seed to provide reproducible executions using this library. set_random(Seed) :- ( nonvar(Seed) -> ( Seed = seed(S) -> From d429b263ebd5fb241155055f89474c6b3da384bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Mon, 5 Dec 2022 00:09:32 +0100 Subject: [PATCH 013/361] Compatible Doclog docs for library(ugraphs) --- src/lib/ugraphs.pl | 260 ++++++++++++++++++++------------------------- 1 file changed, 115 insertions(+), 145 deletions(-) diff --git a/src/lib/ugraphs.pl b/src/lib/ugraphs.pl index 159f4c00..61da18e2 100644 --- a/src/lib/ugraphs.pl +++ b/src/lib/ugraphs.pl @@ -53,7 +53,7 @@ connect_ugraph/3 % +Graph1, -Start, -Graph ]). -/** Graph manipulation library +/** Graph manipulation library The S-representation of a graph is a list of (vertex-neighbours) pairs, where the pairs are in standard order (as produced by keysort) and the @@ -61,55 +61,50 @@ neighbours of each vertex are also in standard order (as produced by sort). This form is convenient for many calculations. A new UGraph from raw data can be created using -vertices_edges_to_ugraph/3. +vertices\_edges\_to\_ugraph/3. Adapted to support some of the functionality of the SICStus ugraphs library by Vitor Santos Costa. Ported from YAP 5.0.1 to SWI-Prolog by Jan Wielemaker. -@author R.A.O'Keefe -@author Vitor Santos Costa -@author Jan Wielemaker -@license BSD-2 or Artistic 2.0 +Ported from SWI-Prolog to Scryer by Adrián Arroyo Calle + +License: BSD-2 or Artistic 2.0 */ :- use_module(library(lists)). :- use_module(library(pairs)). :- use_module(library(ordsets)). -%! vertices(+Graph, -Vertices) +%% vertices(+Graph, -Vertices) % -% Unify Vertices with all vertices appearing in Graph. Example: +% Unify Vertices with all vertices appearing in Graph. Example: % -% ?- vertices([1-[3,5],2-[4],3-[],4-[5],5-[]], L). -% L = [1, 2, 3, 4, 5] +% ?- vertices([1-[3,5],2-[4],3-[],4-[5],5-[]], L). +% L = [1, 2, 3, 4, 5] vertices([], []) :- !. vertices([Vertex-_|Graph], [Vertex|Vertices]) :- vertices(Graph, Vertices). -%! vertices_edges_to_ugraph(+Vertices, +Edges, -UGraph) is det. +%% vertices_edges_to_ugraph(+Vertices, +Edges, -UGraph) is det. % -% Create a UGraph from Vertices and edges. Given a graph with a -% set of Vertices and a set of Edges, Graph must unify with the -% corresponding S-representation. Note that the vertices without -% edges will appear in Vertices but not in Edges. Moreover, it is -% sufficient for a vertice to appear in Edges. +% Create a UGraph from Vertices and edges. Given a graph with a +% set of Vertices and a set of Edges, Graph must unify with the +% corresponding S-representation. Note that the vertices without +% edges will appear in Vertices but not in Edges. Moreover, it is +% sufficient for a vertice to appear in Edges. % -% == -% ?- vertices_edges_to_ugraph([],[1-3,2-4,4-5,1-5], L). -% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[]] -% == +% ?- vertices_edges_to_ugraph([],[1-3,2-4,4-5,1-5], L). +% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[]] % -% In this case all vertices are defined implicitly. The next -% example shows three unconnected vertices: +% In this case all vertices are defined implicitly. The next +% example shows three unconnected vertices: % -% == -% ?- vertices_edges_to_ugraph([6,7,8],[1-3,2-4,4-5,1-5], L). -% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[], 6-[], 7-[], 8-[]] -% == +% ?- vertices_edges_to_ugraph([6,7,8],[1-3,2-4,4-5,1-5], L). +% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[], 6-[], 7-[], 8-[]] vertices_edges_to_ugraph(Vertices, Edges, Graph) :- sort(Edges, EdgeSet), @@ -119,15 +114,13 @@ vertices_edges_to_ugraph(Vertices, Edges, Graph) :- p_to_s_group(VertexSet, EdgeSet, Graph). -%! add_vertices(+Graph, +Vertices, -NewGraph) +%% add_vertices(+Graph, +Vertices, -NewGraph) % -% Unify NewGraph with a new graph obtained by adding the list of -% Vertices to Graph. Example: +% Unify NewGraph with a new graph obtained by adding the list of +% Vertices to Graph. Example: % -% ``` -% ?- add_vertices([1-[3,5],2-[]], [0,1,2,9], NG). -% NG = [0-[], 1-[3,5], 2-[], 9-[]] -% ``` +% ?- add_vertices([1-[3,5],2-[]], [0,1,2,9], NG). +% NG = [0-[], 1-[3,5], 2-[], 9-[]] % replace with real msort/2 when available msort_(List, Sorted) :- @@ -159,23 +152,16 @@ add_empty_vertices([], []). add_empty_vertices([V|G], [V-[]|NG]) :- add_empty_vertices(G, NG). -%! del_vertices(+Graph, +Vertices, -NewGraph) is det. +%% del_vertices(+Graph, +Vertices, -NewGraph) is det. % -% Unify NewGraph with a new graph obtained by deleting the list of -% Vertices and all the edges that start from or go to a vertex in -% Vertices to the Graph. Example: +% Unify NewGraph with a new graph obtained by deleting the list of +% Vertices and all the edges that start from or go to a vertex in +% Vertices to the Graph. Example: % -% == -% ?- del_vertices([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[2,6],8-[]], +% ?- del_vertices([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[2,6],8-[]], % [2,1], % NL). -% NL = [3-[],4-[5],5-[],6-[],7-[6],8-[]] -% == -% -% @compat Upto 5.6.48 the argument order was (+Vertices, +Graph, -% -NewGraph). Both YAP and SWI-Prolog have changed the argument -% order for compatibility with recent SICStus as well as -% consistency with del_edges/3. +% NL = [3-[],4-[5],5-[],6-[],7-[6],8-[]] del_vertices(Graph, Vertices, NewGraph) :- sort(Vertices, V1), % JW: was msort @@ -204,32 +190,28 @@ split_on_del_vertices(>, V, Edges, [_|Vs], Vs, V1, [V-NEdges|NG], NG) :- ord_subtract(Edges, V1, NEdges). split_on_del_vertices(=, _, _, [_|Vs], Vs, _, NG, NG). -%! add_edges(+Graph, +Edges, -NewGraph) +%% add_edges(+Graph, +Edges, -NewGraph) % -% Unify NewGraph with a new graph obtained by adding the list of Edges -% to Graph. Example: +% Unify NewGraph with a new graph obtained by adding the list of Edges +% to Graph. Example: % -% ``` -% ?- add_edges([1-[3,5],2-[4],3-[],4-[5], +% ?- add_edges([1-[3,5],2-[4],3-[],4-[5], % 5-[],6-[],7-[],8-[]], % [1-6,2-3,3-2,5-7,3-2,4-5], % NL). -% NL = [1-[3,5,6], 2-[3,4], 3-[2], 4-[5], +% NL = [1-[3,5,6], 2-[3,4], 3-[2], 4-[5], % 5-[7], 6-[], 7-[], 8-[]] -% ``` add_edges(Graph, Edges, NewGraph) :- p_to_s_graph(Edges, G1), ugraph_union(Graph, G1, NewGraph). -%! ugraph_union(+Graph1, +Graph2, -NewGraph) +%% ugraph_union(+Graph1, +Graph2, -NewGraph) % -% NewGraph is the union of Graph1 and Graph2. Example: +% NewGraph is the union of Graph1 and Graph2. Example: % -% ``` -% ?- ugraph_union([1-[2],2-[3]],[2-[4],3-[1,2,4]],L). -% L = [1-[2], 2-[3,4], 3-[1,2,4]] -% ``` +% ?- ugraph_union([1-[2],2-[3]],[2-[4],3-[1,2,4]],L). +% L = [1-[2], 2-[3,4], 3-[1,2,4]] ugraph_union(Set1, [], Set1) :- !. ugraph_union([], Set2, Set2) :- !. @@ -245,25 +227,23 @@ ugraph_union(<, Head1, Tail1, Head2, Tail2, [Head1|Union]) :- ugraph_union(>, Head1, Tail1, Head2, Tail2, [Head2|Union]) :- ugraph_union([Head1|Tail1], Tail2, Union). -%! del_edges(+Graph, +Edges, -NewGraph) +%% del_edges(+Graph, +Edges, -NewGraph) % -% Unify NewGraph with a new graph obtained by removing the list of -% Edges from Graph. Notice that no vertices are deleted. Example: +% Unify NewGraph with a new graph obtained by removing the list of +% Edges from Graph. Notice that no vertices are deleted. Example: % -% ``` -% ?- del_edges([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[],8-[]], +% ?- del_edges([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[],8-[]], % [1-6,2-3,3-2,5-7,3-2,4-5,1-3], % NL). -% NL = [1-[5],2-[4],3-[],4-[],5-[],6-[],7-[],8-[]] -% ``` +% NL = [1-[5],2-[4],3-[],4-[],5-[],6-[],7-[],8-[]] del_edges(Graph, Edges, NewGraph) :- p_to_s_graph(Edges, G1), graph_subtract(Graph, G1, NewGraph). -%! graph_subtract(+Set1, +Set2, ?Difference) +%% graph_subtract(+Set1, +Set2, ?Difference) % -% Is based on ord_subtract +% Is based on ord_subtract graph_subtract(Set1, [], Set1) :- !. graph_subtract([], _, []). @@ -279,12 +259,12 @@ graph_subtract(<, Head1, Tail1, Head2, Tail2, [Head1|Difference]) :- graph_subtract(>, Head1, Tail1, _, Tail2, Difference) :- graph_subtract([Head1|Tail1], Tail2, Difference). -%! edges(+Graph, -Edges) +%% edges(+Graph, -Edges) % -% Unify Edges with all edges appearing in Graph. Example: +% Unify Edges with all edges appearing in Graph. Example: % -% ?- edges([1-[3,5],2-[4],3-[],4-[5],5-[]], L). -% L = [1-3, 1-5, 2-4, 4-5] +% ?- edges([1-[3,5],2-[4],3-[],4-[5],5-[]], L). +% L = [1-3, 1-5, 2-4, 4-5] edges(Graph, Edges) :- s_to_p_graph(Graph, Edges). @@ -324,15 +304,13 @@ s_to_p_graph([], _, P_Graph, P_Graph) :- !. s_to_p_graph([Neib|Neibs], Vertex, [Vertex-Neib|P], Rest_P) :- s_to_p_graph(Neibs, Vertex, P, Rest_P). -%! transitive_closure(+Graph, -Closure) +%% transitive_closure(+Graph, -Closure) % -% Generate the graph Closure as the transitive closure of Graph. -% Example: +% Generate the graph Closure as the transitive closure of Graph. +% Example: % -% ``` -% ?- transitive_closure([1-[2,3],2-[4,5],4-[6]],L). -% L = [1-[2,3,4,5,6], 2-[4,5,6], 4-[6]] -% ``` +% ?- transitive_closure([1-[2,3],2-[4,5],4-[6]],L). +% L = [1-[2,3,4,5,6], 2-[4,5,6], 4-[6]] transitive_closure(Graph, Closure) :- warshall(Graph, Graph, Closure). @@ -354,23 +332,16 @@ warshall([X-Neibs|G], V, Y, [X-Neibs|NewG]) :- warshall(G, V, Y, NewG). warshall([], _, _, []). -%! transpose_ugraph(Graph, NewGraph) is det. +%% transpose_ugraph(Graph, NewGraph) is det. % -% Unify NewGraph with a new graph obtained from Graph by replacing -% all edges of the form V1-V2 by edges of the form V2-V1. The cost -% is O(|V|*log(|V|)). Notice that an undirected graph is its own -% transpose. Example: +% Unify NewGraph with a new graph obtained from Graph by replacing +% all edges of the form V1-V2 by edges of the form V2-V1. The cost +% is O(|V|*log(|V|)). Notice that an undirected graph is its own +% transpose. Example: % -% == % ?- transpose([1-[3,5],2-[4],3-[],4-[5], % 5-[],6-[],7-[],8-[]], NL). -% NL = [1-[],2-[],3-[1],4-[2],5-[1,4],6-[],7-[],8-[]] -% == -% -% @compat This predicate used to be known as transpose/2. -% Following SICStus 4, we reserve transpose/2 for matrix -% transposition and renamed ugraph transposition to -% transpose_ugraph/2. +% NL = [1-[],2-[],3-[1],4-[2],5-[1,4],6-[],7-[],8-[]] transpose_ugraph(Graph, NewGraph) :- edges(Graph, Edges), @@ -382,13 +353,13 @@ flip_edges([], []). flip_edges([Key-Val|Pairs], [Val-Key|Flipped]) :- flip_edges(Pairs, Flipped). -%! compose(+LeftGraph, +RightGraph, -NewGraph) +%% compose(+LeftGraph, +RightGraph, -NewGraph) % -% Compose NewGraph by connecting the _drains_ of LeftGraph to the -% _sources_ of RightGraph. Example: +% Compose NewGraph by connecting the _drains_ of LeftGraph to the +% _sources_ of RightGraph. Example: % -% ?- compose([1-[2],2-[3]],[2-[4],3-[1,2,4]],L). -% L = [1-[4], 2-[1,2,4], 3-[]] +% ?- compose([1-[2],2-[3]],[2-[4],3-[1,2,4]],L). +% L = [1-[4], 2-[1,2,4], 3-[]] compose(G1, G2, Composition) :- vertices(G1, V1), @@ -423,21 +394,15 @@ compose1(=, V1, Vs1, V1, N2, G2, SoFar, Comp) :- ord_union(N2, SoFar, Next), compose1(Vs1, G2, Next, Comp). -%! top_sort(+Graph, -Sorted) is semidet. -%! top_sort(+Graph, -Sorted, ?Tail) is semidet. +%% top_sort(+Graph, -Sorted) is semidet. % -% Sorted is a topological sorted list of nodes in Graph. A -% toplogical sort is possible if the graph is connected and -% acyclic. In the example we show how topological sorting works -% for a linear graph: +% Sorted is a topological sorted list of nodes in Graph. A +% toplogical sort is possible if the graph is connected and +% acyclic. In the example we show how topological sorting works +% for a linear graph: % -% == -% ?- top_sort([1-[2], 2-[3], 3-[]], L). -% L = [1, 2, 3] -% == -% -% The predicate top_sort/3 is a difference list version of -% top_sort/2. +% ?- top_sort([1-[2], 2-[3], 3-[]], L). +% L = [1, 2, 3] top_sort(Graph, Sorted) :- vertices_and_zeros(Graph, Vertices, Counts0), @@ -445,6 +410,11 @@ top_sort(Graph, Sorted) :- select_zeros(Counts1, Vertices, Zeros), top_sort(Zeros, Sorted, Graph, Vertices, Counts1). +%% top_sort(+Graph, -Sorted, ?Tail) is semidet. +% +% The predicate top\_sort/3 is a difference list version of +% top\_sort/2. + top_sort(Graph, Sorted0, Sorted) :- vertices_and_zeros(Graph, Vertices, Counts0), count_edges(Graph, Vertices, Counts0, Counts1), @@ -520,17 +490,19 @@ decr_list(Neibs, [_|Vertices], [N|Counts1], [N|Counts2], Zi, Zo) :- decr_list(Neibs, Vertices, Counts1, Counts2, Zi, Zo). -%! neighbors(+Vertex, +Graph, -Neigbours) is det. -%! neighbours(+Vertex, +Graph, -Neigbours) is det. + +%% neighbours(+Vertex, +Graph, -Neigbours) is det. % -% Neigbours is a sorted list of the neighbours of Vertex in Graph. -% Example: +% Neigbours is a sorted list of the neighbours of Vertex in Graph. +% Example: % -% ``` -% ?- neighbours(4,[1-[3,5],2-[4],3-[], +% ?- neighbours(4,[1-[3,5],2-[4],3-[], % 4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL). -% NL = [1,2,7,5] -% ``` +% NL = [1,2,7,5] + +%% neighbors(+Vertex, +Graph, -Neigbours) is det. +% +% Same as neighbours/3 neighbors(Vertex, Graph, Neig) :- neighbours(Vertex, Graph, Neig). @@ -542,24 +514,22 @@ neighbours(V,[_|G],Neig) :- neighbours(V,G,Neig). -%! connect_ugraph(+UGraphIn, -Start, -UGraphOut) is det. +%% connect_ugraph(+UGraphIn, -Start, -UGraphOut) is det. % -% Adds Start as an additional vertex that is connected to all vertices -% in UGraphIn. This can be used to create an topological sort for a -% not connected graph. Start is before any vertex in UGraphIn in the -% standard order of terms. No vertex in UGraphIn can be a variable. +% Adds Start as an additional vertex that is connected to all vertices +% in UGraphIn. This can be used to create an topological sort for a +% not connected graph. Start is before any vertex in UGraphIn in the +% standard order of terms. No vertex in UGraphIn can be a variable. % -% Can be used to order a not-connected graph as follows: +% Can be used to order a not-connected graph as follows: % -% ``` -% top_sort_unconnected(Graph, Vertices) :- +% top_sort_unconnected(Graph, Vertices) :- % ( top_sort(Graph, Vertices) % -> true % ; connect_ugraph(Graph, Start, Connected), % top_sort(Connected, Ordered0), % Ordered0 = [Start|Vertices] % ). -% ``` connect_ugraph([], 0, []) :- !. connect_ugraph(Graph, Start, [Start-Vertices|Graph]) :- @@ -567,12 +537,12 @@ connect_ugraph(Graph, Start, [Start-Vertices|Graph]) :- Vertices = [First|_], before(First, Start). -%! before(+Term, -Before) is det. +%% before(+Term, -Before) is det. % -% Unify Before to a term that comes before Term in the standard -% order of terms. +% Unify Before to a term that comes before Term in the standard +% order of terms. % -% @error instantiation_error if Term is unbound. +% Throws instantiation_error if Term is unbound. before(X, _) :- var(X), @@ -585,21 +555,21 @@ before(Number, Start) :- before(_, 0). -%! complement(+UGraphIn, -UGraphOut) +%% complement(+UGraphIn, -UGraphOut) % -% UGraphOut is a ugraph with an edge between all vertices that are -% _not_ connected in UGraphIn and all edges from UGraphIn removed. -% Example: +% UGraphOut is a ugraph with an edge between all vertices that are +% _not_ connected in UGraphIn and all edges from UGraphIn removed. +% Example: % -% ``` -% ?- complement([1-[3,5],2-[4],3-[], +% ?- complement([1-[3,5],2-[4],3-[], % 4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL). -% NL = [1-[2,4,6,7,8],2-[1,3,5,6,7,8],3-[1,2,4,5,6,7,8], +% NL = [1-[2,4,6,7,8],2-[1,3,5,6,7,8],3-[1,2,4,5,6,7,8], % 4-[3,5,6,8],5-[1,2,3,4,6,7,8],6-[1,2,3,4,5,7,8], % 7-[1,2,3,4,5,6,8],8-[1,2,3,4,5,6,7]] -% ``` % -% @tbd Simple two-step algorithm. You could be smarter, I suppose. + + +% TODO: Simple two-step algorithm. You could be smarter, I suppose. complement(G, NG) :- vertices(G,Vs), @@ -611,13 +581,13 @@ complement([V-Ns|G], Vs, [V-INs|NG]) :- ord_subtract(Vs,Ns1,INs), complement(G, Vs, NG). -%! reachable(+Vertex, +UGraph, -Vertices) +%% reachable(+Vertex, +UGraph, -Vertices) % -% True when Vertices is an ordered set of vertices reachable in -% UGraph, including Vertex. Example: +% True when Vertices is an ordered set of vertices reachable in +% UGraph, including Vertex. Example: % -% ?- reachable(1,[1-[3,5],2-[4],3-[],4-[5],5-[]],V). -% V = [1, 3, 5] +% ?- reachable(1,[1-[3,5],2-[4],3-[],4-[5],5-[]],V). +% V = [1, 3, 5] reachable(N, G, Rs) :- reachable([N], G, [N], Rs). From d383e5eb8b648d9511705f507ad9d122e78592a4 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Mon, 5 Dec 2022 23:17:15 -0700 Subject: [PATCH 014/361] avoid pushing stack variables to the heap in get_continuation_chunk (#1644) --- src/machine/system_calls.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index f249f655..0006f53f 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -4336,7 +4336,21 @@ impl Machine { let mut addrs = vec![]; for idx in 1..num_cells + 1 { - addrs.push(self.machine_st.stack[stack_loc!(AndFrame, e, idx)]); + let addr = self.machine_st.stack[stack_loc!(AndFrame, e, idx)]; + let addr = self.machine_st.store(self.machine_st.deref(addr)); + + // avoid pushing stack variables to the heap where they + // must not go. + if addr.is_stack_var() { + let h = self.machine_st.heap.len(); + + self.machine_st.heap.push(heap_loc_as_cell!(h)); + self.machine_st.bind(Ref::heap_cell(h), addr); + + addrs.push(heap_loc_as_cell!(h)); + } else { + addrs.push(addr); + } } let chunk = str_loc_as_cell!(self.machine_st.heap.len()); From d660e4244ff48bbcd558fab07a4dd4a5e9d68209 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Mon, 5 Dec 2022 23:28:44 -0700 Subject: [PATCH 015/361] dereference encoding register in crypto_data_decrypt (#1650) --- src/machine/system_calls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 0006f53f..87ab9e48 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -6318,7 +6318,7 @@ impl Machine { #[inline(always)] pub(crate) fn crypto_data_decrypt(&mut self) { let data = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet")); - let encoding = cell_as_atom!(self.machine_st.registers[5]); + let encoding = cell_as_atom!(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[5]))); let aad = self.string_encoding_bytes(self.machine_st.registers[2], encoding); let stub1_gen = || functor_stub(atom!("crypto_data_decrypt"), 7); From f22a48f57668c742d35f73252def612fb2e2db4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Tue, 6 Dec 2022 11:58:26 +0100 Subject: [PATCH 016/361] Compatible Doclog docs for library(sockets) --- src/lib/sockets.pl | 47 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/src/lib/sockets.pl b/src/lib/sockets.pl index e2b1b723..541c3133 100644 --- a/src/lib/sockets.pl +++ b/src/lib/sockets.pl @@ -1,4 +1,9 @@ - +/** +Predicates for handling network sockets, both as a server and as a client. +As a server, you should open a socket an call socket\_server\_accept/4 to get a stream for each connection. +As a client, you should just open a socket and you will receive a stream. +In both cases, with a stream, you can use the usual predicates to read and write to the stream. +*/ :- module(sockets, [socket_client_open/3, socket_server_open/2, socket_server_accept/4, @@ -7,6 +12,18 @@ :- use_module(library(error)). +%% socket_client_open(+Addr, -Stream, +Options). +% +% Open a socket to a server, returning a stream. Addr must satisfy `Addr = Address:Port`. +% +% The following options are available: +% +% * alias(+Alias): Set an alias to the stream +% * eof_action(+Action): Defined what happens if the end of the stream is reached. Values: `error`, `eof_code` and `reset`. +% * reposition(+Boolean): Specifies whether repositioning is required for the stream. `false` is the default. +% * type(+Type): Type can be `text` or `binary`. Defines the type of the stream, if it's optimized for plain text +% or just binary +% socket_client_open(Addr, Stream, Options) :- ( var(Addr) -> throw(error(instantiation_error, socket_client_open/3)) @@ -27,7 +44,11 @@ socket_client_open(Addr, Stream, Options) :- socket_client_open/3), '$socket_client_open'(Address, Port, Stream, Alias, EOFAction, Reposition, Type). - +%% socket_server_open(+Addr, -ServerSocket). +% +% Open a server socket, returning a ServerSocket. Use that ServerSocket to accept incoming connections in +% socket\_server\_accept/4. Addr must satisfy `Addr = Address:Port`. Depending on the operating system +% configuration, some ports might be reserved for superusers. socket_server_open(Addr, ServerSocket) :- must_be(var, ServerSocket), ( ( integer(Addr) ; var(Addr) ) -> @@ -39,7 +60,19 @@ socket_server_open(Addr, ServerSocket) :- '$socket_server_open'(Address, Port, ServerSocket) ). - +%% socket_server_accept(+ServerSocket, -Client, -Stream, +Options). +% +% Given a ServerSocket and a list of Options, accepts a incoming connection, returning data from the Client and +% a Stream to read or write data. +% +% The following options are available: +% +% * alias(+Alias): Set an alias to the stream +% * eof_action(+Action): Defined what happens if the end of the stream is reached. Values: `error`, `eof_code` and `reset`. +% * reposition(+Boolean): Specifies whether repositioning is required for the stream. `false` is the default. +% * type(+Type): Type can be `text` or `binary`. Defines the type of the stream, if it's optimized for plain text +% or just binary +% socket_server_accept(ServerSocket, Client, Stream, Options) :- must_be(var, Client), must_be(var, Stream), @@ -48,10 +81,14 @@ socket_server_accept(ServerSocket, Client, Stream, Options) :- socket_server_accept/4), '$socket_server_accept'(ServerSocket, Client, Stream, Alias, EOFAction, Reposition, Type). - +%% socket_server_close(+ServerSocket). +% +% Stops listening on that ServerSocket. It's recommended to always close a ServerSocket once it's no longer needed socket_server_close(ServerSocket) :- '$socket_server_close'(ServerSocket). - +%% current_hostname(-HostName). +% +% Returns the current hostname of the computer in which Scryer Prolog is executing right now current_hostname(HostName) :- '$current_hostname'(HostName). From df4b56148f9c505cd170da8eb8b7cf35dfba823a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Tue, 6 Dec 2022 13:19:10 +0100 Subject: [PATCH 017/361] Compatible Doclog docs for library(lists) --- src/lib/lists.pl | 203 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 171 insertions(+), 32 deletions(-) diff --git a/src/lib/lists.pl b/src/lib/lists.pl index 4eb204b1..15d9afa0 100644 --- a/src/lib/lists.pl +++ b/src/lib/lists.pl @@ -1,3 +1,7 @@ +/** +List manipulation predicates +*/ + :- module(lists, [member/2, select/3, append/2, append/3, foldl/4, foldl/5, memberchk/2, reverse/2, length/2, maplist/2, maplist/3, maplist/4, maplist/5, maplist/6, @@ -57,6 +61,18 @@ resource_error(Resource, Context) :- throw(error(resource_error(Resource), Context)). +%% length(?Xs, ?N). +% +% Relates a list to its length (number of items). It can be used to count the elements of a current list or +% to create a list full of free variables with N length. +% +% ?- length([a,b,c], 3). +% true. +% ?- length([a,b,c], N). +% N = 3. +% ?- length(Xs, 3). +% Xs = [_A, _B, _C]. + length(Xs0, N) :- '$skip_max_list'(M, N, Xs0,Xs), !, @@ -95,28 +111,61 @@ length_addendum([_|Xs], N, M) :- M1 is M + 1, length_addendum(Xs, N, M1). - +%% member(?X, ?Xs). +% +% Succeeds when X unifies with an item of the list Xs, which can be at any position. +% +% ?- member(X, "hello world"). +% X = h +% ; ... . +% member(X, [X|_]). member(X, [_|Xs]) :- member(X, Xs). - +%% select(X, Xs0, Xs1). +% +% Succeeds when the list Xs1 is the list Xs0 without the item X +% +% ?- select(c, "abcd", X). +% X = "abd". +% select(X, [X|Xs], Xs). select(X, [Y|Xs], [Y|Ys]) :- select(X, Xs, Ys). - +%% append(+XsXs, ?Xs). +% +% Concatenates a list of lists +% +% ?- append([[1, 2], [3]], Xs). +% Xs = [1, 2, 3]. +% append([], []). append([L0|Ls0], Ls) :- append(L0, Rest, Ls), append(Ls0, Rest). - +%% append(Xs0, Xs1, Xs). +% +% List Xs is the concatenation of Xs0 and Xs1 +% +% ?- append([1,2,3], [4,5,6], Xs). +% Xs = [1, 2, 3, 4, 5, 6]. +% append([], R, R). append([X|L], R, [X|S]) :- append(L, R, S). - +%% memberchk(?X, +Xs). +% +% This predicate is similar to member/2, but it only provides a single answer memberchk(X, Xs) :- member(X, Xs), !. - +%% reverse(?Xs, ?Ys). +% +% Xs is the Ys list in reverse order +% +% ?- reverse([1,2,3], [3,2,1]). +% true. +% reverse(Xs, Ys) :- ( nonvar(Xs) -> reverse(Xs, Ys, [], Xs) ; reverse(Ys, Xs, [], Ys) @@ -126,62 +175,111 @@ reverse([], [], YsRev, YsRev). reverse([_|Xs], [Y1|Ys], YsPreludeRev, Xss) :- reverse(Xs, Ys, [Y1|YsPreludeRev], Xss). +%% maplist(+Predicate, ?Xs0). +% +% This is a metapredicate that applies predicate to each element of the list Xs0 +% +% ?- maplist(write, [1,2,3]). +% 123 true. +% maplist(_, []). maplist(Cont1, [E1|E1s]) :- call(Cont1, E1), maplist(Cont1, E1s). +%% maplist(+Predicate, ?Xs0, ?Xs1). +% +% This is a metapredicate that applies predicate to each element of the lists Xs0 and Xs1. +% +% ?- maplist(length, ["hello", "prolog", "marseille"], Xs1). +% Xs1 = [5,6,9]. +% maplist(_, [], []). maplist(Cont2, [E1|E1s], [E2|E2s]) :- call(Cont2, E1, E2), maplist(Cont2, E1s, E2s). +%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2). +% +% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1 and Xs2. maplist(_, [], [], []). maplist(Cont3, [E1|E1s], [E2|E2s], [E3|E3s]) :- call(Cont3, E1, E2, E3), maplist(Cont3, E1s, E2s, E3s). +%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3). +% +% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2 and Xs3. maplist(_, [], [], [], []). maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s]) :- call(Cont, E1, E2, E3, E4), maplist(Cont, E1s, E2s, E3s, E4s). - +%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4). +% +% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3 and Xs4. maplist(_, [], [], [], [], []). maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s]) :- call(Cont, E1, E2, E3, E4, E5), maplist(Cont, E1s, E2s, E3s, E4s, E5s). - +%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4, ?Xs5). +% +% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3, Xs4 and Xs5. maplist(_, [], [], [], [], [], []). maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s]) :- call(Cont, E1, E2, E3, E4, E5, E6), maplist(Cont, E1s, E2s, E3s, E4s, E5s, E6s). - +%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4, ?Xs5, ?Xs6). +% +% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3, Xs4, Xs5 and Xs6. maplist(_, [], [], [], [], [], [], []). maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s], [E7|E7s]) :- call(Cont, E1, E2, E3, E4, E5, E6, E7), maplist(Cont, E1s, E2s, E3s, E4s, E5s, E6s, E7s). - +%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4, ?Xs5, ?Xs6, ?Xs7). +% +% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3, Xs4, Xs5, Xs6 and Xs7. maplist(_, [], [], [], [], [], [], [], []). maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s], [E7|E7s], [E8|E8s]) :- call(Cont, E1, E2, E3, E4, E5, E6, E7, E8), maplist(Cont, E1s, E2s, E3s, E4s, E5s, E6s, E7s, E8s). - +%% sum_list(+Xs, -Sum). +% +% Takes a lists of numbers and unifies Sum with the result of summing all the elements of the list. +% +% ?- sum_list([2,2,2], 6). +% true. sum_list(Ls, S) :- foldl(lists:sum_, Ls, 0, S). sum_(L, S0, S) :- S is S0 + L. - +%% same_length(?Xs, ?Ys). +% +% Succeeds if Xs and Ys are lists of the same length same_length([], []). same_length([_|As], [_|Bs]) :- same_length(As, Bs). +%% foldl(+Predicate, ?Ls, +A0, ?A). +% +% foldl, sometimes called reduce, is a metapredicate that takes a predicate, a list of items +% and a starting value, and outputs a single value. The predicate _Predicate_ must be able to take the current +% element of the list, the previous value of the computation and the next value of the computation. +% +% For example, if we define sum_ as: +% +% sum_(L, S0, S) :- S is S0 + L. +% +% Then we can define sum\_list/2 as the following: +% +% sum_list(Ls, S) :- foldl(sum_, Ls, 0, S). +% foldl(Goal_3, Ls, A0, A) :- foldl_(Ls, Goal_3, A0, A). @@ -191,7 +289,9 @@ foldl_([L|Ls], G_3, A0, A) :- call(G_3, L, A0, A1), foldl_(Ls, G_3, A1, A). - +%% foldl(+Predicate, ?Ls0, ?Ls1, +A0, ?A). +% +% Same as foldl/4 but with an extra list foldl(Goal_4, Xs, Ys, A0, A) :- foldl_(Xs, Ys, Goal_4, A0, A). @@ -201,6 +301,13 @@ foldl_([X|Xs], [Y|Ys], G_4, A0, A) :- call(G_4, X, Y, A0, A1), foldl_(Xs, Ys, G_4, A1, A). +%% transpose(?Ls, ?Ts). +% +% If Ls is a list of lists, Ts contains the transposition +% +% ?- transpose([[1,1],[2,2]], Ts). +% Ts = [[1,2],[1,2]]. +% transpose(Ls, Ts) :- lists_transpose(Ls, Ts). @@ -214,7 +321,13 @@ transpose_(_, Fs, Lists0, Lists) :- list_first_rest([L|Ls], L, Ls). - +%% list_to_set(+Ls0, -Set). +% +% Takes a list Ls0 and returns a list Set that doesn't contain any repeated element +% +% ?- list_to_set([2,3,4,4,1,2], Set). +% Set = [2,3,4,1]. +% list_to_set(Ls0, Ls) :- maplist(lists:with_var, Ls0, LVs0), keysort(LVs0, LVs), @@ -242,7 +355,12 @@ unify_same(E-V, Prev-Var, E-V) :- ; true ). - +%% nth0(?N, ?Ls, ?E). +% +% Succeeds if in the N position of the list Ls, we found the element E. The elements start counting from zero. +% +% ?- nth0(2, [1,2,3,4], 3). +% true. nth0(N, Es0, E) :- nonvar(N), '$skip_max_list'(Skip, N, Es0,Es1), @@ -277,6 +395,12 @@ nth0_el(N0,N, _,E, [E0|Es0]) :- N1 is N0+1, nth0_el(N1,N, E0,E, Es0). +%% nth1(?N, ?Ls, ?E). +% +% Succeeds if in the N position of the list Ls, we found the element E. The elements start counting from one. +% +% ?- nth1(2, [1,2,3,4], 2). +% true. nth1(N, Es0, E) :- N \== 0, nth0(N, [_|Es0], E), @@ -291,6 +415,12 @@ skipn(N0, Es0,Es, Xs0,Xs) :- skipn(N1, Es1,Es, Xs1,Xs). skipn(0, Es,Es, Xs,Xs). +%% nth0(?N, ?Ls, ?E, ?Rs). +% +% Succeeds if in the N position of the list Ls, we found the element E and the rest of the list is Rs. The elements start counting from zero. +% +% ?- nth0(2, [1,2,3,4], 3, [1,2,4]). +% true. nth0(N, Es0, E, Es) :- integer(N), N >= 0, @@ -315,45 +445,54 @@ nth0_elx(N0,N, E0,E, [E1|Es0], [E0|Es]) :- % p.p.8.5 +%% nth1(?N, ?Ls, ?E, ?Rs). +% +% Succeeds if in the N position of the list Ls, we found the element E and the rest of the list is Rs. The elements start counting from one. +% +% ?- nth1(2, [1,2,3,4], 2, [1,3,4]). +% true. nth1(N, Es0, E, Es) :- N \== 0, nth0(N, [_|Es0], E, [_|Es]), N \== 0. - +%% list_max(+Xs, -Max). +% +% Takes a list Xs and unifies with the maximum value of the list list_max([N|Ns], Max) :- foldl(lists:list_max_, Ns, N, Max). list_max_(N, Max0, Max) :- Max is max(N, Max0). +%% list_min(+Xs, -Min). +% +% Takes a list Xs and unifies with the minimum value of the list list_min([N|Ns], Min) :- foldl(lists:list_min_, Ns, N, Min). list_min_(N, Min0, Min) :- Min is min(N, Min0). -%! permutation(?Xs, ?Ys) is nondet. +%% permutation(?Xs, ?Ys) is nondet. % -% True when Xs is a permutation of Ys. This can solve for Ys given -% Xs or Xs given Ys, or even enumerate Xs and Ys together. The -% predicate permutation/2 is primarily intended to generate -% permutations. Note that a list of length N has N! permutations, -% and unbounded permutation generation becomes prohibitively -% expensive, even for rather short lists (10! = 3,628,800). +% True when Xs is a permutation of Ys. This can solve for Ys given +% Xs or Xs given Ys, or even enumerate Xs and Ys together. The +% predicate permutation/2 is primarily intended to generate +% permutations. Note that a list of length N has N! permutations, +% and unbounded permutation generation becomes prohibitively +% expensive, even for rather short lists (10! = 3,628,800). % -% The example below illustrates that Xs and Ys being proper lists -% is not a sufficient condition to use the above replacement. +% The example below illustrates that Xs and Ys being proper lists +% is not a sufficient condition to use the above replacement. % -% == % ?- permutation([1,2], [X,Y]). -% X = 1, Y = 2 ; -% X = 2, Y = 1 ; -% false. -% == +% X = 1, Y = 2 +% ; X = 2, Y = 1 +% ; false. % -% @error type_error(list, Arg) if either argument is not a proper -% or partial list. +% Throws type\_error(list, Arg) if either argument is not a proper +% or partial list. permutation(Xs, Ys) :- '$skip_max_list'(Xlen, _, Xs, XTail), From 56e5da6680f9259e292285b7a1dd2901d55b13a7 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 6 Dec 2022 20:24:08 +0100 Subject: [PATCH 018/361] introduce and use deref_register(n) --- src/machine/system_calls.rs | 411 ++++++++++++++++-------------------- 1 file changed, 187 insertions(+), 224 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 87ab9e48..f0dbc178 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -988,6 +988,11 @@ impl MachineState { } impl Machine { + #[inline(always)] + pub(crate) fn deref_register(&mut self, i: usize) -> HeapCellValue { + self.machine_st.store(self.machine_st.deref(self.machine_st.registers[i])) + } + #[inline(always)] pub(crate) fn call_inline( &mut self, @@ -995,7 +1000,7 @@ impl Machine { call_at_index: impl Fn(&mut Machine, Atom, usize, IndexPtr) -> CallResult, ) -> CallResult { let arity = arity - 1; - let goal = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let goal = self.deref_register(1); let load_registers = |machine_st: &mut MachineState, goal: HeapCellValue| -> Option { read_heap_cell!(goal, @@ -1060,8 +1065,8 @@ impl Machine { #[inline(always)] pub(crate) fn compile_inline_or_expanded_goal(&mut self) -> CallResult { - let goal = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let module_name = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[4])); + let goal = self.deref_register(1); + let module_name = self.deref_register(4); // supp_vars are the supplementary variables generated by // complete_partial_goal prior to goal_expansion. @@ -1364,7 +1369,7 @@ impl Machine { #[inline(always)] pub(crate) fn bind_from_register(&mut self) { - let reg = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let reg = self.deref_register(2); let n = match Number::try_from(reg) { Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).ok(), Ok(Number::Integer(n)) => n.to_usize(), @@ -1393,9 +1398,10 @@ impl Machine { Some(host) => { let hostname = self.machine_st.atom_tbl.build_with(host); + let a1 = self.deref_register(1); self.machine_st.unify_atom( hostname, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])) + a1 ); return; @@ -1410,7 +1416,7 @@ impl Machine { #[inline(always)] pub(crate) fn current_input(&mut self) -> CallResult { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); let stream = self.user_input; if let Some(var) = addr.as_var() { @@ -1445,7 +1451,7 @@ impl Machine { #[inline(always)] pub(crate) fn current_output(&mut self) -> CallResult { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); let stream = self.user_output; if let Some(var) = addr.as_var() { @@ -1562,9 +1568,7 @@ impl Machine { #[inline(always)] pub(crate) fn file_time(&mut self) { if let Some(file) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { - let which = cell_as_atom!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2] - ))); + let which = cell_as_atom!(self.deref_register(2)); if let Ok(md) = fs::metadata(file.as_str()) { if let Ok(time) = match which { @@ -1677,16 +1681,17 @@ impl Machine { let current_atom = self.machine_st.atom_tbl.build_with(¤t); + let a1 = self.deref_register(1); self.machine_st.unify_complete_string( current_atom, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])), + a1 ); if self.machine_st.fail { return Ok(()); } - let target = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let target = self.deref_register(2); if let Some(next) = self.machine_st.value_to_str_like(target) { if env::set_current_dir(std::path::Path::new(next.as_str())).is_ok() { @@ -1717,9 +1722,10 @@ impl Machine { let canonical_atom = self.machine_st.atom_tbl.build_with(cs); + let a2 = self.deref_register(2); self.machine_st.unify_complete_string( canonical_atom, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2 ); return Ok(()); @@ -1735,7 +1741,8 @@ impl Machine { #[inline(always)] pub(crate) fn atom_chars(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); + let a2 = self.deref_register(2); read_heap_cell!(a1, (HeapCellValueTag::Char) => { @@ -1753,7 +1760,7 @@ impl Machine { if arity == 0 { self.machine_st.unify_complete_string( name, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2, ); } else { self.machine_st.fail = true; @@ -1763,14 +1770,14 @@ impl Machine { if arity == 0 { self.machine_st.unify_complete_string( name, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2, ); } else { self.machine_st.fail = true; } } (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let a2 = self.deref_register(2); if let Some(str_like) = self.machine_st.value_to_str_like(a2) { let atom_cell = match str_like { @@ -1800,7 +1807,7 @@ impl Machine { #[inline(always)] pub(crate) fn atom_codes(&mut self) -> CallResult { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); read_heap_cell!(a1, (HeapCellValueTag::Char, c) => { @@ -1861,7 +1868,7 @@ impl Machine { #[inline(always)] pub(crate) fn atom_length(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); let len: i64 = read_heap_cell!(a1, (HeapCellValueTag::Str, s) => { @@ -1891,16 +1898,17 @@ impl Machine { } ); + let a2 = self.deref_register(2); self.machine_st.unify_fixnum( Fixnum::build_with(len), - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2, ); } #[inline(always)] pub(crate) fn call_continuation(&mut self, last_call: bool) -> CallResult { let stub_gen = || functor_stub(atom!("call_continuation"), 1); - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); match self.machine_st.try_from_list(a1, stub_gen) { Err(e) => Err(e), @@ -1925,7 +1933,7 @@ impl Machine { #[inline(always)] pub(crate) fn chars_to_number(&mut self) -> CallResult { let stub_gen = || functor_stub(atom!("number_chars"), 2); - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); let atom_or_string = self.machine_st.value_to_str_like(a1).unwrap(); self.machine_st.parse_number_from_string( @@ -1938,7 +1946,7 @@ impl Machine { #[inline(always)] pub(crate) fn create_partial_string(&mut self) { let atom = cell_as_atom!( - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])) + self.deref_register(1) ); if atom == atom!("") { @@ -1961,7 +1969,7 @@ impl Machine { #[inline(always)] pub(crate) fn is_partial_string(&mut self) { - let value = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let value = self.deref_register(1); let h = self.machine_st.heap.len(); self.machine_st.heap.push(value); @@ -1978,7 +1986,8 @@ impl Machine { #[inline(always)] pub(crate) fn partial_string_tail(&mut self) { - let pstr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let pstr = self.deref_register(1); + let a2 = self.deref_register(2); read_heap_cell!(pstr, (HeapCellValueTag::PStrLoc, h) => { @@ -1987,20 +1996,20 @@ impl Machine { if HeapCellValueTag::CStr == self.machine_st.heap[h].get_tag() { self.machine_st.unify_atom( atom!("[]"), - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])) + a2 ); } else { unify_fn!( self.machine_st, heap_loc_as_cell!(h+1), - self.machine_st.registers[2] + a2 ); } } (HeapCellValueTag::CStr) => { self.machine_st.unify_atom( atom!("[]"), - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])) + a2 ); } (HeapCellValueTag::Lis, h) => { @@ -2043,18 +2052,20 @@ impl Machine { } } + let addr = self.deref_register(2); + if stream.at_end_of_stream() { stream.set_past_end_of_stream(true); self.machine_st.unify_fixnum( Fixnum::build_with(-1), - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + addr, ); return Ok(()); } - let addr = match self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])) { + match addr { addr if addr.is_var() => addr, addr => match Number::try_from(addr) { Ok(Number::Integer(n)) => { @@ -2136,20 +2147,20 @@ impl Machine { } } + let a2 = self.deref_register(2); + if stream.at_end_of_stream() { let end_of_file = atom!("end_of_file"); stream.set_past_end_of_stream(true); self.machine_st.unify_atom( end_of_file, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2, ); return Ok(()); } - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); - let a2 = read_heap_cell!(a2, (HeapCellValueTag::Char) => { a2 @@ -2233,20 +2244,20 @@ impl Machine { } } + let a2 = self.deref_register(2); + if stream.at_end_of_stream() { let end_of_file = atom!("end_of_file"); stream.set_past_end_of_stream(true); self.machine_st.unify_atom( end_of_file, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2, ); return Ok(()); } - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); - let addr = read_heap_cell!(a2, (HeapCellValueTag::Var | HeapCellValueTag::StackVar | HeapCellValueTag::AttrVar) => { a2 @@ -2319,10 +2330,8 @@ impl Machine { #[inline(always)] pub(crate) fn number_to_chars(&mut self) { - let n = self.machine_st.registers[1]; - let chs = self.machine_st.registers[2]; - - let n = self.machine_st.store(self.machine_st.deref(n)); + let n = self.deref_register(1); + let chs = self.deref_register(2); let string = match Number::try_from(n) { Ok(Number::Float(OrderedFloat(n))) => { @@ -2344,13 +2353,13 @@ impl Machine { let chars_atom = self.machine_st.atom_tbl.build_with(&string.trim()); self.machine_st.unify_complete_string( chars_atom, - self.machine_st.store(self.machine_st.deref(chs)), + chs, ); } #[inline(always)] pub(crate) fn number_to_codes(&mut self) { - let n = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let n = self.deref_register(1); let chs = self.machine_st.registers[2]; let string = match Number::try_from(n) { @@ -2406,7 +2415,8 @@ impl Machine { #[inline(always)] pub(crate) fn char_code(&mut self) -> CallResult { let stub_gen = || functor_stub(atom!("char_code"), 2); - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); + let a2 = self.deref_register(2); let c = read_heap_cell!(a1, (HeapCellValueTag::Atom, (name, _arity)) => { @@ -2423,8 +2433,6 @@ impl Machine { c } _ => { - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); - match Number::try_from(a2) { Ok(Number::Integer(n)) => { let c = match n.to_u32().and_then(std::char::from_u32) { @@ -2462,7 +2470,7 @@ impl Machine { self.machine_st.unify_fixnum( Fixnum::build_with(c as i64), - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2, ); Ok(()) @@ -2470,8 +2478,8 @@ impl Machine { #[inline(always)] pub(crate) fn char_type(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let a1 = self.deref_register(1); + let a2 = self.deref_register(2); let c = read_heap_cell!(a1, (HeapCellValueTag::Char, c) => { @@ -2558,7 +2566,7 @@ impl Machine { #[inline(always)] pub(crate) fn check_cut_point(&mut self) { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); let old_b = cell_as_fixnum!(addr).get_num() as usize; let prev_b = self.machine_st.stack.index_or_frame(self.machine_st.b).prelude.b; @@ -2576,7 +2584,7 @@ impl Machine { #[inline(always)] pub(crate) fn fetch_global_var(&mut self) { - let key = cell_as_atom!(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))); + let key = cell_as_atom!(self.deref_register(1)); let addr = self.machine_st.registers[2]; match self.indices.global_variables.get_mut(&key) { @@ -2621,7 +2629,7 @@ impl Machine { )?; let stub_gen = || functor_stub(atom!("put_code"), 2); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let addr = self.deref_register(2); if addr.is_var() { let err = self.machine_st.instantiation_error(); @@ -2671,7 +2679,7 @@ impl Machine { )?; let stub_gen = || functor_stub(atom!("put_char"), 2); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let addr = self.deref_register(2); if addr.is_var() { let err = self.machine_st.instantiation_error(); @@ -2757,7 +2765,7 @@ impl Machine { )?; let stub_gen = || functor_stub(atom!("put_byte"), 2); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let addr = self.deref_register(2); if addr.is_var() { let err = self.machine_st.instantiation_error(); @@ -2833,7 +2841,7 @@ impl Machine { } let stub_gen = || functor_stub(atom!("get_byte"), 2); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let addr = self.deref_register(2); let addr = if addr.is_var() { addr @@ -2912,13 +2920,15 @@ impl Machine { } } + let addr = self.deref_register(2); + if stream.at_end_of_stream() { let end_of_file = atom!("end_of_file"); stream.set_past_end_of_stream(true); self.machine_st.unify_atom( end_of_file, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])) + addr ); return Ok(()); @@ -2927,8 +2937,6 @@ impl Machine { let stub_gen = || functor_stub(atom!("get_char"), 2); let mut iter = self.machine_st.open_parsing_stream(stream, atom!("get_char"), 2)?; - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); - let addr = if addr.is_var() { addr } else { @@ -2983,7 +2991,7 @@ impl Machine { 3, )?; - let num = match Number::try_from(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2]))) { + let num = match Number::try_from(self.deref_register(2)) { Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).unwrap(), Ok(Number::Integer(n)) => match n.to_usize() { Some(u) => u, @@ -3025,7 +3033,7 @@ impl Machine { } }; - let output = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); + let output = self.deref_register(3); let atom = self.machine_st.atom_tbl.build_with(&string); self.machine_st.unify_complete_string(atom, output); @@ -3057,19 +3065,20 @@ impl Machine { } } + let addr = self.deref_register(2); + if stream.at_end_of_stream() { stream.set_past_end_of_stream(true); self.machine_st.unify_fixnum( Fixnum::build_with(-1), - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + addr, ); return Ok(()); } let stub_gen = || functor_stub(atom!("get_code"), 2); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); let addr = if addr.is_var() { addr @@ -3155,9 +3164,7 @@ impl Machine { if let Some(first_stream) = first_stream { let stream = stream_as_cell!(first_stream); - let var = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - )).as_var().unwrap(); + let var = self.deref_register(1).as_var().unwrap(); self.machine_st.bind(var, stream); } else { @@ -3167,9 +3174,7 @@ impl Machine { #[inline(always)] pub(crate) fn next_stream(&mut self) { - let prev_stream = cell_as_stream!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - ))); + let prev_stream = cell_as_stream!(self.deref_register(1)); let mut next_stream = None; let mut null_streams = BTreeSet::new(); @@ -3191,9 +3196,7 @@ impl Machine { self.indices.streams = self.indices.streams.sub(&null_streams); if let Some(next_stream) = next_stream { - let var = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2] - )).as_var().unwrap(); + let var = self.deref_register(2).as_var().unwrap(); let next_stream = stream_as_cell!(next_stream); self.machine_st.bind(var, next_stream); @@ -3252,9 +3255,10 @@ impl Machine { _ => unreachable!(), }; + let a1 = self.deref_register(1); self.machine_st.unify_char( c, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])), + a1, ); Ok(()) @@ -3262,12 +3266,10 @@ impl Machine { #[inline(always)] pub(crate) fn head_is_dynamic(&mut self) { - 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 (name, arity) = read_heap_cell!( - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + self.deref_register(2), (HeapCellValueTag::Str, s) => { cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity() } @@ -3337,7 +3339,7 @@ impl Machine { #[inline(always)] pub(crate) fn copy_to_lifted_heap(&mut self) { let lh_offset = cell_as_fixnum!( - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])) + self.deref_register(1) ).get_num() as usize; let copy_target = self.machine_st.registers[2]; @@ -3354,7 +3356,7 @@ impl Machine { #[inline(always)] pub(crate) fn delete_attribute(&mut self) { - let ls0 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let ls0 = self.deref_register(1); if let HeapCellValueTag::Lis = ls0.get_tag() { let l1 = ls0.get_value(); @@ -3392,7 +3394,7 @@ impl Machine { #[inline(always)] pub(crate) fn delete_head_attribute(&mut self) { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); debug_assert_eq!(addr.get_tag(), HeapCellValueTag::AttrVar); @@ -3422,9 +3424,7 @@ impl Machine { &mut self, narity: usize, ) -> Result<(Atom, PredicateKey), MachineStub> { - let module_name = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - )); + let module_name = self.deref_register(1); let module_name = read_heap_cell!(module_name, (HeapCellValueTag::Atom, (name, _arity)) => { @@ -3446,9 +3446,7 @@ impl Machine { } ); - let goal = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2] - )); + let goal = self.deref_register(2); let (name, arity, s) = self.machine_st.setup_call_n_init_goal_info(goal, narity)?; @@ -3477,7 +3475,7 @@ impl Machine { #[inline(always)] pub(crate) fn enqueue_attributed_var(&mut self) { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); read_heap_cell!(addr, (HeapCellValueTag::AttrVar, h) => { @@ -3490,7 +3488,7 @@ impl Machine { #[inline(always)] pub(crate) fn get_next_db_ref(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); if let Some(name_var) = a1.as_var() { let mut iter = self.indices.code_dir.iter(); @@ -3508,9 +3506,7 @@ impl Machine { self.machine_st.fail = true; } else if a1.get_tag() == HeapCellValueTag::Atom { let name = cell_as_atom!(a1); - let arity = cell_as_fixnum!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2]) - )).get_num() as usize; + let arity = cell_as_fixnum!(self.deref_register(2)).get_num() as usize; match self.machine_st.get_next_db_ref(&self.indices, &DBRef::NamedPred(name, arity)) { Some(DBRef::NamedPred(name, arity)) => { @@ -3534,12 +3530,12 @@ impl Machine { #[inline(always)] pub(crate) fn get_next_op_db_ref(&mut self) { - let prec = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let prec = self.deref_register(1); if let Some(prec_var) = prec.as_var() { - let spec = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); - let op = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); - let orig_op = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[7])); + let spec = self.deref_register(2); + let op = self.deref_register(3); + let orig_op = self.deref_register(7); let spec_num = if spec.get_tag() == HeapCellValueTag::Atom { (match cell_as_atom!(spec) { @@ -3666,9 +3662,7 @@ impl Machine { match ossified_op_dir.iter().next() { Some(((op_atom, _), (op_prec, op_spec))) => { - let ossified_op_dir_var = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[4] - )).as_var().unwrap(); + let ossified_op_dir_var = self.deref_register(4).as_var().unwrap(); let spec_atom = match *op_spec { FX => atom!("fx"), @@ -3698,9 +3692,9 @@ impl Machine { } } } else { - let spec = cell_as_atom!(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2]))); - let op_atom = cell_as_atom!(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3]))); - let ossified_op_dir_cell = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[4])); + let spec = cell_as_atom!(self.deref_register(2)); + let op_atom = cell_as_atom!(self.deref_register(3)); + let ossified_op_dir_cell = self.deref_register(4); if ossified_op_dir_cell.is_var() { self.machine_st.fail = true; @@ -3783,7 +3777,7 @@ impl Machine { #[inline(always)] pub(crate) fn det_length_rundown(&mut self) -> CallResult { let stub_gen = || functor_stub(atom!("length"), 2); - let len = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let len = self.deref_register(2); let n = match Number::try_from(len) { Ok(Number::Fixnum(n)) => n.get_num() as usize, @@ -3806,7 +3800,7 @@ impl Machine { (0 .. n).map(|i| heap_loc_as_cell!(h + 2 * i + 1)), ); - let tail = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let tail = self.deref_register(1); self.machine_st.bind(tail.as_var().unwrap(), heap_loc_as_cell!(h)); Ok(()) @@ -3814,8 +3808,8 @@ impl Machine { #[inline(always)] pub(crate) fn http_open(&mut self) -> CallResult { - let address_sink = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let method = read_heap_cell!(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])), + let address_sink = self.deref_register(1); + let method = read_heap_cell!(self.deref_register(3), (HeapCellValueTag::Atom, (name, arity)) => { debug_assert_eq!(arity, 0); match name { @@ -3832,8 +3826,8 @@ impl Machine { unreachable!() } ); - let address_status = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[4])); - let address_data = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[5])); + let address_status = self.deref_register(4); + let address_data = self.deref_register(5); let mut bytes: Vec = Vec::new(); if let Some(string) = self.machine_st.value_to_str_like(address_data) { bytes = string.as_str().bytes().collect(); @@ -3915,7 +3909,7 @@ impl Machine { stream_as_cell!(stream) }); - let stream_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let stream_addr = self.deref_register(2); self.machine_st.bind(stream_addr.as_var().unwrap(), stream); } else { @@ -3930,7 +3924,7 @@ impl Machine { #[inline(always)] pub(crate) fn http_listen(&mut self) -> CallResult { - let address_sink = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let address_sink = self.deref_register(1); if let Some(address_str) = self.machine_st.value_to_str_like(address_sink) { let address_string = address_str.as_str(); let addr: SocketAddr = match address_string.to_socket_addrs().ok().and_then(|mut s| s.next()) { @@ -3965,7 +3959,7 @@ impl Machine { }); let http_listener = HttpListener { incoming: rx }; let http_listener = arena_alloc!(http_listener, &mut self.machine_st.arena); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let addr = self.deref_register(2); self.machine_st.bind(addr.as_var().unwrap(), typed_arena_ptr_as_cell!(http_listener)); } Ok(()) @@ -3973,12 +3967,12 @@ impl Machine { #[inline(always)] pub(crate) fn http_accept(&mut self) -> CallResult { - let culprit = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let method = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); - let path = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); - let query = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[5])); - let stream_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[6])); - let handle_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[7])); + let culprit = self.deref_register(1); + let method = self.deref_register(2); + let path = self.deref_register(3); + let query = self.deref_register(5); + let stream_addr = self.deref_register(6); + let handle_addr = self.deref_register(7); read_heap_cell!(culprit, (HeapCellValueTag::Cons, cons_ptr) => { match_untyped_arena_ptr!(cons_ptr, @@ -4056,8 +4050,8 @@ impl Machine { #[inline(always)] pub(crate) fn http_answer(&mut self) -> CallResult { - let culprit = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let status_code = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let culprit = self.deref_register(1); + let status_code = self.deref_register(2); let status_code: u16 = match Number::try_from(status_code) { Ok(Number::Fixnum(n)) => n.get_num() as u16, Ok(Number::Integer(n)) => match n.to_u16() { @@ -4089,7 +4083,7 @@ impl Machine { }, Err(e) => return Err(e) }; - let stream_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[4])); + let stream_addr = self.deref_register(4); read_heap_cell!(culprit, (HeapCellValueTag::Cons, cons_ptr) => { @@ -4139,7 +4133,7 @@ impl Machine { let stream_type = self.machine_st.registers[7]; let options = self.machine_st.to_stream_options(alias, eof_action, reposition, stream_type); - let src_sink = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let src_sink = self.deref_register(1); if let Some(file_spec) = self.machine_st.value_to_str_like(src_sink) { let file_spec = file_spec.as_atom(&mut self.machine_st.atom_tbl); @@ -4157,7 +4151,7 @@ impl Machine { self.indices.stream_aliases.insert(alias, stream); } - let stream_var = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); + let stream_var = self.deref_register(3); self.machine_st.bind(stream_var.as_var().unwrap(), stream_as_cell!(stream)); } else { let err = self.machine_st.domain_error(DomainErrorType::SourceSink, src_sink); @@ -4262,7 +4256,7 @@ impl Machine { #[inline(always)] pub(crate) fn get_attributed_variable_list(&mut self) { - let attr_var = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let attr_var = self.deref_register(1); let attr_var_list = read_heap_cell!(attr_var, (HeapCellValueTag::AttrVar, h) => { h + 1 @@ -4283,7 +4277,7 @@ impl Machine { } ); - let list_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let list_addr = self.deref_register(2); self.machine_st.bind(Ref::heap_cell(attr_var_list), list_addr); } @@ -4323,12 +4317,10 @@ impl Machine { #[inline(always)] pub(crate) fn get_continuation_chunk(&mut self) { - let e = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let e = self.deref_register(1); let e = cell_as_fixnum!(e).get_num() as usize; - let p_functor = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2] - )); + let p_functor = self.deref_register(2); let p = to_local_code_ptr(&self.machine_st.heap, p_functor).unwrap(); @@ -4421,7 +4413,7 @@ impl Machine { #[inline(always)] pub(crate) fn get_double_quotes(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); self.machine_st.unify_atom( match self.machine_st.flags.double_quotes { @@ -4457,7 +4449,7 @@ impl Machine { #[inline(always)] pub(crate) fn halt(&mut self) { - let code = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let code = self.deref_register(1); let code = match Number::try_from(code) { Ok(Number::Fixnum(n)) => i32::try_from(n.get_num()).unwrap(), @@ -4491,8 +4483,8 @@ impl Machine { #[inline(always)] pub(crate) fn install_inference_counter(&mut self) -> CallResult { // A1 = B, A2 = L - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let a1 = self.deref_register(1); + let a2 = self.deref_register(2); let n = match Number::try_from(a2) { Ok(Number::Fixnum(bp)) => bp.get_num() as usize, @@ -4514,7 +4506,7 @@ impl Machine { self.machine_st.increment_call_count_fn = MachineState::increment_call_count; - let a3 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); + let a3 = self.deref_register(3); self.machine_st.unify_big_int(count, a3); Ok(()) @@ -4522,24 +4514,18 @@ impl Machine { #[inline(always)] pub(crate) fn module_exists(&mut self) { - let module = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let module = self.deref_register(1); let module_name = cell_as_atom!(module); self.machine_st.fail = !self.indices.modules.contains_key(&module_name); } - pub(crate) fn predicate_defined(&self) -> bool { - let module_name = cell_as_atom!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - ))); + pub(crate) fn predicate_defined(&mut self) -> bool { + let module_name = cell_as_atom!(self.deref_register(1)); + let name = cell_as_atom!(self.deref_register(2)); + let a3 = self.deref_register(3); - let name = cell_as_atom!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2] - ))); - - let arity = match Number::try_from(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[3] - ))) { + let arity = match Number::try_from(a3) { Ok(Number::Fixnum(n)) => n.get_num() as usize, Ok(Number::Integer(n)) => { if let Some(n) = n.to_usize() { @@ -4563,11 +4549,9 @@ impl Machine { #[inline(always)] pub(crate) fn no_such_predicate(&mut self) -> 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 head = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let head = self.deref_register(2); self.machine_st.fail = read_heap_cell!(head, (HeapCellValueTag::Str, s) => { @@ -4623,8 +4607,8 @@ impl Machine { } #[inline(always)] pub(crate) fn redo_attr_var_binding(&mut self) { - let var = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let value = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let var = self.deref_register(1); + let value = self.deref_register(2); debug_assert_eq!(HeapCellValueTag::AttrVar, var.get_tag()); self.machine_st.heap[var.get_value()] = value; @@ -4653,9 +4637,7 @@ impl Machine { #[inline(always)] pub(crate) fn remove_call_policy_check(&mut self) { - let bp = cell_as_fixnum!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - ))).get_num() as usize; + let bp = cell_as_fixnum!(self.deref_register(1)).get_num() as usize; if bp == self.machine_st.b && self.machine_st.cwil.is_empty() { self.machine_st.cwil.reset(); @@ -4665,13 +4647,13 @@ impl Machine { #[inline(always)] pub(crate) fn remove_inference_counter(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); let bp = cell_as_fixnum!(a1).get_num() as usize; let count = self.machine_st.cwil.remove_limit(bp).clone(); let count = arena_alloc!(count.clone(), &mut self.machine_st.arena); - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let a2 = self.deref_register(2); self.machine_st.unify_big_int(count, a2); } @@ -4720,9 +4702,7 @@ impl Machine { #[inline(always)] pub(crate) fn set_input(&mut self) -> CallResult { - let addr = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - )); + let addr = self.deref_register(1); let stream = self.machine_st.get_stream_or_alias( addr, @@ -4750,7 +4730,7 @@ impl Machine { #[inline(always)] pub(crate) fn set_output(&mut self) -> CallResult { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); let stream = self.machine_st.get_stream_or_alias( addr, &self.indices.stream_aliases, @@ -4792,8 +4772,8 @@ impl Machine { #[inline(always)] pub(crate) fn inference_level(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let a2 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let a1 = self.deref_register(1); + let a2 = self.deref_register(2); let bp = cell_as_fixnum!(a2).get_num() as usize; let prev_b = self.machine_st.stack.index_or_frame(self.machine_st.b).prelude.b; @@ -4807,7 +4787,7 @@ impl Machine { #[inline(always)] pub(crate) fn clean_up_block(&mut self) { - let nb = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let nb = self.deref_register(1); let nb = cell_as_fixnum!(nb).get_num() as usize; let b = self.machine_st.b; @@ -4819,7 +4799,7 @@ impl Machine { #[inline(always)] pub(crate) fn get_ball(&mut self) { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); let h = self.machine_st.heap.len(); if self.machine_st.ball.stub.len() > 0 { @@ -4881,7 +4861,7 @@ impl Machine { pub(crate) fn get_staggered_cut_point(&mut self) { use std::sync::Once; - let b = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let b = self.deref_register(1); static mut SEMICOLON_SECOND_BRANCH_LOC: usize = 0; static LOC_INIT: Once = Once::new(); @@ -4935,7 +4915,7 @@ impl Machine { #[inline(always)] pub(crate) fn next_ep(&mut self) { - let first_arg = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let first_arg = self.deref_register(1); let next_ep_atom = |machine_st: &mut MachineState, name, arity| { debug_assert_eq!(name, atom!("first")); @@ -5004,7 +4984,7 @@ impl Machine { #[inline(always)] pub(crate) fn points_to_continuation_reset_marker(&mut self) { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); let p = match to_local_code_ptr(&self.machine_st.heap, addr) { Some(p) => p + 1, @@ -5021,7 +5001,7 @@ impl Machine { #[inline(always)] pub(crate) fn quoted_token(&mut self) { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); read_heap_cell!(addr, (HeapCellValueTag::Fixnum, n) => { @@ -5102,9 +5082,7 @@ impl Machine { }; let result = heap_loc_as_cell!(term_write_result.heap_loc); - let var = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2] - )).as_var().unwrap(); + let var = self.deref_register(2).as_var().unwrap(); self.machine_st.bind(var, result); } else { @@ -5137,7 +5115,7 @@ impl Machine { #[inline(always)] pub(crate) fn set_seed(&mut self) { - let seed = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let seed = self.deref_register(1); let mut rand = RANDOM_STATE.borrow_mut(); match Number::try_from(seed) { @@ -5152,7 +5130,7 @@ impl Machine { #[inline(always)] pub(crate) fn sleep(&mut self) { - let time = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let time = self.deref_register(1); let time = match Number::try_from(time) { Ok(Number::Float(n)) => n.into_inner(), @@ -5171,8 +5149,8 @@ impl Machine { #[inline(always)] pub(crate) fn socket_client_open(&mut self) -> CallResult { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); - let port = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let addr = self.deref_register(1); + let port = self.deref_register(2); let socket_atom = cell_as_atom!(addr); @@ -5259,7 +5237,7 @@ impl Machine { } }; - let stream_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); + let stream_addr = self.deref_register(3); self.machine_st.bind(stream_addr.as_var().unwrap(), stream); Ok(()) @@ -5267,7 +5245,7 @@ impl Machine { #[inline(always)] pub(crate) fn socket_server_open(&mut self) -> CallResult { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); let socket_atom = cell_as_atom_cell!(addr).get_name(); let socket_atom = if socket_atom == atom!("[]") { @@ -5276,7 +5254,7 @@ impl Machine { socket_atom }; - let port = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let port = self.deref_register(2); let port = if port.is_var() { String::from("0") @@ -5319,7 +5297,7 @@ impl Machine { } }; - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); + let addr = self.deref_register(3); self.machine_st.bind(addr.as_var().unwrap(), typed_arena_ptr_as_cell!(tcp_listener)); if had_zero_port { @@ -5352,7 +5330,7 @@ impl Machine { } } - let culprit = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let culprit = self.deref_register(1); read_heap_cell!(culprit, (HeapCellValueTag::Cons, cons_ptr) => { @@ -5379,12 +5357,8 @@ impl Machine { let tcp_stream = stream_as_cell!(tcp_stream); let client = atom_as_cell!(client); - let client_addr = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2], - )); - let stream_addr = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[3], - )); + let client_addr = self.deref_register(2); + let stream_addr = self.deref_register(3); self.machine_st.bind(client_addr.as_var().unwrap(), client); self.machine_st.bind(stream_addr.as_var().unwrap(), tcp_stream); @@ -5433,7 +5407,7 @@ impl Machine { self.indices.streams.insert(stream); self.machine_st.heap.push(stream_as_cell!(stream)); - let stream_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); + let stream_addr = self.deref_register(3); self.machine_st.bind(stream_addr.as_var().unwrap(), stream_as_cell!(stream)); Ok(()) @@ -5483,7 +5457,7 @@ impl Machine { let stream = Stream::from_tls_stream(atom!("TLS"), stream, &mut self.machine_st.arena); self.indices.streams.insert(stream); - let stream_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[4])); + let stream_addr = self.deref_register(4); self.machine_st.bind(stream_addr.as_var().unwrap(), stream_as_cell!(stream)); } else { unreachable!(); @@ -5494,7 +5468,7 @@ impl Machine { #[inline(always)] pub(crate) fn socket_server_close(&mut self) -> CallResult { - let culprit = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let culprit = self.deref_register(1); read_heap_cell!(culprit, (HeapCellValueTag::Cons, cons_ptr) => { @@ -5543,7 +5517,7 @@ impl Machine { return Err(self.machine_st.error_form(err, stub)); } - let position = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let position = self.deref_register(2); let position = match Number::try_from(position) { Ok(Number::Fixnum(n)) => n.get_num() as u64, @@ -5573,9 +5547,7 @@ impl Machine { 2, )?; - let atom = cell_as_atom!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[2] - ))); + let atom = cell_as_atom!(self.deref_register(2)); let property = match atom { atom!("file_name") => { @@ -5647,7 +5619,7 @@ impl Machine { #[inline(always)] pub(crate) fn store_global_var(&mut self) { - let key = cell_as_atom!(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))); + let key = cell_as_atom!(self.deref_register(1)); let value = self.machine_st.registers[2]; let mut ball = Ball::new(); @@ -5665,8 +5637,8 @@ impl Machine { #[inline(always)] pub(crate) fn store_backtrackable_global_var(&mut self) { - let key = cell_as_atom!(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))); - let new_value = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])); + let key = cell_as_atom!(self.deref_register(1)); + let new_value = self.deref_register(2); match self.indices.global_variables.get_mut(&key) { Some((_, ref mut loc)) => match loc { @@ -5691,9 +5663,10 @@ impl Machine { #[inline(always)] pub(crate) fn term_attributed_variables(&mut self) { if self.machine_st.registers[1].is_constant() { + let a2 = self.deref_register(2); self.machine_st.unify_atom( atom!("[]"), - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])), + a2, ); return; @@ -5709,13 +5682,11 @@ impl Machine { #[inline(always)] pub(crate) fn term_variables(&mut self) { - let a1 = self.machine_st.registers[1]; - let a2 = self.machine_st.registers[2]; - - let stored_v = self.machine_st.store(self.machine_st.deref(a1)); + let stored_v = self.deref_register(1); + let a2 = self.deref_register(2); if stored_v.is_constant() { - self.machine_st.unify_atom(atom!("[]"), self.machine_st.store(self.machine_st.deref(a2))); + self.machine_st.unify_atom(atom!("[]"), a2); return; } @@ -5734,7 +5705,7 @@ impl Machine { pub(crate) fn term_variables_under_max_depth(&mut self) { // Term, MaxDepth, VarList let max_depth = cell_as_fixnum!( - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2])) + self.deref_register(2) ).get_num() as usize; self.machine_st.term_variables_under_max_depth( @@ -5746,7 +5717,7 @@ impl Machine { #[inline(always)] pub(crate) fn truncate_lifted_heap_to(&mut self) { - let a1 = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let a1 = self.deref_register(1); let lh_offset = cell_as_fixnum!(a1).get_num() as usize; self.machine_st.lifted_heap.truncate(lh_offset); @@ -5784,15 +5755,9 @@ impl Machine { #[inline(always)] pub(crate) fn wam_instructions(&mut self) -> CallResult { - let module_name = cell_as_atom!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1]) - )); - - let name = self.machine_st.registers[2]; - let arity = self.machine_st.registers[3]; - - let name = cell_as_atom!(self.machine_st.store(self.machine_st.deref(name))); - let arity = self.machine_st.store(self.machine_st.deref(arity)); + let module_name = cell_as_atom!(self.deref_register(1)); + let name = cell_as_atom!(self.deref_register(2)); + let arity = self.deref_register(3); let arity = match Number::try_from(arity) { Ok(Number::Fixnum(n)) => n.get_num() as usize, @@ -5968,9 +5933,7 @@ impl Machine { &mut self.machine_st.atom_tbl, ); - let result_addr = self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - )); + let result_addr = self.deref_register(1); if let Some(var) = result_addr.as_var() { self.machine_st.bind(var, chars); @@ -5988,9 +5951,10 @@ impl Machine { let buffer = git_version!(cargo_prefix = "cargo:", fallback = "unknown"); let buffer_atom = self.machine_st.atom_tbl.build_with(buffer); + let a1 = self.deref_register(1); self.machine_st.unify_complete_string( buffer_atom, - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])), + a1, ); } @@ -6167,7 +6131,7 @@ impl Machine { let algorithm = cell_as_atom!(self.machine_st.registers[5]); - let length = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[6])); + let length = self.deref_register(6); let length = match Number::try_from(length) { Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).unwrap(), @@ -6229,7 +6193,7 @@ impl Machine { let stub2_gen = || functor_stub(atom!("crypto_password_hash"), 3); let salt = self.machine_st.integers_to_bytevec(self.machine_st.registers[2], stub2_gen); - let iterations = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[3])); + let iterations = self.deref_register(3); let iterations = match Number::try_from(iterations) { Ok(Number::Fixnum(n)) => u64::try_from(n.get_num()).unwrap(), @@ -6318,7 +6282,7 @@ impl Machine { #[inline(always)] pub(crate) fn crypto_data_decrypt(&mut self) { let data = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet")); - let encoding = cell_as_atom!(self.machine_st.store(self.machine_st.deref(self.machine_st.registers[5]))); + let encoding = cell_as_atom!(self.deref_register(5)); let aad = self.string_encoding_bytes(self.machine_st.registers[2], encoding); let stub1_gen = || functor_stub(atom!("crypto_data_decrypt"), 7); @@ -6473,7 +6437,7 @@ impl Machine { #[inline(always)] pub(crate) fn first_non_octet(&mut self) { - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let addr = self.deref_register(1); if let Some(string) = self.machine_st.value_to_str_like(addr) { for c in string.as_str().chars() { @@ -6596,8 +6560,9 @@ impl Machine { } } + let a1 = self.deref_register(1); let command = self.machine_st.value_to_str_like( - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])) + a1 ).unwrap(); match env::var("SHELL") { @@ -6644,7 +6609,7 @@ impl Machine { } }; - if self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])).is_var() { + if self.deref_register(1).is_var() { let b64 = self.machine_st.value_to_str_like(self.machine_st.registers[2]).unwrap(); let bytes = base64::decode_config(b64.as_str(), config); @@ -6676,9 +6641,7 @@ impl Machine { #[inline(always)] pub(crate) fn load_library_as_stream(&mut self) -> CallResult { - let library_name = cell_as_atom!(self.machine_st.store(self.machine_st.deref( - self.machine_st.registers[1] - ))); + let library_name = cell_as_atom!(self.deref_register(1)); use crate::machine::LIBRARIES; @@ -6792,7 +6755,7 @@ impl Machine { #[inline(always)] pub(crate) fn pop_count(&mut self) { - let number = self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1])); + let number = self.deref_register(1); let pop_count = integer_as_cell!(match Number::try_from(number) { Ok(Number::Fixnum(n)) => { Number::Fixnum(Fixnum::build_with(n.get_num().count_ones() as i64)) From 309e5b320eb29dd3aa9cef4c7a2dc4b82dcd7d57 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 7 Dec 2022 23:14:08 +0100 Subject: [PATCH 019/361] more uses of newly available deref_register(n) --- src/machine/system_calls.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index f0dbc178..275c0ce7 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -4165,11 +4165,8 @@ impl Machine { #[inline(always)] pub(crate) fn op_declaration(&mut self) -> CallResult { - let priority = self.machine_st.registers[1]; - let specifier = self.machine_st.registers[2]; - let op = self.machine_st.registers[3]; - - let priority = self.machine_st.store(self.machine_st.deref(priority)); + let priority = self.deref_register(1); + let specifier = cell_as_atom_cell!(self.deref_register(2)).get_name(); let priority = match Number::try_from(priority) { Ok(Number::Integer(n)) => n.to_u16().unwrap(), @@ -4179,10 +4176,7 @@ impl Machine { } }; - let specifier = cell_as_atom_cell!(self.machine_st.store(self.machine_st.deref(specifier))) - .get_name(); - - let op = read_heap_cell!(self.machine_st.store(self.machine_st.deref(op)), + let op = read_heap_cell!(self.deref_register(3), (HeapCellValueTag::Char, c) => { self.machine_st.atom_tbl.build_with(&c.to_string()) } @@ -4283,16 +4277,15 @@ impl Machine { #[inline(always)] pub(crate) fn get_attr_var_queue_delimiter(&mut self) { - let addr = self.machine_st.registers[1]; + let addr = self.deref_register(1); let value = Fixnum::build_with(self.machine_st.attr_var_init.attr_var_queue.len() as i64); - self.machine_st.unify_fixnum(value, self.machine_st.store(self.machine_st.deref(addr))); + self.machine_st.unify_fixnum(value, addr); } #[inline(always)] pub(crate) fn get_attr_var_queue_beyond(&mut self) { - let addr = self.machine_st.registers[1]; - let addr = self.machine_st.store(self.machine_st.deref(addr)); + let addr = self.deref_register(1); let b = match Number::try_from(addr) { Ok(Number::Integer(n)) => n.to_usize(), From e0464d54473e53ef73259ad91b027100dedcc832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Thu, 8 Dec 2022 22:30:48 +0100 Subject: [PATCH 020/361] Compatible Doclog docs for library(ordsets) --- src/lib/ordsets.pl | 187 +++++++++++++++++++++------------------------ 1 file changed, 87 insertions(+), 100 deletions(-) diff --git a/src/lib/ordsets.pl b/src/lib/ordsets.pl index b5886d38..fd17495b 100644 --- a/src/lib/ordsets.pl +++ b/src/lib/ordsets.pl @@ -54,20 +54,19 @@ :- use_module(library(lists)). -/** Ordered set manipulation +/** Ordered set manipulation + Ordered sets are lists with unique elements sorted to the standard order of terms (see sort/2). Exploiting ordering, many of the set operations can be expressed in order N rather than N^2 when dealing with unordered sets that may contain duplicates. The library(ordsets) is available in a number of Prolog implementations. Our predicates are designed to be -compatible with common practice in the Prolog community. The -implementation is incomplete and relies partly on library(oset), an -older ordered set library distributed with SWI-Prolog. New applications -are advised to use library(ordsets). +compatible with common practice in the Prolog community. Some of these predicates match directly to corresponding list operations. It is advised to use the versions from this library to make clear you are operating on ordered sets. An exception is member/2. See -ord_memberchk/2. +ord\_memberchk/2. + The ordsets library is based on the standard order of terms. This implies it can handle all Prolog terms, including variables. Note however, that the ordering is not stable if a term inside the set is @@ -80,13 +79,13 @@ fresh variable. In other cases one should cease using it as an ordset because the order it relies on may have been changed. */ -%! is_ordset(@Term) is semidet. +%% is_ordset(@Term) is semidet. % -% True if Term is an ordered set. All predicates in this library -% expect ordered sets as input arguments. Failing to fullfil this -% assumption results in undefined behaviour. Typically, ordered -% sets are created by predicates from this library, sort/2 or -% setof/3. +% True if Term is an ordered set. All predicates in this library +% expect ordered sets as input arguments. Failing to fullfil this +% assumption results in undefined behaviour. Typically, ordered +% sets are created by predicates from this library, sort/2 or +% setof/3. is_ordset(Term) :- '$skip_max_list'(_, _, Term, Tail), Tail == [], %% is_list(Term), @@ -102,37 +101,35 @@ is_ordset3([H2|T], H) :- is_ordset3(T, H2). -%! ord_empty(?List) is semidet. +%% ord_empty(?List) is semidet. % -% True when List is the empty ordered set. Simply unifies list -% with the empty list. Not part of Quintus. +% True when List is the empty ordered set. Simply unifies list +% with the empty list. Not part of Quintus. ord_empty([]). -%! ord_seteq(+Set1, +Set2) is semidet. +%% ord_seteq(+Set1, +Set2) is semidet. % -% True if Set1 and Set2 have the same elements. As both are -% canonical sorted lists, this is the same as ==/2. -% -% @compat sicstus +% True if Set1 and Set2 have the same elements. As both are +% canonical sorted lists, this is the same as ==/2. ord_seteq(Set1, Set2) :- Set1 == Set2. -%! list_to_ord_set(+List, -OrdSet) is det. +%% list_to_ord_set(+List, -OrdSet) is det. % -% Transform a list into an ordered set. This is the same as -% sorting the list. +% Transform a list into an ordered set. This is the same as +% sorting the list. list_to_ord_set(List, Set) :- sort(List, Set). -%! ord_intersect(+Set1, +Set2) is semidet. +%% ord_intersect(+Set1, +Set2) is semidet. % -% True if both ordered sets have a non-empty intersection. +% True if both ordered sets have a non-empty intersection. ord_intersect([H1|T1], L2) :- ord_intersect_(L2, H1, T1). @@ -148,31 +145,29 @@ ord_intersect__(>, H1, T1, _H2, T2) :- ord_intersect_(T2, H1, T1). -%! ord_disjoint(+Set1, +Set2) is semidet. +%% ord_disjoint(+Set1, +Set2) is semidet. % -% True if Set1 and Set2 have no common elements. This is the -% negation of ord_intersect/2. +% True if Set1 and Set2 have no common elements. This is the +% negation of ord\_intersect/2. ord_disjoint(Set1, Set2) :- \+ ord_intersect(Set1, Set2). -%! ord_intersect(+Set1, +Set2, -Intersection) +%% ord_intersect(+Set1, +Set2, -Intersection) % -% Intersection holds the common elements of Set1 and Set2. +% Intersection holds the common elements of Set1 and Set2. % -% @deprecated Use ord_intersection/3 +% This predicate is **deprecated**. Use ord\_intersection/3 ord_intersect(Set1, Set2, Intersection) :- oset_int(Set1, Set2, Intersection). -%! ord_intersection(+PowerSet, -Intersection) +%% ord_intersection(+PowerSet, -Intersection) % -% Intersection of a powerset. True when Intersection is an ordered -% set holding all elements common to all sets in PowerSet. -% -% @compat sicstus +% Intersection of a powerset. True when Intersection is an ordered +% set holding all elements common to all sets in PowerSet. ord_intersection(PowerSet, Intersection) :- key_by_length(PowerSet, Pairs), @@ -190,10 +185,10 @@ l_int([_-H|T], S0, S) :- l_int(T, S1, S). -%! ord_intersection(+Set1, +Set2, -Intersection) is det. +%% ord_intersection(+Set1, +Set2, -Intersection) is det. % -% Intersection holds the common elements of Set1 and Set2. Uses -% ord_disjoint/2 if Intersection is bound to `[]` on entry. +% Intersection holds the common elements of Set1 and Set2. Uses +% ord\_disjoint/2 if Intersection is bound to `[]` on entry. ord_intersection(Set1, Set2, Intersection) :- ( Intersection == [] @@ -202,13 +197,11 @@ ord_intersection(Set1, Set2, Intersection) :- ). -%! ord_intersection(+Set1, +Set2, ?Intersection, ?Difference) is det. +%% ord_intersection(+Set1, +Set2, ?Intersection, ?Difference) is det. % -% Intersection and difference between two ordered sets. -% Intersection is the intersection between Set1 and Set2, while -% Difference is defined by ord_subtract(Set2, Set1, Difference). -% -% @see ord_intersection/3 and ord_subtract/3. +% Intersection and difference between two ordered sets. +% Intersection is the intersection between Set1 and Set2, while +% Difference is defined by ord\_subtract(Set2, Set1, Difference). ord_intersection([], L, [], L) :- !. ord_intersection([_|_], [], [], []) :- !. @@ -224,35 +217,35 @@ ord_intersection2(>, H1, T1, H2, T2, Intersection, [H2|HDiff]) :- ord_intersection([H1|T1], T2, Intersection, HDiff). -%! ord_add_element(+Set1, +Element, ?Set2) is det. +%% ord_add_element(+Set1, +Element, ?Set2) is det. % -% Insert an element into the set. This is the same as -% ord_union(Set1, [Element], Set2). +% Insert an element into the set. This is the same as +% ord\_union(Set1, [Element], Set2). ord_add_element(Set1, Element, Set2) :- oset_addel(Set1, Element, Set2). -%! ord_del_element(+Set, +Element, -NewSet) is det. +%% ord_del_element(+Set, +Element, -NewSet) is det. % -% Delete an element from an ordered set. This is the same as -% ord_subtract(Set, [Element], NewSet). +% Delete an element from an ordered set. This is the same as +% ord\_subtract(Set, [Element], NewSet). ord_del_element(Set, Element, NewSet) :- oset_delel(Set, Element, NewSet). -%! ord_selectchk(+Item, ?Set1, ?Set2) is semidet. +%% ord_selectchk(+Item, ?Set1, ?Set2) is semidet. % -% Selectchk/3, specialised for ordered sets. Is true when -% select(Item, Set1, Set2) and Set1, Set2 are both sorted lists -% without duplicates. This implementation is only expected to work -% for Item ground and either Set1 or Set2 ground. The "chk" suffix -% is meant to remind you of memberchk/2, which also expects its -% first argument to be ground. ord_selectchk(X, S, T) => -% ord_memberchk(X, S) & \+ ord_memberchk(X, T). +% Selectchk/3, specialised for ordered sets. Is true when +% select(Item, Set1, Set2) and Set1, Set2 are both sorted lists +% without duplicates. This implementation is only expected to work +% for Item ground and either Set1 or Set2 ground. The "chk" suffix +% is meant to remind you of memberchk/2, which also expects its +% first argument to be ground. ord\_selectchk(X, S, T) => +% ord\_memberchk(X, S) & \\+ ord\_memberchk(X, T). % -% @author Richard O'Keefe +% Author: Richard O'Keefe ord_selectchk(Item, [X|Set1], [X|Set2]) :- X @< Item, @@ -266,19 +259,19 @@ ord_selectchk(Item, [Item|Set1], Set1) :- ). -%! ord_memberchk(+Element, +OrdSet) is semidet. +%% ord_memberchk(+Element, +OrdSet) is semidet. % -% True if Element is a member of OrdSet, compared using ==. Note -% that _enumerating_ elements of an ordered set can be done using -% member/2. +% True if Element is a member of OrdSet, compared using ==. Note +% that _enumerating_ elements of an ordered set can be done using +% member/2. % -% Some Prolog implementations also provide ord_member/2, with the -% same semantics as ord_memberchk/2. We believe that having a -% semidet ord_member/2 is unacceptably inconsistent with the *_chk -% convention. Portable code should use ord_memberchk/2 or -% member/2. +% Some Prolog implementations also provide ord\_member/2, with the +% same semantics as ord\_memberchk/2. We believe that having a +% semidet ord\_member/2 is unacceptably inconsistent with the \*\_chk +% convention. Portable code should use ord\_memberchk/2 or +% member/2. % -% @author Richard O'Keefe +% Author: Richard O'Keefe ord_memberchk(Item, [X1,X2,X3,X4|Xs]) :- !, @@ -303,9 +296,9 @@ ord_memberchk(Item, [X1]) :- Item == X1. -%! ord_subset(+Sub, +Super) is semidet. +%% ord_subset(+Sub, +Super) is semidet. % -% Is true if all elements of Sub are in Super +% Is true if all elements of Sub are in Super ord_subset([], _). ord_subset([H1|T1], [H2|T2]) :- @@ -319,22 +312,20 @@ ord_subset_(=, _, T1, T2) :- ord_subset(T1, T2). -%! ord_subtract(+InOSet, +NotInOSet, -Diff) is det. +%% ord_subtract(+InOSet, +NotInOSet, -Diff) is det. % -% Diff is the set holding all elements of InOSet that are not in -% NotInOSet. +% Diff is the set holding all elements of InOSet that are not in +% NotInOSet. ord_subtract(InOSet, NotInOSet, Diff) :- oset_diff(InOSet, NotInOSet, Diff). -%! ord_union(+SetOfSets, -Union) is det. +%% ord_union(+SetOfSets, -Union) is det. % -% True if Union is the union of all elements in the superset -% SetOfSets. Each member of SetOfSets must be an ordered set, the -% sets need not be ordered in any way. -% -% @author Copied from YAP, probably originally by Richard O'Keefe. +% True if Union is the union of all elements in the superset +% SetOfSets. Each member of SetOfSets must be an ordered set, the +% sets need not be ordered in any way. ord_union([], []). ord_union([Set|Sets], Union) :- @@ -355,18 +346,18 @@ ord_union_all(N, Sets0, Union, Sets) :- ). -%! ord_union(+Set1, +Set2, ?Union) is det. +%% ord_union(+Set1, +Set2, ?Union) is det. % -% Union is the union of Set1 and Set2 +% Union is the union of Set1 and Set2 ord_union(Set1, Set2, Union) :- oset_union(Set1, Set2, Union). -%! ord_union(+Set1, +Set2, -Union, -New) is det. +%% ord_union(+Set1, +Set2, -Union, -New) is det. % -% True iff ord_union(Set1, Set2, Union) and -% ord_subtract(Set2, Set1, New). +% True iff ord\_union(Set1, Set2, Union) and +% ord\_subtract(Set2, Set1, New). ord_union([], Set2, Set2, Set2). ord_union([H|T], Set2, Union, New) :- @@ -390,26 +381,22 @@ ord_union_2([H|T], H2, T2, Union, New) :- ord_union(Order, H, T, H2, T2, Union, New). -%! ord_symdiff(+Set1, +Set2, ?Difference) is det. +%% ord_symdiff(+Set1, +Set2, ?Difference) is det. % -% Is true when Difference is the symmetric difference of Set1 and -% Set2. I.e., Difference contains all elements that are not in the -% intersection of Set1 and Set2. The semantics is the same as the -% sequence below (but the actual implementation requires only a -% single scan). +% Is true when Difference is the symmetric difference of Set1 and +% Set2. I.e., Difference contains all elements that are not in the +% intersection of Set1 and Set2. The semantics is the same as the +% sequence below (but the actual implementation requires only a +% single scan). % -% == -% ord_union(Set1, Set2, Union), -% ord_intersection(Set1, Set2, Intersection), -% ord_subtract(Union, Intersection, Difference). -% == +% ord_union(Set1, Set2, Union), +% ord_intersection(Set1, Set2, Intersection), +% ord_subtract(Union, Intersection, Difference). % % For example: % -% == % ?- ord_symdiff([1,2], [2,3], X). % X = [1,3]. -% == ord_symdiff([], Set2, Set2). ord_symdiff([H1|T1], Set2, Difference) :- @@ -457,7 +444,7 @@ ord_symdiff(>, H1, T1, H2, Set2, [H2|Difference]) :- */ -/** Ordered set manipulation +/* Ordered set manipulation This library defines set operations on sets represented as ordered lists. From 21d6220f3ff61cf8e4fcbf18ce06e02d0d12b99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Thu, 8 Dec 2022 23:41:28 +0100 Subject: [PATCH 021/361] Compatible Doclog docs for library(files) --- src/lib/files.pl | 147 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 106 insertions(+), 41 deletions(-) diff --git a/src/lib/files.pl b/src/lib/files.pl index ef73e851..1e856ff1 100644 --- a/src/lib/files.pl +++ b/src/lib/files.pl @@ -1,3 +1,22 @@ +/** Predicates for reasoning about files and directories. + +In this library, directories and files are represented as +*lists of characters*. This is an ideal representation: + +* Lists of characters can be conveniently reasoned about with DCGs + and built-in Prolog predicates from library(lists). This alone + is already a very compelling argument to use them. +* Other Scryer libraries such as library(http/http_open) also already + use lists of characters to represent paths. +* File names are mostly ephemeral, so it is good for efficiency + that they can quickly allocated transiently on the heap, leaving the + atom table mostly unaffected. Indexing is almost never needed + for file names. If needed, it should be added to the engine. +* The previous point is also good for security, since the system + leaves little trace of which files were even accessed. +* Scryer Prolog represents lists of characters extremely compactly. +*/ + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2020, 2022 by Markus Triska (triska@metalevel.at) Part of Scryer Prolog. @@ -67,41 +86,74 @@ :- use_module(library(charsio)). :- use_module(library(dcgs)). +%% directory_files(+Directory, -Files). +% +% Returns the list of files *and* directories available at a specific +% directory in the current system. + directory_files(Directory, Files) :- must_be(chars, Directory), can_be(list, Files), '$directory_files'(Directory, Files). +%% file_size(+File, -Size). +% +% Returns the size (in bytes) of a file. The file must exist. + file_size(File, Size) :- file_must_exist(File, file_size/2), can_be(integer, Size), '$file_size'(File, Size). +%% file_exists(+File). +% +% Succeeds if File is a file that exists in the current system. file_exists(File) :- must_be(chars, File), '$file_exists'(File). +%% directory_exists(+Directory). +% +% Succeeds if Directory is a directory that exists in the current system. directory_exists(Directory) :- must_be(chars, Directory), '$directory_exists'(Directory). +%% make_directory(+Directory). +% +% Succeeds if it creates a new directory named Directory in the current system. +% If you want to create a nested directory, use make\_directory\_path/1. make_directory(Directory) :- must_be(chars, Directory), '$make_directory'(Directory). +%% make_directory_path(+Directory). +% +% Similar to make\_directory/1 but recursively creates directories if they're missing. +% Equivalent to mkdir -p in Unix. make_directory_path(Directory) :- must_be(chars, Directory), '$make_directory_path'(Directory). +%% delete_file(+File). +% +% Succeeds if deletes File from the current system. delete_file(File) :- file_must_exist(File, delete_file/1), '$delete_file'(File). +%% rename_file(+File, +Renamed). +% +% Succeeds if File is renamed to Renamed rename_file(File, Renamed) :- file_must_exist(File, rename_file/2), must_be(chars, Renamed), '$rename_file'(File, Renamed). +%% delete_directory(+Directory). +% +% Succeeds if Directory is deleted from the current system. +% Directory must be empty. delete_directory(Directory) :- directory_must_exist(Directory, delete_directory/1), must_be(chars, Directory), @@ -117,31 +169,31 @@ directory_must_exist(Directory, Context) :- ; throw(error(existence_error(directory, Directory), Context)) ). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Dir0 is the current working directory, and the working directory - is changed to Dir. +%% workind_directory(Dir0, Dir). +% +% Dir0 is the current working directory, and the working directory +% is changed to Dir. - Use working_directory(Ds, Ds) to determine the current working directory, - and leave it as is. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +% Use `working\_directory(Ds, Ds)` to determine the current working directory, +% and leave it as is. working_directory(Dir0, Dir) :- can_be(list, Dir0), can_be(list, Dir), '$working_directory'(Dir0, Dir). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True iff Cs is the canonical, absolute path of Ps. - - All intermediate components are normalized, and all symbolic links - are resolved. - - The predicate fails in the following situations, though not - necessarily *only* in these cases: - - 1. Ps is a path that does not exist. - 2. A non-final component in Ps is not a directory. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% path_canonical(Ps, Cs). +% +% True iff Cs is the canonical, absolute path of Ps. +% +% All intermediate components are normalized, and all symbolic links +% are resolved. +% +% The predicate fails in the following situations, though not +% necessarily *only* in these cases: +% +% 1. Ps is a path that does not exist. +% 2. A non-final component in Ps is not a directory. path_canonical(Ps, Cs) :- must_be(chars, Ps), @@ -155,12 +207,27 @@ path_canonical(Ps, Cs) :- For two time stamps A and B, if A precedes B, then A @< B holds. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% file_modification_time(+File, -T). +% +% For a file File that must exist, it returns a time stamp T with the modification time +% +% T is a time stamp compatible with library(time). file_modification_time(File, T) :- file_time_(File, modification, T). +%% file_access_time(+File, -T). +% +% For a file File that must exist, it returns a time stamp T with the access time +% +% T is a time stamp compatible with library(time). file_access_time(File, T) :- file_time_(File, access, T). +%% file_creation_time(+File, -T). +% +% For a file File that must exist, it returns a time stamp T with the creation time +% +% T is a time stamp compatible with library(time). file_creation_time(File, T) :- file_time_(File, creation, T). @@ -170,29 +237,27 @@ file_time_(File, Which, T) :- read_from_chars(T0, T). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - path_segments(Ps, Segments): True iff Segments are the segments of Ps. - - Segments is the list of components of the path Ps that are - separated by the platform-specific directory separator. Each - segment is a list of characters. - - At least one of the arguments must be instantiated. - - Examples: - - ?- path_segments("/hello/there", Segments). - Segments = [[],"hello","there"]. - - ?- path_segments(Path, ["hello","there"]). - Path = "hello/there". - - - To obtain the platform-specific directory separator, you can use: - - ?- path_segments(Separator, ["",""]). - Separator = "/". -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% path_segments(Ps, Segments). +% +% True iff Segments are the segments of Ps. +% +% Segments is the list of components of the path Ps that are +% separated by the platform-specific directory separator. Each +% segment is a list of characters. +% +% At least one of the arguments must be instantiated. +% +% Examples: +% +% ?- path_segments("/hello/there", Segments). +% Segments = [[],"hello","there"]. +% ?- path_segments(Path, ["hello","there"]). +% Path = "hello/there". +% +% To obtain the platform-specific directory separator, you can use: +% +% ?- path_segments(Separator, ["",""]). +% Separator = "/". path_segments(Path, Segments) :- '$directory_separator'(Sep), From c6aa2068e2068ec68673b9b91dc7b68445c48365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Fri, 9 Dec 2022 23:46:00 +0100 Subject: [PATCH 022/361] Add predicate copy_file/2 in library(files) --- build/instructions_template.rs | 4 ++++ src/lib/files.pl | 10 ++++++++-- src/machine/dispatch.rs | 8 ++++++++ src/machine/system_calls.rs | 13 +++++++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index bc1ca703..f8cce3f4 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -262,6 +262,8 @@ enum SystemClauseType { DeleteFile, #[strum_discriminants(strum(props(Arity = "2", Name = "$rename_file")))] RenameFile, + #[strum_discriminants(strum(props(Arity = "2", Name = "$copy_file")))] + CopyFile, #[strum_discriminants(strum(props(Arity = "2", Name = "$working_directory")))] WorkingDirectory, #[strum_discriminants(strum(props(Arity = "1", Name = "$delete_directory")))] @@ -1611,6 +1613,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallMakeDirectoryPath(_) | &Instruction::CallDeleteFile(_) | &Instruction::CallRenameFile(_) | + &Instruction::CallCopyFile(_) | &Instruction::CallWorkingDirectory(_) | &Instruction::CallDeleteDirectory(_) | &Instruction::CallPathCanonical(_) | @@ -1825,6 +1828,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteMakeDirectoryPath(_) | &Instruction::ExecuteDeleteFile(_) | &Instruction::ExecuteRenameFile(_) | + &Instruction::ExecuteCopyFile(_) | &Instruction::ExecuteWorkingDirectory(_) | &Instruction::ExecuteDeleteDirectory(_) | &Instruction::ExecutePathCanonical(_) | diff --git a/src/lib/files.pl b/src/lib/files.pl index 1e856ff1..7d0afeda 100644 --- a/src/lib/files.pl +++ b/src/lib/files.pl @@ -70,8 +70,9 @@ In this library, directories and files are represented as file_exists/1, directory_exists/1, delete_file/1, - rename_file/2, - delete_directory/1, + rename_file/2, + copy_file/2, + delete_directory/1, make_directory/1, make_directory_path/1, working_directory/2, @@ -150,6 +151,11 @@ rename_file(File, Renamed) :- must_be(chars, Renamed), '$rename_file'(File, Renamed). +copy_file(File, Copied) :- + file_must_exist(File, copy_file/2), + must_be(chars, Copied), + '$copy_file'(File, Copied). + %% delete_directory(+Directory). % % Succeeds if Directory is deleted from the current system. diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index ee57a83e..1cc1d365 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -3533,6 +3533,14 @@ impl Machine { self.rename_file(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallCopyFile(_) => { + self.copy_file(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteCopyFile(_) => { + self.copy_file(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } &Instruction::CallWorkingDirectory(_) => { try_or_throw!(self.machine_st, self.working_directory()); step_or_fail!(self, self.machine_st.p += 1); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 275c0ce7..515baba6 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1653,6 +1653,19 @@ impl Machine { self.machine_st.fail = true; } + #[inline(always)] + pub(crate) fn copy_file(&mut self) { + if let Some(file) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { + if let Some(copied) = self.machine_st.value_to_str_like(self.machine_st.registers[2]) { + if fs::copy(file.as_str(), copied.as_str()).is_ok() { + return; + } + } + } + + self.machine_st.fail = true; + } + #[inline(always)] pub(crate) fn delete_directory(&mut self) { if let Some(dir) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { From 5f703afed11a2c8c191a7a06f19dc8be568606bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sun, 11 Dec 2022 00:08:41 +0100 Subject: [PATCH 023/361] Rename copy_file/2 to file_copy/2 --- build/instructions_template.rs | 8 ++++---- src/lib/files.pl | 11 +++++++---- src/machine/dispatch.rs | 8 ++++---- src/machine/system_calls.rs | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index f8cce3f4..ade61a9c 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -262,8 +262,8 @@ enum SystemClauseType { DeleteFile, #[strum_discriminants(strum(props(Arity = "2", Name = "$rename_file")))] RenameFile, - #[strum_discriminants(strum(props(Arity = "2", Name = "$copy_file")))] - CopyFile, + #[strum_discriminants(strum(props(Arity = "2", Name = "$file_copy")))] + FileCopy, #[strum_discriminants(strum(props(Arity = "2", Name = "$working_directory")))] WorkingDirectory, #[strum_discriminants(strum(props(Arity = "1", Name = "$delete_directory")))] @@ -1613,7 +1613,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallMakeDirectoryPath(_) | &Instruction::CallDeleteFile(_) | &Instruction::CallRenameFile(_) | - &Instruction::CallCopyFile(_) | + &Instruction::CallFileCopy(_) | &Instruction::CallWorkingDirectory(_) | &Instruction::CallDeleteDirectory(_) | &Instruction::CallPathCanonical(_) | @@ -1828,7 +1828,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteMakeDirectoryPath(_) | &Instruction::ExecuteDeleteFile(_) | &Instruction::ExecuteRenameFile(_) | - &Instruction::ExecuteCopyFile(_) | + &Instruction::ExecuteFileCopy(_) | &Instruction::ExecuteWorkingDirectory(_) | &Instruction::ExecuteDeleteDirectory(_) | &Instruction::ExecutePathCanonical(_) | diff --git a/src/lib/files.pl b/src/lib/files.pl index 7d0afeda..e920b5bd 100644 --- a/src/lib/files.pl +++ b/src/lib/files.pl @@ -71,7 +71,7 @@ In this library, directories and files are represented as directory_exists/1, delete_file/1, rename_file/2, - copy_file/2, + file_copy/2, delete_directory/1, make_directory/1, make_directory_path/1, @@ -151,10 +151,13 @@ rename_file(File, Renamed) :- must_be(chars, Renamed), '$rename_file'(File, Renamed). -copy_file(File, Copied) :- - file_must_exist(File, copy_file/2), +%% file_copy(+File, +Copied). +% +% Succeeds if File is copied to Copied +file_copy(File, Copied) :- + file_must_exist(File, file_copy/2), must_be(chars, Copied), - '$copy_file'(File, Copied). + '$file_copy'(File, Copied). %% delete_directory(+Directory). % diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 1cc1d365..a93d9e22 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -3533,12 +3533,12 @@ impl Machine { self.rename_file(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCopyFile(_) => { - self.copy_file(); + &Instruction::CallFileCopy(_) => { + self.file_copy(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCopyFile(_) => { - self.copy_file(); + &Instruction::ExecuteFileCopy(_) => { + self.file_copy(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallWorkingDirectory(_) => { diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 515baba6..f72cbf8a 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1654,7 +1654,7 @@ impl Machine { } #[inline(always)] - pub(crate) fn copy_file(&mut self) { + pub(crate) fn file_copy(&mut self) { if let Some(file) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { if let Some(copied) = self.machine_st.value_to_str_like(self.machine_st.registers[2]) { if fs::copy(file.as_str(), copied.as_str()).is_ok() { From 5f2c77fa74243a6f1192d4ddd5d67c7477efb048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sun, 11 Dec 2022 21:49:41 +0100 Subject: [PATCH 024/361] Add predicate lcm/2 to library(arithmetic) --- src/lib/arithmetic.pl | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/lib/arithmetic.pl b/src/lib/arithmetic.pl index ee719a18..e557c90b 100644 --- a/src/lib/arithmetic.pl +++ b/src/lib/arithmetic.pl @@ -1,4 +1,4 @@ -:- module(arithmetic, [expmod/4, lsb/2, msb/2, number_to_rational/2, +:- module(arithmetic, [expmod/4, lcm/3, lsb/2, msb/2, number_to_rational/2, number_to_rational/3, popcount/2, rational_numerator_denominator/3]). @@ -28,6 +28,22 @@ expmod_(Base0, Expo0, Mod, C, R) :- Base is (Base0 * Base0) mod Mod, expmod_(Base, Expo, Mod, C, R). +%% lcm(+A, +B, -Lcm) is det. +% +% Calculates the Least common multiple for A and B: the smallest positive integer +% that is divisible by both A and B. +% +% A and B need to be integers. +lcm(A, B, X) :- + builtins:must_be_number(A, lcm/2), + builtins:must_be_number(B, lcm/2), + ( \+ integer(A) -> type_error(integer, A, lcm/2) + ; \+ integer(B) -> type_error(integer, B, lcm/2) + ; (A = 0, B = 0) -> X = 0 + ; builtins:can_be_number(X, lcm/2), + X is abs(B) // gcd(A,B) * abs(A) + ). + lsb(X, N) :- builtins:must_be_number(X, lsb/2), ( \+ integer(X) -> type_error(integer, X, lsb/2) From 4dc0114c521d82750a7502654e1c0450bf13f312 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 11 Dec 2022 16:11:57 -0700 Subject: [PATCH 025/361] fix mishandled if-then-else interpretation (#1659) --- build/instructions_template.rs | 4 -- src/lib/builtins.pl | 78 ++++++++++++++++------------------ src/machine/dispatch.rs | 8 ---- src/machine/system_calls.rs | 56 ------------------------ 4 files changed, 37 insertions(+), 109 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index bc1ca703..8bc5d630 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -412,8 +412,6 @@ enum SystemClauseType { GetCurrentBlock, #[strum_discriminants(strum(props(Arity = "1", Name = "$get_cp")))] GetCutPoint, - #[strum_discriminants(strum(props(Arity = "1", Name = "$get_staggered_cp")))] - GetStaggeredCutPoint, #[strum_discriminants(strum(props(Arity = "1", Name = "$get_double_quotes")))] GetDoubleQuotes, #[strum_discriminants(strum(props(Arity = "1", Name = "$install_new_block")))] @@ -1688,7 +1686,6 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallGetBall(_) | &Instruction::CallGetCurrentBlock(_) | &Instruction::CallGetCutPoint(_) | - &Instruction::CallGetStaggeredCutPoint(_) | &Instruction::CallGetDoubleQuotes(_) | &Instruction::CallInstallNewBlock(_) | &Instruction::CallMaybe(_) | @@ -1902,7 +1899,6 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteGetBall(_) | &Instruction::ExecuteGetCurrentBlock(_) | &Instruction::ExecuteGetCutPoint(_) | - &Instruction::ExecuteGetStaggeredCutPoint(_) | &Instruction::ExecuteGetDoubleQuotes(_) | &Instruction::ExecuteInstallNewBlock(_) | &Instruction::ExecuteMaybe(_) | diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 1d2d0aed..11a76238 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -158,9 +158,8 @@ G1 -> G2 :- control_entry_point((G1 -> G2)). :- non_counted_backtracking staggered_if_then/2. staggered_if_then(G1, G2) :- - '$get_staggered_cp'(B), call(G1), - '$set_cp'(B), + !, call(G2). G1 ; G2 :- control_entry_point((G1 ; G2)). @@ -168,10 +167,16 @@ G1 ; G2 :- control_entry_point((G1 ; G2)). :- non_counted_backtracking staggered_sc/2. -staggered_sc(G, _) :- call(G). +staggered_sc(G, _) :- + ( nonvar(G), + G = '$call'(builtins:staggered_if_then(G1, G2)) -> + call(G1), + !, + call(G2) + ; call(G) + ). staggered_sc(_, G) :- call(G). - !. :- non_counted_backtracking set_cp/1. @@ -180,6 +185,7 @@ set_cp(B) :- '$set_cp'(B). ','(G1, G2) :- control_entry_point((G1, G2)). + :- non_counted_backtracking control_entry_point/1. control_entry_point(G) :- @@ -203,47 +209,15 @@ cont_list_goal([Cont], Cont) :- !. cont_list_goal(Conts, '$call'(builtins:dispatch_call_list(Conts))). -:- non_counted_backtracking module_qualified_cut/1. - -module_qualified_cut(Gs) :- - ( functor(Gs, call, 1) -> - arg(1, Gs, G1) - ; Gs = G1 - ), - functor(G1, (:), 2), - arg(2, G1, G2), - G2 == !. - - :- non_counted_backtracking dispatch_prep/3. dispatch_prep(Gs, B, [Cont|Conts]) :- ( callable(Gs) -> - ( functor(Gs, ',', 2) -> - arg(1, Gs, G1), - arg(2, Gs, G2), - dispatch_prep(G1, B, IConts1), - cont_list_goal(IConts1, Cont), - dispatch_prep(G2, B, Conts) - ; functor(Gs, ';', 2) -> - arg(1, Gs, G1), - arg(2, Gs, G2), - dispatch_prep(G1, B, IConts0), - dispatch_prep(G2, B, IConts1), - cont_list_goal(IConts0, Cont0), - cont_list_goal(IConts1, Cont1), - Cont = '$call'(builtins:staggered_sc(Cont0, Cont1)), - Conts = [] - ; functor(Gs, ->, 2) -> - arg(1, Gs, G1), - arg(2, Gs, G2), - dispatch_prep(G1, B, IConts1), - dispatch_prep(G2, B, IConts2), - cont_list_goal(IConts1, Cont1), - cont_list_goal(IConts2, Cont2), - Cont = '$call'(builtins:staggered_if_then(Cont1, Cont2)), - Conts = [] - ; ( Gs == ! ; module_qualified_cut(Gs) ) -> + strip_module(Gs, M, Gs0), + ( nonvar(Gs0), + dispatch_prep_(Gs0, B, [Cont|Conts]) -> + true + ; Gs0 == ! -> Cont = '$call'(builtins:set_cp(B)), Conts = [] ; Cont = Gs, @@ -256,6 +230,28 @@ dispatch_prep(Gs, B, [Cont|Conts]) :- ). +:- non_counted_backtracking dispatch_prep_/3. + +dispatch_prep_((G1, G2), B, [Cont|Conts]) :- + dispatch_prep(G1, B, IConts1), + cont_list_goal(IConts1, Cont), + dispatch_prep(G2, B, Conts). +dispatch_prep_((G1 ; G2), B, [Cont|Conts]) :- + dispatch_prep(G1, B, IConts0), + dispatch_prep(G2, B, IConts1), + cont_list_goal(IConts0, Cont0), + cont_list_goal(IConts1, Cont1), + Cont = '$call'(builtins:staggered_sc(Cont0, Cont1)), + Conts = []. +dispatch_prep_((G1 -> G2), B, [Cont|Conts]) :- + dispatch_prep(G1, B, IConts1), + dispatch_prep(G2, B, IConts2), + cont_list_goal(IConts1, Cont1), + cont_list_goal(IConts2, Cont2), + Cont = '$call'(builtins:staggered_if_then(Cont1, Cont2)), + Conts = []. + + :- non_counted_backtracking dispatch_call_list/1. dispatch_call_list([]). diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index ee57a83e..c9db5966 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4152,14 +4152,6 @@ impl Machine { self.get_cut_point(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetStaggeredCutPoint(_) => { - self.get_staggered_cut_point(); - step_or_fail!(self, self.machine_st.p += 1); - } - &Instruction::ExecuteGetStaggeredCutPoint(_) => { - self.get_staggered_cut_point(); - step_or_fail!(self, self.machine_st.p = self.machine_st.cp); - } &Instruction::CallGetDoubleQuotes(_) => { self.get_double_quotes(); step_or_fail!(self, self.machine_st.p += 1); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 275c0ce7..be24526e 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -4850,62 +4850,6 @@ impl Machine { self.machine_st.unify_fixnum(n, self.machine_st.registers[1]); } - #[inline(always)] - pub(crate) fn get_staggered_cut_point(&mut self) { - use std::sync::Once; - - let b = self.deref_register(1); - - static mut SEMICOLON_SECOND_BRANCH_LOC: usize = 0; - static LOC_INIT: Once = Once::new(); - - let semicolon_second_clause_p = unsafe { - LOC_INIT.call_once(|| { - if let Some(builtins) = self.indices.modules.get(&atom!("builtins")) { - match builtins.code_dir.get(&(atom!("staggered_sc"), 2)).map(|cell| cell.get()) { - Some(ip) if ip.tag() == IndexPtrTag::Index => { - let p = ip.p() as usize; - - match &self.code[p] { - &Instruction::TryMeElse(o) => { - SEMICOLON_SECOND_BRANCH_LOC = p + o; - } - _ => { - unreachable!(); - } - } - } - _ => { - unreachable!(); - } - } - } else { - unreachable!(); - } - }); - - SEMICOLON_SECOND_BRANCH_LOC - }; - - let staggered_b0 = if self.machine_st.b > 0 { - let or_frame = self.machine_st.stack.index_or_frame(self.machine_st.b); - - if or_frame.prelude.bp == semicolon_second_clause_p { - or_frame.prelude.b0 - } else { - self.machine_st.b0 - } - } else { - self.machine_st.b0 - }; - - let staggered_b0 = integer_as_cell!( - Number::arena_from(staggered_b0, &mut self.machine_st.arena) - ); - - self.machine_st.bind(b.as_var().unwrap(), staggered_b0); - } - #[inline(always)] pub(crate) fn next_ep(&mut self) { let first_arg = self.deref_register(1); From a5054c006441b38e2092a9c28137f725413028b3 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Mon, 12 Dec 2022 23:55:43 -0700 Subject: [PATCH 026/361] use append/3 rather than set_difference/3 to gather witnesses in bagof/3 and setof/3 (#1663, #1664) --- src/lib/builtins.pl | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 1d2d0aed..d73ff8df 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -745,11 +745,10 @@ findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses) bagof(Template, Goal, Solution) :- error:can_be(list, Solution), - term_variables(Template, TemplateVars0), - term_variables(Goal, GoalVars0), - sort(TemplateVars0, TemplateVars), - sort(GoalVars0, GoalVars), - set_difference(GoalVars, TemplateVars, Witnesses0), + term_variables(Template, TemplateVars), + term_variables(Goal, GoalVars), + term_variables(TemplateVars+GoalVars, TGVs), + lists:append(TemplateVars, Witnesses0, TGVs), findall_with_existential(Template, Goal, PairedSolutions0, Witnesses0, Witnesses), keysort(PairedSolutions0, PairedSolutions), group_by_variants(PairedSolutions, GroupedSolutions), @@ -773,11 +772,10 @@ iterate_variants_and_sort([_|GroupSolutions], Ws, Solution) :- setof(Template, Goal, Solution) :- error:can_be(list, Solution), - term_variables(Template, TemplateVars0), - term_variables(Goal, GoalVars0), - sort(TemplateVars0, TemplateVars), - sort(GoalVars0, GoalVars), - set_difference(GoalVars, TemplateVars, Witnesses0), + term_variables(Template, TemplateVars), + term_variables(Goal, GoalVars), + term_variables(TemplateVars+GoalVars, TGVs), + lists:append(TemplateVars, Witnesses0, TGVs), findall_with_existential(Template, Goal, PairedSolutions0, Witnesses0, Witnesses), keysort(PairedSolutions0, PairedSolutions), group_by_variants(PairedSolutions, GroupedSolutions), From 0a8fc70ba9c70d8f4617182c92c5d232ef0f648f Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Thu, 15 Dec 2022 23:26:36 -0700 Subject: [PATCH 027/361] detect cyclic bindings in attr_vars_of_term (#1666) --- src/machine/attributed_variables.pl | 1 - src/machine/attributed_variables.rs | 10 ++ src/machine/system_calls.rs | 156 ++++++++++++++-------------- 3 files changed, 88 insertions(+), 79 deletions(-) diff --git a/src/machine/attributed_variables.pl b/src/machine/attributed_variables.pl index 8c656e31..982ec495 100644 --- a/src/machine/attributed_variables.pl +++ b/src/machine/attributed_variables.pl @@ -1,6 +1,5 @@ :- module('$atts', []). - driver(Vars, Values) :- iterate(Vars, Values, ListOfListsOfGoalLists), !, diff --git a/src/machine/attributed_variables.rs b/src/machine/attributed_variables.rs index f74d3981..b11cf34c 100644 --- a/src/machine/attributed_variables.rs +++ b/src/machine/attributed_variables.rs @@ -147,6 +147,16 @@ impl MachineState { let value = unmark_cell_bits!(value); + if h != iter.focus() { + 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); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index be24526e..c70c4dec 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -3354,71 +3354,6 @@ impl Machine { } } - #[inline(always)] - pub(crate) fn delete_attribute(&mut self) { - let ls0 = self.deref_register(1); - - if let HeapCellValueTag::Lis = ls0.get_tag() { - let l1 = ls0.get_value(); - let ls1 = self.machine_st.store(self.machine_st.deref(heap_loc_as_cell!(l1 + 1))); - - if let HeapCellValueTag::Lis = ls1.get_tag() { - let l2 = ls1.get_value(); - - let old_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[l1+1])); - let tail = self.machine_st.store(self.machine_st.deref(heap_loc_as_cell!(l2 + 1))); - - let tail = if tail.is_var() { - heap_loc_as_cell!(l1 + 1) - } else { - tail - }; - - let trail_ref = read_heap_cell!(old_addr, - (HeapCellValueTag::Var, h) => { - TrailRef::AttrVarHeapLink(h) - } - (HeapCellValueTag::Lis, l) => { - TrailRef::AttrVarListLink(l1 + 1, l) - } - _ => { - unreachable!() - } - ); - - self.machine_st.heap[l1 + 1] = tail; - self.machine_st.trail(trail_ref); - } - } - } - - #[inline(always)] - pub(crate) fn delete_head_attribute(&mut self) { - let addr = self.deref_register(1); - - debug_assert_eq!(addr.get_tag(), HeapCellValueTag::AttrVar); - - let h = addr.get_value(); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[h + 1])); - - debug_assert_eq!(addr.get_tag(), HeapCellValueTag::Lis); - - let l = addr.get_value(); - let tail = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[l + 1])); - - let tail = if tail.is_var() { - self.machine_st.heap[h] = heap_loc_as_cell!(h); - self.machine_st.trail(TrailRef::Ref(Ref::attr_var(h))); - - heap_loc_as_cell!(h + 1) - } else { - tail - }; - - self.machine_st.heap[h + 1] = tail; - self.machine_st.trail(TrailRef::AttrVarListLink(h + 1, l)); - } - #[inline(always)] pub(crate) fn dynamic_module_resolution( &mut self, @@ -3473,19 +3408,6 @@ impl Machine { Ok((module_name, key)) } - #[inline(always)] - pub(crate) fn enqueue_attributed_var(&mut self) { - let addr = self.deref_register(1); - - read_heap_cell!(addr, - (HeapCellValueTag::AttrVar, h) => { - self.machine_st.attr_var_init.attr_var_queue.push(h); - } - _ => { - } - ); - } - #[inline(always)] pub(crate) fn get_next_db_ref(&mut self) { let a1 = self.deref_register(1); @@ -4308,6 +4230,84 @@ impl Machine { } } + #[inline(always)] + pub(crate) fn enqueue_attributed_var(&mut self) { + let addr = self.deref_register(1); + + read_heap_cell!(addr, + (HeapCellValueTag::AttrVar, h) => { + self.machine_st.attr_var_init.attr_var_queue.push(h); + } + _ => { + } + ); + } + + #[inline(always)] + pub(crate) fn delete_attribute(&mut self) { + let ls0 = self.deref_register(1); + + if let HeapCellValueTag::Lis = ls0.get_tag() { + let l1 = ls0.get_value(); + let ls1 = self.machine_st.store(self.machine_st.deref(heap_loc_as_cell!(l1 + 1))); + + if let HeapCellValueTag::Lis = ls1.get_tag() { + let l2 = ls1.get_value(); + + let old_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[l1+1])); + let tail = self.machine_st.store(self.machine_st.deref(heap_loc_as_cell!(l2 + 1))); + + let tail = if tail.is_var() { + heap_loc_as_cell!(l1 + 1) + } else { + tail + }; + + let trail_ref = read_heap_cell!(old_addr, + (HeapCellValueTag::Var, h) => { + TrailRef::AttrVarHeapLink(h) + } + (HeapCellValueTag::Lis, l) => { + TrailRef::AttrVarListLink(l1 + 1, l) + } + _ => { + unreachable!() + } + ); + + self.machine_st.heap[l1 + 1] = tail; + self.machine_st.trail(trail_ref); + } + } + } + + #[inline(always)] + pub(crate) fn delete_head_attribute(&mut self) { + let addr = self.deref_register(1); + + debug_assert_eq!(addr.get_tag(), HeapCellValueTag::AttrVar); + + let h = addr.get_value(); + let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[h + 1])); + + debug_assert_eq!(addr.get_tag(), HeapCellValueTag::Lis); + + let l = addr.get_value(); + let tail = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[l + 1])); + + let tail = if tail.is_var() { + self.machine_st.heap[h] = heap_loc_as_cell!(h); + self.machine_st.trail(TrailRef::Ref(Ref::attr_var(h))); + + heap_loc_as_cell!(h + 1) + } else { + tail + }; + + self.machine_st.heap[h + 1] = tail; + self.machine_st.trail(TrailRef::AttrVarListLink(h + 1, l)); + } + #[inline(always)] pub(crate) fn get_continuation_chunk(&mut self) { let e = self.deref_register(1); From 1eff75875182b483f2a5a9b079e93f57835d1e72 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Fri, 16 Dec 2022 00:45:00 -0700 Subject: [PATCH 028/361] update README to point to local wambook (#1668) --- README.md | 2 +- wambook/wambook.pdf | Bin 0 -> 530191 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 wambook/wambook.pdf diff --git a/README.md b/README.md index 66f5cb82..d421bfa0 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ programming, which is itself written in a high-level language. Produce an implementation of the Warren Abstract Machine in Rust, done according to the progression of languages in [Warren's Abstract Machine: A Tutorial -Reconstruction](http://wambook.sourceforge.net/wambook.pdf). +Reconstruction](https://github.com/mthom/scryer-prolog/blob/master/wambook/wambook.pdf). Phase 1 has been completed in that Scryer Prolog implements in some form all of the WAM book, including lists, cuts, Debray allocation, first diff --git a/wambook/wambook.pdf b/wambook/wambook.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e2646ffa0d700f99c4bbf2013a37f64fe144f5e6 GIT binary patch literal 530191 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

on+i(y0h~7z9mPWntM3P9w2_f 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 literal 0 HcmV?d00001 From cf63e8375d810687f8cfa84ce10bc4570693465e Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Fri, 16 Dec 2022 00:42:31 -0700 Subject: [PATCH 029/361] track attr_var_init lengths on the stack (#1667) --- build/instructions_template.rs | 4 ---- src/machine/attributed_variables.pl | 1 - src/machine/attributed_variables.rs | 4 ++-- src/machine/dispatch.rs | 8 -------- src/machine/machine_state.rs | 2 ++ src/machine/mod.rs | 10 ++++++---- src/machine/stack.rs | 1 + src/machine/system_calls.rs | 6 ++++-- 8 files changed, 15 insertions(+), 21 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 8bc5d630..3780f2c9 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -296,8 +296,6 @@ enum SystemClauseType { GetCode, #[strum_discriminants(strum(props(Arity = "1", Name = "$get_single_char")))] GetSingleChar, - #[strum_discriminants(strum(props(Arity = "0", Name = "$reset_attr_var_state")))] - ResetAttrVarState, #[strum_discriminants(strum(props(Arity = "2", Name = "$truncate_if_no_lh_growth_diff")))] TruncateIfNoLiftedHeapGrowthDiff, #[strum_discriminants(strum(props(Arity = "1", Name = "$truncate_if_no_lh_growth")))] @@ -1628,7 +1626,6 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallGetNChars(_) | &Instruction::CallGetCode(_) | &Instruction::CallGetSingleChar(_) | - &Instruction::CallResetAttrVarState(_) | &Instruction::CallTruncateIfNoLiftedHeapGrowthDiff(_) | &Instruction::CallTruncateIfNoLiftedHeapGrowth(_) | &Instruction::CallGetAttributedVariableList(_) | @@ -1841,7 +1838,6 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteGetNChars(_) | &Instruction::ExecuteGetCode(_) | &Instruction::ExecuteGetSingleChar(_) | - &Instruction::ExecuteResetAttrVarState(_) | &Instruction::ExecuteTruncateIfNoLiftedHeapGrowthDiff(_) | &Instruction::ExecuteTruncateIfNoLiftedHeapGrowth(_) | &Instruction::ExecuteGetAttributedVariableList(_) | diff --git a/src/machine/attributed_variables.pl b/src/machine/attributed_variables.pl index 982ec495..c288511b 100644 --- a/src/machine/attributed_variables.pl +++ b/src/machine/attributed_variables.pl @@ -4,7 +4,6 @@ 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]) :- diff --git a/src/machine/attributed_variables.rs b/src/machine/attributed_variables.rs index b11cf34c..2bab7451 100644 --- a/src/machine/attributed_variables.rs +++ b/src/machine/attributed_variables.rs @@ -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(); } } diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index c9db5966..72da8bad 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -3683,14 +3683,6 @@ impl Machine { try_or_throw!(self.machine_st, self.get_single_char()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallResetAttrVarState(_) => { - self.reset_attr_var_state(); - self.machine_st.p += 1; - } - &Instruction::ExecuteResetAttrVarState(_) => { - self.reset_attr_var_state(); - self.machine_st.p = self.machine_st.cp; - } &Instruction::CallTruncateIfNoLiftedHeapGrowthDiff(_) => { self.truncate_if_no_lifted_heap_growth_diff(); step_or_fail!(self, self.machine_st.p += 1); diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 6aa51916..2db02c01 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -849,6 +849,7 @@ impl MachineState { or_frame.prelude.tr = self.tr; or_frame.prelude.h = self.heap.len(); or_frame.prelude.b0 = self.b0; + or_frame.prelude.attr_var_queue_len = self.attr_var_init.attr_var_queue.len(); self.b = b; @@ -876,6 +877,7 @@ impl MachineState { or_frame.prelude.tr = self.tr; or_frame.prelude.h = self.heap.len(); or_frame.prelude.b0 = self.b0; + or_frame.prelude.attr_var_queue_len = self.attr_var_init.attr_var_queue.len(); self.b = b; diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 4cd1fce8..164471f4 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -570,10 +570,11 @@ impl Machine { let old_tr = or_frame.prelude.tr; let curr_tr = self.machine_st.tr; 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.reset_attr_var_state(); self.machine_st.hb = target_h; self.unwind_trail(old_tr, curr_tr); @@ -603,9 +604,10 @@ impl Machine { let old_tr = or_frame.prelude.tr; let curr_tr = self.machine_st.tr; 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(); + self.reset_attr_var_state(attr_var_queue_len); self.machine_st.hb = target_h; self.machine_st.p = self.machine_st.p + offset; @@ -640,7 +642,7 @@ impl Machine { 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 = self.machine_st.p + offset; @@ -676,7 +678,7 @@ impl Machine { 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; diff --git a/src/machine/stack.rs b/src/machine/stack.rs index 388c3008..47a4fbf4 100644 --- a/src/machine/stack.rs +++ b/src/machine/stack.rs @@ -123,6 +123,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)] diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index c70c4dec..3517f76b 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -4623,9 +4623,9 @@ impl Machine { } #[inline(always)] - pub(crate) fn reset_attr_var_state(&mut self) { + pub(crate) fn reset_attr_var_state(&mut self, queue_len: usize) { self.restore_instr_at_verify_attr_interrupt(); - self.machine_st.attr_var_init.reset(); + self.machine_st.attr_var_init.reset(queue_len); } #[inline(always)] @@ -4653,6 +4653,8 @@ impl Machine { #[inline(always)] pub(crate) fn return_from_verify_attr(&mut self) { + self.restore_instr_at_verify_attr_interrupt(); + let e = self.machine_st.e; let frame_len = self.machine_st.stack.index_and_frame(e).prelude.univ_prelude.num_cells; From d804d8a92ea844e795670f4d0e8a90291d8b936e Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 17 Dec 2022 11:54:20 -0700 Subject: [PATCH 030/361] use proper dynamic arities in JmpByCall and JmpByExecute (#1605, #1606) --- build/instructions_template.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 3780f2c9..f58dbfed 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -722,9 +722,9 @@ enum InstructionTemplate { Allocate(usize), // num_frames. #[strum_discriminants(strum(props(Arity = "0", Name = "deallocate")))] Deallocate, - #[strum_discriminants(strum(props(Arity = "3", Name = "jmp_by_call")))] + #[strum_discriminants(strum(props(Arity = "arity", Name = "jmp_by_call")))] JmpByCall(usize, usize), // arity, relative offset. - #[strum_discriminants(strum(props(Arity = "3", Name = "jmp_by_execute")))] + #[strum_discriminants(strum(props(Arity = "arity", Name = "jmp_by_execute")))] JmpByExecute(usize, usize), // arity, relative offset. #[strum_discriminants(strum(props(Arity = "1", Name = "rev_jmp_by")))] RevJmpBy(usize), From 6a995fb62b02900d1958349be70f00c1dbaa9c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sat, 17 Dec 2022 20:43:43 +0100 Subject: [PATCH 031/361] Compatible Doclog docs for library(http/http_open). --- src/lib/http/http_open.pl | 47 +++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/lib/http/http_open.pl b/src/lib/http/http_open.pl index 1c89b5a5..7d0503c2 100644 --- a/src/lib/http/http_open.pl +++ b/src/lib/http/http_open.pl @@ -1,34 +1,37 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2022 by Adrián Arroyo Calle (adrian.arroyocalle@gmail.com) Part of Scryer Prolog. +*/ - http_open(+Address, -Stream, +Options) - ====================================== +/** Make HTTP requests. - Yields Stream to read the body of an HTTP reply from Address. - Address is a list of characters, and includes the method. Both HTTP - and HTTPS are supported. - - Options supported: - - * method(+Method): Sets the HTTP method of the call. Method can be get (default), head, delete, post, put or patch. - * data(+Data): Data to be sent in the request. Useful for POST, PUT and PATCH operations. - * size(-Size): Unifies with the value of the Content-Length header - * request_headers(+RequestHeaders): Headers to be used in the request - * headers(-ListHeaders): Unifies with a list with all headers returned in the response - * status_code(-Code): Unifies with the status code of the request (200, 201, 404, ...) - - Example: - - ?- http_open("https://github.com/mthom/scryer-prolog", S, []). - %@ S = '$stream'(0x7fcfc9e00f00). - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +This library contains the predicate http\_open/3 which allows you to perform HTTP(S) calls. +Useful for making API calls, or parsing websites. It uses Hyper underneath. +*/ :- module(http_open, [http_open/3]). :- use_module(library(lists)). +%% http_open(+Address, -Stream, +Options). +% +% Yields Stream to read the body of an HTTP reply from Address. +% Address is a list of characters, and includes the method. Both HTTP +% and HTTPS are supported. +% +% Options supported: +% +% * `method(+Method)`: Sets the HTTP method of the call. Method can be `get` (default), `head`, `delete`, `post`, `put` or `patch`. +% * `data(+Data)`: Data to be sent in the request. Useful for POST, PUT and PATCH operations. +% * `size(-Size)`: Unifies with the value of the Content-Length header +% * `request_headers(+RequestHeaders)`: Headers to be used in the request +% * `headers(-ListHeaders)`: Unifies with a list with all headers returned in the response +% * `status_code(-Code)`: Unifies with the status code of the request (200, 201, 404, ...) +% +% Example: +% +% ?- http_open("https://www.example.com", S, []), get_n_chars(S, N, HTML). +% S = '$stream'(0x7fb548001be8), N = 1256, HTML = "\n true; Method = get), @@ -65,4 +68,4 @@ parse_http_options_(request_headers(Headers), request_headers(Headers)) :- parse_http_options_(size(Size), size(Size)). parse_http_options_(status_code(Code), status_code(Code)). -parse_http_options_(headers(Headers), headers(Headers)). \ No newline at end of file +parse_http_options_(headers(Headers), headers(Headers)). From 0166c3bf0fc943aadbe5634881728e8a921402f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sat, 17 Dec 2022 22:46:44 +0100 Subject: [PATCH 032/361] Compatible Doclog docs for library(iso_ext) --- src/lib/iso_ext.pl | 94 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 3 deletions(-) diff --git a/src/lib/iso_ext.pl b/src/lib/iso_ext.pl index 26b4713c..2ae3bf84 100644 --- a/src/lib/iso_ext.pl +++ b/src/lib/iso_ext.pl @@ -1,3 +1,9 @@ +/** Useful general predicates that are not ISO standard yet + +Predicates available here are similar to the ones defined in builtin.pl, +but they're not part of the ISO Prolog standard at the moment. +*/ + :- module(iso_ext, [bb_b_put/2, bb_get/2, bb_put/2, @@ -22,25 +28,70 @@ :- meta_predicate(forall(0, 0)). +%% forall(Generate, Test). +% +% For all bindings possible by Generate, Test must be true. +% +% In this example, it checks that all numbers are even: +% +% ?- Ns = [2,4,6], forall(member(N, Ns), 0 is N mod 2). +% Ns = [2,4,6]. forall(Generate, Test) :- \+ (Generate, \+ Test). -%% (non-)backtrackable global variables. +% (non-)backtrackable global variables. +%% bb_put(+Key, +Value). +% +% Sets a global variable named Key (must be an atom) with value Value. +% The global variable isn't backtrackable. Check bb\_b\_put/2 for the +% backtrackable version. +% +% ?- bb_put(city, "Valladolid"). +% true. +% ?- bb_get(city, X). +% X = "Valladolid". +% In this example one can understand the difference between bb\_put/2 and +% bb\_b\_put/2: +% +% ?- bb_put(city, "Valladolid"), (bb_put(city, "Salamanca"), false);(bb_get(city, X)). +% X = "Salamanca". +% ?- bb_put(city, "Valladolid"), (bb_b_put(city, "Salamanca"), false);(bb_get(city, X)). +% X = "Valladolid". bb_put(Key, Value) :- ( atom(Key) -> '$store_global_var'(Key, Value) ; type_error(atom, Key, bb_put/2) ). -%% backtrackable global variables. +% backtrackable global variables. +%% bb_b_put(+Key, +Value). +% +% Sets a global variable named Key (must be an atom) with value Value. +% The global variable is backtrackable. Check bb\_put/2 for the +% non-backtrackable version. +% +% ?- bb_b_put(city, "Valladolid"). +% true. +% ?- bb_get(city, X). +% X = "Valladolid". +% In this example one can understand the difference between bb\_put/2 and +% bb\_b\_put/2: +% +% ?- bb_put(city, "Valladolid"), (bb_put(city, "Salamanca"), false);(bb_get(city, X)). +% X = "Salamanca". +% ?- bb_put(city, "Valladolid"), (bb_b_put(city, "Salamanca"), false);(bb_get(city, X)). +% X = "Valladolid". bb_b_put(Key, Value) :- ( atom(Key) -> '$store_backtrackable_global_var'(Key, Value) ; type_error(atom, Key, bb_b_put/2) ). +%% bb_get(+Key, -Value). +% +% Gets the value Value of a global variable named Key (must be an atom) bb_get(Key, Value) :- ( atom(Key) -> '$fetch_global_var'(Key, Value) @@ -52,12 +103,23 @@ bb_get(Key, Value) :- :- meta_predicate(call_cleanup(0, 0)). +%% call_cleanup(Goal, Cleanup). +% +% Executes Goal and then, either on success or failure, executes Cleanup. +% The success or failure of Cleanup is ignored and choice points created inside are destroyed. call_cleanup(G, C) :- setup_call_cleanup(true, G, C). :- meta_predicate(setup_call_cleanup(0, 0, 0)). :- non_counted_backtracking setup_call_cleanup/3. +%% setup_call_cleanup(Setup, Goal, Cleanup). +% +% If Setup succeeds, Cleanup will be called after the execution of Goal. Goal itself can succeed or not. +% +% In this example, we use the predicate to always close an open file: +% +% ?- setup_call_cleanup(open(File, read, Stream), do_something_with_stream(Stream), close(Stream)). setup_call_cleanup(S, G, C) :- '$get_b_value'(B), '$call_with_inference_counting'(call(S)), @@ -144,6 +206,9 @@ handle_ile(B, _, _) :- :- non_counted_backtracking call_with_inference_limit/3. +%% call_with_inference_limit(Goal, Limit, Result). +% +% Similar to `call(Goal)` but it limits the number of inferences for each solution of Goal. call_with_inference_limit(G, L, R) :- ( integer(L) -> ( L < 0 -> @@ -186,6 +251,10 @@ call_with_inference_limit(_, _, R, Bb, B) :- ), handle_ile(B, Ball, R). +%% partial_string(String, L, L0) +% +% Explicitly construct a partial string "manually". It can be used as an optimized append/3. +% It's not recommended to use this predicate in application code. partial_string(String, L, L0) :- ( String == [] -> L = L0 @@ -195,9 +264,17 @@ partial_string(String, L, L0) :- '$create_partial_string'(Atom, L, L0) ). +%% partial_string(+String) +% +% Succeeds if String is a _partial string_. A partial string is a string composed of several smaller +% strings, even just one. That means all strings in Scryer are partial strings. partial_string(String) :- '$is_partial_string'(String). +%% partial_string_tail(+String, -Tail). +% +% Unifies Tail with the last section of the partial string. +% It's not recommended to use this predicate in application code. partial_string_tail(String, Tail) :- ( partial_string(String) -> '$partial_string_tail'(String, Tail) @@ -209,6 +286,9 @@ partial_string_tail(String, Tail) :- :- meta_predicate(call_nth(0, ?)). +%% call_nth(Goal, N). +% +% Succeeds when Goal succeeded for the Nth time (there are at least N solutions) call_nth(Goal, N) :- can_be(integer, N), ( integer(N) -> @@ -247,16 +327,24 @@ call_nth_nesting(C, ID) :- bb_put(i_call_nth_counter, C). +%% copy_term_nat(Source, Dest) +% +% Similar to copy\_term/2 but without attribute variables copy_term_nat(Source, Dest) :- '$copy_term_without_attr_vars'(Source, Dest). - +%% asserta(Module, Rule_Fact). +% +% Similar to asserta/1 but allows specifying a Module asserta(Module, (Head :- Body)) :- !, '$asserta'(Module, Head, Body). asserta(Module, Fact) :- '$asserta'(Module, Fact, true). +%% assertz(Module, Rule_Fact). +% +% Similar to assertz/1 but allows specifying a Module assertz(Module, (Head :- Body)) :- !, '$assertz'(Module, Head, Body). From 6cb8d7596a52d49c3eadb12f7aecb666a51e03ca Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 18 Dec 2022 17:43:33 +0100 Subject: [PATCH 033/361] dereference more registers, analogous to d660e4244ff48bbcd558fab07a4dd4a5e9d68209 See also #1654 for a nice test case by @notoria which this corrects. --- src/machine/system_calls.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 3517f76b..289a6c97 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5919,10 +5919,10 @@ impl Machine { #[inline(always)] pub(crate) fn crypto_data_hash(&mut self) { - let encoding = cell_as_atom!(self.machine_st.registers[2]); + let encoding = cell_as_atom!(self.deref_register(2)); let bytes = self.string_encoding_bytes(self.machine_st.registers[1], encoding); - let algorithm = cell_as_atom!(self.machine_st.registers[4]); + let algorithm = cell_as_atom!(self.deref_register(4)); let ints_list = match algorithm { atom!("sha3_224") => { @@ -6059,7 +6059,7 @@ impl Machine { #[inline(always)] pub(crate) fn crypto_data_hkdf(&mut self) { - let encoding = cell_as_atom!(self.machine_st.registers[2]); + let encoding = cell_as_atom!(self.deref_register(2)); let data = self.string_encoding_bytes(self.machine_st.registers[1], encoding); let stub1_gen = || functor_stub(atom!("crypto_data_hkdf"), 4); @@ -6068,7 +6068,7 @@ impl Machine { let stub2_gen = || functor_stub(atom!("crypto_data_hkdf"), 4); let info = self.machine_st.integers_to_bytevec(self.machine_st.registers[4], stub2_gen); - let algorithm = cell_as_atom!(self.machine_st.registers[5]); + let algorithm = cell_as_atom!(self.deref_register(5)); let length = self.deref_register(6); @@ -6174,7 +6174,7 @@ impl Machine { #[inline(always)] pub(crate) fn crypto_data_encrypt(&mut self) { - let encoding = cell_as_atom!(self.machine_st.registers[3]); + let encoding = cell_as_atom!(self.deref_register(3)); let data = self.string_encoding_bytes(self.machine_st.registers[1], encoding); let aad = self.string_encoding_bytes(self.machine_st.registers[2], encoding); @@ -6314,7 +6314,7 @@ impl Machine { #[inline(always)] pub(crate) fn ed25519_sign(&mut self) { let key = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet")); - let encoding = cell_as_atom!(self.machine_st.registers[3]); + let encoding = cell_as_atom!(self.deref_register(3)); let data = self.string_encoding_bytes(self.machine_st.registers[2], encoding); let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&key) { @@ -6342,7 +6342,7 @@ impl Machine { #[inline(always)] pub(crate) fn ed25519_verify(&mut self) { let key = self.string_encoding_bytes(self.machine_st.registers[1], atom!("octet")); - let encoding = cell_as_atom!(self.machine_st.registers[3]); + let encoding = cell_as_atom!(self.deref_register(3)); let data = self.string_encoding_bytes(self.machine_st.registers[2], encoding); let stub_gen = || functor_stub(atom!("ed25519_verify"), 5); let signature = self.machine_st.integers_to_bytevec(self.machine_st.registers[4], stub_gen); @@ -6531,8 +6531,8 @@ impl Machine { #[inline(always)] pub(crate) fn chars_base64(&mut self) -> CallResult { - let padding = cell_as_atom!(self.machine_st.registers[3]); - let charset = cell_as_atom!(self.machine_st.registers[4]); + let padding = cell_as_atom!(self.deref_register(3)); + let charset = cell_as_atom!(self.deref_register(4)); let config = if padding == atom!("true") { if charset == atom!("standard") { From 56c1c4e43cf51c7d9e6f82eee292ef59f8d8e68a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Wed, 21 Dec 2022 21:28:16 +0100 Subject: [PATCH 034/361] Compatible Doclog docs for builtins --- src/lib/builtins.pl | 552 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 518 insertions(+), 34 deletions(-) diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 1d2d0aed..89270f6f 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -26,12 +26,30 @@ write_canonical/1, write_canonical/2, write_term/2, write_term/3, writeq/1, writeq/2]). +/** Builtin predicates + +This library, unlike the rest, is loaded by default and it exposes the most fundamental and general +predicates of the Prolog system under the ISO standard. Basic operators, metaprogramming, exceptions, +internal settings and basic I/O are all here. +*/ + % unify. + +%% =(?X, ?Y) +% +% Unify two variables. This is the most basic operation of Prolog. +% Unification also happens when doing head matching in a rule. X = X. +%% true. +% +% Always succeeds true. +%% false. +% +% Always fails false :- '$fail'. @@ -39,22 +57,59 @@ false :- '$fail'. % Once Scryer is bootstrapped, each is replaced with a version that % uses expand_goal to pass the expanded goal along to '$call'. + +%% call(Goal). +% +% Execute the Goal. Typically used when the Goal is not known at compile time. call(_). +%% call(Goal, ExtraArg1). +% +% Execute the Goal with ExtraArg1 appended to the argument list. For example: +% +% ?- call(format("~s~n"), ["Alain Colmerauer"]). +% Alain Colmerauer +% true. +% +% Which is equivalent to: `format("~s~n", ["Alain Colmerauer"]).` call(_, _). +%% call(Goal, ExtraArg1, ExtraArg2). +% +% Execute Goal with ExtraArg1 and ExtraArg2 appended to the argument list. call(_, _, _). +%% call(Goal, ExtraArg1, ExtraArg2, ExtraArg3). +% +% Execute Goal with ExtraArg1, ExtraArg2 and ExtraArg3 appended to the argument list. call(_, _, _, _). +%% call(Goal, ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4). +% +% Execute Goal with ExtraArg1, ExtraArg2, ExtraArg3 and ExtraArg4 appended to the argument list. call(_, _, _, _, _). +%% call(Goal, ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4, ExtraArg5). +% +% Execute Goal with ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4 and ExtraArg5 appended to the argument list. call(_, _, _, _, _, _). +%% call(Goal, ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4, ExtraArg5, ExtraArg6). +% +% Execute Goal with ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4, ExtraArg5 and ExtraArg6 appended +% to the argument list. call(_, _, _, _, _, _, _). +%% call(Goal, ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4, ExtraArg5, ExtraArg6, ExtraArg7). +% +% Execute Goal with ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4, ExtraArg5, ExtraArg6 and ExtraArg7 +% appended to the argument list. call(_, _, _, _, _, _, _, _). +%% call(Goal, ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4, ExtraArg5, ExtraArg6, ExtraArg7, ExtraArg8). +% +% Execute Goal with ExtraArg1, ExtraArg2, ExtraArg3, ExtraArg4, ExtraArg5, ExtraArg6, ExtraArg7 and +% ExtraArg8, appended to the argument list. call(_, _, _, _, _, _, _, _, _). @@ -62,6 +117,29 @@ call(_, _, _, _, _, _, _, _, _). % flags. +%% current_prolog_flag(Flag, Value) +% +% Returns the current Value of several flags in the running system. A flag is a setting which value affects +% internal operation of the Prolog system. Some flags are read-only, while others can be set with set\_prolog\_flag/2. +% +% The flags that Scryer Prolog support are: +% * `max\_arity`: The max arity a predicate can have in Prolog. On Scryer is set to 1023. Read only. +% * `bounded`: `true` if integer arithmethic is bounded between some min/max values. On Scryer is always set +% to `false` since it supports unbounded integer arithmethic. Read only. +% * `integer\_rounding\_function`: Describes the rounding donde by `//` and `rem` functions. On Scryer is +% always set to `toward\_zero`. Read only +% * `double\_quotes`: Determines how double quoted strings are red by Prolog. Scryer uses `chars` by default +% which is a list of one-character atoms. Other values are codes (list of integers representing characters), +% and atom which creates a whole atom for the string value. Read and write. +% * `max\_integer`: Maximum integer supported by the system. As Scryer Prolog has unbounded integer arithmethic, +% checking the value of this flag fails. Read only. +% * `min\_integer`: Minimum integer supported by the system. As Scryer Prolog has unbounded integer arithmethic, +% checking the value of this flag fails. Read only. +% * `occurs\_check`: Returns if the occurs check is enabled. The occurs check prevents the creation cyclic terms. +% Historically the Prolog unification algorithm didn't do that check so changing the value modifies how Prolog +% operates in the low-level. Possible values are `false` (default), `true` (unification has this check +% enabled) and `error` which throws an exception when a cylic term is created. Read ans write. +% current_prolog_flag(Flag, Value) :- Flag == max_arity, !, Value = 1023. current_prolog_flag(max_arity, 1023). current_prolog_flag(Flag, Value) :- Flag == bounded, !, Value = false. @@ -83,6 +161,10 @@ current_prolog_flag(Flag, _) :- nonvar(Flag), throw(error(type_error(atom, Flag), current_prolog_flag/2)). % 8.17.2.3 a +%% set_prolog_flag(Flag, Value). +% +% Changes the internal value of the flag. To see the list of flags supported by Scryer Prolog, +% check current\_prolog\_flag/2. The flags that are read only will fail if you try to change their values set_prolog_flag(Flag, Value) :- (var(Flag) ; var(Value)), throw(error(instantiation_error, set_prolog_flag/2)). % 8.17.1.3 a, b @@ -123,24 +205,38 @@ set_prolog_flag(Flag, _) :- % control operators. +%% fail. +% +% A predicate that always fails fail :- '$fail'. :- meta_predicate \+(0). +%% \+(Goal) +% +% Succeeds if Goal fails \+ G :- call(G), !, false. \+ _. - +%% \=(?X, ?Y) +% +% Succeeds if X and Y can't be unified X \= X :- !, false. _ \= _. :- meta_predicate once(0). +%% once(Goal) +% +% Execute Goal (like call/1) but exactly once, ignoring any kind of alternative solutions the original predicate +% could have generated. once(G) :- call(G), !. - +%% repeat. +% +% This predicate enters an infinite loop, always succeeding and generating infinite choice points repeat. repeat :- repeat. @@ -151,7 +247,9 @@ repeat :- repeat. :- meta_predicate ->(0,0). - +%% ->(G1, G2) +% +% If-then and if-then-else constructs G1 -> G2 :- control_entry_point((G1 -> G2)). @@ -163,6 +261,9 @@ staggered_if_then(G1, G2) :- '$set_cp'(B), call(G2). +%% ;(G1, G2) +% +% Disjunction (or) G1 ; G2 :- control_entry_point((G1 ; G2)). @@ -171,13 +272,20 @@ G1 ; G2 :- control_entry_point((G1 ; G2)). staggered_sc(G, _) :- call(G). staggered_sc(_, G) :- call(G). - +%% !. +% +% Cut operator. Discards the choicepoints created since entering the prediacate in which the operator appears. +% Using cut is not recommended as it introduces a non-declarative flow of programming and makes it more difficult +% to reason about the programs. Also restricts the ability to run the program with alternative execution strategies !. :- non_counted_backtracking set_cp/1. set_cp(B) :- '$set_cp'(B). +%% ,(G1, G2) +% +% Conjuction (and) ','(G1, G2) :- control_entry_point((G1, G2)). :- non_counted_backtracking control_entry_point/1. @@ -350,6 +458,13 @@ univ_errors(Term, List, N) :- :- non_counted_backtracking (=..)/2. +%% =..(Term, List) +% +% Univ operator. Term is a term whose functor is the head of the List, and the rest of arguments of Term +% are in tail of the List. Example: +% +% ?- f(a, X) =.. List. +% List = [f,a,X]. Term =.. List :- univ_errors(Term, List, N), univ_worker(Term, List, N). @@ -476,34 +591,67 @@ must_be_var_names_list_([VarName | VarNames], List) :- ; throw(error(instantiation_error, write_term/2)) ). - +%% write_term(+Term, +Options). +% +% Write Term to the current output stream according to some output syntax options. +% Options are specified in detail in write_term/3. write_term(Term, Options) :- current_output(Stream), write_term(Stream, Term, Options). +%% write_term(+Stream, +Term, +Options). +% +% Write Term to the stream Stream according to some output syntax options. The options avaibale are: +% * `ignore\_ops(+Boolean)` if `true`, the generic term representation is used everywhere. In `false` +% (default), operators do not use that generic term representation. +% * `max\_depth(+N)` if the term is nested deeper than N, print the reminder as ellipses. +% If N = 0 (default), there's no limit. +% * `numbervars(+Boolean)` if true, replaces `$VAR(N)` variables with letters, in order. Default is false. +% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog synytax, are quoted. Default is false. +% * `variable\_names(+List)` assign names to variables in term. List should be a list of terms of format `Name=Var`. write_term(Stream, Term, Options) :- parse_write_options(Options, [IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], write_term/3), '$write_term'(Stream, Term, IgnoreOps, NumberVars, Quoted, VNNames, MaxDepth). +%% write(+Term). +% +% Write Term to the current output stream using a syntax similar to Prolog write(Term) :- current_output(Stream), '$write_term'(Stream, Term, false, true, false, [], 0). +%% write(+Stream, +Term). +% +% Write Term to the stream Stream using a syntax similar to Prolog write(Stream, Term) :- '$write_term'(Stream, Term, false, true, false, [], 0). +%% write_canonical(+Term). +% +% Write Term to the current output stream using canonical Prolog syntax. Can be read back as Prolog terms. write_canonical(Term) :- current_output(Stream), '$write_term'(Stream, Term, true, false, true, [], 0). +%% write_canonical(+Stream, +Term). +% +% Write Term to the stream Stream using canonical Prolog syntax. Can be read back as Prolog terms. write_canonical(Stream, Term) :- '$write_term'(Stream, Term, true, false, true, [], 0). +%% writeq(+Term). +% +% Write Term to the current output stream using a syntax similar to write/1 but quoting the atoms that need to be +% quoted according to Prolog syntax. writeq(Term) :- current_output(Stream), '$write_term'(Stream, Term, false, true, true, [], 0). +%% writeq(+Stream, +Term). +% +% Write Term to the stream Stream using a syntax similar to write/1 but quoting the atoms that need to be +% quoted according to Prolog syntax. writeq(Stream, Term) :- '$write_term'(Stream, Term, false, true, true, [], 0). @@ -530,15 +678,27 @@ parse_read_term_options_(E,_) :- throw(error(domain_error(read_option, E), _)). - +%% read_term(+Stream, -Term, +Options). +% +% Read Term from the stream Stream. It supports several options: +% * `variables(-Vars)` unifies Vars with a list of variables in the term. Similar to do term\_variables/2 with the new term. +% * `variable\_names(-Vars)` unifies Vars with a list `Name=Var` with Name describing the variable name and Var the variable itself that appears in Term. +% * `singletons` similar to `variable\_names` but only reports variables occurring only once in Term. read_term(Stream, Term, Options) :- parse_read_term_options(Options, [Singletons, VariableNames, Variables], read_term/3), '$read_term'(Stream, Term, Singletons, Variables, VariableNames). +%% read_term(-Term, +Options). +% +% Read Term from the current input stream. It supports several options described in more detail in read\_term/3. read_term(Term, Options) :- current_input(Stream), read_term(Stream, Term, Options). +%% read(-Term). +% +% Read Term from the current input stream with default options. **NOTE** This is not a general predicate +% to read input from a file or the user. Use other predicates like phrase\_from\_file/2 for that. read(Term) :- current_input(Stream), read(Stream, Term). @@ -559,6 +719,13 @@ can_be_list(List, PI) :- % term_variables. +%% term_variables(+Term, -Vars). +% +% Unify Vars with a list of unique variables that appear in Term. The variables are sorted depth-first +% and left-to-right. +% +% ?- term_variables(f(X, Y, X, g(Z)), Vars). +% Vars = [X, Y, Z]. term_variables(Term, Vars) :- can_be_list(Vars, term_variables/2), '$term_variables'(Term, Vars). @@ -567,6 +734,13 @@ term_variables(Term, Vars) :- :- non_counted_backtracking catch/3. +%% catch(Goal, Catcher, Recover). +% +% Calls Goal, but if it throws an exception that unifies with Catcher, Recover will be called instead +% and the program will be resumed. Example: +% +% ?- catch(number_chars(X, "not_a_number"), error(syntax_error(_), _), X = 0). +% X = 0. catch(G,C,R) :- '$get_current_block'(Bb), catch(G,C,R,Bb). @@ -607,6 +781,15 @@ handle_ball(_, _, _) :- :- non_counted_backtracking throw/1. +%% throw(+Exception). +% +% Raise the exception Exception. The system looks for the innermost catch/3 for which Exception +% unifies with Catcher. Example: +% +% ?- throw(custom_error(42)). +% throw(custom_error(42)). +% ?- catch(throw(custom_error(42)), custom_error(_), true). +% true. throw(Ball) :- ( var(Ball) -> '$set_ball'(error(instantiation_error,throw/1)) @@ -638,6 +821,18 @@ findall_cleanup(LhLength, Error) :- :- non_counted_backtracking findall/3. +%% findall(Template, Goal, Solutions). +% +% Unify Solutions with a list of all values that variables in Template can take in Goal. +% findall/3 is equivalent to bagof/3 with all free variables scoped to the Goal (`^` operator) +% except that bagof/3 fails when no solutions are found and findall/3 unifies with an empty list. +% Example: +% +% f(1,2). +% f(1,3). +% f(1,4). +% ?- findall(X-Y, f(X, Y), Solutions). +% Solutions = [1-2,1-3,1-4]. findall(Template, Goal, Solutions) :- error:can_be(list, Solutions), '$lh_length'(LhLength), @@ -661,6 +856,9 @@ findall(Template, Goal, Solutions) :- :- non_counted_backtracking findall/4. +%% findall(Template, Goal, Solutions0, Solutions1) +% +% Similar to findall/3 but returns the solutions as the difference list Solutions0-Solutions1. findall(Template, Goal, Solutions0, Solutions1) :- error:can_be(list, Solutions0), error:can_be(list, Solutions1), @@ -743,6 +941,23 @@ findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses) :- non_counted_backtracking bagof/3. +%% bagof(Template, Goal, Solution). +% +% Unify Solution with a list of alternatives of the variables in Template coming from calling Goal. +% If Goal has no solutions, the predicate fails. +% If free variables that are not in Template appear in Goal, the predicate will backtrack over +% the alternatives of those free variables. However, you can use the syntax `Var^Goal` to not bind +% Var in Goal and prevent that. +% +% Example: +% +% f(1, 3). +% f(2, 4). +% ?- bagof(X, f(X, Y), Bag). +% Y = 3, Bag = [1], +% ; Y = 4, Bag = [2]. +% ?- bagof(X, Y^f(X, Y), Bag). +% Bag = [1,2]. bagof(Template, Goal, Solution) :- error:can_be(list, Solution), term_variables(Template, TemplateVars0), @@ -771,6 +986,15 @@ iterate_variants_and_sort([_|GroupSolutions], Ws, Solution) :- :- non_counted_backtracking setof/3. +%% setof(Template, Goal, Solution). +% +% Similar to bagof/3 but Solution is sorted and duplicates are removed. Example: +% +% f(1, 2). +% f(1, 3). +% f(2, 4). +% ?- setof(X, Y^f(X, Y), Set). +% Set = [1, 2]. setof(Template, Goal, Solution) :- error:can_be(list, Solution), term_variables(Template, TemplateVars0), @@ -810,6 +1034,9 @@ setof(Template, Goal, Solution) :- ; throw(error(type_error(callable, H), clause/2)) ). +%% clause(Head, Body). +% +% Succeeds if Head can be unified with a clause head and Body with its corresponding clause body. clause(H, B) :- ( var(H) -> throw(error(instantiation_error, clause/2)) @@ -833,6 +1060,10 @@ clause(H, B) :- :- meta_predicate asserta(:). +%% asserta(Clause). +% +% Asserts (inserts) a new clause (rule or fact) into the current module. +% The clause will be inserted at the beginning of the module. asserta(Clause0) :- loader:strip_subst_module(Clause0, user, Module, Clause), iso_ext:asserta(Module, Clause). @@ -840,6 +1071,10 @@ asserta(Clause0) :- :- meta_predicate assertz(:). +%% assertz(Clause). +% +% Asserts (inserts) a new clause (rule or fact) into the current module. +% The clase will be inserted at the end of the module. assertz(Clause0) :- loader:strip_subst_module(Clause0, user, Module, Clause), iso_ext:assertz(Module, Clause). @@ -847,6 +1082,10 @@ assertz(Clause0) :- :- meta_predicate retract(:). +%% retract(Clause) +% +% Retracts (deletes) a clause present in the current module. +% It only affects dynamic predicates. retract(Clause0) :- loader:strip_module(Clause0, Module, Clause), ( Clause \= (_ :- _) -> @@ -947,6 +1186,10 @@ retract_clause(Head, Body) :- :- meta_predicate retractall(:). +%% retractall(Head) +% +% Retracts (deletes) all clauses that unify which head unifies with Head +% It only affects dynamic predicates. retractall(Head) :- retract_clause(Head, _), false. @@ -984,6 +1227,11 @@ module_abolish(Pred, Module) :- :- meta_predicate abolish(:). +%% abolish(Pred). +% +% Pred should satisfy: `Pred = Name/Arity`. +% Deletes all clauses of a predicate with name Name and arity Arity. +% It only affects dynamic predicates abolish(Pred) :- ( var(Pred) -> throw(error(instantiation_error, abolish/1)) @@ -1024,7 +1272,11 @@ abolish(Pred) :- '$get_next_db_ref'(RName, RArity, RRName, RRArity), '$iterate_db_refs'(RRName, RRArity, Name/Arity). - +%% current_predicate(Pred). +% +% Pred must satisfy: `Pred = Name/Arity`. +% Pred unifies with a predicate description of a predicate that is currently loaded at the moment. +% It can be used to check for existence of a predicate or to enumerate all loaded predicates current_predicate(Pred) :- ( var(Pred) -> '$get_next_db_ref'(RN, RA, _, _), @@ -1055,6 +1307,10 @@ can_be_op_specifier(Spec) :- var(Spec). can_be_op_specifier(Spec) :- op_specifier(Spec). +%% current_op(Priority, Spec, Op) +% +% Succeeds if there's an operator defined with name Op, with spec Spec and priority Priority. +% Can be used to find all operators currently defined. current_op(Priority, Spec, Op) :- ( can_be_op_priority(Priority), can_be_op_specifier(Spec), @@ -1108,6 +1364,12 @@ op_(Priority, OpSpec, Op) :- '$op'(Priority, OpSpec, Op). +%% op(Priority, Spec, Op) +% +% Declares an operated named Op, with priority Priority and a spec Spec. +% The priority is an integer between 0 (null) and 1200. +% Spec can be: `xf`, `yf`, `xfx`, `xfy`, `yfx`, `fy` and `fx` where f indicates the position of the +% operator and x and y the arguments. op(Priority, OpSpec, Op) :- ( var(Priority) -> throw(error(instantiation_error, op/3)) % 8.14.3.3 a) @@ -1129,9 +1391,15 @@ op(Priority, OpSpec, Op) :- ! ; throw(error(type_error(list, Op), op/3)) % 8.14.3.3 f) ). - +%% halt. +% +% Exits the Prolog system with exit code 0 halt :- halt(0). + +%% halt(+ExitCode) +% +% Exits the Prolog system with exit code N halt(N) :- ( var(N) -> throw(error(instantiation_error, halt/1)) % 8.17.4.3 a) @@ -1142,7 +1410,12 @@ halt(N) :- ; throw(error(domain_error(exit_code, N), halt/1)) ). - +%% atom_length(+Atom, -Length). +% +% Succeeds when Atom is an atom of Length characters. Example: +% +% ?- atom_length(marseille, N). +% N = 9. atom_length(Atom, Length) :- ( var(Atom) -> throw(error(instantiation_error, atom_length/2)) % 8.16.1.3 a) @@ -1159,7 +1432,15 @@ atom_length(Atom, Length) :- ; throw(error(type_error(atom, Atom), atom_length/2)) % 8.16.1.3 b) ). - +%% atom_chars(?Atom, ?Chars). +% +% Relates an atom with a string in chars representation. It can be used to convert +% between atoms and strings. Examples: +% +% ?- atom_chars(marseille, X). +% X = "marseille". +% ?- atom_chars(X, "marseille"). +% X = marseille. atom_chars(Atom, List) :- '$skip_max_list'(_, _, List, Tail), ( ( Tail == [] ; var(Tail) ) -> @@ -1180,6 +1461,16 @@ atom_chars(Atom, List) :- ; throw(error(type_error(atom, Atom), atom_chars/2)) ). +%% atom_codes(?Atom, ?Codes). +% +% Relates an atom with a string in codes representation. It can be used to convert +% between atoms and strings. However, codes is not the default representation of double quoutes +% strings in Scryer Prolog. Examples: +% +% ?- atom_codes(marseille, X). +% X = [109,97,114,115,101,105,108,108,101]. +% ?- atom_codes(X, [109,97,114,115,101,105,108,108,101]). +% X = marseille. atom_codes(Atom, List) :- '$skip_max_list'(_, _, List, Tail), ( ( Tail == [] ; var(Tail) ) -> @@ -1200,7 +1491,12 @@ atom_codes(Atom, List) :- ; throw(error(type_error(atom, Atom), atom_codes/2)) ). - +%% atom_concat(?A1, ?A2, ?A12) +% +% Similar to append/3 but operating on atom characters. Example: +% +% ?- atom_concat(a, X, ab). +% X = b. atom_concat(Atom_1, Atom_2, Atom_12) :- error:can_be(atom, Atom_1), error:can_be(atom, Atom_2), @@ -1226,7 +1522,16 @@ atom_concat(Atom_1, Atom_2, Atom_12) :- atom_chars(Atom_12, Atom_12_Chars) ). - +%% sub_atom(+Atom, ?Before, ?Length, ?After, ?SubAtom). +% +% Relates an atom to a subatom inside with some key properties: +% * SubAtom starts at Before characters (0-based) from Atom +% * SubAtom has Length characters +% * After SubAtom there are After characters in Atom +% Example: +% +% ?- sub_atom(abcdefg, 2, 3, X, SubAtom). +% X = 2, SubAtom = cde. sub_atom(Atom, Before, Length, After, Sub_atom) :- error:must_be(atom, Atom), error:can_be(atom, Sub_atom), @@ -1248,7 +1553,12 @@ sub_atom(Atom, Before, Length, After, Sub_atom) :- atom_chars(Sub_atom, LengthChars) ). - +%% char_code(?Char, ?Code) +% +% Relates a Char to its Code (an integer). Example: +% +% ?- char_code(a, X). +% X = 97. char_code(Char, Code) :- ( var(Char) -> ( var(Code) -> @@ -1269,11 +1579,19 @@ char_code(Char, Code) :- ; throw(error(type_error(character, Char), char_code/2)) ). +%% get_char(-Char). +% +% From the current input stream, unify Char with the next character. +% When there are no more characters to read, Char unifies with `end\_of\_file`. get_char(C) :- error:can_be(in_character, C), current_input(S), '$get_char'(S, C). +%% get_char(+Stream, -Char). +% +% From the stream Stream, unify Char with the next character. +% When there are no more characters to read, Char unifies with `end\_of\_file`. get_char(S, C) :- error:can_be(in_character, C), '$get_char'(S, C). @@ -1330,7 +1648,18 @@ codes_or_vars([C|Cs], PI) :- ; codes_or_vars(Cs, PI) ). - +%% number_chars(?N, ?Chars). +% +% Relates a number and its representation as list of chars (string). +% Throws an error if Chars is not the representation of a number. +% Examples: +% +% ?- number_chars(42, X). +% X = "42". +% ?- number_chars(X, "42"). +% X = 42. +% ?- number_chars(X, "not_a_number"). +% error(syntax_error(cannot_parse_big_int),number_chars/2:0). number_chars(N, Chs) :- ( ground(Chs) -> can_be_number(N, number_chars/2), @@ -1352,7 +1681,18 @@ list_of_ints(Ns) :- error:must_be(list, Ns), lists:maplist(error:must_be(integer), Ns). - +%% number_codes(?N, ?Codes). +% +% Relates a number and its representation as list of codes. +% Throws an error if Codes is not the representation of a number. +% Examples: +% +% ?- number_codes(42, X). +% X = [52,50]. +% ?- number_codes(X, [52,50]). +% X = 42. +% ?- number_codes(X, [65]). +% error(syntax_error(cannot_parse_big_int),number_codes/2:0). number_codes(N, Chs) :- ( ground(Chs) -> can_be_number(N, number_codes/2), @@ -1369,7 +1709,16 @@ number_codes(N, Chs) :- '$number_to_codes'(N, Chs) ). - +%% subsumes_term(General, Specific) +% +% Succeeds if General can be made equivalent to Specific by only binding variables +% in Generic. The implementation unifies with occurs check always and ensures that +% the variables of Specific did not change. Some examples: +% +% ?- subsumes_term(f(A, A), f(2, 2)). +% true. +% ?- subsumes_term(f(A, 2), f(2, A)). +% false. subsumes_term(General, Specific) :- \+ \+ ( term_variables(Specific, SVs1), @@ -1378,21 +1727,40 @@ subsumes_term(General, Specific) :- SVs1 == SVs2 ). - +%% unify_with_occurs_check(?X, ?Y). +% +% Unify with occurs check.The occurs check prevents the creation cyclic terms but is +% computationally more expensive. The (=)/2 operator can also do occurs check if enabled +% via set\_prolog\_flag/2. Example: +% +% ?- A = f(A). +% A = f(A). +% ?- unify_with_occurs_check(A, f(A)). +% false. unify_with_occurs_check(X, Y) :- '$unify_with_occurs_check'(X, Y). - +%% current_input(-Stream). +% +% Unifies with the current input stream. current_input(S) :- '$current_input'(S). +%% current_output(-Stream). +% +% Unifies with the current output stream. current_output(S) :- '$current_output'(S). - +%% set_input(+Stream). +% +% Sets the current input stream to Stream. set_input(S) :- ( var(S) -> throw(error(instantiation_error, set_input/1)) ; '$set_input'(S) ). +%% set_output(Stream). +% +% Sets the current output stream to Stream. set_output(S) :- ( var(S) -> throw(error(instantiation_error, set_output/1)) @@ -1434,11 +1802,33 @@ parse_stream_options_(eof_action(Action), eof_action-Action) :- parse_stream_options_(E, _) :- throw(error(domain_error(stream_option, E), _)). % 8.11.5.3i) - +%% open(+File, +Mode, +Stream). +% +% Equivalent to `open(File, Mode, Stream, [])`. open(SourceSink, Mode, Stream) :- open(SourceSink, Mode, Stream, []). - +%% open(+File, +Mode, -Stream, +StreamOptions). +% +% Opens a file named File with a Mode and StreamOptions, and returns a Stream +% that can be used by other predicates to read and write (depending on Mode). +% +% Mode can be: `read`, `write` or `append`. `read` creates a Stream +% that is read-only, `write` is write-only and `append` +% is write-only but at the end of the file. +% +% The following options are available: +% +% * `alias(+Alias)`: Set an alias to the stream +% * `eof\_action(+Action)`: Defined what happens if the end of the stream is reached. Values: `error`, `eof_code` and `reset`. +% * `reposition(+Boolean)`: Specifies whether repositioning is required for the stream. `false` is the default. +% * `type(+Type)`: Type can be `text` or `binary`. Defines the type of the stream, if it's optimized for plain text +% or just binary +% +% Example: +% +% ?- open("README.md", read, S, []), get_n_chars(S, 20, C). +% S = '$stream'(0x55dece980218), C = "\n# Scryer Prolog\n\nS ..." open(SourceSink, Mode, Stream, StreamOptions) :- ( var(SourceSink) -> throw(error(instantiation_error, open/4)) % 8.11.5.3a) @@ -1476,84 +1866,145 @@ parse_close_options_(force(Force), force-Force) :- parse_close_options_(E, _) :- throw(error(domain_error(close_option, E), _)). - +%% close(+Stream, +CloseOptions). +% +% Closes a stream. It takes a CloseOptions list. The only option available is `force` which takes a `true` +% or `false`. close(Stream, CloseOptions) :- parse_close_options(CloseOptions, [Force], close/2), '$close'(Stream, CloseOptions). +%% close(+Stream). +% +% Closes a stream. Equivalent to `close(Stream, []).`. close(Stream) :- '$close'(Stream, []). - +%% flush_output(+Stream). +% +% Flushes the output of the stream Stream flush_output(S) :- '$flush_output'(S). +%% flush_output. +% +% Flushes the output of the current output stream flush_output :- current_output(S), '$flush_output'(S). - +%% get_byte(+Stream, -Byte). +% +% From the stream Stream, unify Byte with the next byte (an integer between 0 and 255) +% When there are no more bytes to read, Byte unifies with -1. get_byte(S, B) :- '$get_byte'(S, B). +%% get_byte(-Byte). +% +% From the current input stream, unify Byte with the next byte (an integer between 0 and 255) +% When there are no more bytes to read, Byte unifies with -1. get_byte(B) :- current_input(S), '$get_byte'(S, B). - +%% put_char(+Char). +% +% Writes to the current output stream the character Char. put_char(C) :- current_output(S), '$put_char'(S, C). +%% put_char(+Stream, +Char). +% +% Writes to the stream Stream the character Char. put_char(S, C) :- '$put_char'(S, C). - +%% put_byte(+Byte). +% +% Writes to the current output stream the byte Byte (should be an integer between 0 and 255). put_byte(C) :- current_output(S), '$put_byte'(S, C). +%% put_byte(+Stream, +Byte). +% +% Writes to the stream Stream the byte Byte (should be an integer between 0 and 255). put_byte(S, C) :- '$put_byte'(S, C). - +%% put_code(+Code). +% +% Writes to the current output stream the character represented by code Code put_code(C) :- current_output(S), '$put_code'(S, C). +%% put_code(+Stream, +Code). +% +% Writes to the stream Stream the character represented by code Code put_code(S, C) :- '$put_code'(S, C). - +%% get_code(-Code). +% +% From the current input stream, unify Code with the character code of the next character. +% When there are no more characters to read, Code unifies with -1. get_code(C) :- current_input(S), '$get_code'(S, C). +%% get_code(+Stream, -Code). +% +% From the stream Stream, unify Code with the character code of the next character. +% When there are no more characters to read, Code unifies with -1. get_code(S, C) :- '$get_code'(S, C). - +%% peek_byte(+Stream, -Byte). +% +% From the stream Stream, unify Byte with the next byte. However, it doesn't move the stream +% position, allowing it to be read again. peek_byte(S, B) :- '$peek_byte'(S, B). +%% peek_byte(-Byte). +% +% From the current input stream, unify Byte with the next byte. However, it doesn't move the stream +% position, allowing it to be read again. peek_byte(B) :- current_input(S), '$peek_byte'(S, B). - +%% peek_code(-Code). +% +% From the current input stream, unify Code with the character code of the next character. +% However, it doesn't move the stream position, allowing it to be read again. peek_code(C) :- current_input(S), '$peek_code'(S, C). +%% peek_code(+Stream, -Code). +% +% From the stream Stream, unify Code with the character code of the next character. +% However, it doesn't move the stream position, allowing it to be read again. peek_code(S, C) :- '$peek_code'(S, C). - +%% peek_char(-Char). +% +% From the current input stream, unify Char with the next character. +% However, it doesn't move the stream position, allowing it to be read again. peek_char(C) :- current_input(S), '$peek_char'(S, C). +%% peek_char(+Stream, -Char). +% +% From the stream Stream, unify Char with the next character. +% However, it doesn't move the stream position, allowing it to be read again. peek_char(S, C) :- '$peek_char'(S, C). @@ -1595,7 +2046,21 @@ stream_iter(S) :- stream_iter_(S0, S) ). - +%% stream_property(Stream, StreamProperty). +% +% For stream Stream, StreamProperty is a property that applies to that stream. +% StreamProperty can be one of the following: +% * `input` if stream is an input stream. +% * `output` if stream is an output stream. +% * `input\_output` if stream is both an input and an output stream. +% * `alias(-Alias)` if the stream has an associated alias. +% * `file\_name(-FileName)` if Stream is associated to a file, unifies with the name of the file +% * `mode(-Mode)`: Mode unifies with the mode of the stream: `read`, `write` or `append`. +% * `position(position_and_lines_read(P, L))` current position of the stream. +% * `end\_of\_stream(-X)` where X can be `not`, `at` or `past` depending if the stream has ended or not. +% * `eof\_action(-X)` where X can be `error`, `eof_code` or `reset` depending on the action that will happen on the end of the file. +% * `reposition(-Boolean)` specifies if reposition has been enabled for this stream. +% * `type(-Type)` where Type can be `text` or `binary`. stream_property(S, P) :- ( nonvar(P), \+ check_stream_property(P, _, _) -> throw(error(domain_error(stream_property, P), stream_property/2)) @@ -1604,7 +2069,9 @@ stream_property(S, P) :- '$stream_property'(S, PropertyName, PropertyValue) ). - +%% at_end_of_stream(+Stream). +% +% Succeeds if the stream Stream has ended at_end_of_stream(S_or_a) :- ( var(S_or_a) -> throw(error(instantiation_error, at_end_of_stream/1)) @@ -1615,13 +2082,18 @@ at_end_of_stream(S_or_a) :- stream_property(S, end_of_stream(E)), ( E = at -> true ; E = past ). +%% at_end_of_stream. +% +% Succeeds if the current input stream has ended at_end_of_stream :- current_input(S), stream_property(S, end_of_stream(E)), !, ( E = at ; E = past ). - +%% set_stream_position(+Stream, +Position). +% +% Sets the current position of the stream Stream to Position. set_stream_position(S_or_a, Position) :- ( var(Position) -> throw(error(instantiation_error, set_stream_position/2)) @@ -1631,18 +2103,30 @@ set_stream_position(S_or_a, Position) :- ; throw(error(domain_error(stream_position, Position), set_stream_position/2)) ). +%% callable(X). +% +% Succeeds if X is bound o an atom or a compund term. callable(X) :- ( nonvar(X), functor(X, F, _), atom(F) -> true ; false ). +%% nl. +% +% Writes a new line character to the current output stream. nl :- current_output(Stream), nl(Stream). +%% nl(+Stream). +% +% Writes a new line character to the stream Stream. nl(Stream) :- put_char(Stream, '\n'). +%% error(ErrorTerm, ImpDef). +% +% Throws an exception of the following structure: `error(ErrorTerm, ImpDef)`. error(Error_term, Imp_def) :- throw(error(Error_term, Imp_def)). From 21e4347b3efd3d33430e82ff3fcee856c678cd24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Wed, 21 Dec 2022 22:32:36 +0100 Subject: [PATCH 035/361] Apply some feedback --- src/lib/builtins.pl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index aef9b32b..d151912a 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -38,18 +38,18 @@ internal settings and basic I/O are all here. %% =(?X, ?Y) % -% Unify two variables. This is the most basic operation of Prolog. +% True if X and Y can be unified. This is the most basic operation of Prolog. % Unification also happens when doing head matching in a rule. X = X. %% true. % -% Always succeeds +% Always true. true. %% false. % -% Always fails +% Always false. false :- '$fail'. @@ -119,8 +119,9 @@ call(_, _, _, _, _, _, _, _, _). %% current_prolog_flag(Flag, Value) % -% Returns the current Value of several flags in the running system. A flag is a setting which value affects -% internal operation of the Prolog system. Some flags are read-only, while others can be set with set\_prolog\_flag/2. +% True iff Flag is a flag supported by the processor, and Value is the value currently associated with it. +% A flag is a setting which value affects internal operation of the Prolog system. Some flags are read-only, +% while others can be set with set\_prolog\_flag/2. % % The flags that Scryer Prolog support are: % * `max\_arity`: The max arity a predicate can have in Prolog. On Scryer is set to 1023. Read only. @@ -163,7 +164,7 @@ current_prolog_flag(Flag, _) :- %% set_prolog_flag(Flag, Value). % -% Changes the internal value of the flag. To see the list of flags supported by Scryer Prolog, +% Sets the internal value of the flag. To see the list of flags supported by Scryer Prolog, % check current\_prolog\_flag/2. The flags that are read only will fail if you try to change their values set_prolog_flag(Flag, Value) :- (var(Flag) ; var(Value)), From 7647ad14b818876bd3908fa0e21952731f10e4ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Wed, 21 Dec 2022 23:19:38 +0100 Subject: [PATCH 036/361] More feedback applied --- src/lib/builtins.pl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index d151912a..4fec5e1f 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -208,7 +208,7 @@ set_prolog_flag(Flag, _) :- %% fail. % -% A predicate that always fails +% A predicate that always fails. The more declarative false/0 should be used instead. fail :- '$fail'. @@ -216,13 +216,13 @@ fail :- '$fail'. %% \+(Goal) % -% Succeeds if Goal fails +% True iff Goal fails \+ G :- call(G), !, false. \+ _. %% \=(?X, ?Y) % -% Succeeds if X and Y can't be unified +% True iff X and Y can't be unified X \= X :- !, false. _ \= _. @@ -237,7 +237,7 @@ once(G) :- call(G), !. %% repeat. % -% This predicate enters an infinite loop, always succeeding and generating infinite choice points +% This predicate succeeds arbitrarily often, generating choice points with that. repeat. repeat :- repeat. @@ -458,7 +458,7 @@ univ_errors(Term, List, N) :- %% =..(Term, List) % -% Univ operator. Term is a term whose functor is the head of the List, and the rest of arguments of Term +% Univ operator. True iff Term is a term whose functor is the head of the List, and the rest of arguments of Term % are in tail of the List. Example: % % ?- f(a, X) =.. List. @@ -1032,7 +1032,7 @@ setof(Template, Goal, Solution) :- %% clause(Head, Body). % -% Succeeds if Head can be unified with a clause head and Body with its corresponding clause body. +% True iff Head can be unified with a clause head and Body with its corresponding clause body. clause(H, B) :- ( var(H) -> throw(error(instantiation_error, clause/2)) @@ -1271,7 +1271,7 @@ abolish(Pred) :- %% current_predicate(Pred). % % Pred must satisfy: `Pred = Name/Arity`. -% Pred unifies with a predicate description of a predicate that is currently loaded at the moment. +% True iff there's a predicate Pred that is currently loaded at the moment. % It can be used to check for existence of a predicate or to enumerate all loaded predicates current_predicate(Pred) :- ( var(Pred) -> @@ -1305,7 +1305,7 @@ can_be_op_specifier(Spec) :- op_specifier(Spec). %% current_op(Priority, Spec, Op) % -% Succeeds if there's an operator defined with name Op, with spec Spec and priority Priority. +% True iff there's an operator defined with name Op, with spec Spec and priority Priority. % Can be used to find all operators currently defined. current_op(Priority, Spec, Op) :- ( can_be_op_priority(Priority), @@ -1408,7 +1408,7 @@ halt(N) :- %% atom_length(+Atom, -Length). % -% Succeeds when Atom is an atom of Length characters. Example: +% True iff Atom is an atom of Length characters. Example: % % ?- atom_length(marseille, N). % N = 9. @@ -1707,7 +1707,7 @@ number_codes(N, Chs) :- %% subsumes_term(General, Specific) % -% Succeeds if General can be made equivalent to Specific by only binding variables +% True iff General can be made equivalent to Specific by only binding variables % in Generic. The implementation unifies with occurs check always and ensures that % the variables of Specific did not change. Some examples: % @@ -1725,7 +1725,7 @@ subsumes_term(General, Specific) :- %% unify_with_occurs_check(?X, ?Y). % -% Unify with occurs check.The occurs check prevents the creation cyclic terms but is +% True iff X and Y unify with occurs check. The occurs check prevents the creation cyclic terms but is % computationally more expensive. The (=)/2 operator can also do occurs check if enabled % via set\_prolog\_flag/2. Example: % @@ -2067,7 +2067,7 @@ stream_property(S, P) :- %% at_end_of_stream(+Stream). % -% Succeeds if the stream Stream has ended +% True iff the stream Stream has ended at_end_of_stream(S_or_a) :- ( var(S_or_a) -> throw(error(instantiation_error, at_end_of_stream/1)) @@ -2080,7 +2080,7 @@ at_end_of_stream(S_or_a) :- %% at_end_of_stream. % -% Succeeds if the current input stream has ended +% True iff the current input stream has ended at_end_of_stream :- current_input(S), stream_property(S, end_of_stream(E)), @@ -2101,7 +2101,7 @@ set_stream_position(S_or_a, Position) :- %% callable(X). % -% Succeeds if X is bound o an atom or a compund term. +% True iff X is bound o an atom or a compund term. callable(X) :- ( nonvar(X), functor(X, F, _), atom(F) -> true From 8ae0a1a4afd8b4e2a9c559d2cafa6fffdb2be671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Thu, 22 Dec 2022 22:36:33 +0100 Subject: [PATCH 037/361] Compatible Doclog docs for library(http/http_server) --- src/lib/http/http_server.pl | 97 ++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 35 deletions(-) diff --git a/src/lib/http/http_server.pl b/src/lib/http/http_server.pl index 0f467fd4..bbe17565 100644 --- a/src/lib/http/http_server.pl +++ b/src/lib/http/http_server.pl @@ -1,51 +1,51 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written in December 2020 by Adrián Arroyo (adrian.arroyocalle@gmail.com) Updated in March 2022 by Adrián Arroyo to use the Hyper backend - Part of Scryer Prolog + Part of Scryer Prolog. + I place this code in the public domain. Use it in any way you want. +*/ - This library provides an starting point to build HTTP server based applications. - It is based on Hyper, which allows for HTTP/1.0, HTTP/1.1 and HTTP/2. However, - some advanced features that Hyper provides are still not accesible. +/** This library provides an starting point to build HTTP server based applications. +It is based on Hyper, which allows for HTTP/1.0, HTTP/1.1 and HTTP/2. However, +some advanced features that Hyper provides are still not accesible. - Usage - ========== - The main predicate of the library is http_listen/2, which needs a port number - (usually 80) and a list of handlers. A handler is a compound term with the functor - as one HTTP method (in lowercase) and followed by a Route Match and a predicate - which will handle the call. +## Usage - text_handler(Request, Response) :- - http_status_code(Response, 200), - http_body(Response, text("Welcome to Scryer Prolog!")). +The main predicate of the library is http\_listen/2, which needs a port number +(usually 80) and a list of handlers. A handler is a compound term with the functor +as one HTTP method (in lowercase) and followed by a Route Match and a predicate +which will handle the call. - parameter_handler(User, Request, Response) :- - http_body(Response, text(User)). + text_handler(Request, Response) :- + http_status_code(Response, 200), + http_body(Response, text("Welcome to Scryer Prolog!")). + + parameter_handler(User, Request, Response) :- + http_body(Response, text(User)). + + http_listen(7890, [ + get(echo, text_handler), % GET /echo + post(user/User, parameter_handler(User)) % POST /user/ + ]). - http_listen(7890, [ - get(echo, text_handler), % GET /echo - post(user/User, parameter_handler(User)) % POST /user/ - ]). - - Every handler predicate will have at least 2-arity, with Request and Response. - Although you can work directly with http_request and http_response terms, it is - recommeded to use the helper predicates, which are easier to understand and cleaner: - - http_headers(Response/Request, Headers) - - http_status_code(Responde, StatusCode) - - http_body(Response/Request, text(Body)) - - http_body(Response/Request, binary(Body)) - - http_body(Request, form(Form)) - - http_body(Response, file(Filename)) - - http_redirect(Response, Url) - - http_query(Request, QueryName, QueryValue) +Every handler predicate will have at least 2-arity, with Request and Response. +Although you can work directly with http\_request and http\_response terms, it is +recommeded to use the helper predicates, which are easier to understand and cleaner: + - `http\_headers(Response/Request, Headers)` + - `http\_status\_code(Responde, StatusCode)` + - `http\_body(Response/Request, text(Body))` + - `http\_body(Response/Request, binary(Body))` + - `http\_body(Request, form(Form))` + - `http\_body(Response, file(Filename))` + - `http\_redirect(Response, Url)` + - `http\_query(Request, QueryName, QueryValue)` Some things that are still missing: - Read forms in multipart format - HTTP Basic Auth - Session handling via cookies - - HTML Templating - - I place this code in the public domain. Use it in any way you want. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + - HTML Templating (but you can use [Teruel](https://github.com/aarroyoc/teruel/) and/or [Marquete](https://github.com/aarroyoc/marquete/) for that) +*/ :- module(http_server, [ @@ -68,6 +68,11 @@ :- use_module(library(pio)). :- use_module(library(time)). +%% http_listen(+Port, +Handlers). +% +% Listens for HTTP connections on port Port. Each handler on the list Handlers should be of the form: `HttpVerb(PathUnification, Predicate)`. +% For example: `get(user/User, get\_info(User))` will match an HTTP request that is a GET, the path unifies with /user/User (where User is a variable) +% and it will call get_info with three arguments: an http\_request term, an http\_response term and User. http_listen(Port, Module:Handlers0) :- must_be(integer, Port), must_be(list, Handlers0), @@ -206,9 +211,20 @@ string_without(Not, [Char|String]) --> string_without(_, []) --> []. +%% http_headers(?Request_Response, ?Headers). +% +% True iff Request_Response is a request or response with headers Headers. Can be used both to get headers (usually in from a request) +% and to add headers (usually in a response). http_headers(http_request(Headers, _, _), Headers). http_headers(http_response(_, _, Headers), Headers). +%% http_body(?Request_Response, ?Body). +% +% True iff Body is the body of the request or response. A body can be of the following types: +% * `bytes(Bytes)` for both requests and responses, interprets the body as bytes +% * `text(Bytes)` for both requests and responses, interprets the body as text +% * `form(Form)` only for requests, interprets the body as an `application/x-www-form-urlencoded` form. +% * `file(File)` only for responses, interprets the body as the content of a file (useful to send static files). http_body(http_request(_, stream(StreamBody), _), bytes(BytesBody)) :- get_n_chars(StreamBody, _, BytesBody). http_body(http_request(_, stream(StreamBody), _), text(TextBody)) :- get_n_chars(StreamBody, _, TextBody). http_body(http_request(Headers, stream(StreamBody), _), form(FormBody)) :- @@ -218,8 +234,19 @@ http_body(http_request(Headers, stream(StreamBody), _), form(FormBody)) :- http_body(http_request(_, Body, _), Body). http_body(http_response(_, Body, _), Body). +%% http_status_code(?Response, ?StatusCode). +% +% True iff the status code of the response Response unifies with StatusCode. http_status_code(http_response(StatusCode, _, _), StatusCode). + +%% http_redirect(-Response, +Uri). +% +% True iff Response is a response that redirects the user to the uri Uri. http_redirect(http_response(307, text("Moved Temporarily"), ["Location"-Uri]), Uri). + +%% http_query(+Request, ?Key, ?Value). +% +% True iff there's a query in request Request with key Key and value Value. http_query(http_request(_, _, Queries), Key, Value) :- member(Key-Value, Queries). parse_queries([Key-Value|Queries]) --> From 07f358c91b6096ce0384702d92114ae9b93cc4ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Thu, 22 Dec 2022 23:23:07 +0100 Subject: [PATCH 038/361] Compatible Doclog docs for library(dif) --- src/lib/dif.pl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/lib/dif.pl b/src/lib/dif.pl index d9c475eb..165493fd 100644 --- a/src/lib/dif.pl +++ b/src/lib/dif.pl @@ -1,3 +1,8 @@ +/** +Provides predicate dif/2. dif/2 is a constraint that is true only if both of its +arguments are different terms. +*/ + :- module(dif, [dif/2]). :- use_module(library(atts)). @@ -38,6 +43,20 @@ verify_attributes(Var, Value, Goals) :- % Probably the world's worst dif/2 implementation. I'm open to % suggestions for improvement. +%% dif(?X, ?Y). +% +% True iff X and Y are different terms. Unlike \\=/2, dif/2 is more declarative because if X and Y can +% unify but they're not yet equal, the decision is delayed, and prevents X and Y to become equal later. +% Examples: +% +% ?- dif(a, a). +% false. +% ?- dif(a, b). +% true. +% ?- dif(X, b). +% dif:dif(X,b). +% ?- dif(X, b), X = b. +% false. dif(X, Y) :- X \== Y, ( X \= Y -> true From aa7b8e52f37681da803585ea80764f7a28edcc4f Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 24 Dec 2022 00:31:39 -0700 Subject: [PATCH 039/361] use '$enqueue_attr_var' when adding attributes only --- src/lib/atts.pl | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/lib/atts.pl b/src/lib/atts.pl index a9369426..372a9bdd 100644 --- a/src/lib/atts.pl +++ b/src/lib/atts.pl @@ -41,8 +41,7 @@ ( L \= Attr -> nonvar(Ls), '$get_from_list'(Ls, V, Attr) - ; L = Attr, - '$enqueue_attr_var'(V) + ; L = Attr ). '$put_attr'(V, Attr) :- @@ -65,8 +64,7 @@ nonvar(Att), ( Att \= Attr -> '$del_attr_buried'(Ls0, Ls1, V, Attr) - ; '$enqueue_attr_var'(V), - '$del_attr_head'(V), + ; '$del_attr_head'(V), '$del_attr'(Ls1, V, Attr) ). @@ -84,8 +82,7 @@ ; Ls1 = [Att | Ls2] -> ( Att \= Attr -> '$del_attr_buried'(Ls1, Ls2, V, Attr) - ; '$enqueue_attr_var'(V), - '$del_attr_non_head'(Ls0), %% set tail of Ls0 = tail of Ls1. can be undone by backtracking. + ; '$del_attr_non_head'(Ls0), %% set tail of Ls0 = tail of Ls1. can be undone by backtracking. '$del_attr_step'(Ls1, V, Attr) ) ). From bb624cc971e02913c039b60a0e16cbf4d06694c7 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 8 Jan 2023 12:10:38 -0700 Subject: [PATCH 040/361] use free lists to allow register re-use (#1612) --- src/codegen.rs | 76 ++++++++++++++++++++++++++++++----------- src/debray_allocator.rs | 24 +++++++++++-- 2 files changed, 79 insertions(+), 21 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 6f722336..dc03a19a 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -242,6 +242,41 @@ fn trim_structure_by_last_arg(instr: &mut Instruction, last_arg: &Term) { } } +trait AddToFreeList<'a, Target: CompilationTarget<'a>> { + fn add_term_to_free_list(&mut self, r: RegType); + fn add_subterm_to_free_list(&mut self, term: &Term); +} + +impl<'a, 'b> AddToFreeList<'a, FactInstruction> for CodeGenerator<'b> { + #[inline(always)] + fn add_term_to_free_list(&mut self, r: RegType) { + self.marker.add_to_free_list(r); + } + + fn add_subterm_to_free_list(&mut self, _term: &Term) {} +} + +impl<'a, 'b> AddToFreeList<'a, QueryInstruction> for CodeGenerator<'b> { + fn add_term_to_free_list(&mut self, _r: RegType) {} + + #[inline(always)] + fn add_subterm_to_free_list(&mut self, term: &Term) { + if let Some(cell) = structure_cell(term) { + self.marker.add_to_free_list(cell.get()); + } + } +} + +fn structure_cell(term: &Term) -> Option<&Cell> { + match term { + &Term::Cons(ref cell, ..) | + &Term::Clause(ref cell, ..) | + Term::PartialString(ref cell, ..) | + Term::CompleteString(ref cell, ..) => Some(cell), + _ => None, + } +} + impl<'b> CodeGenerator<'b> { pub(crate) fn new(atom_tbl: &'b mut AtomTable, settings: CodeGenSettings) -> Self { CodeGenerator { @@ -287,10 +322,9 @@ impl<'b> CodeGenerator<'b> { cell: &'a Cell, var: &Rc, term_loc: GenContext, - is_exposed: bool, target: &mut Code, ) { - if is_exposed || self.get_var_count(var.as_ref()) > 1 { + if self.get_var_count(var.as_ref()) > 1 { self.marker.mark_var::(var.clone(), Level::Deep, cell, term_loc, target); } else { Self::add_or_increment_void_instr::(target); @@ -301,13 +335,9 @@ impl<'b> CodeGenerator<'b> { &mut self, subterm: &'a Term, term_loc: GenContext, - is_exposed: bool, target: &mut Code, ) { match subterm { - &Term::AnonVar if is_exposed => { - self.marker.mark_anon_var::(Level::Deep, term_loc, target); - } &Term::AnonVar => { Self::add_or_increment_void_instr::(target); } @@ -322,7 +352,7 @@ impl<'b> CodeGenerator<'b> { target.push(Target::constant_subterm(constant.clone())); } &Term::Var(ref cell, ref var) => { - self.deep_var_instr::(cell, var, term_loc, is_exposed, target); + self.deep_var_instr::(cell, var, term_loc, target); } }; } @@ -331,11 +361,11 @@ impl<'b> CodeGenerator<'b> { &mut self, iter: Iter, term_loc: GenContext, - is_exposed: bool, ) -> Code where Target: crate::targets::CompilationTarget<'a>, Iter: Iterator>, + CodeGenerator<'b>: AddToFreeList<'a, Target> { let mut target: Code = Vec::new(); @@ -352,6 +382,8 @@ impl<'b> CodeGenerator<'b> { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); target.push(Target::to_structure(name, terms.len(), cell.get())); + as AddToFreeList<'a, Target>>::add_term_to_free_list(self, cell.get()); + if let Some(instr) = target.last_mut() { if let Some(term) = terms.last() { trim_structure_by_last_arg(instr, term); @@ -359,15 +391,24 @@ impl<'b> CodeGenerator<'b> { } for subterm in terms { - self.subterm_to_instr::(subterm, term_loc, is_exposed, &mut target); + self.subterm_to_instr::(subterm, term_loc, &mut target); + } + + for subterm in terms { + as AddToFreeList<'a, Target>>::add_subterm_to_free_list(self, subterm); } } TermRef::Cons(lvl, cell, head, tail) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); target.push(Target::to_list(lvl, cell.get())); - self.subterm_to_instr::(head, term_loc, is_exposed, &mut target); - self.subterm_to_instr::(tail, term_loc, is_exposed, &mut target); + as AddToFreeList<'a, Target>>::add_term_to_free_list(self, cell.get()); + + self.subterm_to_instr::(head, term_loc, &mut target); + self.subterm_to_instr::(tail, term_loc, &mut target); + + as AddToFreeList<'a, Target>>::add_subterm_to_free_list(self, head); + as AddToFreeList<'a, Target>>::add_subterm_to_free_list(self, tail); } TermRef::Literal(lvl @ Level::Shallow, cell, Literal::String(ref string)) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); @@ -382,7 +423,7 @@ impl<'b> CodeGenerator<'b> { let atom = self.atom_tbl.build_with(&string); target.push(Target::to_pstr(lvl, atom, cell.get(), true)); - self.subterm_to_instr::(tail, term_loc, is_exposed, &mut target); + self.subterm_to_instr::(tail, term_loc, &mut target); } TermRef::CompleteString(lvl, cell, atom) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); @@ -823,7 +864,6 @@ impl<'b> CodeGenerator<'b> { iter: ChunkedIterator<'a>, conjunct_info: &ConjunctInfo<'a>, code: &mut Code, - is_exposed: bool, ) -> Result<(), CompilationError> { for (chunk_num, _, terms) in iter.rule_body_iter() { for (i, term) in terms.iter().enumerate() { @@ -859,7 +899,7 @@ impl<'b> CodeGenerator<'b> { conjunct_info.perm_vs.vars_above_threshold(i + 1) }; - self.compile_query_line(term, term_loc, code, num_perm_vars, is_exposed); + self.compile_query_line(term, term_loc, code, num_perm_vars); if self.marker.max_reg_allocated() > MAX_ARITY { return Err(CompilationError::ExceededMaxArity); @@ -931,7 +971,7 @@ impl<'b> CodeGenerator<'b> { self.compile_seq_prelude(&conjunct_info, &mut code); let iter = FactIterator::from_rule_head_clause(args); - let mut fact = self.compile_target::(iter, GenContext::Head, false); + let mut fact = self.compile_target::(iter, GenContext::Head); if self.marker.max_reg_allocated() > MAX_ARITY { return Err(CompilationError::ExceededMaxArity); @@ -945,7 +985,7 @@ impl<'b> CodeGenerator<'b> { } let iter = ChunkedIterator::from_rule_body(p1, clauses); - self.compile_seq(iter, &conjunct_info, &mut code, false)?; + self.compile_seq(iter, &conjunct_info, &mut code)?; unsafe_var_marker.mark_unsafe_instrs(&mut code); @@ -994,7 +1034,6 @@ impl<'b> CodeGenerator<'b> { let mut compiled_fact = self.compile_target::( iter, GenContext::Head, - false, ); if self.marker.max_reg_allocated() > MAX_ARITY { @@ -1018,12 +1057,11 @@ impl<'b> CodeGenerator<'b> { term_loc: GenContext, code: &mut Code, num_perm_vars_left: usize, - is_exposed: bool, ) { self.marker.reset_arg(term.arity()); let iter = query_term_post_order_iter(term); - let query = self.compile_target::(iter, term_loc, is_exposed); + let query = self.compile_target::(iter, term_loc); code.extend(query.into_iter()); self.add_conditional_call(code, term, num_perm_vars_left); diff --git a/src/debray_allocator.rs b/src/debray_allocator.rs index 1c81fcdd..98c1bd6e 100644 --- a/src/debray_allocator.rs +++ b/src/debray_allocator.rs @@ -6,7 +6,7 @@ use crate::forms::Level; use crate::instructions::*; use crate::machine::machine_indices::*; use crate::parser::ast::*; -use crate::targets::CompilationTarget; +use crate::targets::*; use crate::temp_v; @@ -24,6 +24,7 @@ pub(crate) struct DebrayAllocator { arity: usize, // 0 if not at head. contents: IndexMap, FxBuildHasher>, in_use: BTreeSet, + free_list: Vec, } impl DebrayAllocator { @@ -182,14 +183,21 @@ impl DebrayAllocator { fn alloc_reg_to_non_var(&mut self) -> usize { let mut final_index = 0; + while let Some(r) = self.free_list.pop() { + if !self.in_use.contains(&r) { + self.in_use.insert(r); + return r; + } + } + for index in self.temp_lb.. { if !self.in_use.contains(&index) { final_index = index; + self.in_use.insert(final_index); break; } } - self.in_use.insert(final_index); self.temp_lb = final_index + 1; final_index } @@ -203,6 +211,15 @@ impl DebrayAllocator { }, } } + + pub fn add_to_free_list(&mut self, r: RegType) { + if let RegType::Temp(r) = r { + if r > self.arity { + self.in_use.remove(&r); + self.free_list.push(r); + } + } + } } impl Allocator for DebrayAllocator { @@ -214,6 +231,7 @@ impl Allocator for DebrayAllocator { bindings: IndexMap::with_hasher(FxBuildHasher::default()), contents: IndexMap::with_hasher(FxBuildHasher::default()), in_use: BTreeSet::new(), + free_list: vec![], } } @@ -356,11 +374,13 @@ impl Allocator for DebrayAllocator { self.bindings.clear(); self.contents.clear(); self.in_use.clear(); + self.free_list.clear(); } fn reset_contents(&mut self) { self.contents.clear(); self.in_use.clear(); + self.free_list.clear(); } fn advance_arg(&mut self) { From 2fe1d2ef533322531bd12f00cbcb59a688781caf Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 10 Jan 2023 17:10:45 +0100 Subject: [PATCH 041/361] FIXED: correctly reify (/)/2. Example: ?- 0 #==> X #= 1/2. %@ clpz:(X in inf..sup) %@ ; false. This addresses #1501. --- src/lib/clpz.pl | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 2734a683..94b3d11c 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3509,9 +3509,12 @@ L #\ R :- (L #\/ R) #/\ #\ (L #/\ R). undefined, created auxiliary constraints are killed, and the "clpz" attribute is removed from auxiliary variables. - For (/)/2, mod/2 and rem/2, we create a skeleton propagator and + For mod/2, div/2, rem/2 etc. we create a skeleton propagator and remember it as an auxiliary constraint. The pskeleton propagator can use the skeleton when the constraint is defined. + + We cannot use a skeleton propagator for (/)/2, since (/)/2 can + fail in cases such as 0 #==> X #= 1/2, where we expect success. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ parse_reified(E, R, D, @@ -3528,7 +3531,7 @@ parse_reified(E, R, D, m(max(A,B)) => [d(D), p(pgeq(R, A)), p(pgeq(R, B)), p(pmax(A,B,R)), a(A,B,R)], m(min(A,B)) => [d(D), p(pgeq(A, R)), p(pgeq(B, R)), p(pmin(A,B,R)), a(A,B,R)], m(abs(A)) => [g(?(R)#>=0), d(D), p(pabs(A, R)), a(A,R)], - m(A/B) => [skeleton(A,B,D,R,prdiv)], + m(A/B) => [p(preified_slash(A,B,D,R)), a(A,B,R)], m(A//B) => [skeleton(A,B,D,R,ptzdiv)], m(A div B) => [skeleton(A,B,D,R,pdiv)], m(A mod B) => [skeleton(A,B,D,R,pmod)], @@ -5832,6 +5835,26 @@ run_propagator(pimpl(X, Y, Ps), MState) --> ; [] ). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +run_propagator(preified_slash(X, Y, D, R), MState) --> + ( Y == 0 -> + kill(MState), + D = 0 + ; nonvar(X), + nonvar(Y) -> + kill(MState), + ( X mod Y =:= 0 -> + D = 1, + R is X // Y + ; D = 0 + ) + ; D == 1 -> + kill(MState), + queue_goal(X/Y #= R) + ; [] + ). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -7756,6 +7779,7 @@ attribute_goal_(reified_geq(DX,X,DY,Y,_,B)) --> attribute_goal_(reified_and(X,_,Y,_,B)) --> [?(X) #/\ ?(Y) #<==> ?(B)]. attribute_goal_(reified_or(X, _, Y, _, B)) --> [?(X) #\/ ?(Y) #<==> ?(B)]. attribute_goal_(reified_not(X, Y)) --> [#\ ?(X) #<==> ?(Y)]. +attribute_goal_(preified_slash(X, Y, _, R)) --> [?(X)/ ?(Y) #= R]. attribute_goal_(pimpl(X, Y, _)) --> [?(X) #==> ?(Y)]. attribute_goal_(pfunction(Op, A, B, R)) --> { Expr =.. [Op,?(A),?(B)] }, From cc420bd31a397c38b7c51dd36da6f50fcf995600 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 10 Jan 2023 22:06:16 +0100 Subject: [PATCH 042/361] FIXED: reification of (xor)/2. Example: ?- A #= 1 xor 0 #<==> R. %@ clpz:(A#=1#<==>R), clpz:(R in 0..1). --- src/lib/clpz.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 94b3d11c..a49bbd50 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3,7 +3,7 @@ Author: Markus Triska E-mail: triska@metalevel.at WWW: https://www.metalevel.at - Copyright (C): 2016-2022 Markus Triska + Copyright (C): 2016-2023 Markus Triska This library provides CLP(ℤ): @@ -3546,7 +3546,7 @@ parse_reified(E, R, D, m(A>>B) => [function(D,>>,A,B,R)], m(A/\B) => [function(D,/\,A,B,R)], m(A\/B) => [function(D,\/,A,B,R)], - m(xor(A, B)) => [skeleton(A,B,D,R,pxor)], + m(xor(A, B)) => [function(D,xor,A,B,R)], g(true) => [g(domain_error(clpz_expression, E))]] ). From 27711094270dc9f8ccf37ddea993b1d848cdd13e Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 10 Jan 2023 22:50:38 +0100 Subject: [PATCH 043/361] use (#)/1 already internally for describing constraint projections --- src/lib/clpz.pl | 84 ++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index a49bbd50..49efcaf8 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -7700,7 +7700,7 @@ attributes_goals([propagator(P, State)|As]) --> with_clpz(G, clpz:G). unwrap_with(_, V, V) :- var(V), !. -unwrap_with(Goal, ?(V0), V) :- !, call(Goal, V0, V). +unwrap_with(Goal, #V0, V) :- !, call(Goal, V0, V). unwrap_with(Goal, Term0, Term) :- Term0 =.. [F|Args0], maplist(unwrap_with(Goal), Args0, Args), @@ -7709,26 +7709,26 @@ unwrap_with(Goal, Term0, Term) :- bare_integer(V0, V) :- ( integer(V0) -> V = V0 ; V = #(V0) ). attribute_goal_(presidual(Goal)) --> [Goal]. -attribute_goal_(pgeq(A,B)) --> [?(A) #>= ?(B)]. -attribute_goal_(pplus(X,Y,Z)) --> [?(X) + ?(Y) #= ?(Z)]. -attribute_goal_(pneq(A,B)) --> [?(A) #\= ?(B)]. -attribute_goal_(ptimes(X,Y,Z)) --> [?(X) * ?(Y) #= ?(Z)]. -attribute_goal_(absdiff_neq(X,Y,C)) --> [abs(?(X) - ?(Y)) #\= C]. -attribute_goal_(x_eq_abs_plus_v(X,V)) --> [?(X) #= abs(?(X)) + ?(V)]. -attribute_goal_(x_neq_y_plus_z(X,Y,Z)) --> [?(X) #\= ?(Y) + ?(Z)]. -attribute_goal_(x_leq_y_plus_c(X,Y,C)) --> [?(X) #=< ?(Y) + C]. -attribute_goal_(ptzdiv(X,Y,Z)) --> [?(X) // ?(Y) #= ?(Z)]. -attribute_goal_(pdiv(X,Y,Z)) --> [?(X) div ?(Y) #= ?(Z)]. -attribute_goal_(prdiv(X,Y,Z)) --> [?(X) / ?(Y) #= ?(Z)]. -attribute_goal_(pexp(X,Y,Z)) --> [?(X) ^ ?(Y) #= ?(Z)]. -attribute_goal_(psign(X,Y)) --> [?(Y) #= sign(?(X))]. -attribute_goal_(pabs(X,Y)) --> [?(Y) #= abs(?(X))]. -attribute_goal_(pmod(X,M,K)) --> [?(X) mod ?(M) #= ?(K)]. -attribute_goal_(prem(X,Y,Z)) --> [?(X) rem ?(Y) #= ?(Z)]. -attribute_goal_(pmax(X,Y,Z)) --> [?(Z) #= max(?(X),?(Y))]. -attribute_goal_(pmin(X,Y,Z)) --> [?(Z) #= min(?(X),?(Y))]. -attribute_goal_(pxor(X,Y,Z)) --> [?(Z) #= xor(?(X), ?(Y))]. -attribute_goal_(ppopcount(X,Y)) --> [?(Y) #= popcount(?(X))]. +attribute_goal_(pgeq(A,B)) --> [#A #>= #B]. +attribute_goal_(pplus(X,Y,Z)) --> [#X + #Y #= #Z]. +attribute_goal_(pneq(A,B)) --> [#A #\= #B]. +attribute_goal_(ptimes(X,Y,Z)) --> [#X * #Y #= #Z]. +attribute_goal_(absdiff_neq(X,Y,C)) --> [abs(#X - #Y) #\= C]. +attribute_goal_(x_eq_abs_plus_v(X,V)) --> [#X #= abs(#X) + #V]. +attribute_goal_(x_neq_y_plus_z(X,Y,Z)) --> [#X #\= #Y + #Z]. +attribute_goal_(x_leq_y_plus_c(X,Y,C)) --> [#X #=< #Y + C]. +attribute_goal_(ptzdiv(X,Y,Z)) --> [#X // #Y #= #Z]. +attribute_goal_(pdiv(X,Y,Z)) --> [#X div #Y #= #Z]. +attribute_goal_(prdiv(X,Y,Z)) --> [#X / #Y #= #Z]. +attribute_goal_(pexp(X,Y,Z)) --> [#X ^ #Y #= #Z]. +attribute_goal_(psign(X,Y)) --> [#Y #= sign(#X)]. +attribute_goal_(pabs(X,Y)) --> [#Y #= abs(#X)]. +attribute_goal_(pmod(X,M,K)) --> [#X mod #M #= #K]. +attribute_goal_(prem(X,Y,Z)) --> [#X rem #Y #= #Z]. +attribute_goal_(pmax(X,Y,Z)) --> [#Z #= max(#X,#Y)]. +attribute_goal_(pmin(X,Y,Z)) --> [#Z #= min(#X,#Y)]. +attribute_goal_(pxor(X,Y,Z)) --> [#Z #= xor(#X, #Y)]. +attribute_goal_(ppopcount(X,Y)) --> [#Y #= popcount(#X)]. attribute_goal_(scalar_product_neq(Cs,Vs,C)) --> [Left #\= Right], { scalar_product_left_right([-1|Cs], [C|Vs], Left, Right) }. @@ -7758,41 +7758,41 @@ attribute_goal_(rel_tuple(R, Tuple)) --> attribute_goal_(pzcompare(O,A,B)) --> [zcompare(O,A,B)]. % reified constraints attribute_goal_(reified_in(V, D, B)) --> - [V in Drep #<==> ?(B)], + [V in Drep #<==> #B], { domain_to_drep(D, Drep) }. attribute_goal_(reified_tuple_in(Tuple, R, B)) --> { get_attr(R, clpz_relation, Rel) }, - [tuples_in([Tuple], Rel) #<==> ?(B)]. + [tuples_in([Tuple], Rel) #<==> #B]. attribute_goal_(kill_reified_tuples(_,_,_)) --> []. attribute_goal_(tuples_not_in(_,_,_)) --> []. -attribute_goal_(reified_fd(V,B)) --> [finite_domain(V) #<==> ?(B)]. +attribute_goal_(reified_fd(V,B)) --> [finite_domain(V) #<==> #B]. attribute_goal_(pskeleton(X,Y,D,_,Z,F)) --> { Prop =.. [F,X,Y,Z], phrase(attribute_goal_(Prop), Goals), list_goal(Goals, Goal) }, - [?(D) #= 1 #==> Goal, ?(Y) #\= 0 #==> ?(D) #= 1]. + [#D #= 1 #==> Goal, #Y #\= 0 #==> #D #= 1]. attribute_goal_(reified_neq(DX,X,DY,Y,_,B)) --> - conjunction(DX, DY, ?(X) #\= ?(Y), B). + conjunction(DX, DY, #X #\= #Y, B). attribute_goal_(reified_eq(DX,X,DY,Y,_,B)) --> - conjunction(DX, DY, ?(X) #= ?(Y), B). + conjunction(DX, DY, #X #= #Y, B). attribute_goal_(reified_geq(DX,X,DY,Y,_,B)) --> - conjunction(DX, DY, ?(X) #>= ?(Y), B). -attribute_goal_(reified_and(X,_,Y,_,B)) --> [?(X) #/\ ?(Y) #<==> ?(B)]. -attribute_goal_(reified_or(X, _, Y, _, B)) --> [?(X) #\/ ?(Y) #<==> ?(B)]. -attribute_goal_(reified_not(X, Y)) --> [#\ ?(X) #<==> ?(Y)]. -attribute_goal_(preified_slash(X, Y, _, R)) --> [?(X)/ ?(Y) #= R]. -attribute_goal_(pimpl(X, Y, _)) --> [?(X) #==> ?(Y)]. + conjunction(DX, DY, #X #>= #Y, B). +attribute_goal_(reified_and(X,_,Y,_,B)) --> [#X #/\ #Y #<==> #B]. +attribute_goal_(reified_or(X, _, Y, _, B)) --> [#X #\/ #Y #<==> #B]. +attribute_goal_(reified_not(X, Y)) --> [#\ #X #<==> #Y]. +attribute_goal_(preified_slash(X, Y, _, R)) --> [#X/ #Y #= R]. +attribute_goal_(pimpl(X, Y, _)) --> [#X #==> #Y]. attribute_goal_(pfunction(Op, A, B, R)) --> - { Expr =.. [Op,?(A),?(B)] }, - [?(R) #= Expr]. + { Expr =.. [Op,#A,#B] }, + [#R #= Expr]. attribute_goal_(pfunction(Op, A, R)) --> - { Expr =.. [Op,?(A)] }, - [?(R) #= Expr]. + { Expr =.. [Op,#A] }, + [#R #= Expr]. conjunction(A, B, G, D) --> - ( { A == 1, B == 1 } -> [G #<==> ?(D)] - ; { A == 1 } -> [(?(B) #/\ G) #<==> ?(D)] - ; { B == 1 } -> [(?(A) #/\ G) #<==> ?(D)] - ; [(?(A) #/\ ?(B) #/\ G) #<==> ?(D)] + ( { A == 1, B == 1 } -> [G #<==> #D] + ; { A == 1 } -> [(#B #/\ G) #<==> #D] + ; { B == 1 } -> [(#A #/\ G) #<==> #D] + ; [(#A #/\ #B #/\ G) #<==> #D] ). original_goal(original_goal(State, Goal)) --> @@ -7838,7 +7838,7 @@ scalar_plusterm([CV|CVs], T) :- plusterm_(CV, T0, T0+T) :- coeff_var_term(CV, T). -coeff_var_term(C-V, T) :- ( C =:= 1 -> T = ?(V) ; T = C * ?(V) ). +coeff_var_term(C-V, T) :- ( C =:= 1 -> T = #V ; T = C * #V ). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Reified predicates for use with predicates from library(reif). From 73a1ee59fa491e8984c5b1554e033172d7dcad3b Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 10 Jan 2023 22:57:14 +0100 Subject: [PATCH 044/361] replace several more instances of ?/1 by (#)/1 --- src/lib/clpz.pl | 72 ++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 49efcaf8..748a8cd4 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -1917,7 +1917,7 @@ label([], _, Selection, Order, Choice, Optim0, Consistency, Vars) :- exprs_singlevars([], []). exprs_singlevars([E|Es], [SV|SVs]) :- E =.. [F,Expr], - ?(Single) #= Expr, + #Single #= Expr, SV =.. [F,Single], exprs_singlevars(Es, SVs). @@ -2316,7 +2316,7 @@ coeff_int_linsum(C, I, S0, S) :- S is S0 + C*I. sum([], _, Sum, Op, Value) :- call(Op, Sum, Value). sum([C|Cs], [X|Xs], Acc, Op, Value) :- - ?(NAcc) #= Acc + C* ?(X), + #NAcc #= Acc + C* #X, sum(Cs, Xs, NAcc, Op, Value). multiples([], [], _). @@ -2325,7 +2325,7 @@ multiples([C|Cs], [V|Vs], Left) :- ( N =\= 1, gcd(C,N) =:= 1 -> gcd(Cs, N, GCD0), gcd(Left, GCD0, GCD), - ( GCD > 1 -> ?(V) #= GCD * ?(_) + ( GCD > 1 -> #V #= GCD * #_ ; true ) ; true @@ -2563,14 +2563,14 @@ parse_clpz(E, R, m(A*B) => [p(ptimes(A, B, R))], m(A-B) => [p(pplus(R,B,A))], m(-A) => [p(ptimes(-1,A,R))], - m(max(A,B)) => [g(A #=< ?(R)), g(B #=< R), p(pmax(A, B, R))], - m(min(A,B)) => [g(A #>= ?(R)), g(B #>= R), p(pmin(A, B, R))], + m(max(A,B)) => [g(A #=< #R), g(B #=< R), p(pmax(A, B, R))], + m(min(A,B)) => [g(A #>= #R), g(B #>= R), p(pmin(A, B, R))], m(A mod B) => [g(B #\= 0), p(pmod(A, B, R))], m(A rem B) => [g(B #\= 0), p(prem(A, B, R))], - m(abs(A)) => [g(?(R) #>= 0), p(pabs(A, R))], + m(abs(A)) => [g(#R #>= 0), p(pabs(A, R))], m(A/B) => [g(B #\= 0), p(prdiv(A, B, R))], m(A//B) => [g(B #\= 0), p(ptzdiv(A, B, R))], - m(A div B) => [g(?(R) #= (A - (A mod B)) // B)], + m(A div B) => [g(#R #= (A - (A mod B)) // B)], m(A^B) => [p(pexp(A, B, R))], m(sign(A)) => [g(R in -1..1), p(psign(A, R))], % bitwise operations @@ -2765,7 +2765,7 @@ matches([ m_c(any(X) #>= any(Y), left_right_linsum_const(X, Y, Cs, Vs, Const)) => [g(( Cs = [1], Vs = [A] -> geq(A, Const) ; Cs = [-1], Vs = [A] -> Const1 is -Const, geq(Const1, A) - ; Cs = [1,1], Vs = [A,B] -> ?(A) + ?(B) #= ?(S), geq(S, Const) + ; Cs = [1,1], Vs = [A,B] -> #A + #B #= #S, geq(S, Const) ; Cs = [1,-1], Vs = [A,B] -> ( Const =:= 0 -> geq(A, B) ; C1 is -Const, @@ -2777,13 +2777,13 @@ matches([ propagator_init_trigger(x_leq_y_plus_c(A, B, C1)) ) ; Cs = [-1,-1], Vs = [A,B] -> - ?(A) + ?(B) #= ?(S), Const1 is -Const, geq(Const1, S) + #A + #B #= #S, Const1 is -Const, geq(Const1, S) ; scalar_product_(#>=, Cs, Vs, Const) ))], m(any(X) - any(Y) #>= integer(C)) => [d(X, X1), d(Y, Y1), g(C1 is -C), p(x_leq_y_plus_c(Y1, X1, C1))], m(integer(X) #>= any(Z) + integer(A)) => [g(C is X - A), r(C, Z)], m(abs(any(X)-any(Y)) #>= any(Z)) => - [d(X, X1), d(Y, Y1), d(Z, Z1), g((abs(?(A))#= ?(B),Y1+A#=X1,Z1#== integer(I)) => [d(X, RX), g((I>0 -> I1 is -I, RX in inf..I1 \/ I..sup; true))], m(integer(I) #>= abs(any(X))) => [d(X, RX), g(I>=0), g(I1 is -I), g(RX in I1..I)], m(any(X) #>= any(Y)) => [d(X, RX), d(Y, RY), g(geq(RX, RY))], @@ -3530,7 +3530,7 @@ parse_reified(E, R, D, m(-A) => [d(D), p(ptimes(-1,A,R)), a(R)], m(max(A,B)) => [d(D), p(pgeq(R, A)), p(pgeq(R, B)), p(pmax(A,B,R)), a(A,B,R)], m(min(A,B)) => [d(D), p(pgeq(A, R)), p(pgeq(B, R)), p(pmin(A,B,R)), a(A,B,R)], - m(abs(A)) => [g(?(R)#>=0), d(D), p(pabs(A, R)), a(A,R)], + m(abs(A)) => [g(#R#>=0), d(D), p(pabs(A, R)), a(A,R)], m(A/B) => [p(preified_slash(A,B,D,R)), a(A,B,R)], m(A//B) => [skeleton(A,B,D,R,ptzdiv)], m(A div B) => [skeleton(A,B,D,R,pdiv)], @@ -3670,7 +3670,7 @@ reify_(tuples_in(Tuples, Relation), B) --> { maplist(relation_tuple_b_prop(Relation), Tuples, Bs, Ps), maplist(monotonic, Bs, Bs1), fold_statement(conjunction, Bs1, And), - ?(B) #<==> And }, + #B #<==> And }, propagator_init_trigger([B], tuples_not_in(Tuples, Relation, B)), kill_reified_tuples(Bs, Ps, Bs), list(Ps), @@ -3772,7 +3772,7 @@ conjunction(E, Conj, Conj #/\ E). disjunction(E, Disj, Disj #\/ E). -var_eq(V, N, ?(V) #= N). +var_eq(V, N, #V #= N). % Match variables to created skeleton. @@ -4274,7 +4274,7 @@ lex_chain_(Prop, Ls, Prev, Ls) :- lex_le([], []). lex_le([V1|V1s], [V2|V2s]) :- - ?(V1) #=< ?(V2), + #V1 #=< #V2, ( integer(V1) -> ( integer(V2) -> ( V1 =:= V2 -> lex_le(V1s, V2s) ; true ) @@ -6559,7 +6559,7 @@ element_domain(V, VD) :- element_([], _, _, _). element_([I|Is], N0, N, V) :- - ?(I) #\= ?(V) #==> ?(N) #\= N0, + #I #\= #V #==> #N #\= N0, N1 is N0 + 1, element_(Is, N1, N, V). @@ -7103,25 +7103,25 @@ cumulative(Tasks, Options) :- fully_elastic_relaxation(Tasks, Limit) :- maplist(task_duration_consumption, Tasks, Ds, Cs), maplist(area, Ds, Cs, As), - sum(As, #=, ?(Area)), - ?(MinTime) #= (Area + Limit - 1) // Limit, + sum(As, #=, #Area), + #MinTime #= (Area + Limit - 1) // Limit, tasks_minstart_maxend(Tasks, MinStart, MaxEnd), MaxEnd #>= MinStart + MinTime. task_duration_consumption(task(_,D,_,C,_), D, C). -area(X, Y, Area) :- ?(Area) #= ?(X) * ?(Y). +area(X, Y, Area) :- #Area #= #X * #Y. tasks_minstart_maxend(Tasks, Start, End) :- maplist(task_start_end, Tasks, [Start0|Starts], [End0|Ends]), foldl(min_, Starts, Start0, Start), foldl(max_, Ends, End0, End). -max_(E, M0, M) :- ?(M) #= max(E, M0). +max_(E, M0, M) :- #M #= max(E, M0). -min_(E, M0, M) :- ?(M) #= min(E, M0). +min_(E, M0, M) :- #M #= min(E, M0). -task_start_end(task(Start,_,End,_,_), ?(Start), ?(End)). +task_start_end(task(Start,_,End,_,_), #Start, #End). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - All time slots must respect the resource limit. @@ -7136,8 +7136,8 @@ resource_limit(T0, T, Tasks, Bss, L) :- task_bs(Task, InfStart-Bs) :- Task = task(Start,D,End,_,_Id), - ?(D) #> 0, - ?(End) #= ?(Start) + ?(D), + #D #> 0, + #End #= #Start + #D, maplist(finite_domain, [End,Start,D]), fd_inf(Start, InfStart), fd_sup(End, SupEnd), @@ -7147,20 +7147,20 @@ task_bs(Task, InfStart-Bs) :- task_running([], _, _, _). task_running([B|Bs], Start, End, T) :- - ((T #>= Start) #/\ (T #< End)) #<==> ?(B), + ((T #>= Start) #/\ (T #< End)) #<==> #B, T1 is T + 1, task_running(Bs, Start, End, T1). contribution_at(T, Task, Offset-Bs, Contribution) :- Task = task(Start,_,End,C,_), - ?(C) #>= 0, + #C #>= 0, fd_inf(Start, InfStart), fd_sup(End, SupEnd), ( T < InfStart -> Contribution = 0 ; T >= SupEnd -> Contribution = 0 ; Index is T - Offset, nth0(Index, Bs, B), - ?(Contribution) #= B*C + #Contribution #= B*C ). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -7188,10 +7188,10 @@ non_overlapping_(A, B) :- a_not_in_b(B, A). a_not_in_b([_,AX,AW,AY,AH], [_,BX,BW,BY,BH]) :- - ?(AX) #=< ?(BX) #/\ ?(BX) #< ?(AX) + ?(AW) #==> - ?(AY) + ?(AH) #=< ?(BY) #\/ ?(BY) + ?(BH) #=< ?(AY), - ?(AY) #=< ?(BY) #/\ ?(BY) #< ?(AY) + ?(AH) #==> - ?(AX) + ?(AW) #=< ?(BX) #\/ ?(BX) + ?(BW) #=< ?(AX). + #AX #=< #BX #/\ #BX #< #AX + #AW #==> + #AY + #AH #=< #BY #\/ #BY + #BH #=< #AY, + #AY #=< #BY #/\ #BY #< #AY + #AH #==> + #AX + #AW #=< #BX #\/ #BX + #BW #=< #AX. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -7348,7 +7348,7 @@ exprs_values([E0|Es], [V|Vs]) --> { term_variables(E0, EVs0), copy_term(E0, E), term_variables(E, EVs), - ?(V) #= E }, + #V #= E }, match_variables(EVs0, EVs), exprs_values(Es, Vs). @@ -7398,7 +7398,7 @@ source(source(_)). sink(sink(_)). -monotonic(Var, ?(Var)). +monotonic(Var, #Var). arc_normalized(Cs, Arc0, Arc) :- arc_normalized_(Arc0, Cs, Arc). @@ -7457,9 +7457,9 @@ zcompare(Order, A, B) :- propagator_init_trigger([A,B], pzcompare(Order, A, B)) ). -zcompare_(=, A, B) :- ?(A) #= ?(B). -zcompare_(<, A, B) :- ?(A) #< ?(B). -zcompare_(>, A, B) :- ?(A) #> ?(B). +zcompare_(=, A, B) :- #A #= #B. +zcompare_(<, A, B) :- #A #< #B. +zcompare_(>, A, B) :- #A #> #B. %% chain(+Relation, +Zs) % @@ -7492,7 +7492,7 @@ chain_relation(#=<). chain_relation(#>). chain_relation(#>=). -chain(Relation, X, Prev, X) :- call(Relation, ?(Prev), ?(X)). +chain(Relation, X, Prev, X) :- call(Relation, #Prev, #X). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 542b9e1976ee857880b71bd0f6c44b65dccb6caa Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 10 Jan 2023 22:59:11 +0100 Subject: [PATCH 045/361] rely on newly available operator notation for (#)/1 --- src/lib/clpz.pl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 748a8cd4..207499b2 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -922,8 +922,8 @@ expressions with the functor `(?)/1` or `(#)/1`. For example: ?- assertz(clpz:monotonic). true. -?- #(X) #= #(Y) + #(Z). -#(Y)+ #(Z)#= #(X). +?- #X #= #Y + #Z. + clpz:(#Y+ #Z#= #X). ?- X #= 2, X = 1+1. ERROR: Arguments are not sufficiently instantiated @@ -2556,7 +2556,7 @@ parse_clpz(E, R, g(constrain_to_integer(E)), g(E = R)], g(integer(E)) => [g(R = E)], ?(E) => [g(must_be_fd_integer(E)), g(R = E)], - #(E) => [g(must_be_fd_integer(E)), g(R = E)], + #E => [g(must_be_fd_integer(E)), g(R = E)], m(A+B) => [p(pplus(A, B, R))], % power_var_num/3 must occur before */2 to be useful g(power_var_num(E, V, N)) => [p(pexp(V, N, R))], @@ -2614,7 +2614,7 @@ parse_matcher(E, R, Matcher, Clause) :- parse_condition(g(Goal), E, E) --> [Goal, !]. parse_condition(?(E), _, ?(E)) --> [!]. -parse_condition(#(E), _, #(E)) --> [!]. +parse_condition(#E, _, #E) --> [!]. parse_condition(m(Match), _, Match0) --> [!], { copy_term(Match, Match0), @@ -2874,7 +2874,7 @@ matcher(m_c(Matcher,Cond), Gs) --> ). match(any(A), T) --> [A = T]. -match(var(V), T) --> [( nonvar(T), ( T = ?(Var) ; T = #(Var) ) -> +match(var(V), T) --> [( nonvar(T), ( T = ?(Var) ; T = #Var ) -> must_be_fd_integer(Var), V = Var ; v_or_i(T), V = T )]. @@ -2937,7 +2937,7 @@ expr_conds(E, E) --> [integer(E)], { var(E), !, \+ monotonic }. expr_conds(E, E) --> { integer(E) }. expr_conds(?(E), E) --> [integer(E)]. -expr_conds(#(E), E) --> [integer(E)]. +expr_conds(#E, E) --> [integer(E)]. expr_conds(-E0, -E) --> expr_conds(E0, E). expr_conds(abs(E0), abs(E)) --> expr_conds(E0, E). expr_conds(A0+B0, A+B) --> expr_conds(A0, A), expr_conds(B0, B). @@ -3118,7 +3118,7 @@ user:goal_expansion(Goal0, Goal) :- linsum(X, S, S) --> { var(X), !, non_monotonic(X) }, [vn(X,1)]. linsum(I, S0, S) --> { integer(I), S is S0 + I }. linsum(?(X), S, S) --> { must_be_fd_integer(X) }, [vn(X,1)]. -linsum(#(X), S, S) --> { must_be_fd_integer(X) }, [vn(X,1)]. +linsum(#X, S, S) --> { must_be_fd_integer(X) }, [vn(X,1)]. linsum(-A, S0, S) --> mulsum(A, -1, S0, S). linsum(N*A, S0, S) --> { integer(N) }, !, mulsum(A, N, S0, S). linsum(A*N, S0, S) --> { integer(N) }, !, mulsum(A, N, S0, S). @@ -3523,7 +3523,7 @@ parse_reified(E, R, D, g(constrain_to_integer(E)), g(R = E), g(D=1)], g(integer(E)) => [g(R=E), g(D=1)], ?(E) => [g(must_be_fd_integer(E)), g(R=E), g(D=1)], - #(E) => [g(must_be_fd_integer(E)), g(R=E), g(D=1)], + #E => [g(must_be_fd_integer(E)), g(R=E), g(D=1)], m(A+B) => [d(D), p(pplus(A,B,R)), a(A,B,R)], m(A*B) => [d(D), p(ptimes(A,B,R)), a(A,B,R)], m(A-B) => [d(D), p(pplus(R,B,A)), a(A,B,R)], @@ -3576,7 +3576,7 @@ parse_reified(E, R, D, Matcher, Clause) :- reified_condition(g(Goal), E, E, []) --> [{Goal}, !]. reified_condition(?(E), _, ?(E), []) --> [!]. -reified_condition(#(E), _, #(E), []) --> [!]. +reified_condition(#E, _, #E, []) --> [!]. reified_condition(m(Match), _, Match0, Ds) --> [!], { copy_term(Match, Match0), @@ -3640,7 +3640,7 @@ reify(Expr, B, Ps) :- reifiable(E) :- var(E), non_monotonic(E). reifiable(E) :- integer(E), E in 0..1. reifiable(?(E)) :- must_be_fd_integer(E). -reifiable(#(E)) :- must_be_fd_integer(E). +reifiable(#E) :- must_be_fd_integer(E). reifiable(V in _) :- fd_variable(V). reifiable(Expr) :- Expr =.. [Op,Left,Right], @@ -3661,7 +3661,7 @@ reify(E, B) --> { B in 0..1 }, reify_(E, B). reify_(E, B) --> { var(E), !, E = B }. reify_(E, B) --> { integer(E), E = B }. reify_(?(B), B) --> []. -reify_(#(B), B) --> []. +reify_(#B, B) --> []. reify_(V in Drep, B) --> { drep_to_domain(Drep, Dom) }, propagator_init_trigger(reified_in(V,Dom,B)), @@ -7706,7 +7706,7 @@ unwrap_with(Goal, Term0, Term) :- maplist(unwrap_with(Goal), Args0, Args), Term =.. [F|Args]. -bare_integer(V0, V) :- ( integer(V0) -> V = V0 ; V = #(V0) ). +bare_integer(V0, V) :- ( integer(V0) -> V = V0 ; V = #V0 ). attribute_goal_(presidual(Goal)) --> [Goal]. attribute_goal_(pgeq(A,B)) --> [#A #>= #B]. From 0c4d93f01f77b2179f93fa8ea57fb7bbc7badb2b Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 10 Jan 2023 18:24:38 -0700 Subject: [PATCH 046/361] remove add_term_to_free_list from AddToList (#1684) --- src/codegen.rs | 12 ------------ src/debray_allocator.rs | 6 ++---- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index dc03a19a..79574005 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -243,22 +243,14 @@ fn trim_structure_by_last_arg(instr: &mut Instruction, last_arg: &Term) { } trait AddToFreeList<'a, Target: CompilationTarget<'a>> { - fn add_term_to_free_list(&mut self, r: RegType); fn add_subterm_to_free_list(&mut self, term: &Term); } impl<'a, 'b> AddToFreeList<'a, FactInstruction> for CodeGenerator<'b> { - #[inline(always)] - fn add_term_to_free_list(&mut self, r: RegType) { - self.marker.add_to_free_list(r); - } - fn add_subterm_to_free_list(&mut self, _term: &Term) {} } impl<'a, 'b> AddToFreeList<'a, QueryInstruction> for CodeGenerator<'b> { - fn add_term_to_free_list(&mut self, _r: RegType) {} - #[inline(always)] fn add_subterm_to_free_list(&mut self, term: &Term) { if let Some(cell) = structure_cell(term) { @@ -382,8 +374,6 @@ impl<'b> CodeGenerator<'b> { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); target.push(Target::to_structure(name, terms.len(), cell.get())); - as AddToFreeList<'a, Target>>::add_term_to_free_list(self, cell.get()); - if let Some(instr) = target.last_mut() { if let Some(term) = terms.last() { trim_structure_by_last_arg(instr, term); @@ -402,8 +392,6 @@ impl<'b> CodeGenerator<'b> { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); target.push(Target::to_list(lvl, cell.get())); - as AddToFreeList<'a, Target>>::add_term_to_free_list(self, cell.get()); - self.subterm_to_instr::(head, term_loc, &mut target); self.subterm_to_instr::(tail, term_loc, &mut target); diff --git a/src/debray_allocator.rs b/src/debray_allocator.rs index 98c1bd6e..51ac8f70 100644 --- a/src/debray_allocator.rs +++ b/src/debray_allocator.rs @@ -214,10 +214,8 @@ impl DebrayAllocator { pub fn add_to_free_list(&mut self, r: RegType) { if let RegType::Temp(r) = r { - if r > self.arity { - self.in_use.remove(&r); - self.free_list.push(r); - } + self.in_use.remove(&r); + self.free_list.push(r); } } } From c5caa9d311225ee0b88fc350ae48c68684b5de11 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 11 Jan 2023 17:22:38 +0100 Subject: [PATCH 047/361] ADDED: sign/1 is now reifiable. This addresses #1500. --- src/lib/clpz.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 207499b2..4871851b 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3542,6 +3542,7 @@ parse_reified(E, R, D, m(msb(A)) => [function(D,msb,A,R)], m(lsb(A)) => [function(D,lsb,A,R)], m(popcount(A)) => [function(D,popcount,A,R)], + m(sign(A)) => [function(D,sign,A,R)], m(A< [function(D,<<,A,B,R)], m(A>>B) => [function(D,>>,A,B,R)], m(A/\B) => [function(D,/\,A,B,R)], From f213956ceb76a56f5353db754ff57815909570dc Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 11 Jan 2023 17:23:53 +0100 Subject: [PATCH 048/361] use (#)/1 --- src/lib/clpz.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 4871851b..de522ab6 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -2310,7 +2310,7 @@ single_value(V, V) :- var(V), !, non_monotonic(V). single_value(V, V) :- integer(V). single_value(?(V), V) :- fd_variable(V). -coeff_var_plusterm(C, V, T0, T0+(C* ?(V))). +coeff_var_plusterm(C, V, T0, T0+(C* #V)). coeff_int_linsum(C, I, S0, S) :- S is S0 + C*I. From 3a4aa2a54127e2b633aadabf2f75930e6d0f1dbf Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Thu, 12 Jan 2023 23:46:57 -0700 Subject: [PATCH 049/361] tighten deallocate truncation of stack (#1686) --- src/machine/machine_state_impl.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index 00e17649..a77fda72 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -2784,8 +2784,11 @@ impl MachineState { self.cp = frame.prelude.cp; self.e = frame.prelude.e; - if e > self.b { - self.stack.truncate(e); + if self.e > self.b { + let frame = self.stack.index_and_frame(self.e); + let size = AndFrame::size_of(frame.prelude.univ_prelude.num_cells); + + self.stack.truncate(self.e + size); } self.p += 1; From f9e3bdb6b0bed0aa2eed38cde57cae96c8501afb Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Fri, 13 Jan 2023 18:34:29 -0700 Subject: [PATCH 050/361] restore free list usage on structures in facts without crashing lgtunit loader --- src/codegen.rs | 14 ++++++++++++++ src/debray_allocator.rs | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/src/codegen.rs b/src/codegen.rs index 79574005..31219e1a 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -243,14 +243,22 @@ fn trim_structure_by_last_arg(instr: &mut Instruction, last_arg: &Term) { } trait AddToFreeList<'a, Target: CompilationTarget<'a>> { + fn add_term_to_free_list(&mut self, r: RegType); fn add_subterm_to_free_list(&mut self, term: &Term); } impl<'a, 'b> AddToFreeList<'a, FactInstruction> for CodeGenerator<'b> { + fn add_term_to_free_list(&mut self, r: RegType) { + self.marker.add_to_free_list(r); + } + fn add_subterm_to_free_list(&mut self, _term: &Term) {} } impl<'a, 'b> AddToFreeList<'a, QueryInstruction> for CodeGenerator<'b> { + #[inline(always)] + fn add_term_to_free_list(&mut self, _r: RegType) {} + #[inline(always)] fn add_subterm_to_free_list(&mut self, term: &Term) { if let Some(cell) = structure_cell(term) { @@ -374,6 +382,8 @@ impl<'b> CodeGenerator<'b> { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); target.push(Target::to_structure(name, terms.len(), cell.get())); + as AddToFreeList<'a, Target>>::add_term_to_free_list(self, cell.get()); + if let Some(instr) = target.last_mut() { if let Some(term) = terms.last() { trim_structure_by_last_arg(instr, term); @@ -392,6 +402,8 @@ impl<'b> CodeGenerator<'b> { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); target.push(Target::to_list(lvl, cell.get())); + as AddToFreeList<'a, Target>>::add_term_to_free_list(self, cell.get()); + self.subterm_to_instr::(head, term_loc, &mut target); self.subterm_to_instr::(tail, term_loc, &mut target); @@ -965,6 +977,8 @@ impl<'b> CodeGenerator<'b> { return Err(CompilationError::ExceededMaxArity); } + self.marker.reset_free_list(); + let mut unsafe_var_marker = UnsafeVarMarker::new(); if !fact.is_empty() { diff --git a/src/debray_allocator.rs b/src/debray_allocator.rs index 51ac8f70..e9cc73c7 100644 --- a/src/debray_allocator.rs +++ b/src/debray_allocator.rs @@ -218,6 +218,10 @@ impl DebrayAllocator { self.free_list.push(r); } } + + pub fn reset_free_list(&mut self) { + self.free_list.clear(); + } } impl Allocator for DebrayAllocator { From f8e6e0252d7d7470be63e72abddac48d8eed4a44 Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Wed, 18 Jan 2023 19:08:19 +0100 Subject: [PATCH 051/361] Use lastest 1.xx Rust version in Docker build With the previous Rust version 1.61, the build fails with > error[E0658]: use of unstable library feature 'scoped_threads' This has been "stabilized" in Rust 1.63. To avoid the hassle of manually updating the version, we can just default to the latest minor release. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 661ff5d0..0c1195ac 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # See https://github.com/LukeMathWalker/cargo-chef -ARG RUST_VERSION=1.61-buster +ARG RUST_VERSION=1-buster FROM rust:${RUST_VERSION} as planner WORKDIR /scryer-prolog RUN cargo install cargo-chef From 46d1e3bee31625d9bf8bb3cd8eaf27796fa687ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Thu, 19 Jan 2023 21:15:25 +0100 Subject: [PATCH 052/361] Migrate from Markdown to Djot --- src/lib/assoc.pl | 6 +- src/lib/clpb.pl | 49 +++++++----- src/lib/files.pl | 38 ++++----- src/lib/http/http_open.pl | 8 +- src/lib/iso_ext.pl | 72 ++++++++++------- src/lib/lists.pl | 131 ++++++++++++++++++------------- src/lib/ordsets.pl | 66 ++++++++-------- src/lib/random.pl | 4 +- src/lib/sockets.pl | 20 ++--- src/lib/ugraphs.pl | 157 +++++++++++++++++++++++--------------- src/lib/uuid.pl | 26 ++++--- 11 files changed, 334 insertions(+), 243 deletions(-) diff --git a/src/lib/assoc.pl b/src/lib/assoc.pl index 80edb005..cb3a6f62 100644 --- a/src/lib/assoc.pl +++ b/src/lib/assoc.pl @@ -172,7 +172,7 @@ gen_assoc_(Key, t(_,_,_,_,R), Val) :- % % True if Key-Value is an association in Assoc. % -% Throws error: type_error(assoc, Assoc) if Assoc is not an association list. +% Throws error: type\_error(assoc, Assoc) if Assoc is not an association list. get_assoc(Key, Assoc, Val) :- must_be(assoc, Assoc), @@ -218,7 +218,7 @@ get_assoc(>, Key, V, L, R, Val, V, L, NR, NVal) :- % Create an association from a list Pairs of Key-Value pairs. List % must not contain duplicate keys. % -% Throws error: domain_error(unique_key_pairs, List) if List contains duplicate keys +% Throws error: domain\_error(unique\_key\_pairs, List) if List contains duplicate keys list_to_assoc(List, Assoc) :- ( List = [] -> Assoc = t @@ -249,7 +249,7 @@ list_to_assoc(N, List, More, Depth, t(K,V,Balance,L,R)) :- % pairs. The pairs must occur in strictly ascending order of % their keys. % -% Throws error: domain_error(key_ordered_pairs, List) if pairs are not ordered. +% Throws error: domain\_error(key\_ordered\_pairs, List) if pairs are not ordered. ord_list_to_assoc(Sorted, Assoc) :- ( Sorted = [] -> Assoc = t diff --git a/src/lib/clpb.pl b/src/lib/clpb.pl index e2e9ce66..72844408 100644 --- a/src/lib/clpb.pl +++ b/src/lib/clpb.pl @@ -105,53 +105,60 @@ goal_expansion(del_attr(Var, Module), (var(Var) -> put_atts(Var, -Access);true)) Access =.. [Module,_]. -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/** Each CLP(B) variable belongs to exactly one BDD. Each CLP(B) variable gets an attribute (in module "clpb") of the form: - index_root(Index,Root) + ``` + index_root(Index,Root) + ``` where Index is the variable's unique integer index, and Root is the root of the BDD that the variable belongs to. - Each CLP(B) variable also gets an attribute in module clpb_hash: an + Each CLP(B) variable also gets an attribute in module `clpb_hash`: an association table node(LID,HID) -> Node, to keep the BDD reduced. The association table of each variable must be rebuilt on occasion to remove nodes that are no longer reachable. We rebuild the association tables of involved variables after BDDs are merged to build a new root. This only serves to reclaim memory: Keeping a node in a local table even when it no longer occurs in any BDD does - not affect the solver's correctness. However, apply_shortcut/4 + not affect the solver's correctness. However, `apply_shortcut/4` relies on the invariant that every node that occurs in the relevant BDDs is also registered in the table of its branching variable. - A root is a logical variable with a single attribute ("clpb_bdd") + A root is a logical variable with a single attribute ("clpb\_bdd") of the form: - Sat-BDD + ``` + Sat-BDD + ``` where Sat is the SAT formula (in original form) that corresponds to BDD. Sat is necessary to rebuild the BDD after variable aliasing, - and to project all remaining constraints to a list of sat/1 goals. + and to project all remaining constraints to a list of `sat/1` goals. Finally, a BDD is either: - *) The integers 0 or 1, denoting false and true, respectively, or - *) A node of the form + * The integers 0 or 1, denoting false and true, respectively, or + * A node of the form - node(ID, Var, Low, High, Aux) - Where ID is the node's unique integer ID, Var is the - node's branching variable, and Low and High are the - node's low (Var = 0) and high (Var = 1) children. Aux - is a free variable, one for each node, that can be used - to attach attributes and store intermediate results. + ``` + node(ID, Var, Low, High, Aux) + ``` + + Where ID is the node's unique integer ID, Var is the + node's branching variable, and Low and High are the + node's low (Var = 0) and high (Var = 1) children. Aux + is a free variable, one for each node, that can be used + to attach attributes and store intermediate results. Variable aliasing is treated as a conjunction of corresponding SAT formulae. You should think of CLP(B) as a potentially vast collection of BDDs that can range from small to gigantic in size, and which can merge. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +*/ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Type checking. @@ -1108,7 +1115,7 @@ indomain(1). % % Examples: % -% == +% ``` % ?- sat(A =< B), Vs = [A,B], sat_count(+[1|Vs], Count). % Vs = [A, B], % Count = 3, @@ -1120,7 +1127,7 @@ indomain(1). % Vs = [...], % CountOr = 1329227995784915872903807060280344575, % CountAnd = 1. -% == +% ``` @@ -1248,7 +1255,7 @@ random_bindings(VNum, Node) --> % linear objective function over Boolean variables Vs with integer % coefficients Weights. This predicate assigns 0 and 1 to the % variables in Vs such that all stated constraints are satisfied, and -% Maximum is the maximum of sum(Weight_i*V_i) over all admissible +% Maximum is the maximum of `sum(Weight_i*V_i)` over all admissible % assignments. On backtracking, all admissible assignments that % attain the optimum are generated. % @@ -1257,10 +1264,10 @@ random_bindings(VNum, Node) --> % % Example: % -% == +% ``` % ?- sat(A#B), weighted_maximum([1,2,1], [A,B,C], Maximum). % A = 0, B = 1, C = 1, Maximum = 3. -% == +% ``` weighted_maximum(Ws, Vars, Max) :- must_be(list(integer), Ws), diff --git a/src/lib/files.pl b/src/lib/files.pl index e920b5bd..90c1307d 100644 --- a/src/lib/files.pl +++ b/src/lib/files.pl @@ -1,12 +1,12 @@ /** Predicates for reasoning about files and directories. In this library, directories and files are represented as -*lists of characters*. This is an ideal representation: +_lists of characters_. This is an ideal representation: * Lists of characters can be conveniently reasoned about with DCGs - and built-in Prolog predicates from library(lists). This alone + and built-in Prolog predicates from `library(lists)`. This alone is already a very compelling argument to use them. -* Other Scryer libraries such as library(http/http_open) also already +* Other Scryer libraries such as `library(http/http_open)` also already use lists of characters to represent paths. * File names are mostly ephemeral, so it is good for efficiency that they can quickly allocated transiently on the heap, leaving the @@ -123,14 +123,14 @@ directory_exists(Directory) :- %% make_directory(+Directory). % % Succeeds if it creates a new directory named Directory in the current system. -% If you want to create a nested directory, use make\_directory\_path/1. +% If you want to create a nested directory, use `make_directory_path/1`. make_directory(Directory) :- must_be(chars, Directory), '$make_directory'(Directory). %% make_directory_path(+Directory). % -% Similar to make\_directory/1 but recursively creates directories if they're missing. +% Similar to `make_directory/1` but recursively creates directories if they're missing. % Equivalent to mkdir -p in Unix. make_directory_path(Directory) :- must_be(chars, Directory), @@ -182,8 +182,8 @@ directory_must_exist(Directory, Context) :- % % Dir0 is the current working directory, and the working directory % is changed to Dir. - -% Use `working\_directory(Ds, Ds)` to determine the current working directory, +% +% Use `working_directory/2` to determine the current working directory, % and leave it as is. working_directory(Dir0, Dir) :- @@ -220,7 +220,7 @@ path_canonical(Ps, Cs) :- % % For a file File that must exist, it returns a time stamp T with the modification time % -% T is a time stamp compatible with library(time). +% T is a time stamp compatible with `library(time)`. file_modification_time(File, T) :- file_time_(File, modification, T). @@ -228,7 +228,7 @@ file_modification_time(File, T) :- % % For a file File that must exist, it returns a time stamp T with the access time % -% T is a time stamp compatible with library(time). +% T is a time stamp compatible with `library(time)`. file_access_time(File, T) :- file_time_(File, access, T). @@ -236,7 +236,7 @@ file_access_time(File, T) :- % % For a file File that must exist, it returns a time stamp T with the creation time % -% T is a time stamp compatible with library(time). +% T is a time stamp compatible with `library(time)`. file_creation_time(File, T) :- file_time_(File, creation, T). @@ -258,15 +258,19 @@ file_time_(File, Which, T) :- % % Examples: % -% ?- path_segments("/hello/there", Segments). -% Segments = [[],"hello","there"]. -% ?- path_segments(Path, ["hello","there"]). -% Path = "hello/there". -% +% ``` +% ?- path_segments("/hello/there", Segments). +% Segments = [[],"hello","there"]. +% ?- path_segments(Path, ["hello","there"]). +% Path = "hello/there". +% ``` +% % To obtain the platform-specific directory separator, you can use: % -% ?- path_segments(Separator, ["",""]). -% Separator = "/". +% ``` +% ?- path_segments(Separator, ["",""]). +% Separator = "/". +% ``` path_segments(Path, Segments) :- '$directory_separator'(Sep), diff --git a/src/lib/http/http_open.pl b/src/lib/http/http_open.pl index 7d0503c2..17dc0088 100644 --- a/src/lib/http/http_open.pl +++ b/src/lib/http/http_open.pl @@ -5,7 +5,7 @@ /** Make HTTP requests. -This library contains the predicate http\_open/3 which allows you to perform HTTP(S) calls. +This library contains the predicate `http_open/3` which allows you to perform HTTP(S) calls. Useful for making API calls, or parsing websites. It uses Hyper underneath. */ @@ -30,8 +30,10 @@ Useful for making API calls, or parsing websites. It uses Hyper underneath. % % Example: % -% ?- http_open("https://www.example.com", S, []), get_n_chars(S, N, HTML). -% S = '$stream'(0x7fb548001be8), N = 1256, HTML = "\n true; Method = get), diff --git a/src/lib/iso_ext.pl b/src/lib/iso_ext.pl index 2ae3bf84..f22420af 100644 --- a/src/lib/iso_ext.pl +++ b/src/lib/iso_ext.pl @@ -33,9 +33,11 @@ but they're not part of the ISO Prolog standard at the moment. % For all bindings possible by Generate, Test must be true. % % In this example, it checks that all numbers are even: -% -% ?- Ns = [2,4,6], forall(member(N, Ns), 0 is N mod 2). -% Ns = [2,4,6]. +% +% ``` +% ?- Ns = [2,4,6], forall(member(N, Ns), 0 is N mod 2). +% Ns = [2,4,6]. +% ``` forall(Generate, Test) :- \+ (Generate, \+ Test). @@ -44,20 +46,25 @@ forall(Generate, Test) :- %% bb_put(+Key, +Value). % % Sets a global variable named Key (must be an atom) with value Value. -% The global variable isn't backtrackable. Check bb\_b\_put/2 for the +% The global variable isn't backtrackable. Check `bb_b_put/2` for the % backtrackable version. % -% ?- bb_put(city, "Valladolid"). -% true. -% ?- bb_get(city, X). -% X = "Valladolid". -% In this example one can understand the difference between bb\_put/2 and -% bb\_b\_put/2: +% ``` +% ?- bb_put(city, "Valladolid"). +% true. +% ?- bb_get(city, X). +% X = "Valladolid". +% ``` % -% ?- bb_put(city, "Valladolid"), (bb_put(city, "Salamanca"), false);(bb_get(city, X)). -% X = "Salamanca". -% ?- bb_put(city, "Valladolid"), (bb_b_put(city, "Salamanca"), false);(bb_get(city, X)). -% X = "Valladolid". +% In this example one can understand the difference between `bb_put/2` and +% `bb_b_put/2`: +% +% ``` +% ?- bb_put(city, "Valladolid"), (bb_put(city, "Salamanca"), false);(bb_get(city, X)). +% X = "Salamanca". +% ?- bb_put(city, "Valladolid"), (bb_b_put(city, "Salamanca"), false);(bb_get(city, X)). +% X = "Valladolid". +% ``` bb_put(Key, Value) :- ( atom(Key) -> '$store_global_var'(Key, Value) @@ -69,20 +76,25 @@ bb_put(Key, Value) :- %% bb_b_put(+Key, +Value). % % Sets a global variable named Key (must be an atom) with value Value. -% The global variable is backtrackable. Check bb\_put/2 for the +% The global variable is backtrackable. Check `bb_put/2` for the % non-backtrackable version. % -% ?- bb_b_put(city, "Valladolid"). -% true. -% ?- bb_get(city, X). -% X = "Valladolid". -% In this example one can understand the difference between bb\_put/2 and -% bb\_b\_put/2: +% ``` +% ?- bb_b_put(city, "Valladolid"). +% true. +% ?- bb_get(city, X). +% X = "Valladolid". +% ``` % -% ?- bb_put(city, "Valladolid"), (bb_put(city, "Salamanca"), false);(bb_get(city, X)). -% X = "Salamanca". -% ?- bb_put(city, "Valladolid"), (bb_b_put(city, "Salamanca"), false);(bb_get(city, X)). -% X = "Valladolid". +% In this example one can understand the difference between `bb_put/2` and +% `bb_b_put/2`: +% +% ``` +% ?- bb_put(city, "Valladolid"), (bb_put(city, "Salamanca"), false);(bb_get(city, X)). +% X = "Salamanca". +% ?- bb_put(city, "Valladolid"), (bb_b_put(city, "Salamanca"), false);(bb_get(city, X)). +% X = "Valladolid". +% ``` bb_b_put(Key, Value) :- ( atom(Key) -> '$store_backtrackable_global_var'(Key, Value) @@ -119,7 +131,9 @@ call_cleanup(G, C) :- setup_call_cleanup(true, G, C). % % In this example, we use the predicate to always close an open file: % -% ?- setup_call_cleanup(open(File, read, Stream), do_something_with_stream(Stream), close(Stream)). +% ``` +% ?- setup_call_cleanup(open(File, read, Stream), do_something_with_stream(Stream), close(Stream)). +% ``` setup_call_cleanup(S, G, C) :- '$get_b_value'(B), '$call_with_inference_counting'(call(S)), @@ -329,13 +343,13 @@ call_nth_nesting(C, ID) :- %% copy_term_nat(Source, Dest) % -% Similar to copy\_term/2 but without attribute variables +% Similar to `copy_term/2` but without attribute variables copy_term_nat(Source, Dest) :- '$copy_term_without_attr_vars'(Source, Dest). %% asserta(Module, Rule_Fact). % -% Similar to asserta/1 but allows specifying a Module +% Similar to `asserta/1` but allows specifying a Module asserta(Module, (Head :- Body)) :- !, '$asserta'(Module, Head, Body). @@ -344,7 +358,7 @@ asserta(Module, Fact) :- %% assertz(Module, Rule_Fact). % -% Similar to assertz/1 but allows specifying a Module +% Similar to `assertz/1` but allows specifying a Module assertz(Module, (Head :- Body)) :- !, '$assertz'(Module, Head, Body). diff --git a/src/lib/lists.pl b/src/lib/lists.pl index 15d9afa0..cb33e8fe 100644 --- a/src/lib/lists.pl +++ b/src/lib/lists.pl @@ -66,12 +66,14 @@ resource_error(Resource, Context) :- % Relates a list to its length (number of items). It can be used to count the elements of a current list or % to create a list full of free variables with N length. % -% ?- length([a,b,c], 3). -% true. -% ?- length([a,b,c], N). -% N = 3. -% ?- length(Xs, 3). -% Xs = [_A, _B, _C]. +% ``` +% ?- length([a,b,c], 3). +% true. +% ?- length([a,b,c], N). +% N = 3. +% ?- length(Xs, 3). +% Xs = [_A, _B, _C]. +% ``` length(Xs0, N) :- '$skip_max_list'(M, N, Xs0,Xs), @@ -115,10 +117,11 @@ length_addendum([_|Xs], N, M) :- % % Succeeds when X unifies with an item of the list Xs, which can be at any position. % -% ?- member(X, "hello world"). -% X = h -% ; ... . -% +% ``` +% ?- member(X, "hello world"). +% X = h +% ; ... . +% ``` member(X, [X|_]). member(X, [_|Xs]) :- member(X, Xs). @@ -126,9 +129,10 @@ member(X, [_|Xs]) :- member(X, Xs). % % Succeeds when the list Xs1 is the list Xs0 without the item X % -% ?- select(c, "abcd", X). -% X = "abd". -% +% ``` +% ?- select(c, "abcd", X). +% X = "abd". +% ``` select(X, [X|Xs], Xs). select(X, [Y|Xs], [Y|Ys]) :- select(X, Xs, Ys). @@ -136,9 +140,10 @@ select(X, [Y|Xs], [Y|Ys]) :- select(X, Xs, Ys). % % Concatenates a list of lists % -% ?- append([[1, 2], [3]], Xs). -% Xs = [1, 2, 3]. -% +% ``` +% ?- append([[1, 2], [3]], Xs). +% Xs = [1, 2, 3]. +% ``` append([], []). append([L0|Ls0], Ls) :- append(L0, Rest, Ls), @@ -148,15 +153,16 @@ append([L0|Ls0], Ls) :- % % List Xs is the concatenation of Xs0 and Xs1 % -% ?- append([1,2,3], [4,5,6], Xs). -% Xs = [1, 2, 3, 4, 5, 6]. -% +% ``` +% ?- append([1,2,3], [4,5,6], Xs). +% Xs = [1, 2, 3, 4, 5, 6]. +% ``` append([], R, R). append([X|L], R, [X|S]) :- append(L, R, S). %% memberchk(?X, +Xs). % -% This predicate is similar to member/2, but it only provides a single answer +% This predicate is similar to `member/2`, but it only provides a single answer memberchk(X, Xs) :- member(X, Xs), !. %% reverse(?Xs, ?Ys). @@ -179,9 +185,10 @@ reverse([_|Xs], [Y1|Ys], YsPreludeRev, Xss) :- % % This is a metapredicate that applies predicate to each element of the list Xs0 % -% ?- maplist(write, [1,2,3]). -% 123 true. -% +% ``` +% ?- maplist(write, [1,2,3]). +% 123 true. +% ``` maplist(_, []). maplist(Cont1, [E1|E1s]) :- call(Cont1, E1), @@ -191,9 +198,10 @@ maplist(Cont1, [E1|E1s]) :- % % This is a metapredicate that applies predicate to each element of the lists Xs0 and Xs1. % -% ?- maplist(length, ["hello", "prolog", "marseille"], Xs1). -% Xs1 = [5,6,9]. -% +% ``` +% ?- maplist(length, ["hello", "prolog", "marseille"], Xs1). +% Xs1 = [5,6,9]. +% ``` maplist(_, [], []). maplist(Cont2, [E1|E1s], [E2|E2s]) :- call(Cont2, E1, E2), @@ -251,8 +259,10 @@ maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s], [E7|E7 % % Takes a lists of numbers and unifies Sum with the result of summing all the elements of the list. % -% ?- sum_list([2,2,2], 6). -% true. +% ``` +% ?- sum_list([2,2,2], 6). +% true. +% ``` sum_list(Ls, S) :- foldl(lists:sum_, Ls, 0, S). @@ -274,12 +284,15 @@ same_length([_|As], [_|Bs]) :- % % For example, if we define sum_ as: % -% sum_(L, S0, S) :- S is S0 + L. +% ``` +% sum_(L, S0, S) :- S is S0 + L. +% ``` % -% Then we can define sum\_list/2 as the following: -% -% sum_list(Ls, S) :- foldl(sum_, Ls, 0, S). +% Then we can define `sum_list/2` as the following: % +% ``` +% sum_list(Ls, S) :- foldl(sum_, Ls, 0, S). +% ``` foldl(Goal_3, Ls, A0, A) :- foldl_(Ls, Goal_3, A0, A). @@ -291,7 +304,7 @@ foldl_([L|Ls], G_3, A0, A) :- %% foldl(+Predicate, ?Ls0, ?Ls1, +A0, ?A). % -% Same as foldl/4 but with an extra list +% Same as `foldl/4` but with an extra list foldl(Goal_4, Xs, Ys, A0, A) :- foldl_(Xs, Ys, Goal_4, A0, A). @@ -305,9 +318,10 @@ foldl_([X|Xs], [Y|Ys], G_4, A0, A) :- % % If Ls is a list of lists, Ts contains the transposition % -% ?- transpose([[1,1],[2,2]], Ts). -% Ts = [[1,2],[1,2]]. -% +% ``` +% ?- transpose([[1,1],[2,2]], Ts). +% Ts = [[1,2],[1,2]]. +% ``` transpose(Ls, Ts) :- lists_transpose(Ls, Ts). @@ -325,9 +339,10 @@ list_first_rest([L|Ls], L, Ls). % % Takes a list Ls0 and returns a list Set that doesn't contain any repeated element % -% ?- list_to_set([2,3,4,4,1,2], Set). -% Set = [2,3,4,1]. -% +% ``` +% ?- list_to_set([2,3,4,4,1,2], Set). +% Set = [2,3,4,1]. +% ``` list_to_set(Ls0, Ls) :- maplist(lists:with_var, Ls0, LVs0), keysort(LVs0, LVs), @@ -359,8 +374,10 @@ unify_same(E-V, Prev-Var, E-V) :- % % Succeeds if in the N position of the list Ls, we found the element E. The elements start counting from zero. % -% ?- nth0(2, [1,2,3,4], 3). -% true. +% ``` +% ?- nth0(2, [1,2,3,4], 3). +% true. +% ``` nth0(N, Es0, E) :- nonvar(N), '$skip_max_list'(Skip, N, Es0,Es1), @@ -399,8 +416,10 @@ nth0_el(N0,N, _,E, [E0|Es0]) :- % % Succeeds if in the N position of the list Ls, we found the element E. The elements start counting from one. % -% ?- nth1(2, [1,2,3,4], 2). -% true. +% ``` +% ?- nth1(2, [1,2,3,4], 2). +% true. +% ``` nth1(N, Es0, E) :- N \== 0, nth0(N, [_|Es0], E), @@ -419,8 +438,10 @@ skipn(0, Es,Es, Xs,Xs). % % Succeeds if in the N position of the list Ls, we found the element E and the rest of the list is Rs. The elements start counting from zero. % -% ?- nth0(2, [1,2,3,4], 3, [1,2,4]). -% true. +% ``` +% ?- nth0(2, [1,2,3,4], 3, [1,2,4]). +% true. +% ``` nth0(N, Es0, E, Es) :- integer(N), N >= 0, @@ -449,8 +470,10 @@ nth0_elx(N0,N, E0,E, [E1|Es0], [E0|Es]) :- % % Succeeds if in the N position of the list Ls, we found the element E and the rest of the list is Rs. The elements start counting from one. % -% ?- nth1(2, [1,2,3,4], 2, [1,3,4]). -% true. +% ``` +% ?- nth1(2, [1,2,3,4], 2, [1,3,4]). +% true. +% ``` nth1(N, Es0, E, Es) :- N \== 0, nth0(N, [_|Es0], E, [_|Es]), @@ -478,7 +501,7 @@ list_min_(N, Min0, Min) :- % % True when Xs is a permutation of Ys. This can solve for Ys given % Xs or Xs given Ys, or even enumerate Xs and Ys together. The -% predicate permutation/2 is primarily intended to generate +% predicate `permutation/2` is primarily intended to generate % permutations. Note that a list of length N has N! permutations, % and unbounded permutation generation becomes prohibitively % expensive, even for rather short lists (10! = 3,628,800). @@ -486,12 +509,14 @@ list_min_(N, Min0, Min) :- % The example below illustrates that Xs and Ys being proper lists % is not a sufficient condition to use the above replacement. % -% ?- permutation([1,2], [X,Y]). -% X = 1, Y = 2 -% ; X = 2, Y = 1 -% ; false. +% ``` +% ?- permutation([1,2], [X,Y]). +% X = 1, Y = 2 +% ; X = 2, Y = 1 +% ; false. +% ``` % -% Throws type\_error(list, Arg) if either argument is not a proper +% Throws `type_error(list, Arg)` if either argument is not a proper % or partial list. permutation(Xs, Ys) :- diff --git a/src/lib/ordsets.pl b/src/lib/ordsets.pl index fd17495b..354e674f 100644 --- a/src/lib/ordsets.pl +++ b/src/lib/ordsets.pl @@ -57,22 +57,22 @@ /** Ordered set manipulation Ordered sets are lists with unique elements sorted to the standard order -of terms (see sort/2). Exploiting ordering, many of the set operations +of terms (see `sort/2`). Exploiting ordering, many of the set operations can be expressed in order N rather than N^2 when dealing with unordered sets that may contain duplicates. The library(ordsets) is available in a number of Prolog implementations. Our predicates are designed to be compatible with common practice in the Prolog community. Some of these predicates match directly to corresponding list operations. It is advised to use the versions from this library to make -clear you are operating on ordered sets. An exception is member/2. See -ord\_memberchk/2. +clear you are operating on ordered sets. An exception is `member/2`. See +`ord_memberchk/2`. The ordsets library is based on the standard order of terms. This implies it can handle all Prolog terms, including variables. Note however, that the ordering is not stable if a term inside the set is further instantiated. Also note that variable ordering changes if variables in the set are unified with each other or a variable in the -set is unified with a variable that is `older' than the newest variable +set is unified with a variable that is _older_ than the newest variable in the set. In practice, this implies that it is allowed to use member(X, OrdSet) on an ordered set that holds variables only if X is a fresh variable. In other cases one should cease using it as an ordset @@ -84,8 +84,8 @@ because the order it relies on may have been changed. % True if Term is an ordered set. All predicates in this library % expect ordered sets as input arguments. Failing to fullfil this % assumption results in undefined behaviour. Typically, ordered -% sets are created by predicates from this library, sort/2 or -% setof/3. +% sets are created by predicates from this library, `sort/2` or +% `setof/3`. is_ordset(Term) :- '$skip_max_list'(_, _, Term, Tail), Tail == [], %% is_list(Term), @@ -112,7 +112,7 @@ ord_empty([]). %% ord_seteq(+Set1, +Set2) is semidet. % % True if Set1 and Set2 have the same elements. As both are -% canonical sorted lists, this is the same as ==/2. +% canonical sorted lists, this is the same as `==/2`. ord_seteq(Set1, Set2) :- Set1 == Set2. @@ -148,7 +148,7 @@ ord_intersect__(>, H1, T1, _H2, T2) :- %% ord_disjoint(+Set1, +Set2) is semidet. % % True if Set1 and Set2 have no common elements. This is the -% negation of ord\_intersect/2. +% negation of `ord_intersect/2`. ord_disjoint(Set1, Set2) :- \+ ord_intersect(Set1, Set2). @@ -158,7 +158,7 @@ ord_disjoint(Set1, Set2) :- % % Intersection holds the common elements of Set1 and Set2. % -% This predicate is **deprecated**. Use ord\_intersection/3 +% This predicate is *deprecated*. Use `ord_intersection/3` ord_intersect(Set1, Set2, Intersection) :- oset_int(Set1, Set2, Intersection). @@ -188,7 +188,7 @@ l_int([_-H|T], S0, S) :- %% ord_intersection(+Set1, +Set2, -Intersection) is det. % % Intersection holds the common elements of Set1 and Set2. Uses -% ord\_disjoint/2 if Intersection is bound to `[]` on entry. +% `ord_disjoint/2` if Intersection is bound to `[]` on entry. ord_intersection(Set1, Set2, Intersection) :- ( Intersection == [] @@ -201,7 +201,7 @@ ord_intersection(Set1, Set2, Intersection) :- % % Intersection and difference between two ordered sets. % Intersection is the intersection between Set1 and Set2, while -% Difference is defined by ord\_subtract(Set2, Set1, Difference). +% Difference is defined by `ord_subtract(Set2, Set1, Difference)`. ord_intersection([], L, [], L) :- !. ord_intersection([_|_], [], [], []) :- !. @@ -220,7 +220,7 @@ ord_intersection2(>, H1, T1, H2, T2, Intersection, [H2|HDiff]) :- %% ord_add_element(+Set1, +Element, ?Set2) is det. % % Insert an element into the set. This is the same as -% ord\_union(Set1, [Element], Set2). +% `ord_union(Set1, [Element], Set2)`. ord_add_element(Set1, Element, Set2) :- oset_addel(Set1, Element, Set2). @@ -229,7 +229,7 @@ ord_add_element(Set1, Element, Set2) :- %% ord_del_element(+Set, +Element, -NewSet) is det. % % Delete an element from an ordered set. This is the same as -% ord\_subtract(Set, [Element], NewSet). +% `ord_subtract(Set, [Element], NewSet)`. ord_del_element(Set, Element, NewSet) :- oset_delel(Set, Element, NewSet). @@ -237,13 +237,13 @@ ord_del_element(Set, Element, NewSet) :- %% ord_selectchk(+Item, ?Set1, ?Set2) is semidet. % -% Selectchk/3, specialised for ordered sets. Is true when +% `selectchk/3`, specialised for ordered sets. Is true when % select(Item, Set1, Set2) and Set1, Set2 are both sorted lists % without duplicates. This implementation is only expected to work % for Item ground and either Set1 or Set2 ground. The "chk" suffix -% is meant to remind you of memberchk/2, which also expects its -% first argument to be ground. ord\_selectchk(X, S, T) => -% ord\_memberchk(X, S) & \\+ ord\_memberchk(X, T). +% is meant to remind you of `memberchk/2`, which also expects its +% first argument to be ground. `ord_selectchk(X, S, T) => +% ord_memberchk(X, S) & \+ ord_memberchk(X, T).` % % Author: Richard O'Keefe @@ -263,13 +263,13 @@ ord_selectchk(Item, [Item|Set1], Set1) :- % % True if Element is a member of OrdSet, compared using ==. Note % that _enumerating_ elements of an ordered set can be done using -% member/2. +% `member/2`. % -% Some Prolog implementations also provide ord\_member/2, with the -% same semantics as ord\_memberchk/2. We believe that having a -% semidet ord\_member/2 is unacceptably inconsistent with the \*\_chk -% convention. Portable code should use ord\_memberchk/2 or -% member/2. +% Some Prolog implementations also provide `ord_member/2`, with the +% same semantics as `ord_memberchk/2`. We believe that having a +% semidet `ord_member/2` is unacceptably inconsistent with the \*\_chk +% convention. Portable code should use `ord_memberchk/2` or +% `member/2`. % % Author: Richard O'Keefe @@ -356,8 +356,8 @@ ord_union(Set1, Set2, Union) :- %% ord_union(+Set1, +Set2, -Union, -New) is det. % -% True iff ord\_union(Set1, Set2, Union) and -% ord\_subtract(Set2, Set1, New). +% True iff `ord_union(Set1, Set2, Union)` and +% `ord_subtract(Set2, Set1, New)`. ord_union([], Set2, Set2, Set2). ord_union([H|T], Set2, Union, New) :- @@ -389,14 +389,18 @@ ord_union_2([H|T], H2, T2, Union, New) :- % sequence below (but the actual implementation requires only a % single scan). % -% ord_union(Set1, Set2, Union), -% ord_intersection(Set1, Set2, Intersection), -% ord_subtract(Union, Intersection, Difference). +% ``` +% ord_union(Set1, Set2, Union), +% ord_intersection(Set1, Set2, Intersection), +% ord_subtract(Union, Intersection, Difference). +% ``` % -% For example: +% For example: % -% ?- ord_symdiff([1,2], [2,3], X). -% X = [1,3]. +% ``` +% ?- ord_symdiff([1,2], [2,3], X). +% X = [1,3]. +% ``` ord_symdiff([], Set2, Set2). ord_symdiff([H1|T1], Set2, Difference) :- diff --git a/src/lib/random.pl b/src/lib/random.pl index 15c35f3c..a3296797 100644 --- a/src/lib/random.pl +++ b/src/lib/random.pl @@ -30,9 +30,9 @@ random(R) :- % % Generates a random integer number between Lower (inclusive) and Upper (exclusive). % -% Throws instantiation\_error if Lower or Upper are variables. +% Throws `instantiation_error` if Lower or Upper are variables. % -% Throws type\_error if Lower or Upper aren't integers. +% Throws `type_error` if Lower or Upper aren't integers. random_integer(Lower, Upper, R) :- var(R), ( (var(Lower) ; var(Upper)) -> diff --git a/src/lib/sockets.pl b/src/lib/sockets.pl index 541c3133..f6689161 100644 --- a/src/lib/sockets.pl +++ b/src/lib/sockets.pl @@ -1,6 +1,6 @@ /** Predicates for handling network sockets, both as a server and as a client. -As a server, you should open a socket an call socket\_server\_accept/4 to get a stream for each connection. +As a server, you should open a socket an call `socket_server_accept/4` to get a stream for each connection. As a client, you should just open a socket and you will receive a stream. In both cases, with a stream, you can use the usual predicates to read and write to the stream. */ @@ -18,10 +18,10 @@ In both cases, with a stream, you can use the usual predicates to read and write % % The following options are available: % -% * alias(+Alias): Set an alias to the stream -% * eof_action(+Action): Defined what happens if the end of the stream is reached. Values: `error`, `eof_code` and `reset`. -% * reposition(+Boolean): Specifies whether repositioning is required for the stream. `false` is the default. -% * type(+Type): Type can be `text` or `binary`. Defines the type of the stream, if it's optimized for plain text +% * `alias(+Alias)`: Set an alias to the stream +% * `eof_action(+Action)`: Defined what happens if the end of the stream is reached. Values: `error`, `eof_code` and `reset`. +% * `reposition(+Boolean)`: Specifies whether repositioning is required for the stream. `false` is the default. +% * `type(+Type)`: Type can be `text` or `binary`. Defines the type of the stream, if it's optimized for plain text % or just binary % socket_client_open(Addr, Stream, Options) :- @@ -47,7 +47,7 @@ socket_client_open(Addr, Stream, Options) :- %% socket_server_open(+Addr, -ServerSocket). % % Open a server socket, returning a ServerSocket. Use that ServerSocket to accept incoming connections in -% socket\_server\_accept/4. Addr must satisfy `Addr = Address:Port`. Depending on the operating system +% `socket_server_accept/4`. Addr must satisfy `Addr = Address:Port`. Depending on the operating system % configuration, some ports might be reserved for superusers. socket_server_open(Addr, ServerSocket) :- must_be(var, ServerSocket), @@ -67,10 +67,10 @@ socket_server_open(Addr, ServerSocket) :- % % The following options are available: % -% * alias(+Alias): Set an alias to the stream -% * eof_action(+Action): Defined what happens if the end of the stream is reached. Values: `error`, `eof_code` and `reset`. -% * reposition(+Boolean): Specifies whether repositioning is required for the stream. `false` is the default. -% * type(+Type): Type can be `text` or `binary`. Defines the type of the stream, if it's optimized for plain text +% * `alias(+Alias)`: Set an alias to the stream +% * `eof_action(+Action)`: Defined what happens if the end of the stream is reached. Values: `error`, `eof_code` and `reset`. +% * `reposition(+Boolean)`: Specifies whether repositioning is required for the stream. `false` is the default. +% * `type(+Type)`: Type can be `text` or `binary`. Defines the type of the stream, if it's optimized for plain text % or just binary % socket_server_accept(ServerSocket, Client, Stream, Options) :- diff --git a/src/lib/ugraphs.pl b/src/lib/ugraphs.pl index 61da18e2..1d2a90c2 100644 --- a/src/lib/ugraphs.pl +++ b/src/lib/ugraphs.pl @@ -61,14 +61,14 @@ neighbours of each vertex are also in standard order (as produced by sort). This form is convenient for many calculations. A new UGraph from raw data can be created using -vertices\_edges\_to\_ugraph/3. +`vertices_edges_to_ugraph/3`. Adapted to support some of the functionality of the SICStus ugraphs library by Vitor Santos Costa. Ported from YAP 5.0.1 to SWI-Prolog by Jan Wielemaker. -Ported from SWI-Prolog to Scryer by Adrián Arroyo Calle +Ported from SWI-Prolog to Scryer by [Adrián Arroyo Calle](https://adrianistan.eu) License: BSD-2 or Artistic 2.0 */ @@ -81,8 +81,10 @@ License: BSD-2 or Artistic 2.0 % % Unify Vertices with all vertices appearing in Graph. Example: % -% ?- vertices([1-[3,5],2-[4],3-[],4-[5],5-[]], L). -% L = [1, 2, 3, 4, 5] +% ``` +% ?- vertices([1-[3,5],2-[4],3-[],4-[5],5-[]], L). +% L = [1, 2, 3, 4, 5] +% ``` vertices([], []) :- !. vertices([Vertex-_|Graph], [Vertex|Vertices]) :- @@ -97,14 +99,18 @@ vertices([Vertex-_|Graph], [Vertex|Vertices]) :- % edges will appear in Vertices but not in Edges. Moreover, it is % sufficient for a vertice to appear in Edges. % -% ?- vertices_edges_to_ugraph([],[1-3,2-4,4-5,1-5], L). -% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[]] -% +% ``` +% ?- vertices_edges_to_ugraph([],[1-3,2-4,4-5,1-5], L). +% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[]] +% ``` +% % In this case all vertices are defined implicitly. The next % example shows three unconnected vertices: % -% ?- vertices_edges_to_ugraph([6,7,8],[1-3,2-4,4-5,1-5], L). -% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[], 6-[], 7-[], 8-[]] +% ``` +% ?- vertices_edges_to_ugraph([6,7,8],[1-3,2-4,4-5,1-5], L). +% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[], 6-[], 7-[], 8-[]] +% ``` vertices_edges_to_ugraph(Vertices, Edges, Graph) :- sort(Edges, EdgeSet), @@ -119,8 +125,10 @@ vertices_edges_to_ugraph(Vertices, Edges, Graph) :- % Unify NewGraph with a new graph obtained by adding the list of % Vertices to Graph. Example: % -% ?- add_vertices([1-[3,5],2-[]], [0,1,2,9], NG). -% NG = [0-[], 1-[3,5], 2-[], 9-[]] +% ``` +% ?- add_vertices([1-[3,5],2-[]], [0,1,2,9], NG). +% NG = [0-[], 1-[3,5], 2-[], 9-[]] +% ``` % replace with real msort/2 when available msort_(List, Sorted) :- @@ -158,10 +166,12 @@ add_empty_vertices([V|G], [V-[]|NG]) :- % Vertices and all the edges that start from or go to a vertex in % Vertices to the Graph. Example: % -% ?- del_vertices([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[2,6],8-[]], -% [2,1], -% NL). -% NL = [3-[],4-[5],5-[],6-[],7-[6],8-[]] +% ``` +% ?- del_vertices([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[2,6],8-[]], +% [2,1], +% NL). +% NL = [3-[],4-[5],5-[],6-[],7-[6],8-[]] +% ``` del_vertices(Graph, Vertices, NewGraph) :- sort(Vertices, V1), % JW: was msort @@ -195,12 +205,14 @@ split_on_del_vertices(=, _, _, [_|Vs], Vs, _, NG, NG). % Unify NewGraph with a new graph obtained by adding the list of Edges % to Graph. Example: % -% ?- add_edges([1-[3,5],2-[4],3-[],4-[5], -% 5-[],6-[],7-[],8-[]], -% [1-6,2-3,3-2,5-7,3-2,4-5], -% NL). -% NL = [1-[3,5,6], 2-[3,4], 3-[2], 4-[5], -% 5-[7], 6-[], 7-[], 8-[]] +% ``` +% ?- add_edges([1-[3,5],2-[4],3-[],4-[5], +% 5-[],6-[],7-[],8-[]], +% [1-6,2-3,3-2,5-7,3-2,4-5], +% NL). +% NL = [1-[3,5,6], 2-[3,4], 3-[2], 4-[5], +% 5-[7], 6-[], 7-[], 8-[]] +% ``` add_edges(Graph, Edges, NewGraph) :- p_to_s_graph(Edges, G1), @@ -210,8 +222,10 @@ add_edges(Graph, Edges, NewGraph) :- % % NewGraph is the union of Graph1 and Graph2. Example: % -% ?- ugraph_union([1-[2],2-[3]],[2-[4],3-[1,2,4]],L). -% L = [1-[2], 2-[3,4], 3-[1,2,4]] +% ``` +% ?- ugraph_union([1-[2],2-[3]],[2-[4],3-[1,2,4]],L). +% L = [1-[2], 2-[3,4], 3-[1,2,4]] +% ``` ugraph_union(Set1, [], Set1) :- !. ugraph_union([], Set2, Set2) :- !. @@ -232,10 +246,12 @@ ugraph_union(>, Head1, Tail1, Head2, Tail2, [Head2|Union]) :- % Unify NewGraph with a new graph obtained by removing the list of % Edges from Graph. Notice that no vertices are deleted. Example: % -% ?- del_edges([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[],8-[]], -% [1-6,2-3,3-2,5-7,3-2,4-5,1-3], -% NL). -% NL = [1-[5],2-[4],3-[],4-[],5-[],6-[],7-[],8-[]] +% ``` +% ?- del_edges([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[],8-[]], +% [1-6,2-3,3-2,5-7,3-2,4-5,1-3], +% NL). +% NL = [1-[5],2-[4],3-[],4-[],5-[],6-[],7-[],8-[]] +% ``` del_edges(Graph, Edges, NewGraph) :- p_to_s_graph(Edges, G1), @@ -243,7 +259,7 @@ del_edges(Graph, Edges, NewGraph) :- %% graph_subtract(+Set1, +Set2, ?Difference) % -% Is based on ord_subtract +% Is based on `ord_subtract/3` graph_subtract(Set1, [], Set1) :- !. graph_subtract([], _, []). @@ -263,8 +279,10 @@ graph_subtract(>, Head1, Tail1, _, Tail2, Difference) :- % % Unify Edges with all edges appearing in Graph. Example: % -% ?- edges([1-[3,5],2-[4],3-[],4-[5],5-[]], L). -% L = [1-3, 1-5, 2-4, 4-5] +% ``` +% ?- edges([1-[3,5],2-[4],3-[],4-[5],5-[]], L). +% L = [1-3, 1-5, 2-4, 4-5] +% ``` edges(Graph, Edges) :- s_to_p_graph(Graph, Edges). @@ -309,8 +327,10 @@ s_to_p_graph([Neib|Neibs], Vertex, [Vertex-Neib|P], Rest_P) :- % Generate the graph Closure as the transitive closure of Graph. % Example: % -% ?- transitive_closure([1-[2,3],2-[4,5],4-[6]],L). -% L = [1-[2,3,4,5,6], 2-[4,5,6], 4-[6]] +% ``` +% ?- transitive_closure([1-[2,3],2-[4,5],4-[6]],L). +% L = [1-[2,3,4,5,6], 2-[4,5,6], 4-[6]] +% ``` transitive_closure(Graph, Closure) :- warshall(Graph, Graph, Closure). @@ -336,12 +356,14 @@ warshall([], _, _, []). % % Unify NewGraph with a new graph obtained from Graph by replacing % all edges of the form V1-V2 by edges of the form V2-V1. The cost -% is O(|V|*log(|V|)). Notice that an undirected graph is its own +% is O(|V|\*log(|V|)). Notice that an undirected graph is its own % transpose. Example: % -% ?- transpose([1-[3,5],2-[4],3-[],4-[5], -% 5-[],6-[],7-[],8-[]], NL). -% NL = [1-[],2-[],3-[1],4-[2],5-[1,4],6-[],7-[],8-[]] +% ``` +% ?- transpose([1-[3,5],2-[4],3-[],4-[5], +% 5-[],6-[],7-[],8-[]], NL). +% NL = [1-[],2-[],3-[1],4-[2],5-[1,4],6-[],7-[],8-[]] +% ``` transpose_ugraph(Graph, NewGraph) :- edges(Graph, Edges), @@ -358,8 +380,10 @@ flip_edges([Key-Val|Pairs], [Val-Key|Flipped]) :- % Compose NewGraph by connecting the _drains_ of LeftGraph to the % _sources_ of RightGraph. Example: % -% ?- compose([1-[2],2-[3]],[2-[4],3-[1,2,4]],L). -% L = [1-[4], 2-[1,2,4], 3-[]] +% ``` +% ?- compose([1-[2],2-[3]],[2-[4],3-[1,2,4]],L). +% L = [1-[4], 2-[1,2,4], 3-[]] +% ``` compose(G1, G2, Composition) :- vertices(G1, V1), @@ -401,8 +425,10 @@ compose1(=, V1, Vs1, V1, N2, G2, SoFar, Comp) :- % acyclic. In the example we show how topological sorting works % for a linear graph: % -% ?- top_sort([1-[2], 2-[3], 3-[]], L). -% L = [1, 2, 3] +% ``` +% ?- top_sort([1-[2], 2-[3], 3-[]], L). +% L = [1, 2, 3] +% ``` top_sort(Graph, Sorted) :- vertices_and_zeros(Graph, Vertices, Counts0), @@ -412,8 +438,8 @@ top_sort(Graph, Sorted) :- %% top_sort(+Graph, -Sorted, ?Tail) is semidet. % -% The predicate top\_sort/3 is a difference list version of -% top\_sort/2. +% The predicate `top_sort/3` is a difference list version of +% `top_sort/2`. top_sort(Graph, Sorted0, Sorted) :- vertices_and_zeros(Graph, Vertices, Counts0), @@ -496,13 +522,15 @@ decr_list(Neibs, [_|Vertices], [N|Counts1], [N|Counts2], Zi, Zo) :- % Neigbours is a sorted list of the neighbours of Vertex in Graph. % Example: % -% ?- neighbours(4,[1-[3,5],2-[4],3-[], -% 4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL). -% NL = [1,2,7,5] +% ``` +% ?- neighbours(4,[1-[3,5],2-[4],3-[], +% 4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL). +% NL = [1,2,7,5] +% ``` %% neighbors(+Vertex, +Graph, -Neigbours) is det. % -% Same as neighbours/3 +% Same as `neighbours/3`. neighbors(Vertex, Graph, Neig) :- neighbours(Vertex, Graph, Neig). @@ -523,13 +551,15 @@ neighbours(V,[_|G],Neig) :- % % Can be used to order a not-connected graph as follows: % -% top_sort_unconnected(Graph, Vertices) :- -% ( top_sort(Graph, Vertices) -% -> true -% ; connect_ugraph(Graph, Start, Connected), -% top_sort(Connected, Ordered0), -% Ordered0 = [Start|Vertices] -% ). +% ``` +% top_sort_unconnected(Graph, Vertices) :- +% ( top_sort(Graph, Vertices) +% -> true +% ; connect_ugraph(Graph, Start, Connected), +% top_sort(Connected, Ordered0), +% Ordered0 = [Start|Vertices] +% ). +% ``` connect_ugraph([], 0, []) :- !. connect_ugraph(Graph, Start, [Start-Vertices|Graph]) :- @@ -542,7 +572,7 @@ connect_ugraph(Graph, Start, [Start-Vertices|Graph]) :- % Unify Before to a term that comes before Term in the standard % order of terms. % -% Throws instantiation_error if Term is unbound. +% Throws `instantiation_error` if Term is unbound. before(X, _) :- var(X), @@ -561,12 +591,13 @@ before(_, 0). % _not_ connected in UGraphIn and all edges from UGraphIn removed. % Example: % -% ?- complement([1-[3,5],2-[4],3-[], -% 4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL). -% NL = [1-[2,4,6,7,8],2-[1,3,5,6,7,8],3-[1,2,4,5,6,7,8], -% 4-[3,5,6,8],5-[1,2,3,4,6,7,8],6-[1,2,3,4,5,7,8], -% 7-[1,2,3,4,5,6,8],8-[1,2,3,4,5,6,7]] -% +% ``` +% ?- complement([1-[3,5],2-[4],3-[], +% 4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL). +% NL = [1-[2,4,6,7,8],2-[1,3,5,6,7,8],3-[1,2,4,5,6,7,8], +% 4-[3,5,6,8],5-[1,2,3,4,6,7,8],6-[1,2,3,4,5,7,8], +% 7-[1,2,3,4,5,6,8],8-[1,2,3,4,5,6,7]] +% ``` % TODO: Simple two-step algorithm. You could be smarter, I suppose. @@ -586,8 +617,10 @@ complement([V-Ns|G], Vs, [V-INs|NG]) :- % True when Vertices is an ordered set of vertices reachable in % UGraph, including Vertex. Example: % -% ?- reachable(1,[1-[3,5],2-[4],3-[],4-[5],5-[]],V). -% V = [1, 3, 5] +% ``` +% ?- reachable(1,[1-[3,5],2-[4],3-[],4-[5],5-[]],V). +% V = [1, 3, 5] +% ``` reachable(N, G, Rs) :- reachable([N], G, [N], Rs). diff --git a/src/lib/uuid.pl b/src/lib/uuid.pl index b72f9620..64d20360 100644 --- a/src/lib/uuid.pl +++ b/src/lib/uuid.pl @@ -9,20 +9,22 @@ This library provides reasoning and working with [UUID](https://en.wikipedia.org (only version 4 right now). There are three predicates: - * uuidv4/1, to generate a new UUIDv4 - * uuidv4\_string/1, to generate a new UUIDv4 in string hex representation - * uuid\_string/2, to converte between UUID list of bytes and UUID hex representation + + * `uuidv4/1`, to generate a new UUIDv4 + * `uuidv4_string/1`, to generate a new UUIDv4 in string hex representation + * `uuid_string/2`, to converte between UUID list of bytes and UUID hex representation Examples: - ?- uuidv4(X). - X = [42,147,248,242,117,196,79,2,129,159|...]. - ?- uuidv4_string(X). - X = "428499fc-76e3-4240- ...". - ?- uuidv4(X), uuid_string(X, S). - X = [173,12,244,152,139,118,64,139,137,4|...], S = "ad0cf498-8b76-408b- ...". - ?- uuid_string(X, "61ae692e-eaf6-4199-8dd3-9f01db70a20b"). - X = [97,174,105,46,234,246,65,153,141,211|...]. +``` +?- uuidv4(X). + X = [42,147,248,242,117,196,79,2,129,159|...]. +?- uuidv4_string(X). + X = "428499fc-76e3-4240- ...". +?- uuidv4(X), uuid_string(X, S). + X = [173,12,244,152,139,118,64,139,137,4|...], S = "ad0cf498-8b76-408b- ...". +?- uuid_string(X, "61ae692e-eaf6-4199-8dd3-9f01db70a20b"). + X = [97,174,105,46,234,246,65,153,141,211|...]. */ :- module(uuid, [ @@ -64,7 +66,7 @@ uuidv4(Uuid) :- %% uuidv4_string(-UuidString). % % Generates a new UUID v4 (random). It unifies with a string representation of the UUID. -% It is equivalent of calling uuidv4/1 followed by uuid\_string/2. +% It is equivalent of calling `uuidv4/1` followed by `uuid_string/2`. uuidv4_string(String) :- uuidv4(Uuid), uuid_string(Uuid, String). %% uuid_string(?UuidBytes, ?UuidString). From f0727611509f9c2beb37fe6dd1b4eb117c1963be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Thu, 19 Jan 2023 21:18:40 +0100 Subject: [PATCH 053/361] Fix assoc.pl file --- src/lib/assoc.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/assoc.pl b/src/lib/assoc.pl index cb3a6f62..e32e7d82 100644 --- a/src/lib/assoc.pl +++ b/src/lib/assoc.pl @@ -172,7 +172,7 @@ gen_assoc_(Key, t(_,_,_,_,R), Val) :- % % True if Key-Value is an association in Assoc. % -% Throws error: type\_error(assoc, Assoc) if Assoc is not an association list. +% Throws error: `type_error(assoc, Assoc)` if Assoc is not an association list. get_assoc(Key, Assoc, Val) :- must_be(assoc, Assoc), @@ -218,7 +218,7 @@ get_assoc(>, Key, V, L, R, Val, V, L, NR, NVal) :- % Create an association from a list Pairs of Key-Value pairs. List % must not contain duplicate keys. % -% Throws error: domain\_error(unique\_key\_pairs, List) if List contains duplicate keys +% Throws error: `domain_error(unique_key_pairs, List)` if List contains duplicate keys list_to_assoc(List, Assoc) :- ( List = [] -> Assoc = t @@ -249,7 +249,7 @@ list_to_assoc(N, List, More, Depth, t(K,V,Balance,L,R)) :- % pairs. The pairs must occur in strictly ascending order of % their keys. % -% Throws error: domain\_error(key\_ordered\_pairs, List) if pairs are not ordered. +% Throws error: `domain_error(key_ordered_pairs, List)` if pairs are not ordered. ord_list_to_assoc(Sorted, Assoc) :- ( Sorted = [] -> Assoc = t From 03eba9594bc38f726b65110563097279966cd7db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sat, 21 Jan 2023 21:05:13 +0100 Subject: [PATCH 054/361] Compatible Doclog docs for library(charsio) --- src/lib/charsio.pl | 132 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 104 insertions(+), 28 deletions(-) diff --git a/src/lib/charsio.pl b/src/lib/charsio.pl index 4240319d..6b43cc7d 100644 --- a/src/lib/charsio.pl +++ b/src/lib/charsio.pl @@ -1,3 +1,11 @@ +/** High-level predicates to work with chars and strings + +This module contains predicates that relates strings of chars +to other representations, as well as high-level predicates to +read and write chars. + +*/ + :- module(charsio, [char_type/2, chars_utf8bytes/2, get_single_char/1, @@ -65,6 +73,38 @@ extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :- ). +%% char_type(+Char, -Type). +% +% Given a Char, Type is one of the categories that char fits in. +% Possible categories are: +% +% - `alnum` +% - `alpha` +% - `alphabetic` +% - `alphanumeric` +% - `ascii` +% - `ascii_graphic` +% - `ascii_punctuation` +% - `binary_digit` +% - `control` +% - `decimal_digit` +% - `exponent` +% - `graphic` +% - `graphic_token` +% - `hexadecimal_digit` +% - `layout` +% - `lower` +% - `meta` +% - `numeric` +% - `octal_digit` +% - `octet` +% - `prolog` +% - `sign` +% - `solo` +% - `symbolic_control` +% - `symbolic_hexadecimal` +% - `upper` +% - `whitespace` char_type(Char, Type) :- must_be(character, Char), ( ground(Type) -> @@ -106,18 +146,40 @@ ctype(upper). ctype(whitespace). +%% get_single_char(-Char). +% +% Gets a single char from the current input stream. get_single_char(C) :- ( var(C) -> '$get_single_char'(C) ; atom_length(C, 1) -> '$get_single_char'(C) ; type_error(in_character, C, get_single_char/1) ). - +%% read_from_chars(+Chars, -Term). +% +% Given a string made of chars which contains a representation of +% a Prolog term, Term is the Prolog term represented. Example: +% +% ``` +% ?- read_from_chars("f(x,y).", X). +% X = f(x,y). +% ``` read_from_chars(Chars, Term) :- must_be(chars, Chars), '$read_term_from_chars'(Chars, Term). - +%% write_term_to_chars(+Term, +Options, -Chars). +% +% Given a Term which is a Prolog term and a set of options, Chars is +% string representation of that term. Options available are: +% +% * `ignore_ops(+Boolean)` if `true`, the generic term representation is used everywhere. In `false` +% (default), operators do not use that generic term representation. +% * `max_depth(+N)` if the term is nested deeper than N, print the reminder as ellipses. +% If N = 0 (default), there's no limit. +% * `numbervars(+Boolean)` if true, replaces `$VAR(N)` variables with letters, in order. Default is false. +% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog synytax, are quoted. Default is false. +% * `variable_names(+List)` assign names to variables in term. List should be a list of terms of format `Name=Var`. write_term_to_chars(_, Options, _) :- var(Options), instantiation_error(write_term_to_chars/3). write_term_to_chars(Term, Options, Chars) :- @@ -151,6 +213,17 @@ encode(Code, Prefix, Nb) --> % Maps characters and UTF-8 bytes. % If Cs is a variable, parses Bs as a list of UTF-8 bytes. % Otherwise, transform the list of characters Cs to UTF-8 bytes. + +%% chars_utf8bytes(?Chars, ?Bytes). +% +% Maps a string made of chars with a list of UTF-8 bytes. Some examples: +% +% ``` +% ?- chars_utf8bytes("Prolog", X). +% X = [80,114,111,108,111,103]. +% ?- chars_utf8bytes(X, [226, 136, 145]). +% X = "∑". +% ``` chars_utf8bytes(Cs, Bs) :- var(Cs), must_be(list, Bs) -> once(phrase(decode_utf8(Cs), Bs)) @@ -177,7 +250,10 @@ continuation(Code, Chars, Nb) --> [Byte], % each remaining continuation byte (if any) will raise 0xFFFD too continuation(_, ['\xFFFD\'|T], _) --> [_], decode_utf8(T). - +%% read_line_to_chars(+Stream, -Chars, +InitialChars). +% +% Reads chars from stream Stream until it finds a `\n` character. +% InitialChars will be appended at the end of Chars read_line_to_chars(Stream, Cs0, Cs) :- '$get_n_chars'(Stream, 1, Char), % this also works for binary streams ( Char == [] -> Cs0 = Cs @@ -188,13 +264,11 @@ read_line_to_chars(Stream, Cs0, Cs) :- ) ). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Read N characters from Stream. - - If N is a variable, read until EOF, unifying N with the number of - characters read. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - +%% get_n_chars(+Stream, ?N, -Chars). +% +% Read N chars from stream Stream. N can be an integer, in that case +% only N chars are read, or a variable, unifying N with the number of chars +% read until it found EOF. get_n_chars(Stream, N, Cs) :- can_be(integer, N), ( var(N) -> @@ -211,24 +285,26 @@ read_to_eof(Stream, Cs) :- read_to_eof(Stream, Rest) ). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Relation between a list of characters Cs and its Base64 encoding Bs, - also a list of characters. - - At least one of the arguments must be instantiated. - - Options are: - - - padding(Boolean) - Whether to use padding: true (the default) or false. - - charset(C) - Either 'standard' (RFC 4648 §4, the default) or 'url' (RFC 4648 §5). - - Example: - - ?- chars_base64("hello", Bs, []). - Bs = "aGVsbG8=". -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% chars_base64(?Chars, ?Base64, +Options). +% +% Relation between a list of characters Cs and its Base64 encoding Bs, +% also a list of characters. +% +% At least one of the arguments must be instantiated. +% +% Options are: +% +% - `padding(Boolean)` +% Whether to use padding: true (the default) or false. +% - `charset(C)` +% Either 'standard' (RFC 4648 §4, the default) or 'url' (RFC 4648 §5). +% +% Example: +% +% ``` +% ?- chars_base64("hello", Bs, []). +% Bs = "aGVsbG8=". +% ``` chars_base64(Cs, Bs, Options) :- must_be(list, Options), From bca79d12c0ffeafdfaf626afeccc882b1f433b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sun, 22 Jan 2023 17:45:50 +0100 Subject: [PATCH 055/361] Compatible Doclog docs for library(os) --- src/lib/os.pl | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/lib/os.pl b/src/lib/os.pl index 448b5f74..5d31b5ce 100644 --- a/src/lib/os.pl +++ b/src/lib/os.pl @@ -12,6 +12,12 @@ Public domain code. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/** Predicates for reasoning about the operating system (OS) environment. + +This includes predicates about environment variables, calls to shell and +finding out the PID of the running system. +*/ + :- module(os, [getenv/2, setenv/2, unsetenv/1, @@ -24,25 +30,60 @@ :- use_module(library(lists)). :- use_module(library(si)). +%% getenv(+Key, -Value). +% +% True iff Value contains the value of the environment variable Key. +% Example: +% +% ``` +% ?- getenv("LANG", Ls). +% Ls = "en_US.UTF-8". +% ``` getenv(Key, Value) :- must_be_env_var(Key), '$getenv'(Key, Value). +%% setenv(+Key, +Value). +% +% Sets the environment variable Key to Value setenv(Key, Value) :- must_be_env_var(Key), must_be_chars(Value), '$setenv'(Key, Value). +%% unsetenv(+Key). +% +% Unsets the environment variable Key unsetenv(Key) :- must_be_env_var(Key), '$unsetenv'(Key). +%% shell(+Command) +% +% Equivalent to `shell(Command, 0)`. shell(Command) :- shell(Command, 0). + +%% shell(+Command, -Status). +% +% True iff executes Command in a shell of the operating system and the exit code is Status. +% Keep in mind the shell syntax is dependant on the operating system, so it should be +% used very carefully. +% +% Example (using Linux and fish shell): +% +% ``` +% ?- shell("echo $SHELL", Status). +% /bin/fish +% Status = 0. +% ``` shell(Command, Status) :- must_be_chars(Command), can_be(integer, Status), '$shell'(Command, Status). +%% pid(-PID). +% +% True iff PID is the process identification number of current Scryer Prolog instance. pid(PID) :- can_be(integer, PID), '$pid'(PID). From 468d096ccb5bdef688d8067897fdb4de7803bba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sun, 22 Jan 2023 20:26:30 +0100 Subject: [PATCH 056/361] Compatible Doclog docs for library(between). --- src/lib/between.pl | 55 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/lib/between.pl b/src/lib/between.pl index 2711e8e8..db87c45c 100644 --- a/src/lib/between.pl +++ b/src/lib/between.pl @@ -1,3 +1,10 @@ +/** Predicates that generate integers + +These predicates can be used to reason about integers in a reduced domain that +follow some property. `library(clpz)` provides another way of reasoning about +integers that may also be interesting. +*/ + :- module(between, [between/3, gen_int/1, gen_nat/1, numlist/2, numlist/3, repeat/1]). %% TODO: numlist/5. @@ -5,6 +12,24 @@ :- use_module(library(lists), [length/2]). :- use_module(library(error)). +%% between(+Lower, +Upper, -X). +% +% Given Lower and Upper are both integer numbers, true iff X is an integer so that _Lower =< X =< Upper_. +% Can be used both to check if X is between Lower and Upper or to generate an integer between +% Lower and Upper. +% +% Examples: +% +% ``` +% ?- between(10, 20, 15). +% true. +% ?- between(10, 20, 25). +% false. +% ?- between(3, 5, X). +% X = 3 +% ; X = 4 +% ; X = 5. +% ``` between(Lower, Upper, X) :- must_be(integer, Lower), must_be(integer, Upper), @@ -30,6 +55,9 @@ enumerate_nats(I0, N) :- I1 is I0 + 1, enumerate_nats(I1, N). +%% gen_nat(?N) +% +% True iff N is a natural number. gen_nat(N) :- can_be(integer, N), ( var(N) -> enumerate_nats(0, N) @@ -44,6 +72,9 @@ enumerate_ints(I0, N) :- I1 is I0 + 1, enumerate_ints(I1, N). +%% gen_int(?N) +% +% True iff N is an integer. gen_int(N) :- can_be(integer, N), ( var(N) -> enumerate_ints(0, N) @@ -55,9 +86,24 @@ repeat_integer(N) :- repeat_integer(N0) :- N0 > 0, N1 is N0 - 1, repeat_integer(N1). +%% repeat(+N) +% +% Succeeds N times. This predicate is only included for compatibility and *should not be used* +% because it lacks a declarative interpretation. repeat(N) :- must_be(integer, N), repeat_integer(N). +%% numlist(?Upper, ?List) +% +% True iff List is the list of integers _[1, ..., Upper]_. Example: +% +% ``` +% ?- numlist(X, Y). +% X = 1, Y = [1], +% ; X = 2, Y = [1,2] +% ; X = 3, Y = [1,2,3] +% ; ... . +% ``` numlist(Upper, List) :- ( integer(Upper) -> findall(X, between(1, Upper, X), List) ; List = [_|_], length(List, Upper), findall(X, between(1, Upper, X), List) @@ -106,5 +152,14 @@ gen_ints(L, U) :- ), L =< U. +%% numlist(?Lower, ?Upper, ?List). +% +% True iff List is a list of the form _[Lower, ..., Upper]_. +% Example: +% +% ``` +% ?- numlist(5, 10, X). +% X = [5,6,7,8,9,10]. +% ``` numlist(Lower, Upper, List) :- gen_ints(Lower, Upper), findall(X, between(Lower, Upper, X), List). From ddcae2c906a8cc059dd01862b85a1a561ef4a524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sun, 22 Jan 2023 21:22:54 +0100 Subject: [PATCH 057/361] Compatible Doclog docs for library(xpath). --- src/lib/xpath.pl | 323 ++++++++++++++++++++++++----------------------- 1 file changed, 162 insertions(+), 161 deletions(-) diff --git a/src/lib/xpath.pl b/src/lib/xpath.pl index b4d74770..a871f78d 100644 --- a/src/lib/xpath.pl +++ b/src/lib/xpath.pl @@ -100,214 +100,215 @@ :- use_module(library(dcgs)). :- use_module(library(si)). -/** Select nodes in an XML DOM +/** Select nodes in an XML DOM The library xpath.pl provides predicates to select nodes from an XML DOM -tree as produced by library(sgml) based on descriptions inspired by the -XPath language. +tree as produced by `library(sgml)` based on descriptions inspired by the +[XPath language](http://www.w3.org/TR/xpath). -The predicate xpath/3 selects a sub-structure of the DOM +The predicate `xpath/3` selects a sub-structure of the DOM non-deterministically based on an XPath-like specification. Not all -selectors of XPath are implemented, but the ability to mix xpath/3 calls +selectors of XPath are implemented, but the ability to mix `xpath/3` calls with arbitrary Prolog code provides a powerful tool for extracting information from XML parse-trees. - -@see http://www.w3.org/TR/xpath */ element_name(element(Name,_,_), Name). element_attributes(element(_,Attributes,_), Attributes). element_content(element(_,_,Content), Content). -%! xpath_chk(+DOM, +Spec, ?Content) is semidet. +%% xpath_chk(+DOM, +Spec, ?Content) is semidet. % -% Semi-deterministic version of xpath/3. +% Semi-deterministic version of `xpath/3`. xpath_chk(DOM, Spec, Content) :- xpath(DOM, Spec, Content), !. -%! xpath(+DOM, +Spec, ?Content) is nondet. +%% xpath(+DOM, +Spec, ?Content) is nondet. % -% Match an element in a DOM structure. The syntax is inspired by -% XPath, using () rather than [] to select inside an element. -% First we can construct paths using / and //: +% Match an element in a DOM structure. The syntax is inspired by +% XPath, using () rather than [] to select inside an element. +% First we can construct paths using / and //: % -% $ =|//|=Term : -% Select any node in the DOM matching term. -% $ =|/|=Term : -% Match the root against Term. -% $ Term : -% Select the immediate children of the root matching Term. +% - *//Term* +% Select any node in the DOM matching term. % -% The Terms above are of type _callable_. The functor specifies -% the element name. The element name '*' refers to any element. -% The name =self= refers to the top-element itself and is often -% used for processing matches of an earlier xpath/3 query. A term -% NS:Term refers to an XML name in the namespace NS. Optional -% arguments specify additional constraints and functions. The -% arguments are processed from left to right. Defined conditional -% argument values are: +% - */Term* +% Match the root against Term. % -% $ index(?Index) : -% True if the element is the Index-th child of its parent, -% where 1 denotes the first child. Index can be one of: -% $ `Var` : -% `Var` is unified with the index of the matched element. -% $ =last= : -% True for the last element. -% $ =last= - `IntExpr` : -% True for the last-minus-nth element. For example, -% `last-1` is the element directly preceding the last one. -% $ `IntExpr` : -% True for the element whose index equals `IntExpr`. -% $ Integer : -% The N-th element with the given name, with 1 denoting the -% first element. Same as index(Integer). -% $ =last= : -% The last element with the given name. Same as -% index(last). -% $ =last= - IntExpr : -% The IntExpr-th element before the last. -% Same as index(last-IntExpr). +% - *Term* +% Select the immediate children of the root matching Term. % -% Defined function argument values are: +% The Terms above are of type _callable_. The functor specifies +% the element name. The element name `*` refers to any element. +% The name _self_ refers to the top-element itself and is often +% used for processing matches of an earlier `xpath/3` query. A term +% NS:Term refers to an XML name in the namespace NS. Optional +% arguments specify additional constraints and functions. The +% arguments are processed from left to right. Defined conditional +% argument values are: % -% $ =self= : -% Evaluate to the entire element -% $ =content= : -% Evaluate to the content of the element (a list) -% $ =text= : -% Evaluates to all text from the sub-tree, represented -% as a list of characters. -% $ `text(atom)` : -% Evaluates to all text from the sub-tree as an atom. -% $ =normalize_space= : -% As =text=, but uses normalize_space/2 to normalise -% white-space in the output -% $ =number= : -% Extract an integer or float from the value. Ignores -% leading and trailing white-space -% $ =|@|=Attribute : -% Evaluates to the value of the given attribute. Attribute -% can be a compound term. In this case the functor name -% denotes the element and arguments perform transformations -% on the attribute value. Defined transformations are: +% - *`index(?Index)`* +% True if the element is the Index-th child of its parent, +% where 1 denotes the first child. Index can be one of: % -% - number -% Translate the value into a number using -% xsd_number_chars/2. -% - integer -% As `number`, but subsequently transform the value -% into an integer using the round/1 function. -% - float -% As `number`, but subsequently transform the value -% into a float using the float/1 function. -% - lower -% Translate the value to lower case, preserving -% the type. -% - upper -% Translate the value to upper case, preserving -% the type. +% - *`Var`* +% `Var` is unified with the index of the matched element. +% - *`last`* +% True for the last element. +% - *`last - IntExpr`* +% True for the last-minus-nth element. For example, +% `last-1` is the element directly preceding the last one. +% - *`IntExpr`* +% True for the element whose index equals `IntExpr`. +% - *`Integer`* +% The N-th element with the given name, with 1 denoting the +% first element. Same as `index(Integer)`. +% - *`last`* +% The last element with the given name. Same as +% `index(last)`. +% - *`last - IntExpr`* +% The IntExpr-th element before the last. +% Same as `index(last-IntExpr)`. % -% In addition, the argument-list can be _conditions_: +% Defined function argument values are: % -% $ Left = Right : -% Succeeds if the left-hand unifies with the right-hand. -% If the left-hand side is a function, this is evaluated. -% The right-hand side is _never_ evaluated, and thus the -% condition `content = content` defines that the content -% of the element is the atom `content`. -% The functions `lower_case` and `upper_case` can be applied -% to Right (see example below). -% $ contains(Haystack, Needle) : -% Succeeds if Needle is a sub-list of Haystack. -% $ XPath : -% Succeeds if XPath matches in the currently selected -% sub-DOM. For example, the following expression finds -% an =h3= element inside a =div= element, where the =div= -% element itself contains an =h2= child with a =strong= -% child. +% - *`self`* +% Evaluate to the entire element +% - *`content`* +% Evaluate to the content of the element (a list) +% - *`text`* +% Evaluates to all text from the sub-tree, represented +% as a list of characters. +% - *`text(atom)`* +% Evaluates to all text from the sub-tree as an atom. +% - *`normalize_space`* +% As `text`, but uses `normalize_space/2` to normalise +% white-space in the output +% - *`number`* +% Extract an integer or float from the value. Ignores +% leading and trailing white-space +% - *`@Attribute`* +% Evaluates to the value of the given attribute. Attribute +% can be a compound term. In this case the functor name +% denotes the element and arguments perform transformations +% on the attribute value. Defined transformations are: % -% == -% //div(h2/strong)/h3 -% == +% - *`number`* +% Translate the value into a number using +% `xsd_number_chars/2`. +% - *`integer`* +% As `number`, but subsequently transform the value +% into an integer using the `round/1` function. +% - *`float`* +% As `number`, but subsequently transform the value +% into a float using the `float/1` function. +% - *`lower`* +% Translate the value to lower case, preserving +% the type. +% - *`upper`* +% Translate the value to upper case, preserving +% the type. % -% This is equivalent to the conjunction of XPath goals below. +% In addition, the argument-list can be _conditions_: % -% == -% ..., -% xpath(DOM, //(div), Div), -% xpath(Div, h2/strong, _), -% xpath(Div, h3, Result) -% == +% - *`Left = Right`* +% Succeeds if the left-hand unifies with the right-hand. +% If the left-hand side is a function, this is evaluated. +% The right-hand side is _never_ evaluated, and thus the +% condition `content = content` defines that the content +% of the element is the atom `content`. +% The functions `lower_case` and `upper_case` can be applied +% to Right (see example below). +% - *`contains(Haystack, Needle)`* +% Succeeds if Needle is a sub-list of Haystack. +% - *`XPath`* +% Succeeds if XPath matches in the currently selected +% sub-DOM. For example, the following expression finds +% an `h3` element inside a `div` element, where the `div` +% element itself contains an `h2` child with a `strong` +% child. % -% **Examples**: +% ``` +% //div(h2/strong)/h3 +% ``` % -% Match each table-row in DOM: +% This is equivalent to the conjunction of XPath goals below. % -% == -% xpath(DOM, //tr, TR) -% == +% ``` +% ..., +% xpath(DOM, //(div), Div), +% xpath(Div, h2/strong, _), +% xpath(Div, h3, Result) +% ``` % -% Match the last cell of each tablerow in DOM. This example -% illustrates that a result can be the input of subsequent xpath/3 -% queries. Using multiple queries on the intermediate TR term -% guarantee that all results come from the same table-row: +% #### Examples % -% == -% xpath(DOM, //tr, TR), -% xpath(TR, /td(last), TD) -% == +% Match each table-row in DOM: % -% Match each =href= attribute in an element +% ``` +% xpath(DOM, //tr, TR) +% ``` % -% == -% xpath(DOM, //a(@href), HREF) -% == +% Match the last cell of each tablerow in DOM. This example +% illustrates that a result can be the input of subsequent `xpath/3` +% queries. Using multiple queries on the intermediate TR term +% guarantee that all results come from the same table-row: % -% Suppose we have a table containing rows where each first column -% is the name of a product with a link to details and the second -% is the price (a number). The following predicate matches the -% name, URL and price: +% ``` +% xpath(DOM, //tr, TR), +% xpath(TR, /td(last), TD) +% ``` % -% == -% product(DOM, Name, URL, Price) :- -% xpath(DOM, //tr, TR), -% xpath(TR, td(1), C1), -% xpath(C1, /self(normalize_space), Name), -% xpath(C1, a(@href), URL), -% xpath(TR, td(2, number), Price). -% == +% Match each `href` attribute in an `` element % -% Suppose we want to select books with genre="thriller" from a -% tree containing elements =||= +% ``` +% xpath(DOM, //a(@href), HREF) +% ``` % -% == -% thriller(DOM, Book) :- -% xpath(DOM, //book(@genre=thiller), Book). -% == +% Suppose we have a table containing rows where each first column +% is the name of a product with a link to details and the second +% is the price (a number). The following predicate matches the +% name, URL and price: % -% Match the elements =||= _and_ =|
|=: +% ``` +% product(DOM, Name, URL, Price) :- +% xpath(DOM, //tr, TR), +% xpath(TR, td(1), C1), +% xpath(C1, /self(normalize_space), Name), +% xpath(C1, a(@href), URL), +% xpath(TR, td(2, number), Price). +% ``` % -% ```prolog -% //table(@align(lower) = center) -% ``` +% Suppose we want to select books with genre="thriller" from a +% tree containing elements `` % -% Get the `width` and `height` of a `div` element as a number, -% and the `div` node itself: +% ``` +% thriller(DOM, Book) :- +% xpath(DOM, //book(@genre=thiller), Book). +% ``` % -% == -% xpath(DOM, //div(@width(number)=W, @height(number)=H), Div) -% == +% Match the elements `
` _and_ `
`: % -% Note that `div` is an infix operator, so parentheses must be -% used in cases like the following: +% ``` +% //table(@align(lower) = center) +% ``` % -% == -% xpath(DOM, //(div), Div) -% == +% Get the `width` and `height` of a `div` element as a number, +% and the `div` node itself: +% +% ``` +% xpath(DOM, //div(@width(number)=W, @height(number)=H), Div) +% ``` +% +% Note that `div` is an infix operator, so parentheses must be +% used in cases like the following: +% +% ``` +% xpath(DOM, //(div), Div) +% ``` xpath(DOM, Spec, Content) :- in_dom(Spec, DOM, Content). From a7e93db363152deb5aa90e4a0a27539bffef2ce9 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 22 Jan 2023 20:14:36 -0700 Subject: [PATCH 058/361] improve retract/1 and related predicates (#1598) --- build/instructions_template.rs | 10 ++- src/codegen.rs | 4 +- src/lib/builtins.pl | 89 +++++++----------- src/machine/compile.rs | 8 +- src/machine/dispatch.rs | 125 +++++++++++++++++++++++++- src/machine/machine_indices.rs | 13 +-- src/machine/machine_state.rs | 4 +- src/machine/machine_state_impl.rs | 2 +- src/machine/mod.rs | 8 +- src/machine/stack.rs | 15 ++-- src/machine/system_calls.rs | 144 +++++++++++++++++++++++++++--- 11 files changed, 315 insertions(+), 107 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index e4904352..48166239 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -562,6 +562,10 @@ enum SystemClauseType { InlineCallN(usize), #[strum_discriminants(strum(props(Arity = "1", Name = "$is_expanded_or_inlined")))] IsExpandedOrInlined, + #[strum_discriminants(strum(props(Arity = "3", Name = "$get_clause_p")))] + GetClauseP, + #[strum_discriminants(strum(props(Arity = "6", Name = "$invoke_clause_at_p")))] + InvokeClauseAtP, REPL(REPLCodePtr), } @@ -1620,6 +1624,8 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallPrepareCallClause(..) | &Instruction::CallCompileInlineOrExpandedGoal(..) | &Instruction::CallIsExpandedOrInlined(_) | + &Instruction::CallGetClauseP(_) | + &Instruction::CallInvokeClauseAtP(_) | &Instruction::CallEnqueueAttributedVar(_) | &Instruction::CallFetchGlobalVar(_) | &Instruction::CallFirstStream(_) | @@ -1822,7 +1828,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteMakeDirectoryPath(_) | &Instruction::ExecuteDeleteFile(_) | &Instruction::ExecuteRenameFile(_) | - &Instruction::ExecuteFileCopy(_) | + &Instruction::ExecuteFileCopy(_) | &Instruction::ExecuteWorkingDirectory(_) | &Instruction::ExecuteDeleteDirectory(_) | &Instruction::ExecutePathCanonical(_) | @@ -1833,6 +1839,8 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecutePrepareCallClause(..) | &Instruction::ExecuteCompileInlineOrExpandedGoal(..) | &Instruction::ExecuteIsExpandedOrInlined(_) | + &Instruction::ExecuteGetClauseP(_) | + &Instruction::ExecuteInvokeClauseAtP(_) | &Instruction::ExecuteEnqueueAttributedVar(_) | &Instruction::ExecuteFetchGlobalVar(_) | &Instruction::ExecuteFirstStream(_) | diff --git a/src/codegen.rs b/src/codegen.rs index 31219e1a..1aea58d2 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1189,7 +1189,7 @@ impl<'b> CodeGenerator<'b> { if let Some(arg) = arg { let index = code.len(); - if clauses.len() > 1 || self.settings.is_dynamic() { + if clauses.len() > 1 || self.settings.is_extensible { code_offsets.index_term(arg, index, &mut clause_index_info, self.atom_tbl); } } @@ -1211,7 +1211,7 @@ impl<'b> CodeGenerator<'b> { code.extend(clause_code.into_iter()); } - let index_code = if clauses.len() > 1 || self.settings.is_dynamic() { + let index_code = if clauses.len() > 1 || self.settings.is_extensible { code_offsets.compute_indices(skip_stub_try_me_else) } else { vec![] diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index d7f3c536..ea60bc08 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -854,70 +854,30 @@ retract(Clause0) :- retract_module_clause(Head, Body, Module) ). -module_retract_clauses([Clause|Clauses0], Head, Body, Name, Arity, Module) :- - functor(VarHead, Name, Arity), - findall((VarHead :- VarBody), Module:'$clause'(VarHead, VarBody), Clauses1), - ( first_match_index(Clauses1, (Head :- Body), 0, N) -> +retract_clauses([L-P | Ps], Head, Body, Name, Arity, Module) :- + '$invoke_clause_at_p'(Head, Body, L, P, N, Module), + ( integer(N) -> '$retract_clause'(Name, Arity, N, Module) - ; Clause = (Head :- Body) + ; true % the clause at index N has already been retracted in this + % case but unify (Head :- Body) anyway. ), - ( Clauses0 == [] -> ! + ( Ps == [] -> ! ; true ). +retract_clauses([_ | Ps], Head, Body, Name, Arity, Module) :- + retract_clauses(Ps, Head, Body, Name, Arity, Module). - -module_retract_clauses([_|Clauses0], Head, Body, Name, Arity, Module) :- - module_retract_clauses(Clauses0, Head, Body, Name, Arity, Module). - - -call_module_retract(Head, Body, Name, Arity, Module) :- - findall((Head :- Body), Module:'$clause'(Head, Body), Clauses), - module_retract_clauses(Clauses, Head, Body, Name, Arity, Module). - - -retract_module_clause(Head, Body, Module) :- - ( var(Head) -> - throw(error(instantiation_error, retract/1)) - ; callable(Head), - functor(Head, Name, Arity) -> - ( '$no_such_predicate'(Module, Head) -> - '$fail' - ; '$head_is_dynamic'(Module, Head) -> - ( Module == user -> - call_retract(Head, Body, Name, Arity) - ; call_module_retract(Head, Body, Name, Arity, Module) - ) - ; throw(error(permission_error(modify, static_procedure, Name/Arity), retract/1)) - ) - ; throw(error(type_error(callable, Head), retract/1)) - ). - - -first_match_index([Clause | _], Clause, N, N) :- - !. -first_match_index([_ | Clauses], Clause, N0, N) :- - N1 is N0 + 1, - first_match_index(Clauses, Clause, N1, N). - - -retract_clauses([Clause | Clauses0], Head, Body, Name, Arity) :- - functor(VarHead, Name, Arity), - findall((VarHead :- VarBody), builtins:'$clause'(VarHead, VarBody), Clauses1), - ( first_match_index(Clauses1, (Head :- Body), 0, N) -> - '$retract_clause'(Name, Arity, N, user) - ; Clause = (Head :- Body) +call_retract_helper(Head, Body, P, Module) :- + ( Module == user -> + ClauseQualifier = builtins + ; ClauseQualifier = Module ), - ( Clauses0 == [] -> ! - ; true - ). -retract_clauses([_ | Clauses0], Head, Body, Name, Arity) :- - retract_clauses(Clauses0, Head, Body, Name, Arity). - - -call_retract(Head, Body, Name, Arity) :- - findall((Head :- Body), builtins:'$clause'(Head, Body), Clauses), - retract_clauses(Clauses, Head, Body, Name, Arity). + ClauseQualifier:'$clause'(Head, Body), + '$get_clause_p'(Head, P, Module). +call_retract(Head, Body, Name, Arity, Module) :- + findall(P, builtins:call_retract_helper(Head, Body, P, Module), Ps), + retract_clauses(Ps, Head, Body, Name, Arity, Module). retract_clause(Head, Body) :- ( var(Head) -> @@ -932,12 +892,25 @@ retract_clause(Head, Body) :- ; '$no_such_predicate'(user, Head) -> '$fail' ; '$head_is_dynamic'(user, Head) -> - call_retract(Head, Body, Name, Arity) + call_retract(Head, Body, Name, Arity, user) ; throw(error(permission_error(modify, static_procedure, Name/Arity), retract/1)) ) ; throw(error(type_error(callable, Head), retract/1)) ). +retract_module_clause(Head, Body, Module) :- + ( var(Head) -> + throw(error(instantiation_error, retract/1)) + ; callable(Head), + functor(Head, Name, Arity) -> + ( '$no_such_predicate'(Module, Head) -> + '$fail' + ; '$head_is_dynamic'(Module, Head) -> + call_retract(Head, Body, Name, Arity, Module) + ; throw(error(permission_error(modify, static_procedure, Name/Arity), retract/1)) + ) + ; throw(error(type_error(callable, Head), retract/1)) + ). :- meta_predicate retractall(:). diff --git a/src/machine/compile.rs b/src/machine/compile.rs index d7f2492c..db64293d 100644 --- a/src/machine/compile.rs +++ b/src/machine/compile.rs @@ -1869,7 +1869,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 diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index d3a6a9e1..89fa348a 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -1010,7 +1010,6 @@ impl Machine { .stack .index_or_frame(self.machine_st.b) .prelude - .univ_prelude .num_cells; self.machine_st.cc = cell_as_fixnum!( @@ -1081,7 +1080,6 @@ impl Machine { .stack .index_or_frame(self.machine_st.b) .prelude - .univ_prelude .num_cells; self.machine_st.cc = cell_as_fixnum!( @@ -3143,7 +3141,6 @@ impl Machine { .stack .index_or_frame(b) .prelude - .univ_prelude .num_cells; self.machine_st.cc = cell_as_fixnum!( @@ -5088,6 +5085,128 @@ impl Machine { ); } } + &Instruction::CallGetClauseP(_) => { + let module_name = cell_as_atom!(self.deref_register(3)); + + let (n, p) = self.get_clause_p(module_name); + + let r = self.machine_st.registers[2]; + let r = self.machine_st.store(self.machine_st.deref(r)); + + let h = self.machine_st.heap.len(); + self.machine_st.heap.extend(functor!(atom!("-"), [fixnum(n), fixnum(p)])); + + let r = r.as_var().unwrap(); + self.machine_st.bind(r, str_loc_as_cell!(h)); + + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteGetClauseP(_) => { + let module_name = cell_as_atom!(self.deref_register(3)); + + let (n, p) = self.get_clause_p(module_name); + + let r = self.machine_st.registers[2]; + let r = self.machine_st.store(self.machine_st.deref(r)); + + let h = self.machine_st.heap.len(); + self.machine_st.heap.extend(functor!(atom!("-"), [fixnum(n), fixnum(p)])); + + let r = r.as_var().unwrap(); + self.machine_st.bind(r, str_loc_as_cell!(h)); + + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallInvokeClauseAtP(_) => { + let key_cell = self.machine_st.registers[1]; + let key = self.machine_st.name_and_arity_from_heap(key_cell).unwrap(); + + let l = self.machine_st.registers[3]; + let l = self.machine_st.store(self.machine_st.deref(l)); + + let l = match Number::try_from(l) { + Ok(Number::Fixnum(l)) => l.get_num() as usize, + _ => unreachable!(), + }; + + let p = self.machine_st.registers[4]; + let p = self.machine_st.store(self.machine_st.deref(p)); + + let p = match Number::try_from(p) { + Ok(Number::Fixnum(p)) => p.get_num() as usize, + _ => unreachable!(), + }; + + let module_name = cell_as_atom!(self.deref_register(6)); + + let compilation_target = match module_name { + atom!("user") => CompilationTarget::User, + _ => CompilationTarget::Module(module_name), + }; + + let skeleton = self.indices.get_predicate_skeleton_mut( + &compilation_target, + &key, + ).unwrap(); + + match skeleton.target_pos_of_clause_clause_loc(l) { + Some(n) => { + let r = self.machine_st.store(self.machine_st.deref( + self.machine_st.registers[5], + )); + + self.machine_st.unify_fixnum(Fixnum::build_with(n as i64), r); + } + None => {} + } + + self.machine_st.call_at_index(2, p); + } + &Instruction::ExecuteInvokeClauseAtP(_) => { + let key_cell = self.machine_st.registers[1]; + let key = self.machine_st.name_and_arity_from_heap(key_cell).unwrap(); + + let l = self.machine_st.registers[3]; + let l = self.machine_st.store(self.machine_st.deref(l)); + + let l = match Number::try_from(l) { + Ok(Number::Fixnum(l)) => l.get_num() as usize, + _ => unreachable!(), + }; + + let p = self.machine_st.registers[4]; + let p = self.machine_st.store(self.machine_st.deref(p)); + + let p = match Number::try_from(p) { + Ok(Number::Fixnum(p)) => p.get_num() as usize, + _ => unreachable!(), + }; + + let module_name = cell_as_atom!(self.deref_register(6)); + + let compilation_target = match module_name { + atom!("user") => CompilationTarget::User, + _ => CompilationTarget::Module(module_name), + }; + + let skeleton = self.indices.get_predicate_skeleton_mut( + &compilation_target, + &key, + ).unwrap(); + + match skeleton.target_pos_of_clause_clause_loc(l) { + Some(n) => { + let r = self.machine_st.store(self.machine_st.deref( + self.machine_st.registers[5], + )); + + self.machine_st.unify_fixnum(Fixnum::build_with(n as i64), r); + } + None => {} + } + + self.machine_st.execute_at_index(2, p); + } } } diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index 1fcd41e4..11a9d6e8 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -371,22 +371,11 @@ impl IndexStore { module: Atom, ) -> Option { 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()) } } diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 2db02c01..bdaf048c 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -839,7 +839,7 @@ impl MachineState { 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.num_cells = n; or_frame.prelude.e = self.e; or_frame.prelude.cp = self.cp; or_frame.prelude.b = self.b; @@ -867,7 +867,7 @@ impl MachineState { 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.num_cells = n; or_frame.prelude.e = self.e; or_frame.prelude.cp = self.cp; or_frame.prelude.b = self.b; diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index a77fda72..9b69c86b 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -2786,7 +2786,7 @@ impl MachineState { if self.e > self.b { let frame = self.stack.index_and_frame(self.e); - let size = AndFrame::size_of(frame.prelude.univ_prelude.num_cells); + let size = AndFrame::size_of(frame.prelude.num_cells); self.stack.truncate(self.e + size); } diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 164471f4..09883b99 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -555,7 +555,7 @@ impl Machine { 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; for i in 0..n { self.machine_st.registers[i + 1] = or_frame[i]; @@ -589,7 +589,7 @@ impl Machine { 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; for i in 0..n { self.machine_st.registers[i+1] = or_frame[i]; @@ -625,7 +625,7 @@ impl Machine { 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; for i in 0..n { self.machine_st.registers[i+1] = or_frame[i]; @@ -661,7 +661,7 @@ 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]; diff --git a/src/machine/stack.rs b/src/machine/stack.rs index 47a4fbf4..cc86aa34 100644 --- a/src/machine/stack.rs +++ b/src/machine/stack.rs @@ -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 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, @@ -208,7 +203,7 @@ impl Stack { } let and_frame = &mut *(new_ptr as *mut AndFrame); - and_frame.prelude.univ_prelude.num_cells = num_cells; + and_frame.prelude.num_cells = num_cells; e } @@ -232,7 +227,7 @@ impl Stack { } let or_frame = &mut *(new_ptr as *mut OrFrame); - or_frame.prelude.univ_prelude.num_cells = num_cells; + or_frame.prelude.num_cells = num_cells; b } @@ -298,7 +293,7 @@ mod tests { 0// 10 * mem::size_of::() + prelude_size::() ); - 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)); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 0e4b1e16..69dc018e 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -457,6 +457,20 @@ impl BrentAlgState { } impl MachineState { + pub(crate) fn name_and_arity_from_heap(&self, cell: HeapCellValue) -> Option { + read_heap_cell!(self.store(self.deref(cell)), + (HeapCellValueTag::Str, s) => { + Some(cell_as_atom_cell!(self.heap[s]).get_name_and_arity()) + } + (HeapCellValueTag::Atom, (name, _arity)) => { + Some((name, 0)) + } + _ => { + None + } + ) + } + #[inline] pub(crate) fn variable_set( &mut self, @@ -988,6 +1002,116 @@ impl MachineState { } impl Machine { + #[inline(always)] + pub(crate) fn get_clause_p(&self, module_name: Atom) -> (usize, usize) { + use crate::machine::loader::CompilationTarget; + + let key_cell = self.machine_st.registers[1]; + let key = self.machine_st.name_and_arity_from_heap(key_cell).unwrap(); + + let compilation_target = if module_name == atom!("user") { + CompilationTarget::User + } else { + CompilationTarget::Module(module_name) + }; + + let skeleton = self.indices.get_predicate_skeleton( + &compilation_target, + &key, + ).unwrap(); + + if self.machine_st.b > self.machine_st.e { + let or_frame = self.machine_st.stack.index_or_frame(self.machine_st.b); + let bp = or_frame.prelude.bp; + + match &self.code[bp] { + &Instruction::IndexingCode(ref indexing_code) => { + match &indexing_code[or_frame.prelude.boip as usize] { + &IndexingLine::IndexedChoice(ref indexed_choice) => { + let p = or_frame.prelude.biip as usize - 1; + + match &indexed_choice[p] { + &IndexedChoiceInstruction::Try(offset) | + &IndexedChoiceInstruction::Retry(offset) => { + let clause_clause_loc = skeleton.core.clause_clause_locs[p]; + (clause_clause_loc, bp + offset) + } + &IndexedChoiceInstruction::Trust(_) => { + unreachable!() + } + } + } + _ => { + unreachable!() + } + } + } + _ => unreachable!() + } + } else { + let module_name = match compilation_target { + CompilationTarget::User => atom!("builtins"), + CompilationTarget::Module(target) => target, + }; + + let bp = self.indices + .get_predicate_code_index(atom!("$clause"), 2, module_name) + .and_then(|idx| idx.local()) + .unwrap(); + + macro_rules! extract_ptr { + ($ptr: expr) => { + match $ptr { + IndexingCodePtr::External(p) => return ( + skeleton.core.clause_clause_locs.back().cloned().unwrap(), + bp + p, + ), + IndexingCodePtr::Internal(boip) => boip, + _ => unreachable!(), + } + }; + } + + match &self.code[bp] { + &Instruction::IndexingCode(ref indexing_code) => { + let indexing_code_ptr = match &indexing_code[0] { + &IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, c, _, s)) => { + if key.1 > 0 { s } else { c } + } + _ => { + unreachable!() + } + }; + + let boip = extract_ptr!(indexing_code_ptr); + + let boip = match &indexing_code[boip] { + &IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref hm)) => { + boip + extract_ptr!(hm.get(&key).cloned().unwrap()) + } + &IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref hm)) => { + boip + extract_ptr!(hm.get(&Literal::Atom(key.0)).cloned().unwrap()) + } + _ => boip, + }; + + match &indexing_code[boip] { + &IndexingLine::IndexedChoice(ref indexed_choice) => { + return ( + skeleton.core.clause_clause_locs.back().cloned().unwrap(), + bp + indexed_choice.back().unwrap().offset(), + ); + } + _ => unreachable!(), + } + } + _ => { + return (skeleton.core.clause_clause_locs.back().cloned().unwrap(), bp); + } + } + } + } + #[inline(always)] pub(crate) fn deref_register(&mut self, i: usize) -> HeapCellValue { self.machine_st.store(self.machine_st.deref(self.machine_st.registers[i])) @@ -3281,20 +3405,14 @@ impl Machine { pub(crate) fn head_is_dynamic(&mut self) { let module_name = cell_as_atom!(self.deref_register(1)); - let (name, arity) = read_heap_cell!( - self.deref_register(2), - (HeapCellValueTag::Str, s) => { - cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity() + match self.machine_st.name_and_arity_from_heap(self.machine_st.registers[2]) { + Some((name, arity)) => { + self.machine_st.fail = !self.indices.is_dynamic_predicate(module_name, (name, arity)); } - (HeapCellValueTag::Atom, (name, _arity)) => { - (name, 0) + None => { + self.machine_st.fail = true; } - _ => { - unreachable!() - } - ); - - self.machine_st.fail = !self.indices.is_dynamic_predicate(module_name, (name, arity)); + } } #[inline(always)] @@ -4669,7 +4787,7 @@ impl Machine { self.restore_instr_at_verify_attr_interrupt(); let e = self.machine_st.e; - let frame_len = self.machine_st.stack.index_and_frame(e).prelude.univ_prelude.num_cells; + let frame_len = self.machine_st.stack.index_and_frame(e).prelude.num_cells; for i in 1..frame_len - 2 { self.machine_st.registers[i] = self.machine_st.stack[stack_loc!(AndFrame, e, i)]; From 7f177c3d03f6bf6f2f5edc1dc9bcd42a3a28f2c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Mon, 23 Jan 2023 21:03:11 +0100 Subject: [PATCH 059/361] Compatible Doclog docs for library(dcgs) --- src/lib/dcgs.pl | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib/dif.pl | 22 +++++++++++---------- 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/lib/dcgs.pl b/src/lib/dcgs.pl index 77eb2f5e..08870316 100644 --- a/src/lib/dcgs.pl +++ b/src/lib/dcgs.pl @@ -1,3 +1,13 @@ +/** Support for Definite Clause Grammars. + +A Prolog definite clause grammar (DCG) describes a sequence. Operationally, DCGs +can be used to parse, generate, complete and check sequences manifested as lists. + +Check [The Power of Prolog chapter on DCGs](https://www.metalevel.at/prolog/dcg) +to learn more about them. +*/ + + :- module(dcgs, [op(1105, xfy, '|'), phrase/2, @@ -16,9 +26,44 @@ :- meta_predicate phrase(2, ?, ?). +%% phrase(+Body, ?Ls). +% +% True iff Body describes the list Ls. Body must be a DCG body. +% It is equivalent to `phrase(Body, Ls, [])`. +% +% Examples: +% +% ``` +% as --> []. +% as --> [a], as. +% +% ?- phrase(as, Ls). +% Ls = [] +% ; Ls = "a" +% ; Ls = "aa" +% ; Ls = "aaa" +% ; ... . +% +% ?- phrase(as, "aaa"). +% true. +% ``` + phrase(GRBody, S0) :- phrase(GRBody, S0, []). +%% phrase(+Body, ?Ls, ?Ls0). +% +% True iff Body describes part of the list Ls and the rest of Ls is Ls0. +% +% Example: +% +% ``` +% ?- phrase(seq(X), "aaa", Y). +% X = [], Y = "aaa" +% ; X = "a", Y = "aa" +% ; X = "aa", Y = "a" +% ; X = "aaa", Y = []. +% ``` phrase(GRBody, S0, S) :- strip_module(GRBody, M, GRBody1), ( var(GRBody) -> @@ -131,6 +176,9 @@ user:term_expansion(Term0, Term) :- nonvar(Term0), dcg_rule(Term0, Term). + +%% seq(Seq)// +% % Describes a sequence seq(Xs, Cs0,Cs) :- var(Xs), @@ -141,10 +189,14 @@ seq(Xs, Cs0,Cs) :- seq([]) --> []. seq([E|Es]) --> [E], seq(Es). +%% seqq(SeqOfSeqs)// +% % Describes a sequence of sequences seqq([]) --> []. seqq([Es|Ess]) --> seq(Es), seqq(Ess). +%% ...// +% % Describes an arbitrary number of elements ...(Cs0,Cs) :- Cs0 == [], diff --git a/src/lib/dif.pl b/src/lib/dif.pl index 165493fd..33e3e44b 100644 --- a/src/lib/dif.pl +++ b/src/lib/dif.pl @@ -1,5 +1,5 @@ /** -Provides predicate dif/2. dif/2 is a constraint that is true only if both of its +Provides predicate `dif/2`. `dif/2` is a constraint that is true only if both of its arguments are different terms. */ @@ -45,18 +45,20 @@ verify_attributes(Var, Value, Goals) :- %% dif(?X, ?Y). % -% True iff X and Y are different terms. Unlike \\=/2, dif/2 is more declarative because if X and Y can +% True iff X and Y are different terms. Unlike `\=/2`, `dif/2` is more declarative because if X and Y can % unify but they're not yet equal, the decision is delayed, and prevents X and Y to become equal later. % Examples: % -% ?- dif(a, a). -% false. -% ?- dif(a, b). -% true. -% ?- dif(X, b). -% dif:dif(X,b). -% ?- dif(X, b), X = b. -% false. +% ``` +% ?- dif(a, a). +% false. +% ?- dif(a, b). +% true. +% ?- dif(X, b). +% dif:dif(X,b). +% ?- dif(X, b), X = b. +% false. +% ``` dif(X, Y) :- X \== Y, ( X \= Y -> true From bdeabcdd89561e0243c8f3d047093f7caec729ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Mon, 23 Jan 2023 23:51:27 +0100 Subject: [PATCH 060/361] Website page --- INDEX.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 INDEX.md diff --git a/INDEX.md b/INDEX.md new file mode 100644 index 00000000..56fbd633 --- /dev/null +++ b/INDEX.md @@ -0,0 +1,66 @@ +# Scryer Prolog + +``` +?- append("Hello, ", X, "Hello, Scryer Prolog!"). + X = "Scryer Prolog!". +``` + +![scryer](scryer.png){width=128 style=float:right;} Scryer Prolog is a free software ISO Prolog system intended to be an industrial +strength production environment *and* a testbed for bleeding edge research in +logic and constraint programming. + +Some of the Scryer Prolog features are: + +* ISO standard compliant +* Integrated constraint progamming libraries: clp(B), clp(Z). +* Definite Clause Grammars +* Coroutining support (`dif/2`, `freeze/2`, ...) +* Tabling and SLG resolution +* Compact string representation +* Network libraries (TCP sockets, HTTP server, HTTP client, ...) +* Cryptographical predicates +* WAM based engine, cross-platform made in Rust +* _and more..._ + +## What is Prolog? + +Prolog is a logic programming language created by [Alain Colmerauer](https://en.wikipedia.org/wiki/Alain_Colmerauer) and [Robert Kowalski](https://en.wikipedia.org/wiki/Robert_Kowalski) in 1972. +The idea behind Prolog is try to express a task in language similar to First Order Logic. +Prolog systems include _unification_ and _non-determinism_ as key concepts upon which we build programs. + +A Prolog program is made up of predicates which define a relation between its arguments. A predicate +is made from clauses. A clause can be either a fact or a rule. There's also a toplevel, which we +can use to ask and reason about our task. + +It's still to this day one of the best examples and one of the most popular languages in the field +of logic programming. That's because Prolog allows us to elegantly solve many tasks with short and +general programs. + +If you want to learn more about Prolog history, [check this video](https://www.youtube.com/watch?v=74Ig_QKndvE). + +## Where can I learn Prolog? + +There are a lot of classical Prolog books. Those books can teach you the basics of Prolog. Some +examples are: _The Art of Prolog (Shapiro)_, _Programming in Prolog (Cloksin, Mellish)_ and _The Craft +of Prolog (O'Keefe)_. However, most of them are not updated to _modern_ Prolog. +We recommend _[The Power of Prolog (Markus Triska)](https://www.metalevel.at/prolog)_ for modern Prolog. For reference about +the builtin Prolog modules and libraries in Scryer, check the documentation site. It's this! + +## Downloads + +The latest version of Scryer Prolog is *0.9.1*. And it's already useful for lots of tasks. + +Scryer Prolog can be compiled from source, instructions are on the [GitHub README](https://github.com/mthom/scryer-prolog). It runs on Linux, macOS and Windows. Other operating systems may work but they're not regularly tested. + +If you're in Linux, maybe your distribution already has an Scryer Prolog package. + +There's also a [Docker image](https://github.com/mthom/scryer-prolog#docker-install) available. + +## Support and discussions + +If Scryer Prolog crashes or yields unexpected errors, consider filing +an [issue](https://github.com/mthom/scryer-prolog/issues). + +To get in touch with the Scryer Prolog community, participate in +[discussions](https://github.com/mthom/scryer-prolog/discussions) +or visit our #scryer IRC channel on [Libera](https://libera.chat)! \ No newline at end of file From 909f2e1058e07d840ec6d0445b69a18953a91ca6 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 24 Jan 2023 20:06:32 +0100 Subject: [PATCH 061/361] =?UTF-8?q?DOC:=20improve=20CLP(=E2=84=A4)=20DocLo?= =?UTF-8?q?g=20documentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/clpz.pl | 563 ++++++++++++++++++++++++------------------------ 1 file changed, 283 insertions(+), 280 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index de522ab6..fc2df129 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -284,30 +284,31 @@ exclude_([L|Ls0], Goal, Ls) :- /** Constraint Logic Programming over Integers -## Introduction {#clpz-intro} +## Introduction This library provides CLP(ℤ): Constraint Logic Programming over Integers. CLP(ℤ) is an instance of the general CLP(.) scheme, extending logic programming with reasoning over specialised domains. CLP(ℤ) lets us -reason about **integers** in a way that honors the relational nature +reason about *integers* in a way that honors the relational nature of Prolog. There are two major use cases of CLP(ℤ) constraints: - 1. [**declarative integer arithmetic**](<#clpz-integer-arith>) - 2. solving **combinatorial problems** such as planning, scheduling + 1. [*declarative integer arithmetic*](<#clpz-integer-arith>) + + 2. solving *combinatorial problems* such as planning, scheduling and allocation tasks. The predicates of this library can be classified as: - * _arithmetic_ constraints like #=/2, #>/2 and #\=/2 [](<#clpz-arithmetic>) - * the _membership_ constraints in/2 and ins/2 [](<#clpz-membership>) - * the _enumeration_ predicates indomain/1, label/1 and labeling/2 [](<#clpz-enumeration>) - * _combinatorial_ constraints like all_distinct/1 and global_cardinality/2 [](<#clpz-global>) - * _reification_ predicates such as #<==>/2 [](<#clpz-reification-predicates>) - * _reflection_ predicates such as fd_dom/2 [](<#clpz-reflection-predicates>) + * _arithmetic_ constraints like `#=/2`, `#>/2` and `#\=/2` + * the _membership_ constraints `in/2` and `ins/2` + * the _enumeration_ predicates `indomain/1`, `label/1` and `labeling/2` + * _combinatorial_ constraints like `all_distinct/1` and `global_cardinality/2` + * _reification_ predicates such as `#<==>/2` + * _reflection_ predicates such as `fd_dom/2` In most cases, [_arithmetic constraints_](<#clpz-arith-constraints>) are the only predicates you will ever need from this library. When @@ -324,16 +325,16 @@ is highly advisable that you make CLP(ℤ) constraints available in all your programs. One way to do this is to put the following directive in your =|~/.scryerrc|= initialisation file: -== +``` :- use_module(library(clpz)). -== +``` All example programs that appear in the CLP(ℤ) documentation assume that you have done this. Important concepts and principles of this library are illustrated by means of usage examples that are available in a public git repository: -[**github.com/triska/clpz**](https://github.com/triska/clpz) +[*https://github.com/triska/clpz*](https://github.com/triska/clpz) If you are used to the complicated operational considerations that low-level arithmetic primitives necessitate, then moving to CLP(ℤ) @@ -353,7 +354,7 @@ primitives are impure limitations that are better deferred to more advanced lectures. More information about CLP(ℤ) constraints and their implementation is -contained in: [**metalevel.at/drt.pdf**](https://www.metalevel.at/drt.pdf) +contained in: [*metalevel.at/drt.pdf*](https://www.metalevel.at/drt.pdf) The best way to discuss applying, improving and extending CLP(ℤ) constraints is to use the dedicated `clpz` tag on @@ -369,37 +370,37 @@ arithmetic constraints is that they are true _relations_ and can be used in all directions. For most programs, arithmetic constraints are the only predicates you will ever need from this library. -The most important arithmetic constraint is #=/2, which subsumes both -`(is)/2` and `(=:=)/2` over integers. Use #=/2 to make your programs -more general. +The most important arithmetic constraint is `(#=)/2`, which subsumes +both `(is)/2` and `(=:=)/2` over integers. Use `(#=)/2` to make your +programs more general. In total, the arithmetic constraints are: - | Expr1 `#=` Expr2 | Expr1 equals Expr2 | - | Expr1 `#\=` Expr2 | Expr1 is not equal to Expr2 | - | Expr1 `#>=` Expr2 | Expr1 is greater than or equal to Expr2 | - | Expr1 `#=<` Expr2 | Expr1 is less than or equal to Expr2 | - | Expr1 `#>` Expr2 | Expr1 is greater than Expr2 | - | Expr1 `#<` Expr2 | Expr1 is less than Expr2 | +| Expr1 `#=` Expr2 | Expr1 equals Expr2 | +| Expr1 `#\=` Expr2 | Expr1 is not equal to Expr2 | +| Expr1 `#>=` Expr2 | Expr1 is greater than or equal to Expr2 | +| Expr1 `#=<` Expr2 | Expr1 is less than or equal to Expr2 | +| Expr1 `#>` Expr2 | Expr1 is greater than Expr2 | +| Expr1 `#<` Expr2 | Expr1 is less than Expr2 | `Expr1` and `Expr2` denote *arithmetic expressions*, which are: - | _integer_ | Given value | - | _variable_ | Unknown integer | - | ?(_variable_) | Unknown integer | - | -Expr | Unary minus | - | Expr + Expr | Addition | - | Expr * Expr | Multiplication | - | Expr - Expr | Subtraction | - | Expr ^ Expr | Exponentiation | - | min(Expr,Expr) | Minimum of two expressions | - | max(Expr,Expr) | Maximum of two expressions | - | Expr `mod` Expr | Modulo induced by floored division | - | Expr `rem` Expr | Modulo induced by truncated division | - | abs(Expr) | Absolute value | - | sign(Expr) | Sign (-1, 0, 1) of Expr | - | Expr // Expr | Truncated integer division | - | Expr div Expr | Floored integer division | +| _integer_ | Given value | +| _variable_ | Unknown integer | +| #(_variable_) | Unknown integer | +| -Expr | Unary minus | +| Expr + Expr | Addition | +| Expr * Expr | Multiplication | +| Expr - Expr | Subtraction | +| Expr ^ Expr | Exponentiation | +| min(Expr,Expr) | Minimum of two expressions | +| max(Expr,Expr) | Maximum of two expressions | +| Expr `mod` Expr | Modulo induced by floored division | +| Expr `rem` Expr | Modulo induced by truncated division | +| abs(Expr) | Absolute value | +| sign(Expr) | Sign (-1, 0, 1) of Expr | +| Expr // Expr | Truncated integer division | +| Expr div Expr | Floored integer division | where `Expr` again denotes an arithmetic expression. @@ -416,19 +417,19 @@ reason about integers. Therefore, it is recommended that you put the following directive in your =|~/.scryerrc|= initialisation file to make CLP(ℤ) constraints available in all your programs: -== +``` :- use_module(library(clpz)). -== +``` Throughout the following, it is assumed that you have done this. The most basic use of CLP(ℤ) constraints is _evaluation_ of arithmetic expressions involving integers. For example: -== +``` ?- X #= 1+2. -X = 3. -== + X = 3. +``` This could in principle also be achieved with the lower-level predicate `(is)/2`. However, an important advantage of arithmetic @@ -436,22 +437,22 @@ constraints is their purely relational nature: Constraints can be used in _all directions_, also if one or more of their arguments are only partially instantiated. For example: -== +``` ?- 3 #= Y+2. -Y = 1. -== + Y = 1. +``` This relational nature makes CLP(ℤ) constraints easy to explain and use, and well suited for beginners and experienced Prolog programmers alike. In contrast, when using low-level integer arithmetic, we get: -== +``` ?- 3 is Y+2. -ERROR: is/2: Arguments are not sufficiently instantiated + error(instantiation_error,(is)/2). ?- 3 =:= Y+2. -ERROR: =:=/2: Arguments are not sufficiently instantiated -== + error(instantiation_error,(is)/2). +``` Due to the necessary operational considerations, the use of these low-level arithmetic predicates is considerably harder to understand @@ -467,19 +468,19 @@ constraints at compilation time so that low-level arithmetic predicates are _automatically_ used whenever possible. For example, the predicate: -== +``` positive_integer(N) :- N #>= 1. -== +``` is executed as if it were written as: -== +``` positive_integer(N) :- ( integer(N) -> N >= 1 ; N #>= 1 ). -== +``` This illustrates why the performance of CLP(ℤ) constraints is almost always completely satisfactory when they are used in modes that can be @@ -504,50 +505,50 @@ simple example. Consider first a rather conventional definition of `n_factorial/2`, relating each natural number _N_ to its factorial _F_: -== +``` n_factorial(0, 1). n_factorial(N, F) :- N #> 0, N1 #= N - 1, n_factorial(N1, F1), F #= N * F1. -== +``` This program uses CLP(ℤ) constraints _instead_ of low-level arithmetic throughout, and everything that _would have worked_ with low-level arithmetic _also_ works with CLP(ℤ) constraints, retaining roughly the same performance. For example: -== +``` ?- n_factorial(47, F). -F = 258623241511168180642964355153611979969197632389120000000000 ; -false. -== + F = 258623241511168180642964355153611979969197632389120000000000 +; false. +``` Now the point: Due to the increased flexibility and generality of CLP(ℤ) constraints, we are free to _reorder_ the goals as follows: -== +``` n_factorial(0, 1). n_factorial(N, F) :- N #> 0, N1 #= N - 1, F #= N * F1, n_factorial(N1, F1). -== +``` In this concrete case, _termination_ properties of the predicate are improved. For example, the following queries now both terminate: -== +``` ?- n_factorial(N, 1). -N = 0 ; -N = 1 ; -false. + N = 0 +; N = 1 +; false. ?- n_factorial(N, 3). -false. -== + false. +``` To make the predicate terminate if _any_ argument is instantiated, add the (implied) constraint `F #\= 0` before the recursive call. @@ -558,8 +559,8 @@ The value of CLP(ℤ) constraints does _not_ lie in completely freeing us from _all_ procedural phenomena. For example, the two programs do not even have the same _termination properties_ in all cases. Instead, the primary benefit of CLP(ℤ) constraints is that they allow -you to try different execution orders and apply [**declarative -debugging**](https://www.metalevel.at/prolog/debugging.html) +you to try different execution orders and apply [*declarative +debugging*](https://www.metalevel.at/prolog/debugging.html) techniques _at all_! Reordering goals (and clauses) can significantly impact the performance of Prolog programs, and you are free to try different variants if you use declarative approaches. Moreover, since @@ -601,7 +602,7 @@ and by enumeration predicates like labeling/2. As another example, consider _Sudoku_: It is a popular puzzle over integers that can be easily solved with CLP(ℤ) constraints. -== +``` sudoku(Rows) :- length(Rows, 9), maplist(same_length(Rows), Rows), append(Rows, Vs), Vs ins 1..9, @@ -627,23 +628,23 @@ problem(1, [[_,_,_,_,_,_,_,_,_], [5,_,_,_,_,_,_,7,3], [_,_,2,_,1,_,_,_,_], [_,_,_,_,4,_,_,_,9]]). -== +``` Sample query: -== -?- problem(1, Rows), sudoku(Rows), maplist(writeln, Rows). -[9,8,7,6,5,4,3,2,1] -[2,4,6,1,7,3,9,8,5] -[3,5,1,9,2,8,7,4,6] -[1,2,8,5,3,7,6,9,4] -[6,3,4,8,9,2,1,5,7] -[7,9,5,4,6,1,8,3,2] -[5,1,9,2,8,6,4,7,3] -[4,7,2,3,1,9,5,6,8] -[8,6,3,7,4,5,2,1,9] -Rows = [[9, 8, 7, 6, 5, 4, 3, 2|...], ... , [...|...]]. -== +``` +?- problem(1, Rows), sudoku(Rows), maplist(portray_clause, Rows). +[9,8,7,6,5,4,3,2,1]. +[2,4,6,1,7,3,9,8,5]. +[3,5,1,9,2,8,7,4,6]. +[1,2,8,5,3,7,6,9,4]. +[6,3,4,8,9,2,1,5,7]. +[7,9,5,4,6,1,8,3,2]. +[5,1,9,2,8,6,4,7,3]. +[4,7,2,3,1,9,5,6,8]. +[8,6,3,7,4,5,2,1,9]. + Rows = [[9,8,7,6,5,4,3,2,1]|...]. +``` In this concrete case, the constraint solver is strong enough to find the unique solution without any search. @@ -653,31 +654,29 @@ the unique solution without any search. Here is an example session with a few queries and their answers: -== +``` ?- X #> 3. -X in 4..sup. + clpz:(X in 4..sup). ?- X #\= 20. -X in inf..19\/21..sup. + clpz:(X in inf..19\/21..sup). ?- 2*X #= 10. -X = 5. + X = 5. ?- X*X #= 144. -X in -12\/12. + clpz:(X in-12\/12) +; false. ?- 4*X + 2*Y #= 24, X + Y #= 9, [X,Y] ins 0..sup. -X = 3, -Y = 6. + X = 3, Y = 6. ?- X #= Y #<==> B, X in 0..3, Y in 4..5. -B = 0, -X in 0..3, -Y in 4..5. -== + B = 0, clpz:(X in 0..3), clpz:(Y in 4..5). +``` The answers emitted by the toplevel are called _residual programs_, -and the goals that comprise each answer are called **residual goals**. +and the goals that comprise each answer are called *residual goals*. In each case above, and as for all pure programs, the residual program is declaratively equivalent to the original query. From the residual goals, it is clear that the constraint solver has deduced additional @@ -689,12 +688,12 @@ make sure that all constrained variables are displayed. To make the constraints a variable is involved in available as a Prolog term for further reasoning within your program, use copy_term/3. For example: -== +``` ?- X #= Y + Z, X in 0..5, copy_term([X,Y,Z], [X,Y,Z], Gs). Gs = [clpz: (X in 0..5), clpz: (Y+Z#=X)], X in 0..5, Y+Z#=X. -== +``` This library also provides _reflection_ predicates (like fd_dom/2, fd_size/2 etc.) with which we can inspect a variable's current @@ -722,7 +721,7 @@ cryptoarithmetic puzzle SEND + MORE = MONEY, where different letters denote distinct integers between 0 and 9. It can be modeled in CLP(ℤ) as follows: -== +``` puzzle([S,E,N,D] + [M,O,R,E] = [M,O,N,E,Y]) :- Vars = [S,E,N,D,M,O,R,Y], Vars ins 0..9, @@ -731,14 +730,14 @@ puzzle([S,E,N,D] + [M,O,R,E] = [M,O,N,E,Y]) :- M*1000 + O*100 + R*10 + E #= M*10000 + O*1000 + N*100 + E*10 + Y, M #\= 0, S #\= 0. -== +``` Notice that we are _not_ using labeling/2 in this predicate, so that we can first execute and observe the modeling part in isolation. Sample query and its result (actual variables replaced for readability): -== +``` ?- puzzle(As+Bs=Cs). As = [9, A2, A3, A4], Bs = [1, 0, B3, A2], @@ -750,7 +749,7 @@ A3 in 5..8, A4 in 2..8, B3 in 2..8, C5 in 2..8. -== +``` From this answer, we see that this core relation _terminates_ and is in fact _deterministic_. Moreover, we see from the residual goals that @@ -761,13 +760,11 @@ parts are cleanly separated. Labeling can then be used to search for solutions in a separate predicate or goal: -== +``` ?- puzzle(As+Bs=Cs), label(As). -As = [9, 5, 6, 7], -Bs = [1, 0, 8, 5], -Cs = [1, 0, 6, 5, 2] ; -false. -== + As = [9,5,6,7], Bs = [1,0,8,5], Cs = [1,0,6,5,2] +; false. +``` In this case, it suffices to label a subset of variables to find the puzzle's unique solution, since the constraint solver is strong enough @@ -801,12 +798,12 @@ column, and which are subject to certain constraints. In fact, let us now generalize the task to the so-called _N queens puzzle_, which is obtained by replacing 8 by _N_ everywhere it occurs in the above description. We implement the above considerations in the -**core relation** `n_queens/2`, where the first argument is the number +*core relation* `n_queens/2`, where the first argument is the number of queens (which is identical to the number of rows and columns of the generalized chessboard), and the second argument is a list of _N_ integers that represents a solution in the form described above. -== +``` n_queens(N, Qs) :- length(Qs, N), Qs ins 1..N, @@ -821,7 +818,7 @@ safe_queens([Q|Qs], Q0, D0) :- abs(Q0 - Q) #\= D0, D1 #= D0 + 1, safe_queens(Qs, Q0, D1). -== +``` Note that all these predicates can be used in _all directions_: We can use them to _find_ solutions, _test_ solutions and _complete_ @@ -829,22 +826,25 @@ partially instantiated solutions. The original task can be readily solved with the following query: -== +``` ?- n_queens(8, Qs), label(Qs). -Qs = [1, 5, 8, 6, 3, 7, 2, 4] . -== + Qs = [1,5,8,6,3,7,2,4] +; ... . +``` Using suitable labeling strategies, we can easily find solutions with 80 queens and more: -== +``` ?- n_queens(80, Qs), labeling([ff], Qs). -Qs = [1, 3, 5, 44, 42, 4, 50, 7, 68|...] . + Qs = [1,3,5,44,42,4,50,7,68,57,76,61,6,39,30,40,8,54,36,41,...] +; ... . ?- time((n_queens(90, Qs), labeling([ff], Qs))). -% 5,904,401 inferences, 0.722 CPU in 0.737 seconds (98% CPU) -Qs = [1, 3, 5, 50, 42, 4, 49, 7, 59|...] . -== + % CPU time: 31.351s + Qs = [1,3,5,50,42,4,49,7,59,48,46,63,6,55,47,64,8,70,58,67,...] +; ... . +``` Experimenting with different search strategies is easy because we have separated the core relation from the actual search. @@ -868,7 +868,7 @@ If necessary, we can use `once/1` to commit to the first optimal solution. However, it is often very valuable to see alternative solutions that are _also_ optimal, so that we can choose among optimal solutions by other criteria. For the sake of -[**purity**](https://www.metalevel.at/prolog/purity.html) and +[*purity*](https://www.metalevel.at/prolog/purity.html) and completeness, we recommend to avoid `once/1` and other constructs that lead to impurities in CLP(ℤ) programs. @@ -883,13 +883,13 @@ _reified_, which means reflecting their truth values into Boolean values represented by the integers 0 and 1. Let P and Q denote reifiable constraints or Boolean variables, then: - | #\ Q | True iff Q is false | - | P #\/ Q | True iff either P or Q | - | P #/\ Q | True iff both P and Q | - | P #\ Q | True iff either P or Q, but not both | - | P #<==> Q | True iff P and Q are equivalent | - | P #==> Q | True iff P implies Q | - | P #<== Q | True iff Q implies P | +| #\ Q | True iff Q is false | +| P #\/ Q | True iff either P or Q | +| P #/\ Q | True iff both P and Q | +| P #\ Q | True iff either P or Q, but not both | +| P #<==> Q | True iff P and Q are equivalent | +| P #==> Q | True iff P implies Q | +| P #<== Q | True iff Q implies P | The constraints of this table are reifiable as well. @@ -902,32 +902,32 @@ In the default execution mode, CLP(ℤ) constraints still exhibit some non-relational properties. For example, _adding_ constraints can yield new solutions: -== +``` ?- X #= 2, X = 1+1. -false. + false. ?- X = 1+1, X #= 2, X = 1+1. -X = 1+1. -== + X = 1+1. +``` This behaviour is highly problematic from a logical point of view, and it may render declarative debugging techniques inapplicable. -Assert `clpz:monotonic` to make CLP(ℤ) **monotonic**: This means +Assert `clpz:monotonic` to make CLP(ℤ) *monotonic*: This means that _adding_ new constraints _cannot_ yield new solutions. When this flag is `true`, we must wrap variables that occur in arithmetic expressions with the functor `(?)/1` or `(#)/1`. For example: -== +``` ?- assertz(clpz:monotonic). -true. + true. ?- #X #= #Y + #Z. clpz:(#Y+ #Z#= #X). ?- X #= 2, X = 1+1. -ERROR: Arguments are not sufficiently instantiated -== + error(instantiation_error,instantiation_error(unknown(_408),1)). +``` The wrapper can be omitted for variables that are already constrained to integers. @@ -942,7 +942,7 @@ As an example of how it can be done currently, let us define a new custom constraint `oneground(X,Y,Z)`, where Z shall be 1 if at least one of X and Y is instantiated: -== +``` :- multifile clpz:run_propagator/2. oneground(X, Y, Z) :- @@ -956,7 +956,7 @@ clpz:run_propagator(oneground(X, Y, Z), MState) :- ; integer(Y) -> clpz:kill(MState), Z = 1 ; true ). -== +``` First, clpz:make_propagator/2 is used to transform a user-defined representation of the new constraint to an internal form. With @@ -973,12 +973,12 @@ that can be used to prevent further invocations of the propagator when the constraint has become entailed, by using clpz:kill/1. An example of using the new constraint: -== +``` ?- oneground(X, Y, Z), Y = 5. Y = 5, Z = 1, X in inf..sup. -== +``` @author [Markus Triska](https://www.metalevel.at) */ @@ -1718,7 +1718,7 @@ intervals_to_domain(Is, D) :- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% ?Var in +Domain +%% in(?Var, +Domain) % % Var is an element of Domain. Domain is one of: % @@ -1745,7 +1745,7 @@ fd_variable(V) :- ; type_error(integer, V) ). -%% +Vars ins +Domain +%% ins(+Vars, +Domain) % % The variables in the list Vars are elements of Domain. @@ -1850,18 +1850,18 @@ label(Vs) :- labeling([], Vs). % must make Expr ground. If several such options are specified, they % are interpreted from left to right, e.g.: % -% == +% ``` % ?- [X,Y] ins 10..20, labeling([max(X),min(Y)],[X,Y]). -% == +% ``` % % This generates solutions in descending order of X, and for each % binding of X, solutions are generated in ascending order of Y. To % obtain the incomplete behaviour that other systems exhibit with % "maximize(Expr)" and "minimize(Expr)", use once/1, e.g.: % -% == +% ``` % once(labeling([max(Expr)], Vars)) -% == +% ``` % % Labeling is always complete, always terminates, and yields no % redundant solutions. @@ -2232,12 +2232,12 @@ all_different([X|Right], Left, Orig) :- % can detect that not all variables can assume distinct values given % the following domains: % -% == +% ``` % ?- maplist(in, Vs, % [1\/3..4, 1..2\/4, 1..2\/4, 1..3, 1..3, 1..6]), % all_distinct(Vs). % false. -% == +% ``` all_distinct(Ls) :- fd_must_be_list(Ls, all_distinct(Ls)-1), @@ -2268,13 +2268,13 @@ zero_or_more([_|_], N) :- N #> 0. % The sum of elements of the list Vars is in relation Rel to Expr. % Rel is one of #=, #\=, #<, #>, #=< or #>=. For example: % -% == +% ``` % ?- [A,B,C] ins 0..sup, sum([A,B,C], #=, 100). % A in 0..100, % A+B+C#=100, % B in 0..100, % C in 0..100. -% == +% ``` sum(Vs, Op, Value) :- must_be(list, Vs), @@ -2902,24 +2902,24 @@ match_goal(p(Prop), _) --> -%% ?X #>= ?Y +%% #>=(?X, ?Y) % -% Same as Y #=< X. When reasoning over integers, replace >=/2 by #>=/2 +% Same as Y #=< X. When reasoning over integers, replace (>=)/2 by (#>=)/2 % to obtain more general relations. X #>= Y :- clpz_geq(X, Y). clpz_geq(X, Y) :- clpz_geq_(X, Y), reinforce(X), reinforce(Y). -%% ?X #=< ?Y +%% #=<(?X, ?Y) % % The arithmetic expression X is less than or equal to Y. When -% reasoning over integers, replace == X. -%% ?X #= ?Y +%% #=(?X, ?Y) % % The arithmetic expression X equals Y. When reasoning over integers, % replace is/2 by #=/2 to obtain more general relations. @@ -3272,10 +3272,10 @@ integer_kroot_leq(L, U, N, K, R) :- ) ). -%% ?X #\= ?Y +%% #\=(?X, ?Y) % % The arithmetic expressions X and Y evaluate to distinct integers. -% When reasoning over integers, replace =\=/2 by #\=/2 to obtain more +% When reasoning over integers, replace (=\=)/2 by (#\=)/2 to obtain more % general relations. X #\= Y :- clpz_neq(X, Y), do_queue. @@ -3303,7 +3303,7 @@ neq_num(X, N) --> ). -%% ?X #> ?Y +%% #>(?X, ?Y) % % Same as Y #< X. @@ -3312,14 +3312,14 @@ X #> Y :- X #>= Y + 1. %% #<(?X, ?Y) % % The arithmetic expression X is less than Y. When reasoning over -% integers, replace Y :- X #>= Y + 1. % Ms = [ pair(1, 2)-pair(3, 4), % pair(1, 3)-pair(2, 4), % pair(1, 4)-pair(2, 3)]. -% == +% ``` X #< Y :- Y #> X. -%% #\ +Q +%% #\(+Q) % % The reifiable constraint Q does _not_ hold. For example, to obtain % the complement of a domain: % -% == +% ``` % ?- #\ X in -3..0\/10..80. % X in inf.. -4\/1..9\/81..sup. -% == +% ``` #\ Q :- reify(Q, 0), do_queue. -%% ?P #<==> ?Q +%% #<==>(?P, ?Q) % % P and Q are equivalent. For example: % -% == +% ``` % ?- X #= 4 #<==> B, X #\= 4. % B = 0, % X in inf..3\/5..sup. -% == +% ``` % The following example uses reified constraints to relate a list of % finite domain variables to the number of occurrences of a given value: % -% == +% ``` % vs_n_num(Vs, N, Num) :- % maplist(eq_b(N), Vs, Bs), % sum(Bs, #=, Num). % % eq_b(X, Y, B) :- X #= Y #<==> B. -% == +% ``` % % Sample queries and their results: % -% == +% ``` % ?- Vs = [X,Y,Z], Vs ins 0..1, vs_n_num(Vs, 4, Num). % Vs = [X, Y, Z], % Num = 0, @@ -3377,11 +3377,11 @@ X #< Y :- Y #> X. % X = 2, % Y = 2, % Z = 2. -% == +% ``` L #<==> R :- reify(L, B), reify(R, B), do_queue. -%% ?P #==> ?Q +%% #==>(?P, ?Q) % % P implies Q. @@ -3404,13 +3404,13 @@ L #==> R :- append(LPs, RPs, Ps), propagator_init_trigger([LB,RB], pimpl(LB,RB,Ps)). -%% ?P #<== ?Q +%% #<==(?P, ?Q) % % Q implies P. L #<== R :- R #==> L. -%% ?P #/\ ?Q +%% #/\(?P, ?Q) % % P and Q hold. @@ -3439,19 +3439,19 @@ conjunctive_neqs_vals(A #/\ B) --> conjunctive_neqs_vals(A), conjunctive_neqs_vals(B). -%% ?P #\/ ?Q +%% #\/(?P, ?Q) % % P or Q holds. For example, the sum of natural numbers below 1000 % that are multiples of 3 or 5: % -% == +% ``` % ?- findall(N, (N mod 3 #= 0 #\/ N mod 5 #= 0, N in 0..999, % indomain(N)), % Ns), % sum(Ns, #=, Sum). % Ns = [0, 3, 5, 6, 9, 10, 12, 15, 18|...], % Sum = 233168. -% == +% ``` L #\/ R :- ( disjunctive_eqs_var_drep(L #\/ R, Var, Drep) -> Var in Drep @@ -3483,7 +3483,7 @@ disjunctive_eqs_vals(A #\/ B) --> disjunctive_eqs_vals(A), disjunctive_eqs_vals(B). -%% ?P #\ ?Q +%% #\(?P, ?Q) % % Either P holds or Q holds, but not both. @@ -4296,18 +4296,18 @@ lex_le([V1|V1s], [V2|V2s]) :- % example, if 1 is compatible with 2 and 5, and 4 is compatible with 0 % and 3: % -% == +% ``` % ?- tuples_in([[X,Y]], [[1,2],[1,5],[4,0],[4,3]]), X = 4. % X = 4, % Y in 0\/3. -% == +% ``` % % As another example, consider a train schedule represented as a list % of quadruples, denoting departure and arrival places and times for % each train. In the following program, Ps is a feasible journey of % length 3 from A to D via trains that are part of the given schedule. % -% == +% ``` % trains([[1,2,0,1], % [2,3,4,5], % [2,3,0,1], @@ -4321,14 +4321,14 @@ lex_le([V1|V1s], [V2|V2s]) :- % T4 #> T3, % trains(Ts), % tuples_in(Ps, Ts). -% == +% ``` % % In this example, the unique solution is found without labeling: % -% == +% ``` % ?- threepath(1, 4, Ps). % Ps = [[1, 2, 0, 1], [2, 3, 4, 5], [3, 4, 8, 9]]. -% == +% ``` tuples_in(Tuples, Relation) :- must_be(list(list), Tuples), @@ -6444,24 +6444,24 @@ num_subsets([S|Ss], Dom, Num0, Num, NonSubs) :- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% serialized(+Starts, +Durations) +%% serialized(+Starts, +Durations) % -% Describes a set of non-overlapping tasks. -% Starts = [S_1,...,S_n], is a list of variables or integers, -% Durations = [D_1,...,D_n] is a list of non-negative integers. -% Constrains Starts and Durations to denote a set of -% non-overlapping tasks, i.e.: S_i + D_i =< S_j or S_j + D_j =< -% S_i for all 1 =< i < j =< n. Example: +% Describes a set of non-overlapping tasks. +% Starts = [S_1,...,S_n], is a list of variables or integers, +% Durations = [D_1,...,D_n] is a list of non-negative integers. +% Constrains Starts and Durations to denote a set of +% non-overlapping tasks, i.e.: S_i + D_i =< S_j or S_j + D_j =< +% S_i for all 1 =< i < j =< n. Example: % -% == -% ?- length(Vs, 3), -% Vs ins 0..3, -% serialized(Vs, [1,2,3]), -% label(Vs). -% Vs = [0, 1, 3] ; -% Vs = [2, 0, 3] ; -% false. -% == +% ``` +% ?- length(Vs, 3), +% Vs ins 0..3, +% serialized(Vs, [1,2,3]), +% label(Vs). +% Vs = [0,1,3] +% ; Vs = [2,0,3] +% ; false. +% ``` % % @see Dorndorf et al. 2000, "Constraint Propagation Techniques for the % Disjunctive Scheduling Problem" @@ -6541,10 +6541,10 @@ serialize_upper_bound(I, D_I, J, D_J, MState) --> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% element(?N, +Vs, ?V) +%% element(?N, +Vs, ?V) % -% The N-th element of the list of finite domain variables Vs is V. -% Analogous to nth1/3. +% The N-th element of the list of finite domain variables Vs is V. +% Analogous to nth1/3. element(N, Is, V) :- must_be(list, Is), @@ -6576,38 +6576,39 @@ integers_remaining([V|Vs], N0, Dom, D0, D) :- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% global_cardinality(+Vs, +Pairs) +%% global_cardinality(+Vs, +Pairs) % -% Global Cardinality constraint. Equivalent to -% global_cardinality(Vs, Pairs, []). Example: +% Global Cardinality constraint. Equivalent to +% `global_cardinality(Vs, Pairs, [])`. Example: % -% == -% ?- Vs = [_,_,_], global_cardinality(Vs, [1-2,3-_]), label(Vs). -% Vs = [1, 1, 3] ; -% Vs = [1, 3, 1] ; -% Vs = [3, 1, 1]. -% == +% ``` +% ?- Vs = [_,_,_], global_cardinality(Vs, [1-2,3-_]), label(Vs). +% Vs = [1,1,3] +% ; Vs = [1,3,1] +% ; Vs = [3,1,1] +% ; false. +% ``` global_cardinality(Xs, Pairs) :- global_cardinality(Xs, Pairs, []). -%% global_cardinality(+Vs, +Pairs, +Options) +%% global_cardinality(+Vs, +Pairs, +Options) % -% Global Cardinality constraint. Vs is a list of finite domain -% variables, Pairs is a list of Key-Num pairs, where Key is an -% integer and Num is a finite domain variable. The constraint holds -% iff each V in Vs is equal to some key, and for each Key-Num pair -% in Pairs, the number of occurrences of Key in Vs is Num. Options -% is a list of options. Supported options are: +% Global Cardinality constraint. Vs is a list of finite domain +% variables, Pairs is a list of Key-Num pairs, where Key is an +% integer and Num is a finite domain variable. The constraint holds +% iff each V in Vs is equal to some key, and for each Key-Num pair +% in Pairs, the number of occurrences of Key in Vs is Num. Options +% is a list of options. Supported options are: % -% * consistency(value) -% A weaker form of consistency is used. +% `consistency(value)` +% A weaker form of consistency is used. % -% * cost(Cost, Matrix) -% Matrix is a list of rows, one for each variable, in the order -% they occur in Vs. Each of these rows is a list of integers, one -% for each key, in the order these keys occur in Pairs. When -% variable v_i is assigned the value of key k_j, then the -% associated cost is Matrix_{ij}. Cost is the sum of all costs. +% `cost(Cost, Matrix)` +% Matrix is a list of rows, one for each variable, in the order +% they occur in Vs. Each of these rows is a list of integers, one +% for each key, in the order these keys occur in Pairs. When +% variable v\_i is assigned the value of key k\_j, then the +% associated cost is Matrix\_{ij}. Cost is the sum of all costs. global_cardinality(Xs, Pairs, Options) :- must_be(list(list), [Xs,Pairs,Options]), @@ -6959,21 +6960,22 @@ all_neq([X|Xs], C) :- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% circuit(+Vs) +%% circuit(+Vs) % -% True iff the list Vs of finite domain variables induces a -% Hamiltonian circuit. The k-th element of Vs denotes the -% successor of node k. Node indexing starts with 1. Examples: +% True iff the list Vs of finite domain variables induces a +% Hamiltonian circuit. The k-th element of Vs denotes the +% successor of node k. Node indexing starts with 1. Examples: % -% == -% ?- length(Vs, _), circuit(Vs), label(Vs). -% Vs = [] ; -% Vs = [1] ; -% Vs = [2, 1] ; -% Vs = [2, 3, 1] ; -% Vs = [3, 1, 2] ; -% Vs = [2, 3, 4, 1] . -% == +% ``` +% ?- length(Vs, _), circuit(Vs), label(Vs). +% Vs = [] +% ; Vs = [1] +% ; Vs = [2,1] +% ; Vs = [2,3,1] +% ; Vs = [3,1,2] +% ; Vs = [2,3,4,1] +% ; ... . +% ``` circuit(Vs) :- must_be(list, Vs), @@ -7060,21 +7062,21 @@ cumulative(Tasks) :- cumulative(Tasks, [limit(1)]). % For example, given the following predicate that relates three tasks % of durations 2 and 3 to a list containing their starting times: % -% == +% ``` % tasks_starts(Tasks, [S1,S2,S3]) :- % Tasks = [task(S1,3,_,1,_), % task(S2,2,_,1,_), % task(S3,2,_,1,_)]. -% == +% ``` % % We can use cumulative/2 as follows, and obtain a schedule: % -% == +% ``` % ?- tasks_starts(Tasks, Starts), Starts ins 0..10, % cumulative(Tasks, [limit(2)]), label(Starts). % Tasks = [task(0, 3, 3, 1, _G36), task(0, 2, 2, 1, _G45), ...], % Starts = [0, 0, 2] . -% == +% ``` cumulative(Tasks, Options) :- must_be(list(list), [Tasks,Options]), @@ -7204,22 +7206,23 @@ a_not_in_b([_,AX,AW,AY,AH], [_,BX,BW,BY,BH]) :- % example, a list of binary finite domain variables is constrained to % contain at least two consecutive ones: % -% == -% two_consecutive_ones(Vs) :- -% automaton(Vs, [source(a),sink(c)], -% [arc(a,0,a), arc(a,1,b), -% arc(b,0,a), arc(b,1,c), -% arc(c,0,c), arc(c,1,c)]). -% == +% ``` +% two_consecutive_ones(Vs) :- +% automaton(Vs, [source(a),sink(c)], +% [arc(a,0,a), arc(a,1,b), +% arc(b,0,a), arc(b,1,c), +% arc(c,0,c), arc(c,1,c)]). +% ``` % % Example query: % -% == -% ?- length(Vs, 3), two_consecutive_ones(Vs), label(Vs). -% Vs = [0, 1, 1] ; -% Vs = [1, 1, 0] ; -% Vs = [1, 1, 1]. -% == +% ``` +% ?- length(Vs, 3), two_consecutive_ones(Vs), label(Vs). +% Vs = [0,1,1] +% ; Vs = [1,1,0] +% ; Vs = [1,1,1] +% ; false. +% ``` automaton(Sigs, Ns, As) :- automaton(_, _, Sigs, Ns, As, [], [], _). @@ -7255,7 +7258,7 @@ automaton(Sigs, Ns, As) :- automaton(_, _, Sigs, Ns, As, [], [], _). % number of inflexions, which are switches between strictly ascending % and strictly descending subsequences: % -% == +% ``` % sequence_inflexions(Vs, N) :- % variables_signature(Vs, Sigs), % automaton(Sigs, _, Sigs, @@ -7276,11 +7279,11 @@ automaton(Sigs, Ns, As) :- automaton(_, _, Sigs, Ns, As, [], [], _). % Prev #< V #<==> S #= 1, % Prev #> V #<==> S #= 2, % variables_signature_(Vs, V, Sigs). -% == +% ``` % % Example queries: % -% == +% ``` % ?- sequence_inflexions([1,2,3,3,2,1,3,0], N). % N = 3. % @@ -7288,7 +7291,7 @@ automaton(Sigs, Ns, As) :- automaton(_, _, Sigs, Ns, As, [], [], _). % sequence_inflexions(Ls, 3), label(Ls). % Ls = [0, 1, 0, 1, 0] ; % Ls = [1, 0, 1, 0, 1]. -% == +% ``` template_var_path(V, Var, []) :- var(V), !, V == Var. template_var_path(T, Var, [N|Ns]) :- @@ -7416,7 +7419,7 @@ arc_normalized_(arc(S0,L,S), Cs, arc(S0,L,S,Cs)). % deterministic while preserving their generality and completeness. % For example: % -% == +% ``` % n_factorial(N, F) :- % zcompare(C, N, 0), % n_factorial_(C, N, F). @@ -7425,27 +7428,27 @@ arc_normalized_(arc(S0,L,S), Cs, arc(S0,L,S,Cs)). % n_factorial_(>, N, F) :- % F #= F0*N, N1 #= N - 1, % n_factorial(N1, F0). -% == +% ``` % % This version is deterministic if the first argument is instantiated, % because first argument indexing can distinguish the two different % clauses: % -% == +% ``` % ?- n_factorial(30, F). -% F = 265252859812191058636308480000000. -% == +% F = 265252859812191058636308480000000. +% ``` % % The predicate can still be used in all directions, including the % most general query: % -% == +% ``` % ?- n_factorial(N, F). -% N = 0, -% F = 1 ; -% N = F, F = 1 ; -% N = F, F = 2 . -% == +% N = 0, F = 1 +% ; N = 1, F = 1 +% ; N = 2, F = 2 +% ; ... . +% ``` zcompare(Order, A, B) :- ( nonvar(Order) -> @@ -7469,11 +7472,11 @@ zcompare_(>, A, B) :- #A #> #B. % Relation, in the order they appear in the list. Relation must be #=, % #=<, #>=, #< or #>. For example: % -% == +% ``` % ?- chain(#>=, [X,Y,Z]). % X#>=Y, % Y#>=Z. -% == +% ``` chain(Relation, Zs) :- must_be(list, Zs), @@ -7556,22 +7559,22 @@ fd_size(X, S) :- % following code, you can convert a _finite_ domain to a list of % integers: % -% == +% ``` % dom_integers(D, Is) :- phrase(dom_integers_(D), Is). % % dom_integers_(I) --> { integer(I) }, [I]. % dom_integers_(L..U) --> { numlist(L, U, Is) }, Is. % dom_integers_(D1\/D2) --> dom_integers_(D1), dom_integers_(D2). -% == +% ``` % % Example: % -% == +% ``` % ?- X in 1..5, X #\= 4, fd_dom(X, D), dom_integers(D, Is). % D = 1..3\/5, % Is = [1,2,3,5], % X in 1..3\/5. -% == +% ``` fd_dom(X, Drep) :- ( fd_get(X, XD, _) -> From b7d06540e601c63a1b5c271eb3341f607ac3783d Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 24 Jan 2023 21:11:07 +0100 Subject: [PATCH 062/361] DOC: convert library(crypto) documentation to DocLog format --- src/lib/crypto.pl | 696 +++++++++++++++++++++++----------------------- 1 file changed, 349 insertions(+), 347 deletions(-) diff --git a/src/lib/crypto.pl b/src/lib/crypto.pl index 458283b7..71c0420e 100644 --- a/src/lib/crypto.pl +++ b/src/lib/crypto.pl @@ -1,20 +1,20 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2020, 2021, 2022 by Markus Triska (triska@metalevel.at) + Written 2020-2023 by Markus Triska (triska@metalevel.at) Part of Scryer Prolog. - Predicates for cryptographic applications. +/** Predicates for cryptographic applications. - This library assumes that the Prolog flag double_quotes is set to chars. + This library assumes that the Prolog flag `double_quotes` is set to `chars`. In Scryer Prolog, lists of characters are very efficiently represented, and strings have the advantage that the atom table remains unmodified. Especially for cryptographic applications, it is an advantage that using strings leaves little trace of what was processed in the system. - For predicates that accept an encoding/1 option to specify the encoding - of the input data, if encoding(octet) is used, then the input can also - be specified as a list of bytes, i.e., integers between 0 and 255. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + For predicates that accept an `encoding/1` option to specify the encoding + of the input data, if `encoding(octet)` is used, then the input can also + be specified as a list of _bytes_, i.e., integers between 0 and 255. +*/ :- module(crypto, [hex_bytes/2, % ?Hex, ?Bytes @@ -48,20 +48,20 @@ :- use_module(library(si)). :- use_module(library(iso_ext), [partial_string/3]). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - hex_bytes(?Hex, ?Bytes) is det. - - Relation between a hexadecimal sequence and a list of bytes. Hex - is a string of hexadecimal numbers. Bytes is a list of *integers* - between 0 and 255 that represent the sequence as a list of bytes. - At least one of the arguments must be instantiated. - - Example: - - ?- hex_bytes("501ACE", Bs). - Bs = [80,26,206]. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% hex_bytes(?Hex, ?Bytes) is det. +% +% Relation between a hexadecimal sequence and a list of bytes. Hex +% is a string of hexadecimal numbers. Bytes is a list of _integers_ +% between 0 and 255 that represent the sequence as a list of bytes. +% At least one of the arguments must be instantiated. +% +% Example: +% +% ``` +% ?- hex_bytes("501ACE", Bs). +% Bs = [80,26,206]. +% ``` hex_bytes(Hs, Bytes) :- ( ground(Hs) -> @@ -113,47 +113,52 @@ must_be_octet_chars(Chars, Context) :- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cryptographically secure random numbers ======================================= - - crypto_n_random_bytes(+N, -Bytes) is det - - Bytes is unified with a list of N cryptographically secure - pseudo-random bytes. Each byte is an integer between 0 and 255. If - the internal pseudo-random number generator (PRNG) has not been - seeded with enough entropy to ensure an unpredictable byte - sequence, an exception is thrown. - - One way to relate such a list of bytes to an _integer_ is to use - CLP(ℤ) constraints as follows: - - :- use_module(library(clpz)). - :- use_module(library(lists)). - - bytes_integer(Bs, N) :- - foldl(pow, Bs, 0-0, N-_). - - pow(B, N0-I0, N-I) :- - B in 0..255, - N #= N0 + B*256^I0, - I #= I0 + 1. - - With this definition, we can generate a random 256-bit integer - _from_ a list of 32 random _bytes_: - - ?- crypto_n_random_bytes(32, Bs), - bytes_integer(Bs, I). - Bs = [146,166,162,210,242,7,25,132,64,94|...], - I = 337420085690608915485...(56 digits omitted). - - The above relation also works in the other direction, letting you - translate an integer _to_ a list of bytes. In addition, you can - use hex_bytes/2 to convert bytes to _tokens_ that can be easily - exchanged in your applications. - - ?- crypto_n_random_bytes(12, Bs), - hex_bytes(Hex, Bs). - Bs = [34,25,50,72,58,63,50,172,32,46|...], Hex = "221932483a3f32ac202 ...". - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% crypto_n_random_bytes(+N, -Bytes) is det. +% +% Bytes is unified with a list of N cryptographically secure +% pseudo-random bytes. Each byte is an integer between 0 and 255. If +% the internal pseudo-random number generator (PRNG) has not been +% seeded with enough entropy to ensure an unpredictable byte +% sequence, an exception is thrown. +% +% One way to relate such a list of bytes to an _integer_ is to use +% CLP(ℤ) constraints as follows: +% +% ``` +% :- use_module(library(clpz)). +% :- use_module(library(lists)). +% +% bytes_integer(Bs, N) :- +% foldl(pow, Bs, 0-0, N-_). +% +% pow(B, N0-I0, N-I) :- +% B in 0..255, +% N #= N0 + B*256^I0, +% I #= I0 + 1. +% ``` +% +% With this definition, we can generate a random 256-bit integer +% _from_ a list of 32 random _bytes_: +% +% ``` +% ?- crypto_n_random_bytes(32, Bs), +% bytes_integer(Bs, I). +% Bs = [146,166,162,210,242,7,25,132,64,94|...], +% I = 337420085690608915485...(56 digits omitted). +% ``` +% +% The above relation also works in the other direction, letting you +% translate an integer _to_ a list of bytes. In addition, you can +% use `hex_bytes/2` to convert bytes to _tokens_ that can be easily +% exchanged in your applications. +% +% ``` +% ?- crypto_n_random_bytes(12, Bs), +% hex_bytes(Hex, Bs). +% Bs = [34,25,50,72,58,63,50,172,32,46|...], Hex = "221932483a3f32ac202 ...". +% ``` crypto_n_random_bytes(N, Bs) :- must_be(integer, N), @@ -165,30 +170,34 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Hashing ======= - - crypto_data_hash(+Data, -Hash, +Options) - - Where Data is a list of characters, and Hash is the computed hash - as a list of hexadecimal characters. - - Options is a list of: - - - algorithm(+A) - where A is one of ripemd160, sha256, sha384, sha512, sha512_256, - sha3_224, sha3_256, sha3_384, sha3_512, blake2s256, blake2b512, - or a variable. If A is a variable, then it is unified with the - default algorithm, which is an algorithm that is considered - cryptographically secure at the time of this writing. - - encoding(+Encoding) - The default encoding is utf8. The alternative is octet, - to treat the input as a list of raw bytes. - - Example: - - ?- crypto_data_hash("abc", Hs, [algorithm(sha256)]). - Hs = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad". - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% crypto_data_hash(+Data, -Hash, +Options) +% +% Where Data is a list of characters, and Hash is the computed hash +% as a list of hexadecimal characters. +% +% Options is a list of: +% +% - `algorithm(+A)` +% where `A` is one of `ripemd160`, `sha256`, `sha384`, `sha512`, +% `sha512_256`, `sha3_224`, `sha3_256`, `sha3_384`, +% `sha3_512`, `blake2s256`, `blake2b512`, or a variable. If `A` is +% a variable, then it is unified with the default algorithm, +% which is an algorithm that is considered cryptographically +% secure at the time of this writing. +% +% - `encoding(+Encoding)` +% The default encoding is `utf8`. The alternative is `octet`, to +% treat the input as a list of raw bytes. +% +% Example: +% +% ``` +% ?- crypto_data_hash("abc", Hs, [algorithm(sha256)]). +% Hs = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad". +% ``` + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SHA256 is the current default for several hash-related predicates. It is deemed sufficiently secure for the foreseeable future. Yet, @@ -238,38 +247,36 @@ hash_algorithm(blake2s256). hash_algorithm(blake2b512). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - crypto_data_hkdf(+Data, +Length, -Bytes, +Options) is det. - - Concentrate possibly dispersed entropy of Data and then expand it - to the desired length. Data is a list of characters. - - Bytes is unified with a list of bytes of length Length, and is - suitable as input keying material and initialization vectors to - symmetric encryption algorithms. - - Admissible options are: - - - algorithm(+Algorithm) - One of sha256, sha384 or sha512. If you specify a variable, - then it is unified with the algorithm that was used, which is a - cryptographically secure algorithm by default. - - info(+Info) - Optional context and application specific information, - specified as a list of characters. The default is []. - - salt(+List) - Optionally, a list of bytes that are used as salt. The - default is all zeroes. - - encoding(+Encoding) - The default encoding is utf8. The alternative is octet, - to treat the input as a list of raw bytes. - - The `info/1` option can be used to generate multiple keys from a - single master key, using for example values such as "key" and - "iv", or the name of a file that is to be encrypted. - - See crypto_n_random_bytes/2 to obtain a suitable salt. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% crypto_data_hkdf(+Data, +Length, -Bytes, +Options) is det. +% +% Concentrate possibly dispersed entropy of Data and then expand it +% to the desired length. Data is a list of characters. +% +% Bytes is unified with a list of bytes of length Length, and is +% suitable as input keying material and initialization vectors to +% symmetric encryption algorithms. +% +% Admissible options are: +% +% - `algorithm(+Algorithm)` +% One of `sha256`, `sha384` or `sha512`. If you specify a variable, +% then it is unified with the algorithm that was used, which is a +% cryptographically secure algorithm by default. +% - `info(+Info)` +% Optional context and application specific information, +% specified as a list of characters. The default is `[]`. +% - `salt(+List)` +% Optionally, a list of bytes that are used as salt. The +% default is all zeroes. +% - `encoding(+Encoding)` +% The default encoding is `utf8`. The alternative is `octet`, +% to treat the input as a list of raw bytes. +% +% The `info/1` option can be used to generate multiple keys from a +% single master key, using for example values such as "key" and +% "iv", or the name of a file that is to be encrypted. +% +% See `crypto_n_random_bytes/2` to obtain a suitable salt. crypto_data_hkdf(Data0, L, Bytes, Options0) :- functor_hash_options(algorithm, Algorithm, Options0, Options), @@ -323,14 +330,12 @@ chars_bytes_(Cs, Bytes, Context) :- know if you need to rely on any specifics of this format. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - crypto_password_hash(+Password, ?Hash) is semidet. - - If Hash is instantiated, the predicate succeeds _iff_ the hash - matches the given password. Otherwise, the call is equivalent to - crypto_password_hash(Password, Hash, []) and computes a - password-based hash using the default options. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% crypto_password_hash(+Password, ?Hash) is semidet. +% +% If Hash is instantiated, the predicate succeeds _iff_ the hash +% matches the given password. Otherwise, the call is equivalent to +% `crypto_password_hash(Password, Hash, [])` and computes a +% password-based hash using the default options. crypto_password_hash(Password0, Hash) :- ( nonvar(Hash) -> @@ -353,58 +358,56 @@ dollar_segments(Ls, Segments) :- ). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - crypto_password_hash(+Password, -Hash, +Options) is det. - - Derive Hash based on Password. This predicate is similar to - crypto_data_hash/3 in that it derives a hash from given data. - However, it is tailored for the specific use case of _passwords_. - One essential distinction is that for this use case, the derivation - of a hash should be _as slow as possible_ to counteract brute-force - attacks over possible passwords. - - Another important distinction is that equal passwords must yield, - with very high probability, _different_ hashes. For this reason, - cryptographically strong random numbers are automatically added to - the password before a hash is derived. - - Hash is unified with a string that contains the computed hash and - all parameters that were used, except for the password. Instead of - storing passwords, store these hashes. Later, you can verify the - validity of a password with crypto_password_hash/2, comparing the - then entered password to the stored hash. If you need to export this - atom, you should treat it as opaque ASCII data with up to 255 bytes - of length. The maximal length may increase in the future. - - Admissible options are: - - - algorithm(+Algorithm) - The algorithm to use. Currently, the only available algorithm - is 'pbkdf2-sha512', which is therefore also the default. - - cost(+C) - C is an integer, denoting the binary logarithm of the number - of _iterations_ used for the derivation of the hash. This - means that the number of iterations is set to 2^C. Currently, - the default is 17, and thus more than one hundred _thousand_ - iterations. You should set this option as high as your server - and users can tolerate. The default is subject to change and - will likely increase in the future or adapt to new algorithms. - - salt(+Salt) - Use the given list of bytes as salt. By default, - cryptographically secure random numbers are generated for this - purpose. The default is intended to be secure, and constitutes - the typical use case of this predicate. - - Currently, PBKDF2 with SHA-512 is used as the hash derivation - function, using 128 bits of salt. All default parameters, including - the algorithm, are subject to change, and other algorithms will also - become available in the future. Since computed hashes store all - parameters that were used during their derivation, such changes will - not affect the operation of existing deployments. Note though that - new hashes will then be computed with the new default parameters. - - See crypto_data_hkdf/4 for generating keys from Hash. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% crypto_password_hash(+Password, -Hash, +Options) is det. +% +% Derive Hash based on Password. This predicate is similar to +% `crypto_data_hash/3` in that it derives a hash from given data. +% However, it is tailored for the specific use case of _passwords_. +% One essential distinction is that for this use case, the derivation +% of a hash should be _as slow as possible_ to counteract brute-force +% attacks over possible passwords. +% +% Another important distinction is that equal passwords must yield, +% with very high probability, _different_ hashes. For this reason, +% cryptographically strong random numbers are automatically added to +% the password before a hash is derived. +% +% Hash is unified with a string that contains the computed hash and +% all parameters that were used, except for the password. Instead of +% storing passwords, store these hashes. Later, you can verify the +% validity of a password with `crypto_password_hash/2`, comparing the +% then entered password to the stored hash. If you need to export this +% atom, you should treat it as opaque ASCII data with up to 255 bytes +% of length. The maximal length may increase in the future. +% +% Admissible options are: +% +% - `algorithm(+Algorithm)` +% The algorithm to use. Currently, the only available algorithm +% is `'pbkdf2-sha512'`, which is therefore also the default. +% - `cost(+C)` +% C is an integer, denoting the binary logarithm of the number +% of _iterations_ used for the derivation of the hash. This +% means that the number of iterations is set to 2^C. Currently, +% the default is 17, and thus more than one hundred _thousand_ +% iterations. You should set this option as high as your server +% and users can tolerate. The default is subject to change and +% will likely increase in the future or adapt to new algorithms. +% - `salt(+Salt)` +% Use the given list of bytes as salt. By default, +% cryptographically secure random numbers are generated for this +% purpose. The default is intended to be secure, and constitutes +% the typical use case of this predicate. +% +% Currently, PBKDF2 with SHA-512 is used as the hash derivation +% function, using 128 bits of salt. All default parameters, including +% the algorithm, are subject to change, and other algorithms will also +% become available in the future. Since computed hashes store all +% parameters that were used during their derivation, such changes will +% not affect the operation of existing deployments. Note though that +% new hashes will then be computed with the new default parameters. +% +% See `crypto_data_hkdf/4` for generating keys from Hash. crypto_password_hash(Password0, Hash, Options) :- chars_bytes_(Password0, Password, crypto_password_hash/3), @@ -435,97 +438,94 @@ bytes_base64(Bytes, Base64) :- chars_base64(Chars, Base64, [padding(false)]) ). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - crypto_data_encrypt(+PlainText, - +Algorithm, - +Key, - +IV, - -CipherText, - +Options). - - Encrypt the given PlainText, using the symmetric algorithm - Algorithm, key Key, and initialization vector (or nonce) IV, to - give CipherText. - - PlainText must be a list of characters, Key and IV must be lists of - bytes, and CipherText is created as a list of characters. - - Keys and IVs can be chosen at random (using for example - crypto_n_random_bytes/2) or derived from input keying material (IKM) - using for example crypto_data_hkdf/4. This input is often a shared - secret, such as a negotiated point on an elliptic curve, or the hash - that was computed from a password via crypto_password_hash/3 with a - freshly generated and specified _salt_. - - Reusing the same combination of Key and IV typically leaks at least - _some_ information about the plaintext. For example, identical - plaintexts will then correspond to identical ciphertexts. For some - algorithms, reusing an IV with the same Key has disastrous results - and can cause the loss of all properties that are otherwise - guaranteed. Especially in such cases, an IV is also called a - _nonce_ (number used once). - - It is safe to store and transfer the used initialization vector (or - nonce) in plain text, but the key _must be kept secret_. - - Currently, the only supported algorithm is 'chacha20-poly1305', a - powerful and efficient _authenticated_ encryption scheme, providing - secrecy and at the same time reliable protection against undetected - _modifications_ of the encrypted data. This is a very good choice - for virtually all use cases. It is a stream cipher and can encrypt - data of any length up to 256 GB. Further, the encrypted data has - exactly the same length as the original, and no padding is used. - - Options: - - - encoding(+Encoding) - Encoding to use for PlainText. Default is utf8. The alternative - is octet to treat PlainText as raw bytes. - - - tag(-List) - For authenticated encryption schemes, List is unified with a - list of _bytes_ holding the tag. This tag must be provided for - decryption. - - - aad(+Data) - Data is additional authenticated data (AAD), a list of - characters. It is authenticated in that it influences the tag, - but it is not encrypted. The encoding/1 option also specifies - the encoding of Data. - - Here is an example encryption and decryption, using the ChaCha20 - stream cipher with the Poly1305 authenticator. This cipher uses a - 256-bit key and a 96-bit nonce, i.e., 32 and 12 _bytes_, - respectively: - - ?- Algorithm = 'chacha20-poly1305', - crypto_n_random_bytes(32, Key), - crypto_n_random_bytes(12, IV), - crypto_data_encrypt("this text is to be encrypted", Algorithm, - Key, IV, CipherText, [tag(Tag)]), - crypto_data_decrypt(CipherText, Algorithm, - Key, IV, RecoveredText, [tag(Tag)]). - - Yielding: - - Algorithm = 'chacha20-poly1305', - Key = [113,247,153,134,177,220,13,193,50,150|...], - IV = [135,20,149,153,63,35,68,114,247,171|...], - CipherText = "\x94\0Ej\x94\®Â\x95\óÑÆXÃn¾ð©b\x1c\ ...", - RecoveredText = "this text is to be ...", - Tag = [152,117,152,17,162,75,150,206,144,40|...] - - In this example, we use crypto_n_random_bytes/2 to generate a key - and nonce from cryptographically secure random numbers. For - repeated applications, you must ensure that a nonce is only used - _once_ together with the same key. Note that for _authenticated_ - encryption schemes, the _tag_ that was computed during encryption - is necessary for decryption. It is safe to store and transfer the - tag in plain text. - - See also crypto_data_decrypt/6, and hex_bytes/2 for conversion - between bytes and hex encoding. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% crypto_data_encrypt(+PlainText, +Algorithm, +Key, +IV, -CipherText, +Options). +% +% Encrypt the given PlainText, using the symmetric algorithm +% Algorithm, key Key, and initialization vector (or nonce) IV, to +% give CipherText. +% +% PlainText must be a list of characters, Key and IV must be lists of +% bytes, and CipherText is created as a list of characters. +% +% Keys and IVs can be chosen at random (using for example +% `crypto_n_random_bytes/2`) or derived from input keying material (IKM) +% using for example `crypto_data_hkdf/4`. This input is often a shared +% secret, such as a negotiated point on an elliptic curve, or the hash +% that was computed from a password via `crypto_password_hash/3` with a +% freshly generated and specified _salt_. +% +% Reusing the same combination of Key and IV typically leaks at least +% _some_ information about the plaintext. For example, identical +% plaintexts will then correspond to identical ciphertexts. For some +% algorithms, reusing an IV with the same Key has disastrous results +% and can cause the loss of all properties that are otherwise +% guaranteed. Especially in such cases, an IV is also called a +% _nonce_ (number used once). +% +% It is safe to store and transfer the used initialization vector (or +% nonce) in plain text, but the key _must be kept secret_. +% +% Currently, the only supported algorithm is 'chacha20-poly1305', a +% powerful and efficient _authenticated_ encryption scheme, providing +% secrecy and at the same time reliable protection against undetected +% _modifications_ of the encrypted data. This is a very good choice +% for virtually all use cases. It is a stream cipher and can encrypt +% data of any length up to 256 GB. Further, the encrypted data has +% exactly the same length as the original, and no padding is used. +% +% Options: +% +% - `encoding(+Encoding)` +% Encoding to use for PlainText. Default is utf8. The alternative +% is octet to treat PlainText as raw bytes. +% +% - `tag(-List)` +% For authenticated encryption schemes, List is unified with a +% list of _bytes_ holding the tag. This tag must be provided for +% decryption. +% +% - `aad(+Data)` +% Data is additional authenticated data (AAD), a list of +% characters. It is authenticated in that it influences the tag, +% but it is not encrypted. The `encoding/1` option also specifies +% the encoding of Data. +% +% Here is an example encryption and decryption, using the ChaCha20 +% stream cipher with the Poly1305 authenticator. This cipher uses a +% 256-bit key and a 96-bit nonce, i.e., 32 and 12 _bytes_, +% respectively: +% +% ``` +% ?- Algorithm = 'chacha20-poly1305', +% crypto_n_random_bytes(32, Key), +% crypto_n_random_bytes(12, IV), +% crypto_data_encrypt("this text is to be encrypted", Algorithm, +% Key, IV, CipherText, [tag(Tag)]), +% crypto_data_decrypt(CipherText, Algorithm, +% Key, IV, RecoveredText, [tag(Tag)]). +% ``` +% +% Yielding: +% +% ``` +% Algorithm = 'chacha20-poly1305', +% Key = [113,247,153,134,177,220,13,193,50,150|...], +% IV = [135,20,149,153,63,35,68,114,247,171|...], +% CipherText = "\x94\0Ej\x94\®Â\x95\óÑÆXÃn¾ð©b\x1c\ ...", +% RecoveredText = "this text is to be ...", +% Tag = [152,117,152,17,162,75,150,206,144,40|...] +% ``` +% +% In this example, we use `crypto_n_random_bytes/2` to generate a key +% and nonce from cryptographically secure random numbers. For +% repeated applications, you must ensure that a nonce is only used +% _once_ together with the same key. Note that for _authenticated_ +% encryption schemes, the _tag_ that was computed during encryption +% is necessary for decryption. It is safe to store and transfer the +% tag in plain text. +% +% See also `crypto_data_decrypt/6`, and `hex_bytes/2` for conversion +% between bytes and hex encoding. crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :- options_data_chars(Options, PlainText0, PlainText, Encoding), @@ -549,37 +549,30 @@ algorithm_key_iv('chacha20-poly1305', Key, IV) :- length(Key, 32), length(IV, 12). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - crypto_data_decrypt(+CipherText, - +Algorithm, - +Key, - +IV, - -PlainText, - +Options). - - Decrypt the given CipherText, using the symmetric algorithm - Algorithm, key Key, and initialization vector IV, to give - PlainText. CipherText must be a list of characters, and Key and IV - must be lists of bytes. PlainText is created as a list of - characters. - - Currently, the only supported algorithm is 'chacha20-poly1305', - a very secure, fast and versatile authenticated encryption method. - - Options is a list of: - - - encoding(+Encoding) - Encoding to use for PlainText. The default is utf8. The - alternative is octet, which is used if the data are raw bytes. - - - tag(+Tag) - For authenticated encryption schemes, the tag must be specified as - a list of bytes exactly as they were generated upon encryption. - - - aad(+Data) - Any additional authenticated data (AAD) must be specified. The - encoding/1 option also specifies the encoding of Data. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% crypto_data_decrypt(+CipherText, +Algorithm, +Key, +IV, -PlainText, +Options). +% +% Decrypt the given CipherText, using the symmetric algorithm +% Algorithm, key Key, and initialization vector IV, to give +% PlainText. CipherText must be a list of characters, and Key and IV +% must be lists of bytes. PlainText is created as a list of +% characters. +% +% Currently, the only supported algorithm is 'chacha20-poly1305', +% a very secure, fast and versatile authenticated encryption method. +% +% Options is a list of: +% +% - `encoding(+Encoding)` +% Encoding to use for PlainText. The default is utf8. The +% alternative is octet, which is used if the data are raw bytes. +% +% - `tag(+Tag)` +% For authenticated encryption schemes, the tag must be specified as +% a list of bytes exactly as they were generated upon encryption. +% +% - `aad(+Data)` +% Any additional authenticated data (AAD) must be specified. The +% `encoding/1` option also specifies the encoding of Data. crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :- option(tag(Tag), Options, []), @@ -617,49 +610,53 @@ encoding_chars(utf8, Cs, Cs) :- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Digital signatures with Ed25519 =============================== - - - ed25519_new_keypair(-Pair) - Yields a new Ed25519 key pair Pair, a list of characters. The - pair contains the private key and must be kept absolutely secret. - Pair can be used for signing. Its public key can be obtained - with ed25519_keypair_public_key/2. - - - ed25519_keypair_public_key(+Pair, -PublicKey) - PublicKey is the public key of the given key pair. The public key - can be used for signature verification, and can be shared freely. - The public key is represented as a list of characters. - - - ed25519_sign(+Key, +Data, -Signature, +Options) - Key and Data must be lists of characters. Key is a key pair in - PKCS#8 v2 format as generated by ed25519_new_keypair/1. Sign Data - with Key, yielding Signature as a list of hexadecimal characters. - - - ed25519_verify(+Key, +Data, +Signature, +Options) - Key and Data must be lists of characters. Key is a public key. - Succeeds if Data was signed with the private key corresponding to - Key, where Signature is a list of hexadecimal characters as - generated by ed25519_sign/4. Fails otherwise. - - Currently, the only option for signing and verifying is: - - - encoding(+Encoding) - The default encoding of Data is utf8. The alternative is octet, - which treats Data as a list of raw bytes. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% ed25519_new_keypair(-Pair) +% +% Yields a new Ed25519 key pair Pair, a list of characters. The +% pair contains the private key and must be kept absolutely secret. +% Pair can be used for signing. Its public key can be obtained +% with `ed25519_keypair_public_key/2`. + ed25519_new_keypair(Pair) :- '$ed25519_new_keypair'(Pair). +%% ed25519_keypair_public_key(+Pair, -PublicKey) +% +% PublicKey is the public key of the given key pair. The public key +% can be used for signature verification, and can be shared freely. +% The public key is represented as a list of characters. + ed25519_keypair_public_key(Pair, PublicKey) :- must_be_octet_chars(Pair, ed25519_keypair_public_key), '$ed25519_keypair_public_key'(Pair, PublicKey). +%% ed25519_sign(+Key, +Data, -Signature, +Options) +% +% Key and Data must be lists of characters. Key is a key pair in +% PKCS#8 v2 format as generated by `ed25519_new_keypair/1`. Sign Data +% with Key, yielding Signature as a list of hexadecimal characters. + ed25519_sign(Key, Data0, Signature, Options) :- must_be_octet_chars(Key, ed25519_sign), options_data_chars(Options, Data0, Data, Encoding), '$ed25519_sign'(Key, Data, Encoding, Signature0), hex_bytes(Signature, Signature0). +%% ed25519_verify(+Key, +Data, +Signature, +Options) +% +% Key and Data must be lists of characters. Key is a public key. +% Succeeds if Data was signed with the private key corresponding to +% Key, where Signature is a list of hexadecimal characters as +% generated by `ed25519_sign/4`. Fails otherwise. +% +% Currently, the only option for signing and verifying is: +% +% - `encoding(+Encoding)` +% The default encoding of Data is `utf8`. The alternative is `octet`, +% which treats Data as a list of raw bytes. + ed25519_verify(Key, Data0, Signature0, Options) :- must_be_octet_chars(Key, ed25519_verify), options_data_chars(Options, Data0, Data, Encoding), @@ -669,38 +666,43 @@ ed25519_verify(Key, Data0, Signature0, Options) :- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - X25519: ECDH key exchange over Curve25519 ========================================= - - Points on Curve25519 are represented as lists of characters that denote - the u-coordinate of the Montgomery curve. - - - curve25519_generator(-Gs) - Gs is the generator point of Curve25519. - - - curve25519_scalar_mult(+Scalar, +Ps, -Rs) - Scalar must be an integer between 0 and 2^256-1, - or a list of 32 bytes, and Ps must be a point on the curve. - Computes the point Rs = Scalar*Ps as mandated by X25519. - - Alice and Bob can use this to establish a shared secret as follows, - where Gs is the generator point of Curve25519: - - 1. Alice creates a random integer a and sends As = a*Gs to Bob. - 2. Bob creates a random integer b and sends Bs = b*Gs to Alice. - 3. Alice computes Rs = a*Bs. - 4. Bob computes Rs = b*As. - 5. Alice and Bob use crypto_data_hkdf/4 on Rs with suitable - (same) parameters to obtain lists of bytes that can be used as - keys and initialization vectors for symmetric encryption. - - If a and b are kept secret, this method is considered very secure. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% curve25519_generator(-Gs) +% +% Points on Curve25519 are represented as lists of characters that +% denote the u-coordinate of the Montgomery curve. Gs is the +% generator point of Curve25519. + curve25519_generator(Gs) :- length(Gs0, 32), Gs0 = [9|Zs], maplist(=(0), Zs), maplist(char_code, Gs, Gs0). +%% curve25519_scalar_mult(+Scalar, +Ps, -Rs) +% +% Scalar must be an integer between 0 and 2^256-1, +% or a list of 32 bytes, and Ps must be a point on the curve. +% Computes the point _Rs = Scalar*Ps as_ mandated by X25519. +% +% Alice and Bob can use this to establish a shared secret as follows, +% where Gs is the generator point of Curve25519: +% +% 1. Alice creates a random integer _a_ and sends _As = a*Gs_ to Bob. +% +% 2. Bob creates a random integer _b_ and sends _Bs = b*Gs_ to Alice. +% +% 3. Alice computes _Rs = a*Bs_. +% +% 4. Bob computes _Rs = b*As_. +% +% 5. Alice and Bob use `crypto_data_hkdf/4` on Rs with suitable +% (same) parameters to obtain lists of bytes that can be used as +% keys and initialization vectors for symmetric encryption. +% +% If _a_ and _b_ are kept secret, this method is considered very secure. + curve25519_scalar_mult(Scalar, Point, Result) :- ( integer_si(Scalar) -> length(ScalarBytes, 32), From d742d4cde99941cabf86fb84ff5b38a61481ef95 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 24 Jan 2023 22:10:40 +0100 Subject: [PATCH 063/361] DOC: convert library(time) documentation to DocLog format --- src/lib/time.pl | 99 +++++++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 40 deletions(-) diff --git a/src/lib/time.pl b/src/lib/time.pl index 20187c2d..47e02fc5 100644 --- a/src/lib/time.pl +++ b/src/lib/time.pl @@ -1,47 +1,11 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2020, 2021 by Markus Triska (triska@metalevel.at) + Written 2020-2023 by Markus Triska (triska@metalevel.at) Part of Scryer Prolog. - - This library provides predicates for reasoning about time. - - current_time(T) yields the current system time in an opaque form, - called a time stamp. Use format_time//2 to describe strings that - contain attributes of the time stamp. - - The nonterminal format_time//2 describes a list of characters that - are formatted according to a format string. Usage: - - phrase(format_time(FormatString, TimeStamp), Cs) - - TimeStamp represents a moment in time in an opaque form, as for - example obtained by current_time/1. - - FormatString is a list of characters that are interpreted literally, - except for the following specifiers (and possibly more in the future): - - %Y year of the time stamp. Example: 2020. - %m month number (01-12), zero-padded to 2 digits - %d day number (01-31), zero-padded to 2 digits - %H hour number (00-24), zero-padded to 2 digits - %M minute number (00-59), zero-padded to 2 digits - %S second number (00-60), zero-padded to 2 digits - %b abbreviated month name, always 3 letters - %a abbreviated weekday name, always 3 letters - %A full weekday name - %j day of the year (001-366), zero-padded to 3 digits - %% the literal % - - Example: - - ?- current_time(T), phrase(format_time("%d.%m.%Y (%H:%M:%S)", T), Cs). - T = [...], Cs = "11.06.2020 (00:24:32)". - - sleep(S) sleeps for S seconds (a floating point number). - - time(Goal) reports the execution time of Goal. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/** This library provides predicates for reasoning about time. +*/ + :- module(time, [max_sleep_time/1, sleep/1, time/1, current_time/1, format_time//2]). :- use_module(library(format)). @@ -51,10 +15,51 @@ :- use_module(library(lists)). :- use_module(library(charsio), [read_from_chars/2]). + +%% current_time(-T) +% +% Yields the current system time _T_ in an opaque form, called a +% _time stamp_. Use `format_time//2` to describe strings that contain +% attributes of the time stamp. + current_time(T) :- '$current_time'(T0), read_from_chars(T0, T). +%% format_time(FormatString, TimeStamp)// +% +% The nonterminal format_time//2 describes a list of characters that +% are formatted according to a format string. Usage: +% +% ``` +% phrase(format_time(FormatString, TimeStamp), Cs) +% ``` +% +% TimeStamp represents a moment in time in an opaque form, as for +% example obtained by `current_time/1`. +% +% FormatString is a list of characters that are interpreted literally, +% except for the following specifiers (and possibly more in the future): +% +% | %Y | year of the time stamp. Example: 2020. | +% | %m | month number (01-12), zero-padded to 2 digits | +% | %d | day number (01-31), zero-padded to 2 digits | +% | %H | hour number (00-24), zero-padded to 2 digits | +% | %M | minute number (00-59), zero-padded to 2 digits | +% | %S | second number (00-60), zero-padded to 2 digits | +% | %b | abbreviated month name, always 3 letters | +% | %a | abbreviated weekday name, always 3 letters | +% | %A | full weekday name | +% | %j | day of the year (001-366), zero-padded to 3 digits | +% | %% | the literal % | +% +% Example: +% +% ``` +% ?- current_time(T), phrase(format_time("%d.%m.%Y (%H:%M:%S)", T), Cs). +% T = [...], Cs = "11.06.2020 (00:24:32)". +% ``` + format_time([], _) --> []. format_time(['%','%'|Fs], T) --> !, "%", format_time(Fs, T). format_time(['%',Spec|Fs], T) --> !, @@ -65,8 +70,17 @@ format_time(['%',Spec|Fs], T) --> !, format_time(Fs, T). format_time([F|Fs], T) --> [F], format_time(Fs, T). +%% max_sleep_time(T) +% +% The maximum admissible time span for `sleep/1`. + max_sleep_time(0xfffffffffffffbff). + +%% sleep(S) +% +% Sleeps for S seconds (a floating point number or integer). + sleep(T) :- builtins:must_be_number(T, sleep), ( T < 0 -> @@ -91,6 +105,11 @@ time_next_id(N) :- ), asserta(time_id(N)). + +%% time(Goal) +% +% Reports the execution time of Goal. + time(Goal) :- '$cpu_now'(T0), time_next_id(ID), From 6cb8020f6213bc413bf46e76eda5a15bd6a6d0a8 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 21 Jan 2023 16:16:53 +0100 Subject: [PATCH 064/361] DOC: add CLP(B) documentation in DocLog format --- src/lib/clpb.pl | 310 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 279 insertions(+), 31 deletions(-) diff --git a/src/lib/clpb.pl b/src/lib/clpb.pl index 72844408..546286ea 100644 --- a/src/lib/clpb.pl +++ b/src/lib/clpb.pl @@ -1,6 +1,6 @@ /* CLP(B): Constraint Logic Programming over Boolean Variables - Copyright (C): 2019 Markus Triska + Copyright (C): 2019-2023 Markus Triska All rights reserved. E-mail: triska@metalevel.at @@ -105,60 +105,309 @@ goal_expansion(del_attr(Var, Module), (var(Var) -> put_atts(Var, -Access);true)) Access =.. [Module,_]. -/** +/** Constraint Logic Programming over Boolean variables + +## Introduction + +This library provides CLP(B), Constraint Logic Programming over +Boolean variables. It can be used to model and solve combinatorial +problems such as verification, allocation and covering tasks. + +CLP(B) is an instance of the general CLP(_X_) scheme, +extending logic programming with reasoning over specialised domains. + +The implementation is based on reduced and ordered Binary Decision +Diagrams (BDDs). + +Benchmarks and usage examples of this library are available from: +[*https://www.metalevel.at/clpb/*](https://www.metalevel.at/clpb/) + +## Boolean expressions + +A _Boolean expression_ is one of: + +| `0` | false | +| `1` | true | +| _variable_ | unknown truth value | +| _atom_ | universally quantified variable | +| ~ _Expr_ | logical NOT | +| _Expr_ + _Expr_ | logical OR | +| _Expr_ * _Expr_ | logical AND | +| _Expr_ # _Expr_ | exclusive OR | +| _Var_ ^ _Expr_ | existential quantification | +| _Expr_ =:= _Expr_ | equality | +| _Expr_ =\= _Expr_ | disequality (same as #) | +| _Expr_ =< _Expr_ | less or equal (implication) | +| _Expr_ >= _Expr_ | greater or equal | +| _Expr_ < _Expr_ | less than | +| _Expr_ > _Expr_ | greater than | +| card(Is,Exprs) | cardinality constraint (_see below_) | +| `+(Exprs)` | n-fold disjunction (_see below_) | +| `*(Exprs)` | n-fold conjunction (_see below_) | + +where _Expr_ again denotes a Boolean expression. + +The Boolean expression `card(Is,Exprs)` is true iff the number of true +expressions in the list `Exprs` is a member of the list `Is` of +integers and integer ranges of the form `From-To`. For example, to +state that precisely two of the three variables `X`, `Y` and `Z` are +`true`, you can use `sat(card([2],[X,Y,Z]))`. + +`+(Exprs)` and `*(Exprs)` denote, respectively, the disjunction and +conjunction of all elements in the list `Exprs` of Boolean +expressions. + +Atoms denote parametric values that are universally quantified. All +universal quantifiers appear implicitly in front of the entire +expression. In residual goals, universally quantified variables always +appear on the right-hand side of equations. Therefore, they can be +used to express functional dependencies on input variables. + +## Interface predicates + +The most frequently used CLP(B) predicates are: + + * `sat(+Expr)` + True iff the Boolean expression Expr is satisfiable. + + * `taut(+Expr, -T)` + If Expr is a tautology with respect to the posted constraints, succeeds + with *T = 1*. If Expr cannot be satisfied, succeeds with *T = 0*. + Otherwise, it fails. + + * `labeling(+Vs)` + Assigns truth values to the variables Vs such that all constraints + are satisfied. + +The unification of a CLP(B) variable _X_ with a term _T_ is equivalent +to posting the constraint sat(X=:=T). + +## Examples + +Here is an example session with a few queries and their answers: + +``` +?- use_module(library(clpb)). + true. + +?- sat(X*Y). + X = 1, Y = 1. + +?- sat(X * ~X). + false. + +?- taut(X * ~X, T). + T = 0, clpb:sat(X=:=X). + +?- sat(X^Y^(X+Y)). + clpb:sat(X=:=X), clpb:sat(Y=:=Y). + +?- sat(X*Y + X*Z), labeling([X,Y,Z]). + X = 1, Y = 0, Z = 1 +; X = 1, Y = 1, Z = 0 +; X = 1, Y = 1, Z = 1. + +?- sat(X =< Y), sat(Y =< Z), taut(X =< Z, T). + T = 1, clpb:sat(X=:=X*Y), clpb:sat(Y=:=Y*Z). + +?- sat(1#X#a#b). + sat(X=:=a#b). +``` + +The pending residual goals constrain remaining variables to Boolean +expressions and are declaratively equivalent to the original query. +The last example illustrates that when applicable, remaining variables +are expressed as functions of universally quantified variables. + +## Obtaining BDDs + +By default, CLP(B) residual goals appear in (approximately) algebraic +normal form (ANF). This projection is often computationally expensive. +We can assert `clpb:clpb_residuals(bdd)` to see the BDD representation +of all constraints. This results in faster projection to residual +goals, and is also useful for learning more about BDDs. For example: + +``` +?- asserta(clpb:clpb_residuals(bdd)). + true. + +?- sat(X#Y). +node(3)- (v(X, 0)->node(2);node(1)), +node(1)- (v(Y, 1)->true;false), +node(2)- (v(Y, 1)->false;true). +``` + +Note that this representation cannot be pasted back on the toplevel, +and its details are subject to change. Use copy_term/3 to obtain +such answers as Prolog terms. + +The variable order of the BDD is determined by the order in which the +variables first appear in constraints. To obtain different orders, +we can for example use: + +``` +?- sat(+[1,Y,X]), sat(X#Y). +node(3)- (v(Y, 0)->node(2);node(1)), +node(1)- (v(X, 1)->true;false), +node(2)- (v(X, 1)->false;true). +``` + +## Enabling monotonic CLP(B) + +In the default execution mode, CLP(B) constraints are _not_ monotonic. +This means that _adding_ constraints can yield new solutions. For +example: + +``` +?- sat(X=:=1), X = 1+0. + false. + +?- X = 1+0, sat(X=:=1), X = 1+0. + X = 1+0. +``` + +This behaviour is highly problematic from a logical point of view, and +it may render [*declarative +debugging*](https://www.metalevel.at/prolog/debugging) +techniques inapplicable. + +Assert `clpb:monotonic` to make CLP(B) *monotonic*. If this mode is +enabled, then you must wrap CLP(B) variables with the functor +`v/1`. For example: + +``` +?- asserta(clpb:monotonic). + true. + +?- sat(v(X)=:=1#1). + X = 0. +``` + +## Example: Pigeons + +In this example, we are attempting to place _I_ pigeons into _J_ holes +in such a way that each hole contains at most one pigeon. One +interesting property of this task is that it can be formulated using +only _cardinality constraints_ (`card/2`). Another interesting aspect +is that this task has no short resolution refutations in general. + +In the following, we use [*Prolog DCG +notation*](https://www.metalevel.at/prolog/dcg) to describe a +list `Cs` of CLP(B) constraints that must all be satisfied. + +``` +:- use_module(library(clpb)). +:- use_module(library(clpz)). +:- use_module(library(lists)). +:- use_module(library(dcgs)). + +pigeon(I, J, Rows, Cs) :- + length(Rows, I), length(Row, J), + maplist(same_length(Row), Rows), + transpose(Rows, TRows), + phrase((all_cards(Rows,[1]),all_cards(TRows,[0,1])), Cs). + +all_cards([], _) --> []. +all_cards([Ls|Lss], Cs) --> [card(Cs,Ls)], all_cards(Lss, Cs). +``` + +Example queries: + +``` +?- pigeon(9, 8, Rows, Cs), sat(*(Cs)). + false. + +?- pigeon(2, 3, Rows, Cs), sat(*(Cs)), + append(Rows, Vs), labeling(Vs), + maplist(portray_clause, Rows). +[0,0,1]. +[0,1,0]. +etc. +``` + +## Example: Boolean circuit + +Consider a Boolean circuit that express the Boolean function =|XOR|= +with 4 =|NAND|= gates. We can model such a circuit with CLP(B) +constraints as follows: + +``` +:- use_module(library(clpb)). + +nand_gate(X, Y, Z) :- sat(Z =:= ~(X*Y)). + +xor(X, Y, Z) :- + nand_gate(X, Y, T1), + nand_gate(X, T1, T2), + nand_gate(Y, T1, T3), + nand_gate(T2, T3, Z). +``` + +Using universally quantified variables, we can show that the circuit +does compute =|XOR|= as intended: + +``` +?- xor(x, y, Z). +sat(Z=:=x#y). +``` + +## Acknowledgments + +The interface predicates of this library follow the example of +[*SICStus Prolog*](https://sicstus.sics.se). + +Use SICStus Prolog for higher performance in many cases. + +*/ + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Each CLP(B) variable belongs to exactly one BDD. Each CLP(B) variable gets an attribute (in module "clpb") of the form: - ``` - index_root(Index,Root) - ``` + index_root(Index,Root) where Index is the variable's unique integer index, and Root is the root of the BDD that the variable belongs to. - Each CLP(B) variable also gets an attribute in module `clpb_hash`: an + Each CLP(B) variable also gets an attribute in module clpb_hash: an association table node(LID,HID) -> Node, to keep the BDD reduced. The association table of each variable must be rebuilt on occasion to remove nodes that are no longer reachable. We rebuild the association tables of involved variables after BDDs are merged to build a new root. This only serves to reclaim memory: Keeping a node in a local table even when it no longer occurs in any BDD does - not affect the solver's correctness. However, `apply_shortcut/4` + not affect the solver's correctness. However, apply_shortcut/4 relies on the invariant that every node that occurs in the relevant BDDs is also registered in the table of its branching variable. - A root is a logical variable with a single attribute ("clpb\_bdd") + A root is a logical variable with a single attribute ("clpb_bdd") of the form: - ``` - Sat-BDD - ``` + Sat-BDD where Sat is the SAT formula (in original form) that corresponds to BDD. Sat is necessary to rebuild the BDD after variable aliasing, - and to project all remaining constraints to a list of `sat/1` goals. + and to project all remaining constraints to a list of sat/1 goals. Finally, a BDD is either: - * The integers 0 or 1, denoting false and true, respectively, or - * A node of the form + *) The integers 0 or 1, denoting false and true, respectively, or + *) A node of the form - ``` - node(ID, Var, Low, High, Aux) - ``` - - Where ID is the node's unique integer ID, Var is the - node's branching variable, and Low and High are the - node's low (Var = 0) and high (Var = 1) children. Aux - is a free variable, one for each node, that can be used - to attach attributes and store intermediate results. + node(ID, Var, Low, High, Aux) + Where ID is the node's unique integer ID, Var is the + node's branching variable, and Low and High are the + node's low (Var = 0) and high (Var = 1) children. Aux + is a free variable, one for each node, that can be used + to attach attributes and store intermediate results. Variable aliasing is treated as a conjunction of corresponding SAT formulae. You should think of CLP(B) as a potentially vast collection of BDDs that can range from small to gigantic in size, and which can merge. -*/ +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Type checking. @@ -1117,16 +1366,14 @@ indomain(1). % % ``` % ?- sat(A =< B), Vs = [A,B], sat_count(+[1|Vs], Count). -% Vs = [A, B], -% Count = 3, -% sat(A=:=A*B). +% Vs = [A,B], Count = 3, clpb:sat(A=:=A*B). % % ?- length(Vs, 120), % sat_count(+Vs, CountOr), % sat_count(*(Vs), CountAnd). -% Vs = [...], -% CountOr = 1329227995784915872903807060280344575, -% CountAnd = 1. +% Vs = [...], +% CountOr = 1329227995784915872903807060280344575, +% CountAnd = 1. % ``` @@ -1266,7 +1513,8 @@ random_bindings(VNum, Node) --> % % ``` % ?- sat(A#B), weighted_maximum([1,2,1], [A,B,C], Maximum). -% A = 0, B = 1, C = 1, Maximum = 3. +% A = 0, B = 1, C = 1, Maximum = 3 +% ; false. % ``` weighted_maximum(Ws, Vars, Max) :- From 1c08b56e05d0426edb1788c390918f82985dcd1d Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 21 Jan 2023 16:01:30 +0100 Subject: [PATCH 065/361] strengthen reified division for divisor == 1 --- src/lib/clpz.pl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index de522ab6..0677b840 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -5842,6 +5842,10 @@ run_propagator(preified_slash(X, Y, D, R), MState) --> ( Y == 0 -> kill(MState), D = 0 + ; Y == 1 -> + kill(MState), + D = 1, + R = X ; nonvar(X), nonvar(Y) -> kill(MState), From af9f0f81d8b546f97d768bacc75d9ae0452a5171 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 24 Jan 2023 22:42:58 +0100 Subject: [PATCH 066/361] DOC: convert library(sgml) documentation to DocLog format --- src/lib/sgml.pl | 111 +++++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/src/lib/sgml.pl b/src/lib/sgml.pl index a0370e8c..bccba1f3 100644 --- a/src/lib/sgml.pl +++ b/src/lib/sgml.pl @@ -2,57 +2,70 @@ Predicates for parsing HTML and XML documents. Written 2020-2022 by Markus Triska (triska@metalevel.at) Part of Scryer Prolog. - - Currently, two predicates are provided: - - - load_html(+Source, -Es, +Options) - - load_xml(+Source, -Es, +Options) - - These predicates parse HTML and XML documents, respectively. - - Source must be one of: - - - a list of characters with the document contents - - stream(S), specifying a stream S from which to read the content - - file(Name), where Name is a list of characters specifying a file name. - - Es is unified with the abstract syntax tree of the parsed document, - represented as a list of elements where each is of the form: - - * a list of characters, representing text - * element(Name, Attrs, Children) - - Name, an atom, is the name of the tag - - Attrs is a list of Key=Value pairs: - Key is an atom, and Value is a list of characters - - Children is a list of elements as specified here. - - Currently, Options are ignored. In the future, more options may be - provided to control parsing. - - Example: - - ?- load_html("Hello!", Es, []). - - Yielding: - - Es = [element(html,[], - [element(head,[], - [element(title,[], - ["Hello!"])]), - element(body,[],[])])]. - - library(xpath) provides convenient reasoning about parsed documents. - For example, to fetch the title of the document above, we can use: - - ?- load_html("Hello!", Es, []), - xpath(Es, //title(text), T). - - Yielding T = "Hello!". - - Use http_open/3 from library(http/http_open) to read answers from - web servers via streams. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/** Predicates for parsing HTML and XML documents. + +Currently, two predicates are provided: + + - `load_html(+Source, -Es, +Options)` + - `load_xml(+Source, -Es, +Options)` + +These predicates parse HTML and XML documents, respectively. + +Source must be one of: + + - a list of characters with the document contents + - `stream(S)`, specifying a stream S from which to read the content + - `file(Name)`, where Name is a list of characters specifying a file name. + +Es is unified with the abstract syntax tree of the parsed document, +represented as a list of elements where each is of the form: + + * a list of characters, representing text + + * `element(Name, Attrs, Children)` + + - `Name`, an atom, is the name of the tag + + - `Attrs` is a list of `Key=Value` pairs: + `Key` is an atom, and `Value` is a list of characters + + - `Children` is a list of elements as specified here. + +Currently, Options are ignored. In the future, more options may be +provided to control parsing. + +Example: + +``` + ?- load_html("Hello!", Es, []). +``` + +Yielding: + +``` + Es = [element(html,[], + [element(head,[], + [element(title,[], + ["Hello!"])]), + element(body,[],[])])]. +``` + +`library(xpath)` provides convenient reasoning about parsed documents. +For example, to fetch the title of the document above, we can use: + +``` + ?- load_html("Hello!", Es, []), + xpath(Es, //title(text), T). +``` + +Yielding `T = "Hello!"`. + +Use `http_open/3` from `library(http/http_open)` to read answers from +web servers via streams. +*/ + :- module(sgml, [load_html/3, load_xml/3]). From 43a297b6918ab1d037f3d01be2e778ce1b94a0f3 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 25 Jan 2023 00:06:53 +0100 Subject: [PATCH 067/361] DOC: add DocLog documentation for library(diag) --- src/lib/diag.pl | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/lib/diag.pl b/src/lib/diag.pl index 0fa3896f..636ed0a4 100644 --- a/src/lib/diag.pl +++ b/src/lib/diag.pl @@ -1,7 +1,46 @@ :- module(diag, [wam_instructions/2]). +/** Diagnostics library + + The predicate `wam_instructions/2` _decompiles_ a predicate so that + we can inspect its Warren Abstract Machine (WAM) instructions. + In this way, we can verify and reason about compiled programs, + and detect opportunities for optimization. + + For example, we have: + +``` +?- use_module(library(lists)). + true. +?- use_module(library(diag)). + true. +?- use_module(library(format)). + true. +?- wam_instructions(append/3, Is), + maplist(portray_clause, Is). +switch_on_term(1,external(1),external(2),external(6),fail). +try_me_else(4). +get_constant(level(shallow),[],x(1)). +get_value(x(2),3). +proceed. +trust_me(0). +get_list(level(shallow),x(1)). +unify_variable(x(4)). +unify_variable(x(1)). +get_list(level(shallow),x(3)). +unify_value(x(4)). +unify_variable(x(3)). +execute(append,3). + Is = [switch_on_term(1,external(1),external(2),external(6),fail)|...]. +``` +*/ + + :- use_module(library(error)). +%% wam_instructions(+PI, -Instrs) +% +% _Instrs_ are the WAM instructions corresponding to predicate indicator _PI_. wam_instructions(Clause, Listing) :- ( nonvar(Clause) -> From 64be8e0fba06d15ad56dd652703c9de853cd8fe8 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 25 Jan 2023 00:19:14 +0100 Subject: [PATCH 068/361] DOC: add DocLog documentation for library(debug) --- src/lib/debug.pl | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/lib/debug.pl b/src/lib/debug.pl index ab84f15b..e237d8e1 100644 --- a/src/lib/debug.pl +++ b/src/lib/debug.pl @@ -1,4 +1,22 @@ -% Source: https://stackoverflow.com/a/30791637 +/** Declarative debugging. + + This library provides three predicates with associated operators. + The operators can be placed in front of goals to debug Prolog + programs. + + Of these predicates, the most frequently used is `(*)/1`, with + associated prefix operator `*` (star). Placing `*` in front of a + goal means to _generalize away_ the goal. `* Goal` acts as if `Goal` + did not appear at all in the source code. It is declaratively + equivalent to _commenting out_ the goal, and easier to write, + because `*` can also be placed in front of the last goal in a clause + without any additional changes. + + Source: [https://stackoverflow.com/a/30791637](https://stackoverflow.com/a/30791637) + +*/ + + :- module(debug, [ op(900, fx, $), @@ -15,12 +33,25 @@ :- meta_predicate $(0). :- meta_predicate $-(0). +%% $-(Goal) +% +% Portray exceptions thrown by Goal. + $-(G_0) :- catch(G_0, Ex, ( portray_clause(exception:Ex:G_0), throw(Ex) ) ). +%% $(Goal) +% +% Provide a _trace_ for calls of Goal. + $(G_0) :- portray_clause(call:G_0), $-G_0, portray_clause(exit:G_0). +%% *(Goal) +% +% Generalize away Goal. + + *(_). From 6d99912b7b8baf3cbaf4dc621e713ecbdbd65a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Wed, 25 Jan 2023 19:48:01 +0100 Subject: [PATCH 069/361] Compatible Doclog docs for library(arithmetic) and small fixes on INDEX.md --- INDEX.md | 16 +++++++++------- src/lib/arithmetic.pl | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/INDEX.md b/INDEX.md index 56fbd633..7653cfc4 100644 --- a/INDEX.md +++ b/INDEX.md @@ -12,13 +12,13 @@ logic and constraint programming. Some of the Scryer Prolog features are: * ISO standard compliant -* Integrated constraint progamming libraries: clp(B), clp(Z). -* Definite Clause Grammars -* Coroutining support (`dif/2`, `freeze/2`, ...) +* Integrated constraint programming libraries: [clp(B)](/clpb.html), [clp(Z)](/clpz.html). +* [Definite Clause Grammars](/dcgs.html) +* Coroutining support ([`dif/2`](/dif.html), [`freeze/2`](/freeze.html), ...) * Tabling and SLG resolution * Compact string representation -* Network libraries (TCP sockets, HTTP server, HTTP client, ...) -* Cryptographical predicates +* Network libraries ([TCP sockets](/sockets.html), [HTTP server](/http/http_server.html), [HTTP client](/http/http_open.html), ...) +* [Cryptographical predicates](/crypto.html) * WAM based engine, cross-platform made in Rust * _and more..._ @@ -36,12 +36,14 @@ It's still to this day one of the best examples and one of the most popular lang of logic programming. That's because Prolog allows us to elegantly solve many tasks with short and general programs. -If you want to learn more about Prolog history, [check this video](https://www.youtube.com/watch?v=74Ig_QKndvE). +If you want a more detailed description of Prolog, check [A Tour of Prolog](https://www.youtube.com/watch?v=8XUutFBbUrg). + +If you want to learn more about Prolog history, [check this video](https://www.youtube.com/watch?v=74Ig_QKndvE) and [this talk](https://prologyear.logicprogramming.org/videos/PrologDay_Session_1_talk.mp4). ## Where can I learn Prolog? There are a lot of classical Prolog books. Those books can teach you the basics of Prolog. Some -examples are: _The Art of Prolog (Shapiro)_, _Programming in Prolog (Cloksin, Mellish)_ and _The Craft +examples are: _The Art of Prolog (Shapiro)_, _Programming in Prolog (Clocksin, Mellish)_ and _The Craft of Prolog (O'Keefe)_. However, most of them are not updated to _modern_ Prolog. We recommend _[The Power of Prolog (Markus Triska)](https://www.metalevel.at/prolog)_ for modern Prolog. For reference about the builtin Prolog modules and libraries in Scryer, check the documentation site. It's this! diff --git a/src/lib/arithmetic.pl b/src/lib/arithmetic.pl index e557c90b..3093d907 100644 --- a/src/lib/arithmetic.pl +++ b/src/lib/arithmetic.pl @@ -1,3 +1,8 @@ +/** Arithmetic predicates + +These predicates are additions to standard the arithmetic functions provided by `is/2`. +*/ + :- module(arithmetic, [expmod/4, lcm/3, lsb/2, msb/2, number_to_rational/2, number_to_rational/3, popcount/2, rational_numerator_denominator/3]). @@ -6,6 +11,10 @@ :- use_module(library(error)). :- use_module(library(lists), [append/3, member/2]). + +%% expmod(+Base, +Expo, +Mod, -R). +% +% Modular exponentiation. Base, Expo and Mod must be integers. expmod(Base, Expo, Mod, R) :- ( member(N, [Base, Expo, Mod]), var(N) -> instantiation_error(expmod/4) ; member(N, [Base, Expo, Mod]), \+ integer(N) -> @@ -44,6 +53,9 @@ lcm(A, B, X) :- X is abs(B) // gcd(A,B) * abs(A) ). +%% lsb(+X, -N). +% +% True iff N is the least significat bit of integer X lsb(X, N) :- builtins:must_be_number(X, lsb/2), ( \+ integer(X) -> type_error(integer, X, lsb/2) @@ -53,6 +65,9 @@ lsb(X, N) :- msb_(X1, -1, N) ). +%% msb(+X, -N). +% +% True iff N is the most significant bit of integer X msb(X, N) :- builtins:must_be_number(X, msb/2), ( \+ integer(X) -> type_error(integer, X, msb/2) @@ -68,6 +83,9 @@ msb_(X, M, N) :- M1 is M + 1, msb_(X1, M1, N). +%% number_to_rational(+Real, -Fraction). +% +% True iff given a number Real, Fraction is the same number represented as a fraction. number_to_rational(Real, Fraction) :- ( var(Real) -> instantiation_error(number_to_rational/2) ; integer(Real) -> Fraction is Real rdiv 1 @@ -126,12 +144,20 @@ simplify_fraction(A0/B0, A/B) :- A is A0 // G, B is B0 // G. +%% rational_numerator_denominator(+Fraction, -Numerator, -Denominator). +% +% True iff given a fraction Fraction, Numerator is the numerator of that fraction +% and Denominator the denominator. rational_numerator_denominator(R, N, D) :- write_term_to_chars(R, [], Cs), append(Ns, [' ', r, d, i, v, ' '|Ds], Cs), number_chars(N, Ns), number_chars(D, Ds). +%% popcount(+Number, -Bits1). +% +% True iff given an integer Number, Bits1 is the amount of 1 bits the binary representation +% of that number has. popcount(X, N) :- must_be(integer, X), '$popcount'(X, N). From d755bb7e12511480cddef22d540bd12f2893096c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Wed, 25 Jan 2023 21:04:10 +0100 Subject: [PATCH 070/361] Apply feedback on builtins --- src/lib/builtins.pl | 233 +++++++++++++++++++++++++------------------- 1 file changed, 135 insertions(+), 98 deletions(-) diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 4fec5e1f..e7d6e841 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -121,22 +121,22 @@ call(_, _, _, _, _, _, _, _, _). % % True iff Flag is a flag supported by the processor, and Value is the value currently associated with it. % A flag is a setting which value affects internal operation of the Prolog system. Some flags are read-only, -% while others can be set with set\_prolog\_flag/2. +% while others can be set with `set_prolog_flag/2`. % % The flags that Scryer Prolog support are: -% * `max\_arity`: The max arity a predicate can have in Prolog. On Scryer is set to 1023. Read only. +% * `max_arity`: The max arity a predicate can have in Prolog. On Scryer is set to 1023. Read only. % * `bounded`: `true` if integer arithmethic is bounded between some min/max values. On Scryer is always set % to `false` since it supports unbounded integer arithmethic. Read only. -% * `integer\_rounding\_function`: Describes the rounding donde by `//` and `rem` functions. On Scryer is -% always set to `toward\_zero`. Read only -% * `double\_quotes`: Determines how double quoted strings are red by Prolog. Scryer uses `chars` by default +% * `integer_rounding_function`: Describes the rounding donde by `//` and `rem` functions. On Scryer is +% always set to `toward_zero`. Read only +% * `double_quotes`: Determines how double quoted strings are red by Prolog. Scryer uses `chars` by default % which is a list of one-character atoms. Other values are codes (list of integers representing characters), % and atom which creates a whole atom for the string value. Read and write. -% * `max\_integer`: Maximum integer supported by the system. As Scryer Prolog has unbounded integer arithmethic, +% * `max_integer`: Maximum integer supported by the system. As Scryer Prolog has unbounded integer arithmethic, % checking the value of this flag fails. Read only. -% * `min\_integer`: Minimum integer supported by the system. As Scryer Prolog has unbounded integer arithmethic, +% * `min_integer`: Minimum integer supported by the system. As Scryer Prolog has unbounded integer arithmethic, % checking the value of this flag fails. Read only. -% * `occurs\_check`: Returns if the occurs check is enabled. The occurs check prevents the creation cyclic terms. +% * `occurs_check`: Returns if the occurs check is enabled. The occurs check prevents the creation cyclic terms. % Historically the Prolog unification algorithm didn't do that check so changing the value modifies how Prolog % operates in the low-level. Possible values are `false` (default), `true` (unification has this check % enabled) and `error` which throws an exception when a cylic term is created. Read ans write. @@ -165,7 +165,7 @@ current_prolog_flag(Flag, _) :- %% set_prolog_flag(Flag, Value). % % Sets the internal value of the flag. To see the list of flags supported by Scryer Prolog, -% check current\_prolog\_flag/2. The flags that are read only will fail if you try to change their values +% check `current_prolog_flag/2`. The flags that are read only will fail if you try to change their values set_prolog_flag(Flag, Value) :- (var(Flag) ; var(Value)), throw(error(instantiation_error, set_prolog_flag/2)). % 8.17.1.3 a, b @@ -208,7 +208,7 @@ set_prolog_flag(Flag, _) :- %% fail. % -% A predicate that always fails. The more declarative false/0 should be used instead. +% A predicate that always fails. The more declarative `false/0` should be used instead. fail :- '$fail'. @@ -231,7 +231,7 @@ _ \= _. %% once(Goal) % -% Execute Goal (like call/1) but exactly once, ignoring any kind of alternative solutions the original predicate +% Execute Goal (like `call/1`) but exactly once, ignoring any kind of alternative solutions the original predicate % could have generated. once(G) :- call(G), !. @@ -592,7 +592,7 @@ must_be_var_names_list_([VarName | VarNames], List) :- %% write_term(+Term, +Options). % % Write Term to the current output stream according to some output syntax options. -% Options are specified in detail in write_term/3. +% Options are specified in detail in `write_term/3`. write_term(Term, Options) :- current_output(Stream), write_term(Stream, Term, Options). @@ -600,13 +600,13 @@ write_term(Term, Options) :- %% write_term(+Stream, +Term, +Options). % % Write Term to the stream Stream according to some output syntax options. The options avaibale are: -% * `ignore\_ops(+Boolean)` if `true`, the generic term representation is used everywhere. In `false` +% * `ignore_ops(+Boolean)` if `true`, the generic term representation is used everywhere. In `false` % (default), operators do not use that generic term representation. -% * `max\_depth(+N)` if the term is nested deeper than N, print the reminder as ellipses. +% * `max_depth(+N)` if the term is nested deeper than N, print the reminder as ellipses. % If N = 0 (default), there's no limit. % * `numbervars(+Boolean)` if true, replaces `$VAR(N)` variables with letters, in order. Default is false. % * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog synytax, are quoted. Default is false. -% * `variable\_names(+List)` assign names to variables in term. List should be a list of terms of format `Name=Var`. +% * `variable_names(+List)` assign names to variables in term. List should be a list of terms of format `Name=Var`. write_term(Stream, Term, Options) :- parse_write_options(Options, [IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], write_term/3), '$write_term'(Stream, Term, IgnoreOps, NumberVars, Quoted, VNNames, MaxDepth). @@ -640,7 +640,7 @@ write_canonical(Stream, Term) :- %% writeq(+Term). % -% Write Term to the current output stream using a syntax similar to write/1 but quoting the atoms that need to be +% Write Term to the current output stream using a syntax similar to `write/1` but quoting the atoms that need to be % quoted according to Prolog syntax. writeq(Term) :- current_output(Stream), @@ -648,7 +648,7 @@ writeq(Term) :- %% writeq(+Stream, +Term). % -% Write Term to the stream Stream using a syntax similar to write/1 but quoting the atoms that need to be +% Write Term to the stream Stream using a syntax similar to `write/1` but quoting the atoms that need to be % quoted according to Prolog syntax. writeq(Stream, Term) :- '$write_term'(Stream, Term, false, true, true, [], 0). @@ -679,16 +679,16 @@ parse_read_term_options_(E,_) :- %% read_term(+Stream, -Term, +Options). % % Read Term from the stream Stream. It supports several options: -% * `variables(-Vars)` unifies Vars with a list of variables in the term. Similar to do term\_variables/2 with the new term. -% * `variable\_names(-Vars)` unifies Vars with a list `Name=Var` with Name describing the variable name and Var the variable itself that appears in Term. -% * `singletons` similar to `variable\_names` but only reports variables occurring only once in Term. +% * `variables(-Vars)` unifies Vars with a list of variables in the term. Similar to do `term_variables/2` with the new term. +% * `variable_names(-Vars)` unifies Vars with a list `Name=Var` with Name describing the variable name and Var the variable itself that appears in Term. +% * `singletons` similar to `variable_names` but only reports variables occurring only once in Term. read_term(Stream, Term, Options) :- parse_read_term_options(Options, [Singletons, VariableNames, Variables], read_term/3), '$read_term'(Stream, Term, Singletons, Variables, VariableNames). %% read_term(-Term, +Options). % -% Read Term from the current input stream. It supports several options described in more detail in read\_term/3. +% Read Term from the current input stream. It supports several options described in more detail in `read_term/3`. read_term(Term, Options) :- current_input(Stream), read_term(Stream, Term, Options). @@ -696,7 +696,7 @@ read_term(Term, Options) :- %% read(-Term). % % Read Term from the current input stream with default options. **NOTE** This is not a general predicate -% to read input from a file or the user. Use other predicates like phrase\_from\_file/2 for that. +% to read input from a file or the user. Use other predicates like `phrase_from_file/2` for that. read(Term) :- current_input(Stream), read(Stream, Term). @@ -719,7 +719,7 @@ can_be_list(List, PI) :- %% term_variables(+Term, -Vars). % -% Unify Vars with a list of unique variables that appear in Term. The variables are sorted depth-first +% True iff given a Term, Vars is a list of all the unique variables that appear in Term. The variables are sorted depth-first % and left-to-right. % % ?- term_variables(f(X, Y, X, g(Z)), Vars). @@ -737,8 +737,10 @@ term_variables(Term, Vars) :- % Calls Goal, but if it throws an exception that unifies with Catcher, Recover will be called instead % and the program will be resumed. Example: % -% ?- catch(number_chars(X, "not_a_number"), error(syntax_error(_), _), X = 0). -% X = 0. +% ``` +% ?- catch(number_chars(X, "not_a_number"), error(syntax_error(_), _), X = 0). +% X = 0. +% ``` catch(G,C,R) :- '$get_current_block'(Bb), catch(G,C,R,Bb). @@ -781,13 +783,15 @@ handle_ball(_, _, _) :- %% throw(+Exception). % -% Raise the exception Exception. The system looks for the innermost catch/3 for which Exception +% Raise the exception Exception. The system looks for the innermost `catch/3` for which Exception % unifies with Catcher. Example: % -% ?- throw(custom_error(42)). -% throw(custom_error(42)). -% ?- catch(throw(custom_error(42)), custom_error(_), true). -% true. +% ``` +% ?- throw(custom_error(42)). +% throw(custom_error(42)). +% ?- catch(throw(custom_error(42)), custom_error(_), true). +% true. +% ``` throw(Ball) :- ( var(Ball) -> '$set_ball'(error(instantiation_error,throw/1)) @@ -822,15 +826,17 @@ findall_cleanup(LhLength, Error) :- %% findall(Template, Goal, Solutions). % % Unify Solutions with a list of all values that variables in Template can take in Goal. -% findall/3 is equivalent to bagof/3 with all free variables scoped to the Goal (`^` operator) -% except that bagof/3 fails when no solutions are found and findall/3 unifies with an empty list. +% `findall/3` is equivalent to `bagof/3` with all free variables scoped to the Goal (`^` operator) +% except that `bagof/3` fails when no solutions are found and `findall/3` unifies with an empty list. % Example: % -% f(1,2). -% f(1,3). -% f(1,4). -% ?- findall(X-Y, f(X, Y), Solutions). -% Solutions = [1-2,1-3,1-4]. +% ``` +% f(1,2). +% f(1,3). +% f(1,4). +% ?- findall(X-Y, f(X, Y), Solutions). +% Solutions = [1-2,1-3,1-4]. +% ``` findall(Template, Goal, Solutions) :- error:can_be(list, Solutions), '$lh_length'(LhLength), @@ -856,7 +862,7 @@ findall(Template, Goal, Solutions) :- %% findall(Template, Goal, Solutions0, Solutions1) % -% Similar to findall/3 but returns the solutions as the difference list Solutions0-Solutions1. +% Similar to `findall/3` but returns the solutions as the difference list Solutions0-Solutions1. findall(Template, Goal, Solutions0, Solutions1) :- error:can_be(list, Solutions0), error:can_be(list, Solutions1), @@ -949,13 +955,15 @@ findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses) % % Example: % -% f(1, 3). -% f(2, 4). -% ?- bagof(X, f(X, Y), Bag). -% Y = 3, Bag = [1], -% ; Y = 4, Bag = [2]. -% ?- bagof(X, Y^f(X, Y), Bag). -% Bag = [1,2]. +% ``` +% f(1, 3). +% f(2, 4). +% ?- bagof(X, f(X, Y), Bag). +% Y = 3, Bag = [1], +% ; Y = 4, Bag = [2]. +% ?- bagof(X, Y^f(X, Y), Bag). +% Bag = [1,2]. +% ``` bagof(Template, Goal, Solution) :- error:can_be(list, Solution), term_variables(Template, TemplateVars), @@ -985,13 +993,15 @@ iterate_variants_and_sort([_|GroupSolutions], Ws, Solution) :- %% setof(Template, Goal, Solution). % -% Similar to bagof/3 but Solution is sorted and duplicates are removed. Example: +% Similar to `bagof/3` but Solution is sorted and duplicates are removed. Example: % -% f(1, 2). -% f(1, 3). -% f(2, 4). -% ?- setof(X, Y^f(X, Y), Set). -% Set = [1, 2]. +% ``` +% f(1, 2). +% f(1, 3). +% f(2, 4). +% ?- setof(X, Y^f(X, Y), Set). +% Set = [1, 2]. +% ``` setof(Template, Goal, Solution) :- error:can_be(list, Solution), term_variables(Template, TemplateVars), @@ -1410,8 +1420,10 @@ halt(N) :- % % True iff Atom is an atom of Length characters. Example: % -% ?- atom_length(marseille, N). -% N = 9. +% ``` +% ?- atom_length(marseille, N). +% N = 9. +% ``` atom_length(Atom, Length) :- ( var(Atom) -> throw(error(instantiation_error, atom_length/2)) % 8.16.1.3 a) @@ -1433,10 +1445,12 @@ atom_length(Atom, Length) :- % Relates an atom with a string in chars representation. It can be used to convert % between atoms and strings. Examples: % -% ?- atom_chars(marseille, X). -% X = "marseille". -% ?- atom_chars(X, "marseille"). -% X = marseille. +% ``` +% ?- atom_chars(marseille, X). +% X = "marseille". +% ?- atom_chars(X, "marseille"). +% X = marseille. +% ``` atom_chars(Atom, List) :- '$skip_max_list'(_, _, List, Tail), ( ( Tail == [] ; var(Tail) ) -> @@ -1463,10 +1477,12 @@ atom_chars(Atom, List) :- % between atoms and strings. However, codes is not the default representation of double quoutes % strings in Scryer Prolog. Examples: % -% ?- atom_codes(marseille, X). -% X = [109,97,114,115,101,105,108,108,101]. -% ?- atom_codes(X, [109,97,114,115,101,105,108,108,101]). -% X = marseille. +% ``` +% ?- atom_codes(marseille, X). +% X = [109,97,114,115,101,105,108,108,101]. +% ?- atom_codes(X, [109,97,114,115,101,105,108,108,101]). +% X = marseille. +% ``` atom_codes(Atom, List) :- '$skip_max_list'(_, _, List, Tail), ( ( Tail == [] ; var(Tail) ) -> @@ -1489,10 +1505,14 @@ atom_codes(Atom, List) :- %% atom_concat(?A1, ?A2, ?A12) % -% Similar to append/3 but operating on atom characters. Example: +% Similar to `append/3` but operating on atom characters. +% If you find yourself using this predicate, consider using strings instead. +% Example: % -% ?- atom_concat(a, X, ab). -% X = b. +% ``` +% ?- atom_concat(a, X, ab). +% X = b. +% ``` atom_concat(Atom_1, Atom_2, Atom_12) :- error:can_be(atom, Atom_1), error:can_be(atom, Atom_2), @@ -1521,13 +1541,18 @@ atom_concat(Atom_1, Atom_2, Atom_12) :- %% sub_atom(+Atom, ?Before, ?Length, ?After, ?SubAtom). % % Relates an atom to a subatom inside with some key properties: +% % * SubAtom starts at Before characters (0-based) from Atom % * SubAtom has Length characters % * After SubAtom there are After characters in Atom +% +% If you find yourself using this predicate, consider using strings. % Example: % -% ?- sub_atom(abcdefg, 2, 3, X, SubAtom). -% X = 2, SubAtom = cde. +% ``` +% ?- sub_atom(abcdefg, 2, 3, X, SubAtom). +% X = 2, SubAtom = cde. +% ``` sub_atom(Atom, Before, Length, After, Sub_atom) :- error:must_be(atom, Atom), error:can_be(atom, Sub_atom), @@ -1553,8 +1578,10 @@ sub_atom(Atom, Before, Length, After, Sub_atom) :- % % Relates a Char to its Code (an integer). Example: % -% ?- char_code(a, X). -% X = 97. +% ``` +% ?- char_code(a, X). +% X = 97. +% ``` char_code(Char, Code) :- ( var(Char) -> ( var(Code) -> @@ -1578,7 +1605,7 @@ char_code(Char, Code) :- %% get_char(-Char). % % From the current input stream, unify Char with the next character. -% When there are no more characters to read, Char unifies with `end\_of\_file`. +% When there are no more characters to read, Char unifies with `end_of_file`. get_char(C) :- error:can_be(in_character, C), current_input(S), @@ -1587,7 +1614,7 @@ get_char(C) :- %% get_char(+Stream, -Char). % % From the stream Stream, unify Char with the next character. -% When there are no more characters to read, Char unifies with `end\_of\_file`. +% When there are no more characters to read, Char unifies with `end_of_file`. get_char(S, C) :- error:can_be(in_character, C), '$get_char'(S, C). @@ -1650,12 +1677,14 @@ codes_or_vars([C|Cs], PI) :- % Throws an error if Chars is not the representation of a number. % Examples: % -% ?- number_chars(42, X). -% X = "42". -% ?- number_chars(X, "42"). -% X = 42. -% ?- number_chars(X, "not_a_number"). -% error(syntax_error(cannot_parse_big_int),number_chars/2:0). +% ``` +% ?- number_chars(42, X). +% X = "42". +% ?- number_chars(X, "42"). +% X = 42. +% ?- number_chars(X, "not_a_number"). +% error(syntax_error(cannot_parse_big_int),number_chars/2:0). +% ``` number_chars(N, Chs) :- ( ground(Chs) -> can_be_number(N, number_chars/2), @@ -1683,12 +1712,14 @@ list_of_ints(Ns) :- % Throws an error if Codes is not the representation of a number. % Examples: % -% ?- number_codes(42, X). -% X = [52,50]. -% ?- number_codes(X, [52,50]). -% X = 42. -% ?- number_codes(X, [65]). -% error(syntax_error(cannot_parse_big_int),number_codes/2:0). +% ``` +% ?- number_codes(42, X). +% X = [52,50]. +% ?- number_codes(X, [52,50]). +% X = 42. +% ?- number_codes(X, [65]). +% error(syntax_error(cannot_parse_big_int),number_codes/2:0). +% ``` number_codes(N, Chs) :- ( ground(Chs) -> can_be_number(N, number_codes/2), @@ -1711,10 +1742,12 @@ number_codes(N, Chs) :- % in Generic. The implementation unifies with occurs check always and ensures that % the variables of Specific did not change. Some examples: % -% ?- subsumes_term(f(A, A), f(2, 2)). -% true. -% ?- subsumes_term(f(A, 2), f(2, A)). -% false. +% ``` +% ?- subsumes_term(f(A, A), f(2, 2)). +% true. +% ?- subsumes_term(f(A, 2), f(2, A)). +% false. +% ``` subsumes_term(General, Specific) :- \+ \+ ( term_variables(Specific, SVs1), @@ -1727,12 +1760,14 @@ subsumes_term(General, Specific) :- % % True iff X and Y unify with occurs check. The occurs check prevents the creation cyclic terms but is % computationally more expensive. The (=)/2 operator can also do occurs check if enabled -% via set\_prolog\_flag/2. Example: +% via `set_prolog_flag/2`. Example: % -% ?- A = f(A). -% A = f(A). -% ?- unify_with_occurs_check(A, f(A)). -% false. +% ``` +% ?- A = f(A). +% A = f(A). +% ?- unify_with_occurs_check(A, f(A)). +% false. +% ``` unify_with_occurs_check(X, Y) :- '$unify_with_occurs_check'(X, Y). %% current_input(-Stream). @@ -1816,15 +1851,17 @@ open(SourceSink, Mode, Stream) :- % The following options are available: % % * `alias(+Alias)`: Set an alias to the stream -% * `eof\_action(+Action)`: Defined what happens if the end of the stream is reached. Values: `error`, `eof_code` and `reset`. +% * `eof_action(+Action)`: Defined what happens if the end of the stream is reached. Values: `error`, `eof_code` and `reset`. % * `reposition(+Boolean)`: Specifies whether repositioning is required for the stream. `false` is the default. % * `type(+Type)`: Type can be `text` or `binary`. Defines the type of the stream, if it's optimized for plain text % or just binary % % Example: % -% ?- open("README.md", read, S, []), get_n_chars(S, 20, C). -% S = '$stream'(0x55dece980218), C = "\n# Scryer Prolog\n\nS ..." +% ``` +% ?- open("README.md", read, S, []), get_n_chars(S, 20, C). +% S = '$stream'(0x55dece980218), C = "\n# Scryer Prolog\n\nS ..." +% ``` open(SourceSink, Mode, Stream, StreamOptions) :- ( var(SourceSink) -> throw(error(instantiation_error, open/4)) % 8.11.5.3a) @@ -2048,13 +2085,13 @@ stream_iter(S) :- % StreamProperty can be one of the following: % * `input` if stream is an input stream. % * `output` if stream is an output stream. -% * `input\_output` if stream is both an input and an output stream. +% * `input_output` if stream is both an input and an output stream. % * `alias(-Alias)` if the stream has an associated alias. -% * `file\_name(-FileName)` if Stream is associated to a file, unifies with the name of the file +% * `file_name(-FileName)` if Stream is associated to a file, unifies with the name of the file % * `mode(-Mode)`: Mode unifies with the mode of the stream: `read`, `write` or `append`. % * `position(position_and_lines_read(P, L))` current position of the stream. -% * `end\_of\_stream(-X)` where X can be `not`, `at` or `past` depending if the stream has ended or not. -% * `eof\_action(-X)` where X can be `error`, `eof_code` or `reset` depending on the action that will happen on the end of the file. +% * `end_of_stream(-X)` where X can be `not`, `at` or `past` depending if the stream has ended or not. +% * `eof_action(-X)` where X can be `error`, `eof_code` or `reset` depending on the action that will happen on the end of the file. % * `reposition(-Boolean)` specifies if reposition has been enabled for this stream. % * `type(-Type)` where Type can be `text` or `binary`. stream_property(S, P) :- From 22b815dc5c5e6b1e18ee0047bbee82f13124f916 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 25 Jan 2023 22:29:14 +0100 Subject: [PATCH 071/361] DOC: correctly format the table using DocLog syntax --- src/lib/time.pl | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/lib/time.pl b/src/lib/time.pl index 47e02fc5..003f623b 100644 --- a/src/lib/time.pl +++ b/src/lib/time.pl @@ -28,32 +28,32 @@ current_time(T) :- %% format_time(FormatString, TimeStamp)// % -% The nonterminal format_time//2 describes a list of characters that -% are formatted according to a format string. Usage: +% The nonterminal format_time//2 describes a list of characters that +% are formatted according to a format string. Usage: % % ``` % phrase(format_time(FormatString, TimeStamp), Cs) % ``` % -% TimeStamp represents a moment in time in an opaque form, as for -% example obtained by `current_time/1`. +% TimeStamp represents a moment in time in an opaque form, as for +% example obtained by `current_time/1`. % -% FormatString is a list of characters that are interpreted literally, -% except for the following specifiers (and possibly more in the future): +% FormatString is a list of characters that are interpreted literally, +% except for the following specifiers (and possibly more in the future): % -% | %Y | year of the time stamp. Example: 2020. | -% | %m | month number (01-12), zero-padded to 2 digits | -% | %d | day number (01-31), zero-padded to 2 digits | -% | %H | hour number (00-24), zero-padded to 2 digits | -% | %M | minute number (00-59), zero-padded to 2 digits | -% | %S | second number (00-60), zero-padded to 2 digits | -% | %b | abbreviated month name, always 3 letters | -% | %a | abbreviated weekday name, always 3 letters | -% | %A | full weekday name | -% | %j | day of the year (001-366), zero-padded to 3 digits | -% | %% | the literal % | +% | `%Y` | year of the time stamp. Example: 2020. | +% | `%m` | month number (01-12), zero-padded to 2 digits | +% | `%d` | day number (01-31), zero-padded to 2 digits | +% | `%H` | hour number (00-24), zero-padded to 2 digits | +% | `%M` | minute number (00-59), zero-padded to 2 digits | +% | `%S` | second number (00-60), zero-padded to 2 digits | +% | `%b` | abbreviated month name, always 3 letters | +% | `%a` | abbreviated weekday name, always 3 letters | +% | `%A` | full weekday name | +% | `%j` | day of the year (001-366), zero-padded to 3 digits | +% | `%%` | the literal `%` | % -% Example: +% Example: % % ``` % ?- current_time(T), phrase(format_time("%d.%m.%Y (%H:%M:%S)", T), Cs). From cc7e7216110a3a0f07976486211d29c81933d46e Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 25 Jan 2023 23:17:54 +0100 Subject: [PATCH 072/361] DOC: convert library(format) documentation to DocLog format --- src/lib/format.pl | 170 +++++++++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 78 deletions(-) diff --git a/src/lib/format.pl b/src/lib/format.pl index fd2a9816..f44091c1 100644 --- a/src/lib/format.pl +++ b/src/lib/format.pl @@ -1,83 +1,17 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2020, 2021, 2022 by Markus Triska (triska@metalevel.at) + Written 2020-2023 by Markus Triska (triska@metalevel.at) Part of Scryer Prolog. - - This library provides the nonterminal format_//2 to describe - formatted strings. format/[2,3] are provided for impure output. - - Usage: - ====== - - phrase(format_(FormatString, Arguments), Ls) - - format_//2 describes a list of characters Ls that are formatted - according to FormatString. FormatString is a string (i.e., - a list of characters) that specifies the layout of Ls. - The characters in FormatString are used literally, except - for the following tokens with special meaning: - - ~w use the next available argument from Arguments here - ~q use the next argument here, formatted as by writeq/1 - ~a use the next argument here, which must be an atom - ~s use the next argument here, which must be a string - ~d use the next argument here, which must be an integer - ~f use the next argument here, a floating point number - ~Nf where N is an integer: format the float argument - using N digits after the decimal point - ~Nd like ~d, placing the last N digits after a decimal point; - if N is 0 or omitted, no decimal point is used. - ~ND like ~Nd, separating digits to the left of the decimal point - in groups of three, using the character "," (comma) - ~NU like ~ND, using "_" (underscore) to separate groups of digits - ~NL format an integer so that at most N digits appear on a line. - If N is 0 or omitted, it defaults to 72. - ~Nr where N is an integer between 2 and 36: format the - next argument, which must be an integer, in radix N. - The characters "a" to "z" are used for radices 10 to 36. - If N is omitted, it defaults to 8 (octal). - ~NR like ~Nr, except that "A" to "Z" are used for radices > 9 - ~| place a tab stop at this position - ~N| where N is an integer: place a tab stop at text column N - ~N+ where N is an integer: place a tab stop N characters - after the previous tab stop (or start of line) - ~t distribute spaces evenly between the two closest tab stops - ~`Ct like ~t, use character C instead of spaces to fill the space - ~n newline - ~Nn N newlines - ~i ignore the next argument - ~~ the literal ~ - - Instead of ~N, you can write ~* to use the next argument from Arguments - as the numeric argument. - - The predicate format/2 is like format_//2, except that it outputs - the text on the terminal instead of describing it declaratively. - - format/3, used as format(Stream, FormatString, Arguments), outputs - the described string to the given Stream. If Stream is a binary - stream, then the code of each emitted character must be in 0..255. - - If at all possible, format_//2 should be used, to stress pure parts - that enable easy testing etc. If necessary, you can emit the list Ls - with maplist(put_char, Ls) or, much faster, with format("~s", [Ls]). - Ideally, however, you use phrase_to_file/[2,3] or phrase_to_stream/2 - from library(pio) to write the described list directly to a file - or stream, respectively: phrase_to_stream(format_(..., [...]), S). - The advantage of this is that an ideal implementation writes - the characters as they become known, without manifesting the list. - - The entire library only works if the Prolog flag double_quotes - is set to chars, the default value in Scryer Prolog. This should - also stay that way, to encourage a sensible environment. - - Example: - - ?- phrase(format_("~s~n~`.t~w!~12|", ["hello",there]), Cs). - %@ Cs = "hello\n......there!". - I place this code in the public domain. Use it in any way you want. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/** This library provides the nonterminal `format_//2` to describe + formatted strings. `format/[2,3]` are provided for _impure_ output. + + The entire library only works if the Prolog flag `double_quotes` + is set to `chars`, the default value in Scryer Prolog. This should + also stay that way, to encourage a sensible environment. +*/ + :- module(format, [format_//2, format/2, format/3, @@ -94,6 +28,61 @@ :- use_module(library(between)). :- use_module(library(pio)). +%% format_(+FormatString, +Arguments)// +% +% Usage: +% +% ``` +% phrase(format_(FormatString, Arguments), Ls) +% ``` +% +% `format_//2` describes a list of characters Ls that are formatted +% according to FormatString. FormatString is a string (i.e., a list of +% characters) that specifies the layout of Ls. The characters in +% FormatString are used literally, except for the following tokens +% with special meaning: +% +% | ~w | use the next available argument from Arguments here | +% | ~q | use the next argument here, formatted as by `writeq/1` | +% | ~a | use the next argument here, which must be an atom | +% | ~s | use the next argument here, which must be a string | +% | ~d | use the next argument here, which must be an integer | +% | ~f | use the next argument here, a floating point number | +% | ~Nf | where N is an integer: format the float argument | +% | | using N digits after the decimal point | +% | ~Nd | like ~d, placing the last N digits after a decimal point; | +% | | if N is 0 or omitted, no decimal point is used. | +% | ~ND | like ~Nd, separating digits to the left of the decimal point | +% | | in groups of three, using the character "," (comma) | +% | ~NU | like ~ND, using "_" (underscore) to separate groups of digits | +% | ~NL | format an integer so that at most N digits appear on a line. | +% | | If N is 0 or omitted, it defaults to 72. | +% | ~Nr | where N is an integer between 2 and 36: format the | +% | | next argument, which must be an integer, in radix N. | +% | | The characters "a" to "z" are used for radices 10 to 36. | +% | | If N is omitted, it defaults to 8 (octal). | +% | ~NR | like ~Nr, except that "A" to "Z" are used for radices > 9 | +% | ~| | place a tab stop at this position | +% | ~N| | where N is an integer: place a tab stop at text column N | +% | ~N+ | where N is an integer: place a tab stop N characters | +% | | after the previous tab stop (or start of line) | +% | ~t | distribute spaces evenly between the two closest tab stops | +% | ~`Ct | like ~t, use character C instead of spaces to fill the space | +% | ~n | newline | +% | ~Nn | N newlines | +% | ~i | ignore the next argument | +% | \~\~ | the literal ~ | +% +% Instead of `~N`, you can write `~*` to use the next argument from +% Arguments as the numeric argument. +% +% Example: +% +% ``` +% ?- phrase(format_("~s~n~`.t~w!~12|", ["hello",there]), Cs). +% Cs = "hello\n......there!". +% ``` + format_(Fs, Args) --> { must_be(list, Fs), must_be(list, Args), @@ -414,10 +403,32 @@ digits(uppercase, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"). Impure I/O, implemented as a small wrapper over format_//2. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% format(+Fs, +Args) +% +% The predicate `format/2` is like `format_//2`, except that it +% outputs the text on the terminal instead of describing it +% declaratively as a list of characters. +% +% If at all possible, `format_//2` should be used, to stress pure +% parts that enable easy testing etc. If necessary, you can emit the +% described list of characters `Ls` with `maplist(put_char, Ls)` or, +% much faster, with `format("~s", [Ls])`. Ideally, however, you use +% `phrase_to_file/[2,3]` or `phrase_to_stream/2` from `library(pio)` +% to write the described list directly to a file or stream, +% respectively: `phrase_to_stream(format_(..., [...]), S)`. The +% advantage of this is that an ideal implementation writes the +% characters as they become known, without manifesting the list. + format(Fs, Args) :- current_output(Stream), format(Stream, Fs, Args). +%% format(Stream, FormatString, Arguments) +% +% Output the described string to the given Stream. If Stream is a +% binary stream, then the code of each emitted character must be in +% 0..255. + format(Stream, Fs, Args) :- phrase_to_stream(format_(Fs, Args), Stream), flush_output(Stream). @@ -486,11 +497,14 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa In the eventual library organization, portray_clause/1 and related predicates may be placed in their own dedicated library. - - portray_clause/1 is useful for printing solutions in such a way - that they can be read back with read/1. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +%% portray_clause(+Term) +% +% `portray_clause/1` is useful for printing solutions in such a way +% that they can be read back with `read/1`. + portray_clause(Term) :- current_output(Out), portray_clause(Out, Term). From 7f8f137aa0214573703896a8ab1d065b59d6c753 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 25 Jan 2023 23:27:45 +0100 Subject: [PATCH 073/361] DOC: convert library(si) documentation to DocLog format --- src/lib/si.pl | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/lib/si.pl b/src/lib/si.pl index d697c8a8..a1705343 100644 --- a/src/lib/si.pl +++ b/src/lib/si.pl @@ -1,28 +1,36 @@ -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Safe type tests - =============== +/** Safe type tests. - "si" stands for "sufficiently instantiated". + "si" stands for "sufficiently instantiated". It can also be read as + "safe inference", so possibly also other predicates are candidates + for this library. - These predicates: + A safe type test: - - throw instantiation errors if the argument is + - throws an *instantiation error* if the argument is not sufficiently instantiated to make a sound decision - - succeed if the argument is of the specified type - - fail otherwise. + - *succeeds* if the argument is of the specified type + - *fails* otherwise. - For instance, atom_si(A) yields an *instantiation error* if A is a + For instance, `atom_si(A)` yields an *instantiation error* if `A` is a variable. This is logically sound, since in that case the argument is not sufficiently instantiated to make any decision. - The definitions are taken from: + The definitions are taken from [Safer type tests in Prolog](https://stackoverflow.com/questions/27306453/safer-type-tests-in-prolog). - https://stackoverflow.com/questions/27306453/safer-type-tests-in-prolog + Examples: - "si" can also be read as "safe inference", so possibly also other - predicates are candidates for this library. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +``` +?- chars_si(Cs). + error(instantiation_error,list_si/1). +?- chars_si([h|Cs]). + error(instantiation_error,list_si/1). +?- chars_si("hello"). + true. +?- chars_si(hello). + false. +``` +*/ :- module(si, [atom_si/1, integer_si/1, From a29227d0d4195c180d43f91647179e6075e6cfff Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 25 Jan 2023 23:40:55 +0100 Subject: [PATCH 074/361] DOC: add link to "Indexing dif/2" in DocLog format --- src/lib/reif.pl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/lib/reif.pl b/src/lib/reif.pl index 0e43a897..55618e84 100644 --- a/src/lib/reif.pl +++ b/src/lib/reif.pl @@ -1,3 +1,16 @@ +/** Predicates from [*Indexing dif/2*](https://arxiv.org/abs/1607.01590). + +Example: + +``` +?- tfilter(=(a), [X,Y], Es). + X = a, Y = a, Es = "aa" +; X = a, Es = "a", dif:dif(a,Y) +; Y = a, Es = "a", dif:dif(a,X) +; Es = [], dif:dif(a,X), dif:dif(a,Y). +``` +*/ + :- module(reif, [if_/3, (=)/3, (',')/3, (;)/3, cond_t/3, dif/3, memberd_t/3, tfilter/3, tmember/2, tmember_t/3, tpartition/4]). From a8ea2b0f9735a853b50efc7e657ee364555fc7e0 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 25 Jan 2023 23:54:30 +0100 Subject: [PATCH 075/361] DOC: add documentation for library(freeze) in DocLog format --- src/lib/freeze.pl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib/freeze.pl b/src/lib/freeze.pl index b9f8005d..c6554fa4 100644 --- a/src/lib/freeze.pl +++ b/src/lib/freeze.pl @@ -1,5 +1,8 @@ :- module(freeze, [freeze/2]). +/** Provides the constraint `freeze/2`. +*/ + :- use_module(library(atts)). :- use_module(library(dcgs)). @@ -19,6 +22,15 @@ verify_attributes(Var, Other, Goals) :- ). verify_attributes(_, _, []). +%% freeze(Var, Goal) +% +% Schedules Goal to be executed when Var is instantiated. This can +% be useful to observe the exact moment a variable becomes bound to a +% more concrete term, for example when creating animations of search +% processes. Higher-level constructs such as `phrase_from_file/2` can +% also be implemented with `freeze/2`, by scheduling a goal that +% reads additional data from a file as soon as it is needed. + freeze(X, Goal) :- put_atts(Fresh, frozen(Goal)), Fresh = X. From b04d845ec05b1b8b402cf61ab8df13a3d1cec742 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 26 Jan 2023 00:09:41 +0100 Subject: [PATCH 076/361] DOC: convert library(pio) documentation to DocLog format --- src/lib/pio.pl | 69 ++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/src/lib/pio.pl b/src/lib/pio.pl index 42f641db..fdc5bcb1 100644 --- a/src/lib/pio.pl +++ b/src/lib/pio.pl @@ -1,13 +1,11 @@ -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Pure I/O - ======== +/** Pure I/O. Our goal is to encourage the use of definite clause grammars (DCGs) - for describing strings. The predicates phrase_from_file/[2,3], - phrase_to_file/[2,3] and phrase_to_stream/2 let us apply DCGs + for describing strings. The predicates `phrase_from_file/[2,3]`, + `phrase_to_file/[2,3]` and `phrase_to_stream/2` let us apply DCGs transparently to files and streams, and therefore decouple side-effects from declarative descriptions. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +*/ :- module(pio, [phrase_from_file/2, phrase_from_file/3, @@ -29,16 +27,18 @@ :- meta_predicate(phrase_to_file(2, ?, ?)). :- meta_predicate(phrase_to_stream(2, ?)). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - phrase_from_file(GRBody, File) - - True if grammar rule body GRBody covers the contents of File, - represented as a list of characters. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% phrase_from_file(+GRBody, +File) +% +% True if grammar rule body GRBody covers the contents of File, +% represented as a list of characters. phrase_from_file(NT, File) :- phrase_from_file(NT, File, []). +%% phrase_from_file(+GRBody, +File, +Options) +% +% Like `phrase_from_file/2`, using Options to open the file. + phrase_from_file(NT, File, Options) :- ( var(File) -> instantiation_error(phrase_from_file/3) ; must_be(list, Options), @@ -68,23 +68,22 @@ reader_step(Stream, Pos, Xs0) :- stream_to_lazy_list(Stream, Xs) ). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - phrase_to_stream(+GRBody, +Stream) - - Emit the list of characters described by the grammar rule body - GRBody to Stream. - - An ideal implementation of phrase_to_stream/2 writes each character - as soon as it becomes known and no choice-points remain, and thus - avoids the manifestation of the entire string in memory. See #691 - for more information. - - The current preliminary implementation is provided so that Prolog - programmers can already get used to describing output with DCGs, - and then writing it to a file when necessary. This simple - implementation suffices as long as the entire contents can be - represented in memory, and thus covers a large number of use cases. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% phrase_to_stream(+GRBody, +Stream) +% +% Emit the list of characters described by the grammar rule body +% GRBody to Stream. +% +% An ideal implementation of `phrase_to_stream/2` writes each +% character as soon as it becomes known and no choice-points remain, +% and thus avoids the manifestation of the entire string in memory. +% See [#691](https://github.com/mthom/scryer-prolog/issues/691) for +% more information. +% +% The current preliminary implementation is provided so that Prolog +% programmers can already get used to describing output with DCGs, +% and then writing it to a file when necessary. This simple +% implementation suffices as long as the entire contents can be +% represented in memory, and thus covers a large number of use cases. phrase_to_stream(GRBody, Stream) :- phrase(GRBody, Cs), @@ -101,14 +100,18 @@ phrase_to_stream(GRBody, Stream) :- % maplist(put_char(Stream), Cs). It also works for binary streams. '$put_chars'(Stream, Cs). -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - phrase_to_file(+GRBody, +File), writing the string described - by GRBody to File. -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +%% phrase_to_file(+GRBody, +File) +% +% Write the string described by GRBody to File. phrase_to_file(GRBody, File) :- phrase_to_file(GRBody, File, []). + +%% phrase_to_file(+GRBody, +File, +Options) +% +% Like `phrase_to_file/2`, using Options to open the file. + phrase_to_file(GRBody, File, Options) :- setup_call_cleanup(open(File, write, Stream, Options), phrase_to_stream(GRBody, Stream), From ca4aaf44de8b9ff7eaf29264104db80ef36a92a8 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 26 Jan 2023 00:20:53 +0100 Subject: [PATCH 077/361] DOC: initial documentation for library(tabling) in DocLog format --- src/lib/tabling.pl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/lib/tabling.pl b/src/lib/tabling.pl index 94dd4238..a63dd54f 100644 --- a/src/lib/tabling.pl +++ b/src/lib/tabling.pl @@ -1,3 +1,27 @@ +/** Tabling, also called SLG resolution. + + SLG resolution is an alternative execution strategy that sometimes + helps to improve termination and performance characters of Prolog + predicates. + + To enable this execution strategy for a Prolog predicate, add a + `(table)/1` directive, using the prefix operator `table` that this + module defines. For example, to enable tabling for the predicate + `p/2`, use: + +``` +:- use_module(library(tabling)). + +:- table p/2. + +... +``` + + The possibility to apply different execution strategies is one of + the greatest attractions of pure Prolog code, and one of the + strongest arguments for keeping to the pure core of Prolog as far + as possible. +*/ :- module(tabling, [ start_tabling/2, % +Wrapper, :Worker. From 90cf713186efef25d845b9db1454eb82e6da2e9c Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 26 Jan 2023 00:25:15 +0100 Subject: [PATCH 078/361] add link to Desouter et al., "Tabling as a Library with Delimited Control" --- src/lib/tabling.pl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/tabling.pl b/src/lib/tabling.pl index a63dd54f..606635fb 100644 --- a/src/lib/tabling.pl +++ b/src/lib/tabling.pl @@ -21,6 +21,8 @@ the greatest attractions of pure Prolog code, and one of the strongest arguments for keeping to the pure core of Prolog as far as possible. + + Scryer Prolog implements tabling as described by Desouter et al. in [*Tabling as a Library with Delimited Control*](https://www.ijcai.org/Proceedings/16/Papers/619.pdf). */ :- module(tabling, From 996496c3f5373000780b5155ca974ff8102235a8 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 26 Jan 2023 00:36:05 +0100 Subject: [PATCH 079/361] DOC: initial documentation for library(pairs) in DocLog format --- src/lib/pairs.pl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/lib/pairs.pl b/src/lib/pairs.pl index 8ed74777..17216b49 100644 --- a/src/lib/pairs.pl +++ b/src/lib/pairs.pl @@ -1,3 +1,10 @@ +/** Reasoning about pairs. + + Pairs are Prolog terms with principal functor `(-)/2`. A pair + often has the form `Key-Value`. The predicates of this library + relate pairs to keys and values. +*/ + :- module(pairs, [pairs_keys_values/3, pairs_keys/2, pairs_values/2, @@ -7,12 +14,25 @@ :- meta_predicate map_list_to_pairs(2, ?, ?). +%% pairs_keys_values(?Pairs, ?Keys, ?Values) +% +% The first argument is a list of Pairs, the second the corresponding +% Keys, and the third argument the corresponding values. + pairs_keys_values([], [], []). pairs_keys_values([A-B|ABs], [A|As], [B|Bs]) :- pairs_keys_values(ABs, As, Bs). +%% pairs_keys(?Pairs, ?Keys) +% +% Same as `pairs_keys_values(Pairs, Keys, _)`. + pairs_keys(Ps, Ks) :- pairs_keys_values(Ps, Ks, _). +%% pairs_values(?Pairs, ?Values) +% +% Same as `pairs_keys_values(Pairs, _, Values)`. + pairs_values(Ps, Vs) :- pairs_keys_values(Ps, _, Vs). map_list_to_pairs(Pred, Ls, Ps) :- From 7ca782b92d21df1cb2fab5fdb5d9a1a270c69cf1 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 26 Jan 2023 00:40:08 +0100 Subject: [PATCH 080/361] DOC: convert code samples in library(lambda) to DocLog format --- src/lib/lambda.pl | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/lib/lambda.pl b/src/lib/lambda.pl index 56332255..d4aacaa5 100644 --- a/src/lib/lambda.pl +++ b/src/lib/lambda.pl @@ -50,11 +50,13 @@ programming based on call/N. Lambda expressions are represented by ordinary Prolog terms. There are two kinds of lambda expressions: +``` Free+\X1^X2^ ..^XN^Goal \X1^X2^ ..^XN^Goal +``` -The second is a shorthand for t+\X1^X2^..^XN^Goal. +The second is a shorthand for `t+\X1^X2^..^XN^Goal`. Xi are the parameters. @@ -70,20 +72,20 @@ currently not checked. Violations may lead to unexpected bindings. In the following example the parentheses around X>3 are necessary. -== +``` ?- use_module(library(lambda)). ?- use_module(library(lists)). ?- maplist(\X^(X>3),[4,5,9]). true. -== +``` In the following X is a variable that is shared by both instances of the lambda expression. The second query illustrates the cooperation of continuations and lambdas. The lambda expression is in this case a continuation expecting a further argument. -== +``` ?- use_module(library(dif)). true. @@ -92,11 +94,12 @@ continuation expecting a further argument. ?- Xs = [A,B], maplist(X+\dif(X), Xs). Xs = [A,B], dif:dif(X,A), dif:dif(X,B). -== +``` The following queries are all equivalent. To see this, use -the fact f(x,y). -== +the fact `f(x,y)`. + +``` ?- call(f,A1,A2). ?- call(\X^f(X),A1,A2). ?- call(\X^Y^f(X,Y), A1,A2). @@ -105,10 +108,10 @@ the fact f(x,y). ?- call(f(A1),A2). ?- f(A1,A2). A1 = x, A2 = y. -== +``` Further discussions -http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/ISO-Hiord +[http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/ISO-Hiord](http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/ISO-Hiord) @tbd Static expansion similar to apply_macros. @author Ulrich Neumerkel From 2e9ec653a8c201fe68e6c932bdf078bf5c2ff04d Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 26 Jan 2023 00:46:06 +0100 Subject: [PATCH 081/361] DOC: convert library(simplex) documentation to DocLog format --- src/lib/simplex.pl | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/lib/simplex.pl b/src/lib/simplex.pl index 001571a8..0482d130 100644 --- a/src/lib/simplex.pl +++ b/src/lib/simplex.pl @@ -77,9 +77,9 @@ thesis project, for example. A *linear programming problem* or simply *linear program* (LP) consists of: - - a set of _linear_ **constraints** - - a set of **variables** - - a _linear_ **objective function**. + - a set of _linear_ *constraints* + - a set of *variables* + - a _linear_ *objective function*. The goal is to assign values to the variables so as to _maximize_ (or minimize) the value of the objective function while satisfying all @@ -107,10 +107,10 @@ non-negativity constraints should therefore be stated explicitly. This is the "radiation therapy" example, taken from _Introduction to Operations Research_ by Hillier and Lieberman. -[**Prolog DCG notation**](https://www.metalevel.at/prolog/dcg) is +[*Prolog DCG notation*](https://www.metalevel.at/prolog/dcg) is used to _implicitly_ thread the state through posting the constraints: -== +``` :- use_module(library(simplex)). :- use_module(library(dcgs)). @@ -125,15 +125,15 @@ post_constraints --> constraint([0.6*x1, 0.4*x2] >= 6), constraint([x1] >= 0), constraint([x2] >= 0). -== +``` An example query: -== +``` ?- radiation(S), variable_value(S, x1, Val1), variable_value(S, x2, Val2). S = solved(...), Val1 = 15 rdiv 2, Val2 = 9 rdiv 2. -== +``` ## Example 2 {#simplex-ex-2} @@ -143,7 +143,7 @@ Here is an instance of the knapsack problem described above, where `C variables, `x(1)` and `x(2)` that denote how many items to take of each type. -== +``` :- use_module(library(simplex)). knapsack(S) :- @@ -155,15 +155,15 @@ knapsack_constraints(S) :- constraint([6*x(1), 4*x(2)] =< 8, S0, S1), constraint([x(1)] =< 1, S1, S2), constraint([x(2)] =< 2, S2, S). -== +``` An example query yields: -== +``` ?- knapsack(S), variable_value(S, x(1), X1), variable_value(S, x(2), X2). S = solved(...), X1 = 1 rdiv 1, X2 = 1 rdiv 2. -== +``` That is, we are to take the one item of the first type, and half of one of the items of the other type to maximize the total value of items in the @@ -171,23 +171,23 @@ knapsack. If items can not be split, integrality constraints have to be imposed: -== +``` knapsack_integral(S) :- knapsack_constraints(S0), constraint(integral(x(1)), S0, S1), constraint(integral(x(2)), S1, S2), maximize([7*x(1), 4*x(2)], S2, S). -== +``` Now the result is different: -== +``` ?- knapsack_integral(S), variable_value(S, x(1), X1), variable_value(S, x(2), X2). X1 = 0 X2 = 2 -== +``` That is, we are to take only the _two_ items of the second type. Notice in particular that always choosing the remaining item with best @@ -207,7 +207,7 @@ The task is to find a _minimal_ number of these coins that amount to 111 units in total. We introduce variables `c(1)`, `c(5)` and `c(20)` denoting how many coins to take of the respective type: -== +``` :- use_module(library(simplex)). coins(S) :- @@ -226,16 +226,16 @@ coins --> constraint(integral(c(5))), constraint(integral(c(20))), minimize([c(1), c(5), c(20)]). -== +``` An example query: -== +``` ?- coins(S), variable_value(S, c(1), C1), variable_value(S, c(5), C5), variable_value(S, c(20), C20). S = solved(...), C1 = 1 rdiv 1, C5 = 2 rdiv 1, C20 = 5 rdiv 1. -== +``` @author [Markus Triska](https://www.metalevel.at) */ From 58fb8517172769f2b5d5c0296a5c39e01666b64c Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Fri, 27 Jan 2023 00:22:48 +0100 Subject: [PATCH 082/361] correct table layout for entries that themselves contain | --- src/lib/format.pl | 60 +++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/lib/format.pl b/src/lib/format.pl index f44091c1..32ad2ff9 100644 --- a/src/lib/format.pl +++ b/src/lib/format.pl @@ -42,36 +42,36 @@ % FormatString are used literally, except for the following tokens % with special meaning: % -% | ~w | use the next available argument from Arguments here | -% | ~q | use the next argument here, formatted as by `writeq/1` | -% | ~a | use the next argument here, which must be an atom | -% | ~s | use the next argument here, which must be a string | -% | ~d | use the next argument here, which must be an integer | -% | ~f | use the next argument here, a floating point number | -% | ~Nf | where N is an integer: format the float argument | -% | | using N digits after the decimal point | -% | ~Nd | like ~d, placing the last N digits after a decimal point; | -% | | if N is 0 or omitted, no decimal point is used. | -% | ~ND | like ~Nd, separating digits to the left of the decimal point | -% | | in groups of three, using the character "," (comma) | -% | ~NU | like ~ND, using "_" (underscore) to separate groups of digits | -% | ~NL | format an integer so that at most N digits appear on a line. | -% | | If N is 0 or omitted, it defaults to 72. | -% | ~Nr | where N is an integer between 2 and 36: format the | -% | | next argument, which must be an integer, in radix N. | -% | | The characters "a" to "z" are used for radices 10 to 36. | -% | | If N is omitted, it defaults to 8 (octal). | -% | ~NR | like ~Nr, except that "A" to "Z" are used for radices > 9 | -% | ~| | place a tab stop at this position | -% | ~N| | where N is an integer: place a tab stop at text column N | -% | ~N+ | where N is an integer: place a tab stop N characters | -% | | after the previous tab stop (or start of line) | -% | ~t | distribute spaces evenly between the two closest tab stops | -% | ~`Ct | like ~t, use character C instead of spaces to fill the space | -% | ~n | newline | -% | ~Nn | N newlines | -% | ~i | ignore the next argument | -% | \~\~ | the literal ~ | +% | `~w` | use the next available argument from Arguments here | +% | `~q` | use the next argument here, formatted as by `writeq/1` | +% | `~a` | use the next argument here, which must be an atom | +% | `~s` | use the next argument here, which must be a string | +% | `~d` | use the next argument here, which must be an integer | +% | `~f` | use the next argument here, a floating point number | +% | `~Nf` | where N is an integer: format the float argument | +% | | using N digits after the decimal point | +% | `~Nd` | like ~d, placing the last N digits after a decimal point; | +% | | if N is 0 or omitted, no decimal point is used. | +% | `~ND` | like ~Nd, separating digits to the left of the decimal point | +% | | in groups of three, using the character "," (comma) | +% | `~NU` | like ~ND, using "_" (underscore) to separate groups of digits | +% | `~NL` | format an integer so that at most N digits appear on a line. | +% | | If N is 0 or omitted, it defaults to 72. | +% | `~Nr` | where N is an integer between 2 and 36: format the | +% | | next argument, which must be an integer, in radix N. | +% | | The characters "a" to "z" are used for radices 10 to 36. | +% | | If N is omitted, it defaults to 8 (octal). | +% | `~NR` | like ~Nr, except that "A" to "Z" are used for radices > 9 | +% | `~|` | place a tab stop at this position | +% | `~N|` | where N is an integer: place a tab stop at text column N | +% | `~N+` | where N is an integer: place a tab stop N characters | +% | | after the previous tab stop (or start of line) | +% | `~t` | distribute spaces evenly between the two closest tab stops | +% | ``~`Ct`` | like ~t, use character C instead of spaces to fill the space | +% | `~n` | newline | +% | `~Nn` | N newlines | +% | `~i` | ignore the next argument | +% | `~~` | the literal ~ | % % Instead of `~N`, you can write `~*` to use the next argument from % Arguments as the numeric argument. From 4110cfcfcb41147ec0fed8011cb565b33f04f315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sat, 28 Jan 2023 16:00:27 +0100 Subject: [PATCH 083/361] Fix library(http/http_server) docs. Other minor fixes --- INDEX.md | 2 +- src/lib/http/http_server.pl | 59 ++++++++++++++++++++----------------- src/lib/lists.pl | 2 ++ 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/INDEX.md b/INDEX.md index 7653cfc4..b17a7190 100644 --- a/INDEX.md +++ b/INDEX.md @@ -15,7 +15,7 @@ Some of the Scryer Prolog features are: * Integrated constraint programming libraries: [clp(B)](/clpb.html), [clp(Z)](/clpz.html). * [Definite Clause Grammars](/dcgs.html) * Coroutining support ([`dif/2`](/dif.html), [`freeze/2`](/freeze.html), ...) -* Tabling and SLG resolution +* [Tabling and SLG resolution](/tabling.html) * Compact string representation * Network libraries ([TCP sockets](/sockets.html), [HTTP server](/http/http_server.html), [HTTP client](/http/http_open.html), ...) * [Cryptographical predicates](/crypto.html) diff --git a/src/lib/http/http_server.pl b/src/lib/http/http_server.pl index bbe17565..7f2de002 100644 --- a/src/lib/http/http_server.pl +++ b/src/lib/http/http_server.pl @@ -6,45 +6,49 @@ */ /** This library provides an starting point to build HTTP server based applications. -It is based on Hyper, which allows for HTTP/1.0, HTTP/1.1 and HTTP/2. However, +It is based on [Hyper](https://hyper.rs/), which allows for HTTP/1.0, HTTP/1.1 and HTTP/2. However, some advanced features that Hyper provides are still not accesible. ## Usage -The main predicate of the library is http\_listen/2, which needs a port number +The main predicate of the library is `http_listen/2`, which needs a port number (usually 80) and a list of handlers. A handler is a compound term with the functor as one HTTP method (in lowercase) and followed by a Route Match and a predicate which will handle the call. - text_handler(Request, Response) :- - http_status_code(Response, 200), - http_body(Response, text("Welcome to Scryer Prolog!")). - - parameter_handler(User, Request, Response) :- - http_body(Response, text(User)). - - http_listen(7890, [ - get(echo, text_handler), % GET /echo - post(user/User, parameter_handler(User)) % POST /user/ - ]). +``` +text_handler(Request, Response) :- + http_status_code(Response, 200), + http_body(Response, text("Welcome to Scryer Prolog!")). + +parameter_handler(User, Request, Response) :- + http_body(Response, text(User)). + +http_listen(7890, [ + get(echo, text_handler), % GET /echo + post(user/User, parameter_handler(User)) % POST /user/ +]). +``` Every handler predicate will have at least 2-arity, with Request and Response. -Although you can work directly with http\_request and http\_response terms, it is +Although you can work directly with `http_request` and `http_response` terms, it is recommeded to use the helper predicates, which are easier to understand and cleaner: - - `http\_headers(Response/Request, Headers)` - - `http\_status\_code(Responde, StatusCode)` - - `http\_body(Response/Request, text(Body))` - - `http\_body(Response/Request, binary(Body))` - - `http\_body(Request, form(Form))` - - `http\_body(Response, file(Filename))` - - `http\_redirect(Response, Url)` - - `http\_query(Request, QueryName, QueryValue)` - Some things that are still missing: + - `http_headers(Response/Request, Headers)` + - `http_status_code(Responde, StatusCode)` + - `http_body(Response/Request, text(Body))` + - `http_body(Response/Request, binary(Body))` + - `http_body(Request, form(Form))` + - `http_body(Response, file(Filename))` + - `http_redirect(Response, Url)` + - `http_query(Request, QueryName, QueryValue)` + +Some things that are still missing: + - Read forms in multipart format - HTTP Basic Auth - Session handling via cookies - - HTML Templating (but you can use [Teruel](https://github.com/aarroyoc/teruel/) and/or [Marquete](https://github.com/aarroyoc/marquete/) for that) + - HTML Templating (but you can use [Teruel](https://github.com/aarroyoc/teruel/), [Marquete](https://github.com/aarroyoc/marquete/) or [Djota](https://github.com/aarroyoc/djota) for that) */ @@ -71,8 +75,8 @@ recommeded to use the helper predicates, which are easier to understand and clea %% http_listen(+Port, +Handlers). % % Listens for HTTP connections on port Port. Each handler on the list Handlers should be of the form: `HttpVerb(PathUnification, Predicate)`. -% For example: `get(user/User, get\_info(User))` will match an HTTP request that is a GET, the path unifies with /user/User (where User is a variable) -% and it will call get_info with three arguments: an http\_request term, an http\_response term and User. +% For example: `get(user/User, get_info(User))` will match an HTTP request that is a GET, the path unifies with /user/User (where User is a variable) +% and it will call `get_info` with three arguments: an `http_request` term, an `http_response` term and User. http_listen(Port, Module:Handlers0) :- must_be(integer, Port), must_be(list, Handlers0), @@ -213,7 +217,7 @@ string_without(_, []) --> %% http_headers(?Request_Response, ?Headers). % -% True iff Request_Response is a request or response with headers Headers. Can be used both to get headers (usually in from a request) +% True iff `Request_Response` is a request or response with headers Headers. Can be used both to get headers (usually in from a request) % and to add headers (usually in a response). http_headers(http_request(Headers, _, _), Headers). http_headers(http_response(_, _, Headers), Headers). @@ -221,6 +225,7 @@ http_headers(http_response(_, _, Headers), Headers). %% http_body(?Request_Response, ?Body). % % True iff Body is the body of the request or response. A body can be of the following types: +% % * `bytes(Bytes)` for both requests and responses, interprets the body as bytes % * `text(Bytes)` for both requests and responses, interprets the body as text % * `form(Form)` only for requests, interprets the body as an `application/x-www-form-urlencoded` form. diff --git a/src/lib/lists.pl b/src/lib/lists.pl index cb33e8fe..1bdaf61e 100644 --- a/src/lib/lists.pl +++ b/src/lib/lists.pl @@ -73,6 +73,8 @@ resource_error(Resource, Context) :- % N = 3. % ?- length(Xs, 3). % Xs = [_A, _B, _C]. +% ?- length("chars", N). +% N = 5. % ``` length(Xs0, N) :- From 5bae8fcaf80c91a52d860b3e3b316c859786a3c0 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 28 Jan 2023 09:42:26 +0100 Subject: [PATCH 084/361] FIXED: use lsb/2 and msb/2 from library(arithmetic) This addresses #1720. --- src/lib/clpz.pl | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 04ba0a79..14a790fc 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -2962,8 +2962,8 @@ expr_conds(A0>>B0, A>>B) --> expr_conds(A0, A), expr_conds(B0, B). expr_conds(A0/\B0, A/\B) --> expr_conds(A0, A), expr_conds(B0, B). expr_conds(A0\/B0, A\/B) --> expr_conds(A0, A), expr_conds(B0, B). expr_conds(xor(A0,B0), xor(A,B)) --> expr_conds(A0, A), expr_conds(B0, B). -expr_conds(lsb(A0), lsb(A)) --> expr_conds(A0, A). -expr_conds(msb(A0), msb(A)) --> expr_conds(A0, A). +% expr_conds(lsb(A0), lsb(A)) --> expr_conds(A0, A). +% expr_conds(msb(A0), msb(A)) --> expr_conds(A0, A). expr_conds(popcount(A0), Count) --> expr_conds(A0, A), [I is A, arithmetic:popcount(I, Count)]. @@ -3539,8 +3539,8 @@ parse_reified(E, R, D, m(A^B) => [d(D), p(pexp(A,B,R)), a(A,B,R)], % bitwise operations m(\A) => [function(D,\,A,R)], - m(msb(A)) => [function(D,msb,A,R)], - m(lsb(A)) => [function(D,lsb,A,R)], + m(msb(A)) => [g(#A#>0) ,function(D,msb,A,R)], + m(lsb(A)) => [g(#A#>0), function(D,lsb,A,R)], m(popcount(A)) => [function(D,popcount,A,R)], m(sign(A)) => [function(D,sign,A,R)], m(A< [function(D,<<,A,B,R)], @@ -5683,8 +5683,11 @@ run_propagator(pfunction(Op,A,B,R), MState) --> run_propagator(pfunction(Op,A,R), MState) --> ( integer(A) -> kill(MState), - Expr =.. [Op,A], - R is Expr + ( Op == msb -> { msb(A, R) } + ; Op == lsb -> { lsb(A, R) } + ; Expr =.. [Op,A], + R is Expr + ) ; [] ). From 0d8c7f8785b60a03a9c40a699660c07015f69651 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 28 Jan 2023 10:03:23 +0100 Subject: [PATCH 085/361] small documentation adjustments --- src/lib/clpz.pl | 81 +++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 14a790fc..88e78d78 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -282,14 +282,14 @@ exclude_([L|Ls0], Goal, Ls) :- :- op(700, xfx, cis_lt). :- op(1200, xfx, ++>). -/** Constraint Logic Programming over Integers +/** Constraint Logic Programming over Integers ## Introduction This library provides CLP(ℤ): Constraint Logic Programming over Integers. -CLP(ℤ) is an instance of the general CLP(.) scheme, extending logic +CLP(ℤ) is an instance of the general CLP(_X_) scheme, extending logic programming with reasoning over specialised domains. CLP(ℤ) lets us reason about *integers* in a way that honors the relational nature of Prolog. @@ -303,7 +303,7 @@ There are two major use cases of CLP(ℤ) constraints: The predicates of this library can be classified as: - * _arithmetic_ constraints like `#=/2`, `#>/2` and `#\=/2` + * _arithmetic_ constraints like `(#=)/2`, `(#>)/2` and `(#\=)/2` * the _membership_ constraints `in/2` and `ins/2` * the _enumeration_ predicates `indomain/1`, `label/1` and `labeling/2` * _combinatorial_ constraints like `all_distinct/1` and `global_cardinality/2` @@ -314,7 +314,7 @@ In most cases, [_arithmetic constraints_](<#clpz-arith-constraints>) are the only predicates you will ever need from this library. When reasoning over integers, simply replace low-level arithmetic predicates like `(is)/2` and `(>)/2` by the corresponding CLP(ℤ) -constraints like #=/2 and #>/2 to honor and preserve declarative +constraints like `(#=)/2` and `(#>)/2` to honor and preserve declarative properties of your programs. For satisfactory performance, arithmetic constraints are implicitly rewritten at compilation time so that low-level fallback predicates are automatically used whenever @@ -323,7 +323,7 @@ possible. Almost all Prolog programs also reason about integers. Therefore, it is highly advisable that you make CLP(ℤ) constraints available in all your programs. One way to do this is to put the following directive in -your =|~/.scryerrc|= initialisation file: +your `~/.scryerrc` initialisation file: ``` :- use_module(library(clpz)). @@ -410,12 +410,12 @@ supported. ## Declarative integer arithmetic {#clpz-integer-arith} -The [_arithmetic constraints_](<#clpz-arith-constraints>) #=/2, #>/2 -etc. are meant to be used _instead_ of the primitives `(is)/2`, -`(=:=)/2`, `(>)/2` etc. over integers. Almost all Prolog programs also -reason about integers. Therefore, it is recommended that you put the -following directive in your =|~/.scryerrc|= initialisation file to make -CLP(ℤ) constraints available in all your programs: +The [_arithmetic constraints_](#clpz-arith-constraints) `(#=)/2`, +`(#>)/2` etc. are meant to be used _instead_ of the primitives +`(is)/2`, `(=:=)/2`, `(>)/2` etc. over integers. Almost all Prolog +programs also reason about integers. Therefore, it is recommended that +you put the following directive in your =|~/.scryerrc|= initialisation +file to make CLP(ℤ) constraints available in all your programs: ``` :- use_module(library(clpz)). @@ -499,7 +499,7 @@ used instead. ## Example: Factorial relation {#clpz-factorial} -We illustrate the benefit of using #=/2 for more generality with a +We illustrate the benefit of using `(#=)/2` for more generality with a simple example. Consider first a rather conventional definition of `n_factorial/2`, @@ -560,7 +560,7 @@ us from _all_ procedural phenomena. For example, the two programs do not even have the same _termination properties_ in all cases. Instead, the primary benefit of CLP(ℤ) constraints is that they allow you to try different execution orders and apply [*declarative -debugging*](https://www.metalevel.at/prolog/debugging.html) +debugging*](https://www.metalevel.at/prolog/debugging) techniques _at all_! Reordering goals (and clauses) can significantly impact the performance of Prolog programs, and you are free to try different variants if you use declarative approaches. Moreover, since @@ -568,27 +568,27 @@ all CLP(ℤ) constraints _always terminate_, placing them earlier can at most _improve_, never worsen, the termination properties of your programs. An additional benefit of CLP(ℤ) constraints is that they eliminate the complexity of introducing `(is)/2` and `(=:=)/2` to -beginners, since _both_ predicates are subsumed by #=/2 when reasoning -over integers. +beginners, since _both_ predicates are subsumed by `(#=)/2` when +reasoning over integers. ## Combinatorial constraints {#clpz-combinatorial} In addition to subsuming and replacing low-level arithmetic predicates, CLP(ℤ) constraints are often used to solve combinatorial problems such as planning, scheduling and allocation tasks. Among the -most frequently used *combinatorial constraints* are all_distinct/1, -global_cardinality/2 and cumulative/2. This library also provides -several other constraints like disjoint2/1 and automaton/8, which are +most frequently used *combinatorial constraints* are `all_distinct/1`, +`global_cardinality/2` and `cumulative/2`. This library also provides +several other constraints like `disjoint2/1` and `automaton/8`, which are useful in more specialized applications. ## Domains {#clpz-domains} Each CLP(ℤ) variable has an associated set of admissible integers, which we call the variable's *domain*. Initially, the domain of each -CLP(ℤ) variable is the set of _all_ integers. CLP(ℤ) constraints -like #=/2, #>/2 and #\=/2 can at most reduce, and never extend, the -domains of their arguments. The constraints in/2 and ins/2 let us -explicitly state domains of CLP(ℤ) variables. The process of +CLP(ℤ) variable is the set of _all_ integers. CLP(ℤ) constraints like +`(#=)/2`, `(#>)/2` and `(#\=)/2` can at most reduce, and never extend, +the domains of their arguments. The constraints `(in)/2` and `(ins)/2` +let us explicitly state domains of CLP(ℤ) variables. The process of determining and adjusting domains of variables is called constraint *propagation*, and it is performed automatically by this library. When the domain of a variable contains only one element, then the variable @@ -683,10 +683,10 @@ goals, it is clear that the constraint solver has deduced additional domain restrictions in many cases. To inspect residual goals, it is best to let the toplevel display them -for us. Wrap the call of your predicate into call_residue_vars/2 to +for us. Wrap the call of your predicate into `call_residue_vars/2` to make sure that all constrained variables are displayed. To make the constraints a variable is involved in available as a Prolog term for -further reasoning within your program, use copy_term/3. For example: +further reasoning within your program, use `copy_term/3`. For example: ``` ?- X #= Y + Z, X in 0..5, copy_term([X,Y,Z], [X,Y,Z], Gs). @@ -695,8 +695,8 @@ X in 0..5, Y+Z#=X. ``` -This library also provides _reflection_ predicates (like fd_dom/2, -fd_size/2 etc.) with which we can inspect a variable's current +This library also provides _reflection_ predicates (like `fd_dom/2`, +`fd_size/2` etc.) with which we can inspect a variable's current domain. These predicates can be useful if you want to implement your own labeling strategies. @@ -732,7 +732,7 @@ puzzle([S,E,N,D] + [M,O,R,E] = [M,O,N,E,Y]) :- M #\= 0, S #\= 0. ``` -Notice that we are _not_ using labeling/2 in this predicate, so that +Notice that we are _not_ using `labeling/2` in this predicate, so that we can first execute and observe the modeling part in isolation. Sample query and its result (actual variables replaced for readability): @@ -853,7 +853,7 @@ separated the core relation from the actual search. ## Optimisation {#clpz-optimisation} -We can use labeling/2 to minimize or maximize the value of a CLP(ℤ) +We can use `labeling/2` to minimize or maximize the value of a CLP(ℤ) expression, and generate solutions in increasing or decreasing order of the value. See the labeling options `min(Expr)` and `max(Expr)`, respectively. @@ -868,7 +868,7 @@ If necessary, we can use `once/1` to commit to the first optimal solution. However, it is often very valuable to see alternative solutions that are _also_ optimal, so that we can choose among optimal solutions by other criteria. For the sake of -[*purity*](https://www.metalevel.at/prolog/purity.html) and +[*purity*](https://www.metalevel.at/prolog/purity) and completeness, we recommend to avoid `once/1` and other constructs that lead to impurities in CLP(ℤ) programs. @@ -878,10 +878,11 @@ numbers. ## Reification {#clpz-reification} -The constraints in/2, #=/2, #\=/2, #/2, #==/2 can be -_reified_, which means reflecting their truth values into Boolean -values represented by the integers 0 and 1. Let P and Q denote -reifiable constraints or Boolean variables, then: +The constraints `(in)/2`, `(#=)/2`, `(#\=)/2`, `(#<)/2`, `(#>)/2`, +`(#=<)/2`, and `(#>=)/2` can be _reified_, which means reflecting +their truth values into Boolean values represented by the integers 0 +and 1. Let P and Q denote reifiable constraints or Boolean variables, +then: | #\ Q | True iff Q is false | | P #\/ Q | True iff either P or Q | @@ -958,19 +959,19 @@ clpz:run_propagator(oneground(X, Y, Z), MState) :- ). ``` -First, clpz:make_propagator/2 is used to transform a user-defined +First, `clpz:make_propagator/2` is used to transform a user-defined representation of the new constraint to an internal form. With -clpz:init_propagator/2, this internal form is then attached to X and +`clpz:init_propagator/2`, this internal form is then attached to X and Y. From now on, the propagator will be invoked whenever the domains of -X or Y are changed. Then, clpz:trigger_once/1 is used to give the +X or Y are changed. Then, `clpz:trigger_once/1` is used to give the propagator its first chance for propagation even though the variables' -domains have not yet changed. Finally, clpz:run_propagator/2 is +domains have not yet changed. Finally, `clpz:run_propagator/2` is extended to define the actual propagator. As explained, this predicate is automatically called by the constraint solver. The first argument is the user-defined representation of the constraint as used in -clpz:make_propagator/2, and the second argument is a mutable state +`clpz:make_propagator/2`, and the second argument is a mutable state that can be used to prevent further invocations of the propagator when -the constraint has become entailed, by using clpz:kill/1. An example +the constraint has become entailed, by using `clpz:kill/1`. An example of using the new constraint: ``` @@ -2922,7 +2923,7 @@ X #=< Y :- Y #>= X. %% #=(?X, ?Y) % % The arithmetic expression X equals Y. When reasoning over integers, -% replace is/2 by #=/2 to obtain more general relations. +% replace `(is)/2` by `(#=)/2` to obtain more general relations. X #= Y :- clpz_equal(X, Y). From 814b631543fc83e1829789e6c36d86c84860eb2f Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 28 Jan 2023 10:43:42 +0100 Subject: [PATCH 086/361] use DocLog syntax for section anchors and links within the document --- src/lib/clpz.pl | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 88e78d78..ebcaa249 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -296,7 +296,7 @@ of Prolog. There are two major use cases of CLP(ℤ) constraints: - 1. [*declarative integer arithmetic*](<#clpz-integer-arith>) + 1. [*declarative integer arithmetic*](#clpz-integer-arith) 2. solving *combinatorial problems* such as planning, scheduling and allocation tasks. @@ -310,7 +310,7 @@ The predicates of this library can be classified as: * _reification_ predicates such as `#<==>/2` * _reflection_ predicates such as `fd_dom/2` -In most cases, [_arithmetic constraints_](<#clpz-arith-constraints>) +In most cases, [_arithmetic constraints_](#clpz-arith-constraints) are the only predicates you will ever need from this library. When reasoning over integers, simply replace low-level arithmetic predicates like `(is)/2` and `(>)/2` by the corresponding CLP(ℤ) @@ -362,7 +362,8 @@ constraints is to use the dedicated `clpz` tag on foremost CLP(ℤ) experts regularly participate in these discussions and will help you for free on this platform. -## Arithmetic constraints {#clpz-arith-constraints} +{#clpz-arith-constraints} +## Arithmetic constraints In modern Prolog systems, *arithmetic constraints* subsume and supersede low-level predicates over integers. The main advantage of @@ -408,7 +409,8 @@ The bitwise operations `(\)/1`, `(/\)/2`, `(\/)/2`, `(>>)/2`, `(<<)/2`, `lsb/1`, `msb/1`, `popcount/1` and `(xor)/2` are also supported. -## Declarative integer arithmetic {#clpz-integer-arith} +{#clpz-integer-arith} +## Declarative integer arithmetic The [_arithmetic constraints_](#clpz-arith-constraints) `(#=)/2`, `(#>)/2` etc. are meant to be used _instead_ of the primitives @@ -460,7 +462,7 @@ and should therefore be deferred to more advanced lectures. For supported expressions, CLP(ℤ) constraints are drop-in replacements of these low-level arithmetic predicates, often yielding -more general programs. See [`n_factorial/2`](<#clpz-factorial>) for an +more general programs. See [`n_factorial/2`](#clpz-factorial) for an example. This library uses goal_expansion/2 to automatically rewrite @@ -497,7 +499,8 @@ primitives by providing declarative alternatives that are meant to be used instead. -## Example: Factorial relation {#clpz-factorial} +{#clpz-factorial} +## Example: Factorial relation We illustrate the benefit of using `(#=)/2` for more generality with a simple example. @@ -571,7 +574,8 @@ eliminate the complexity of introducing `(is)/2` and `(=:=)/2` to beginners, since _both_ predicates are subsumed by `(#=)/2` when reasoning over integers. -## Combinatorial constraints {#clpz-combinatorial} +{#clpz-combinatorial} +## Combinatorial constraints In addition to subsuming and replacing low-level arithmetic predicates, CLP(ℤ) constraints are often used to solve combinatorial @@ -581,7 +585,8 @@ most frequently used *combinatorial constraints* are `all_distinct/1`, several other constraints like `disjoint2/1` and `automaton/8`, which are useful in more specialized applications. -## Domains {#clpz-domains} +{#clpz-domains} +## Domains Each CLP(ℤ) variable has an associated set of admissible integers, which we call the variable's *domain*. Initially, the domain of each @@ -597,7 +602,8 @@ is automatically unified to that element. Domains are taken into account when further constraints are stated, and by enumeration predicates like labeling/2. -## Example: Sudoku {#clpz-sudoku} +{#clpz-sudoku} +## Example: Sudoku As another example, consider _Sudoku_: It is a popular puzzle over integers that can be easily solved with CLP(ℤ) constraints. @@ -650,7 +656,8 @@ In this concrete case, the constraint solver is strong enough to find the unique solution without any search. -## Residual goals {#clpz-residual-goals} +{#clpz-residual-goals} +## Residual goals Here is an example session with a few queries and their answers: @@ -700,7 +707,8 @@ This library also provides _reflection_ predicates (like `fd_dom/2`, domain. These predicates can be useful if you want to implement your own labeling strategies. -## Core relations and search {#clpz-search} +{#clpz-search} +## Core relations and search Using CLP(ℤ) constraints to solve combinatorial tasks typically consists of two phases: @@ -772,7 +780,8 @@ to reduce the domains of remaining variables to singleton sets. In general though, it is necessary to label all variables to obtain ground solutions. -## Example: Eight queens puzzle {#clpz-n-queens} +{#clpz-n-queens} +## Example: Eight queens puzzle We illustrate the concepts of the preceding sections by means of the so-called _eight queens puzzle_. The task is to place 8 queens on an @@ -851,7 +860,8 @@ separated the core relation from the actual search. -## Optimisation {#clpz-optimisation} +{#clpz-optimisation} +## Optimisation We can use `labeling/2` to minimize or maximize the value of a CLP(ℤ) expression, and generate solutions in increasing or decreasing order @@ -876,7 +886,8 @@ Related to optimisation with CLP(ℤ) constraints are `library(simplex)` and CLP(Q) which reason about _linear_ constraints over rational numbers. -## Reification {#clpz-reification} +{#clpz-reification} +## Reification The constraints `(in)/2`, `(#=)/2`, `(#\=)/2`, `(#<)/2`, `(#>)/2`, `(#=<)/2`, and `(#>=)/2` can be _reified_, which means reflecting @@ -897,7 +908,8 @@ The constraints of this table are reifiable as well. When reasoning over Boolean variables, also consider using CLP(B) constraints as provided by `library(clpb)`. -## Enabling monotonic CLP(ℤ) {#clpz-monotonicity} +{#clpz-monotonicity} +## Enabling monotonic CLP(ℤ) In the default execution mode, CLP(ℤ) constraints still exhibit some non-relational properties. For example, _adding_ constraints can yield @@ -933,7 +945,8 @@ expressions with the functor `(?)/1` or `(#)/1`. For example: The wrapper can be omitted for variables that are already constrained to integers. -## Custom constraints {#clpz-custom-constraints} +{#clpz-custom-constraints} +## Custom constraints We can define custom constraints. The mechanism to do this is not yet finalised, and we welcome suggestions and descriptions of use cases From 95278c221b60c1643171bffdf96878a4b889f61a Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 29 Jan 2023 21:48:22 +0100 Subject: [PATCH 087/361] DOC: use valid Prolog terms as predicate indicators --- src/lib/clpz.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index ebcaa249..d6223a45 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -304,10 +304,10 @@ There are two major use cases of CLP(ℤ) constraints: The predicates of this library can be classified as: * _arithmetic_ constraints like `(#=)/2`, `(#>)/2` and `(#\=)/2` - * the _membership_ constraints `in/2` and `ins/2` + * the _membership_ constraints `(in)/2` and `(ins)/2` * the _enumeration_ predicates `indomain/1`, `label/1` and `labeling/2` * _combinatorial_ constraints like `all_distinct/1` and `global_cardinality/2` - * _reification_ predicates such as `#<==>/2` + * _reification_ predicates such as `(#<==>)/2` * _reflection_ predicates such as `fd_dom/2` In most cases, [_arithmetic constraints_](#clpz-arith-constraints) @@ -7569,7 +7569,7 @@ fd_size(X, S) :- %% fd_dom(+Var, -Dom) % -% Dom is the current domain (see in/2) of Var. This predicate is +% Dom is the current domain (see `(in)/2`) of Var. This predicate is % useful if you want to reason about domains. It is _not_ needed if % you only want to display remaining domains; instead, separate your % model from the search part and let the toplevel display this From f347baafa3797c3d344b743ca0aa3e4dbfe603b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sun, 29 Jan 2023 22:36:16 +0100 Subject: [PATCH 088/361] Compatible Doclog docs for library(csv) --- src/lib/csv.pl | 79 +++++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/src/lib/csv.pl b/src/lib/csv.pl index 478d425c..d6ad8f73 100644 --- a/src/lib/csv.pl +++ b/src/lib/csv.pl @@ -1,54 +1,67 @@ -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Predicates for parsing CSV data +/** Predicates for parsing CSV data +## Read CSV files. - Read csv files +Only two options with default values: - Only two options with default values : - - token_separator(',') - - with_header(true) +- `token_separator(',')` +- `with_header(true)` - Examples +### Examples: - * parsing a csv string: +Parsing a CSV string: - ?- use_module(library(csv)). - ?- use_module(library(dcgs)). - ?- phrase(parse_csv(Data), "col1,col2,col3,col4\none,2,,three"). - Data = frame(["col1","col2","col3","col4"],[["one",2,[],"three"]]). +``` +?- use_module(library(csv)). +?- use_module(library(dcgs)). +?- phrase(parse_csv(Data), "col1,col2,col3,col4\none,2,,three"). + Data = frame(["col1","col2","col3","col4"],[["one",2,[],"three"]]). +``` - * with some options: +With some options: - ?- phrase(parse_csv(Data, [with_header(false), token_separator(';')]), "one;2;;three"). - Data = frame([],[["one",2,[],"three"]]). +``` +?- phrase(parse_csv(Data, [with_header(false), token_separator(';')]), "one;2;;three"). + Data = frame([],[["one",2,[],"three"]]). +``` - * parsing a csv file: +Parsing a CSV file: - ?- use_module(library(csv)). - ?- use_module(library(pio)). - ?- phrase_from_file(parse_csv(frame(Header, Rows)), './test.csv'). +``` +?- use_module(library(csv)). +?- use_module(library(pio)). +?- phrase_from_file(parse_csv(frame(Header, Rows)), './test.csv'). +``` +## Write CSV files - Write csv files +Four options with default values : - Four options with default values : - - line_separator('\n') - - token_separator(',') - - with_header(true) - - null_value(empty) +- `line_separator('\n')` +- `token_separator(',')` +- `with_header(true)` +- `null_value(empty)` - Examples +### Examples - * writing a csv file: +Writing a CSV file: - ?- use_module(library(csv)). - ?- write_csv('./test.csv', frame(["col1","col2","col3","col4"], [["one",2,[],"three"]])). +``` +?- use_module(library(csv)). +?- write_csv('./test.csv', frame(["col1","col2","col3","col4"], [["one",2,[],"three"]])). +``` - * with some options +With some options - ?- use_module(library(csv)). - ?- write_csv('./test.csv', frame(["col1","col2","col3","col4"], [["one",2,[],"three"]]), [with_header(false), line_separator('\r\n'), token_separator(';'), null_value('\\N')]). -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +``` +?- use_module(library(csv)). +?- write_csv('./test.csv', frame( + ["col1","col2","col3","col4"], + [["one",2,[],"three"]] + ), + [with_header(false), line_separator('\r\n'), token_separator(';'), null_value('\\N')]). +``` +*/ :- module(csv, [ parse_csv//1, From 36b3150225b6d614e4f7caab62db91169db83ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Mon, 30 Jan 2023 18:56:37 +0100 Subject: [PATCH 089/361] Negative shifts (fixes #1719 and #1718) --- src/machine/arithmetic_ops.rs | 68 ++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index 6e12b8c6..87911d09 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -633,7 +633,9 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result> n2, arena)); - } else { + } else if let Ok(n2) = u32::try_from(n2_i * -1) { + return Ok(Number::arena_from(n1 << n2, arena)); + } else { return Ok(Number::arena_from(n1 >> u32::max_value(), arena)); } } @@ -642,22 +644,34 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result Ok(Number::arena_from(n1 >> n2, arena)), - _ => Ok(Number::arena_from(n1 >> u32::max_value(), arena)), + _ => { + if let Some(n2) = Integer::from(&*n2 * -1).to_u32() { + Ok(Number::arena_from(n1 << n2, arena)) + } else { + Ok(Number::arena_from(n1 >> u32::max_value(), arena)) + } + }, } } (Number::Integer(n1), Number::Fixnum(n2)) => match u32::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, - )), + _ => { + if let Ok(n2) = u32::try_from(n2.get_num() * -1) { + Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)) + } else { + Ok(Number::arena_from(Integer::from(&*n1 >> u32::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, - )), + _ => { + if let Some(n2) = Integer::from(&*n2 * -1).to_u32() { + Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)) + } else { + Ok(Number::arena_from(Integer::from(&*n1 >> u32::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)), @@ -680,7 +694,9 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result> n2, arena)); + } else { return Ok(Number::arena_from(n1 << u32::max_value(), arena)); } } @@ -689,22 +705,34 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result Ok(Number::arena_from(n1 << n2, arena)), - _ => Ok(Number::arena_from(n1 << u32::max_value(), arena)), + _ => { + if let Some(n2) = Integer::from(&*n2 * -1).to_u32() { + Ok(Number::arena_from(n1 >> n2, arena)) + } else { + Ok(Number::arena_from(n1 << u32::max_value(), arena)) + } + } } } (Number::Integer(n1), Number::Fixnum(n2)) => match u32::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, - )), + _ => { + if let Ok(n2) = u32::try_from(n2.get_num() * -1) { + Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)) + } else { + Ok(Number::arena_from(Integer::from(&*n1 << u32::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, - )), + _ => { + if let Some(n2) = Integer::from(&*n2 * -1).to_u32() { + Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)) + } else { + Ok(Number::arena_from(Integer::from(&*n1 << u32::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)), From 5667ec8699bf86da8288107bfc8817b2398629a8 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 24 Jan 2023 16:17:03 -0700 Subject: [PATCH 090/361] update tokio version --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 05e54911..63784cd9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2253,9 +2253,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.21.2" +version = "1.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" dependencies = [ "autocfg 1.1.0", "bytes", @@ -2268,7 +2268,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.42.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6e42de23..6c98ea43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ static_assertions = "1.1.0" ryu = "1.0.9" hyper = { version = "0.14", features = ["full"] } hyper-tls = "0.5.0" -tokio = { version = "1", features = ["full"] } +tokio = { version = "1.24.2", features = ["full"] } futures = "0.3" [dev-dependencies] From ce56a7303e9a84a28031655acd56299537890a5c Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 31 Jan 2023 00:15:57 -0700 Subject: [PATCH 091/361] avoid arena allocation of stream in read_term_from_chars (#1266) --- src/arena.rs | 18 +++++++++--------- src/machine/streams.rs | 11 +++++++++-- src/machine/system_calls.rs | 16 +++++++++++++--- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/arena.rs b/src/arena.rs index 3e75e120..b055c8db 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -698,9 +698,9 @@ unsafe fn drop_slab_in_place(value: &mut AllocSlab) { ArenaHeaderTag::HttpReadStream => { ptr::drop_in_place(value.payload_offset::>>()); } - ArenaHeaderTag::HttpWriteStream => { - ptr::drop_in_place(value.payload_offset::>>()); - } + ArenaHeaderTag::HttpWriteStream => { + ptr::drop_in_place(value.payload_offset::>>()); + } ArenaHeaderTag::ReadlineStream => { ptr::drop_in_place(value.payload_offset::>()); } @@ -721,12 +721,12 @@ unsafe fn drop_slab_in_place(value: &mut AllocSlab) { ArenaHeaderTag::TcpListener => { ptr::drop_in_place(value.payload_offset::()); } - ArenaHeaderTag::HttpListener => { - ptr::drop_in_place(value.payload_offset::()); - } - ArenaHeaderTag::HttpResponse => { - ptr::drop_in_place(value.payload_offset::()); - } + ArenaHeaderTag::HttpListener => { + ptr::drop_in_place(value.payload_offset::()); + } + ArenaHeaderTag::HttpResponse => { + ptr::drop_in_place(value.payload_offset::()); + } ArenaHeaderTag::StandardOutputStream => { ptr::drop_in_place(value.payload_offset::>()); } diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 9e558af5..932e7f78 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -102,6 +102,13 @@ impl EOFAction { #[derive(Debug)] pub struct ByteStream(Cursor>); +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 { @@ -1132,14 +1139,14 @@ impl Stream { Ok(()) } - Stream::HttpWrite(ref mut http_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 _); } Ok(()) - } + } Stream::InputFile(mut file_stream) => { // close the stream by dropping the inner File. unsafe { diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 69dc018e..93dcdb09 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5138,10 +5138,20 @@ impl Machine { #[inline(always)] pub(crate) fn read_term_from_chars(&mut self) -> CallResult { if let Some(atom_or_string) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { - let chars = atom_or_string.to_string(); - let stream = Stream::from_owned_string(chars, &mut self.machine_st.arena); + let chars = CharReader::new(ByteStream::from_string(atom_or_string.to_string())); + let mut parser = Parser::new(chars, &mut self.machine_st); - let term_write_result = match self.machine_st.read(stream, &self.indices.op_dir) { + let term_write_result = parser.read_term(&CompositeOpDir::new(&self.indices.op_dir, None)) + .map_err(CompilationError::from) + .and_then(|term| { + write_term_to_heap( + &term, + &mut self.machine_st.heap, + &mut self.machine_st.atom_tbl, + ) + }); + + let term_write_result = match term_write_result { Ok(term_write_result) => term_write_result, Err(e) => { let stub = functor_stub(atom!("read_term_from_chars"), 2); From da4c0a359b99217b717a7a25e1e48cd8fab48a82 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 1 Feb 2023 23:26:52 +0100 Subject: [PATCH 092/361] correct DocLog ~/.scryerrc rendering --- src/lib/clpz.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index d6223a45..0e860bf3 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -416,7 +416,7 @@ The [_arithmetic constraints_](#clpz-arith-constraints) `(#=)/2`, `(#>)/2` etc. are meant to be used _instead_ of the primitives `(is)/2`, `(=:=)/2`, `(>)/2` etc. over integers. Almost all Prolog programs also reason about integers. Therefore, it is recommended that -you put the following directive in your =|~/.scryerrc|= initialisation +you put the following directive in your `~/.scryerrc` initialisation file to make CLP(ℤ) constraints available in all your programs: ``` From e8408ca93f971e43bf2f1acfb8434fe44af0ffdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Thu, 2 Feb 2023 21:35:35 +0100 Subject: [PATCH 093/361] Minor fixes to docs --- INDEX.md | 6 +++--- src/lib/builtins.pl | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/INDEX.md b/INDEX.md index b17a7190..907d1e9c 100644 --- a/INDEX.md +++ b/INDEX.md @@ -5,7 +5,7 @@ X = "Scryer Prolog!". ``` -![scryer](scryer.png){width=128 style=float:right;} Scryer Prolog is a free software ISO Prolog system intended to be an industrial +![scryer](scryer.png){width=128 style=float:right;} [Scryer Prolog](https://github.com/mthom/scryer-prolog) is a free software ISO Prolog system intended to be an industrial strength production environment *and* a testbed for bleeding edge research in logic and constraint programming. @@ -38,7 +38,7 @@ general programs. If you want a more detailed description of Prolog, check [A Tour of Prolog](https://www.youtube.com/watch?v=8XUutFBbUrg). -If you want to learn more about Prolog history, [check this video](https://www.youtube.com/watch?v=74Ig_QKndvE) and [this talk](https://prologyear.logicprogramming.org/videos/PrologDay_Session_1_talk.mp4). +If you want to learn more about Prolog history, check the videos [l'Aventure Prolog](https://www.youtube.com/watch?v=74Ig_QKndvE) and [50 years of Prolog and beyond](https://prologyear.logicprogramming.org/videos/PrologDay_Session_1_talk.mp4). ## Where can I learn Prolog? @@ -61,7 +61,7 @@ There's also a [Docker image](https://github.com/mthom/scryer-prolog#docker-inst ## Support and discussions If Scryer Prolog crashes or yields unexpected errors, consider filing -an [issue](https://github.com/mthom/scryer-prolog/issues). +an [issue](https://github.com/mthom/scryer-prolog/issues). To get in touch with the Scryer Prolog community, participate in [discussions](https://github.com/mthom/scryer-prolog/discussions) diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index ee33c957..878919cf 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -600,6 +600,7 @@ write_term(Term, Options) :- %% write_term(+Stream, +Term, +Options). % % Write Term to the stream Stream according to some output syntax options. The options avaibale are: +% % * `ignore_ops(+Boolean)` if `true`, the generic term representation is used everywhere. In `false` % (default), operators do not use that generic term representation. % * `max_depth(+N)` if the term is nested deeper than N, print the reminder as ellipses. @@ -2056,6 +2057,7 @@ stream_iter(S) :- % % For stream Stream, StreamProperty is a property that applies to that stream. % StreamProperty can be one of the following: +% % * `input` if stream is an input stream. % * `output` if stream is an output stream. % * `input_output` if stream is both an input and an output stream. From 17450520ba1febbb8fc826c12cb6e29117a0bdda Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Thu, 2 Feb 2023 20:49:14 -0700 Subject: [PATCH 094/361] shift by usize instead of u32 in shl and shr (#1718, #1719) --- src/machine/arithmetic_ops.rs | 80 +++++++++++++---------------------- 1 file changed, 30 insertions(+), 50 deletions(-) diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index 87911d09..2ad43e73 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -624,6 +624,10 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result { let n1_i = n1.get_num(); @@ -631,47 +635,33 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result> n2, arena)); - } else if let Ok(n2) = u32::try_from(n2_i * -1) { - 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)), _ => { - if let Some(n2) = Integer::from(&*n2 * -1).to_u32() { - Ok(Number::arena_from(n1 << n2, arena)) - } else { - 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)), _ => { - if let Ok(n2) = u32::try_from(n2.get_num() * -1) { - Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)) - } else { - 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)), _ => { - if let Some(n2) = Integer::from(&*n2 * -1).to_u32() { - Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)) - } else { - 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)), @@ -685,6 +675,10 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result { let n1_i = n1.get_num(); @@ -692,12 +686,10 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result> 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)) => { @@ -706,33 +698,21 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result Ok(Number::arena_from(n1 << n2, arena)), _ => { - if let Some(n2) = Integer::from(&*n2 * -1).to_u32() { - Ok(Number::arena_from(n1 >> n2, arena)) - } else { - 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)), _ => { - if let Ok(n2) = u32::try_from(n2.get_num() * -1) { - Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)) - } else { - 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)), _ => { - if let Some(n2) = Integer::from(&*n2 * -1).to_u32() { - Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)) - } else { - 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)), From 196e9c1e47c827b6e45c6a8e331774ed33274fb6 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Fri, 3 Feb 2023 20:44:11 +0100 Subject: [PATCH 095/361] DOC: teletype font for reification --- src/lib/clpz.pl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 0e860bf3..5823aa42 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -895,13 +895,13 @@ their truth values into Boolean values represented by the integers 0 and 1. Let P and Q denote reifiable constraints or Boolean variables, then: -| #\ Q | True iff Q is false | -| P #\/ Q | True iff either P or Q | -| P #/\ Q | True iff both P and Q | -| P #\ Q | True iff either P or Q, but not both | -| P #<==> Q | True iff P and Q are equivalent | -| P #==> Q | True iff P implies Q | -| P #<== Q | True iff Q implies P | +| `#\ Q` | True iff Q is false | +| `P #\/ Q` | True iff either P or Q | +| `P #/\ Q` | True iff both P and Q | +| `P #\ Q` | True iff either P or Q, but not both | +| `P #<==> Q` | True iff P and Q are equivalent | +| `P #==> Q` | True iff P implies Q | +| `P #<== Q` | True iff Q implies P | The constraints of this table are reifiable as well. From 2c2a9fe01e9946707b87257d3d8e04cc7775c16e Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Fri, 3 Feb 2023 22:27:08 -0700 Subject: [PATCH 096/361] correct shl stub_gen --- src/machine/arithmetic_ops.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index 2ad43e73..774b848f 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -671,7 +671,7 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result Result { let stub_gen = || { - let shl_atom = atom!(">>"); + let shl_atom = atom!("<<"); functor_stub(shl_atom, 2) }; From 2fcec4fff94ec1fd0ceed2c134c3a6db1206a51c Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 4 Feb 2023 18:14:16 -0700 Subject: [PATCH 097/361] include wambook errata --- wambook/errata.txt | 169 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 wambook/errata.txt diff --git a/wambook/errata.txt b/wambook/errata.txt new file mode 100644 index 00000000..43ec62d0 --- /dev/null +++ b/wambook/errata.txt @@ -0,0 +1,169 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +Errata for: + + Warren's Abstract Machine: A Tutorial Reconstruction + Hassan Ait-Kaci + MIT Press, Cambridge, MA + 1991 + + ISBN 0-262-51058-8 (paper) + ISBN 0-262-01123-9 (cloth) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +I am enclosing below the most up-to-date list of typos, bugs, and their - +easy - fixes... Anything else that you will find, please report back to +me. Who knows, one day when my stack is finally empty (ha!), I'll work on a +second edition with extensions. In the mean time, please accept my +apologies for your painful reading. On the other hand, my book's bugs and +typos are a wonderful indicator of who did or not actually try to implement +the code therein! + +These bug reports and fixes are to be credited to James Anhalt III +(anhalt@cs.ucla.edu), Dan Friedman (dfried@cs.indiana.edu), Michael Levy +(mlevy@csr.uvic.ca), Donald A. Smith (dsmith@chaos.cs.brandeis.edu), and +Neng Fa Zhou (zhou@csce.kyushu-u.ac.jp). Big thanks to all. + +-hak ___________________________________________________________________ + Hassan Ait-Kaci, Professor + ___________________________________________________________________ + School of Computing Science phone: +1 (604) 291 55 89 + Simon Fraser University fax: +1 (604) 291 30 45 + Burnaby, British Columbia email: hak@cs.sfu.ca + V5A 1S6, Canada url: http://www.isg.sfu.ca/~hak/ + ___________________________________________________________________ + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +In the definition of get_structure (fig 2.6, page 13) S should be +initialized to 1 before exiting get_structure in either READ or WRITE +modes. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +There is a problem with stack frames allocation. Namely, the book defines +ALLOCATE as: + + (*) if E > B + then newE <- E + CODE[STACK[E + 1] - 1] + 2 + else ... + +While this should be: + + (**) if E > B + then newE <- E + CODE[CP - 1] + 2 + else ... + +The following code shows why: + + a/0 allocate + ... + call b/0,1 + L1 ... + b/0 allocate + ... + call c/0,3 + L2 ... + c/0 allocate + ... + +Now when b/0 calls c/0 the stack should look like: + + |-------| + |CE: |0 <- a's environment + |-------| + |CP: |1 + |-------| + |Y1: |2 + |-------| +E -> |CE: 0 |3 <- b's environment + |-------| + |CP: L1 |4 + |-------| + |Y1: |5 + |-------| + |Y2: |6 + |-------| + |Y3: |7 + |-------| + | |8 <- we want c's environment to start here + |-------| + | |9 + |-------| + +CP = L2 +P = c/0 + +So when c/0 executes allocate ... + +With (*) the new value of E would be: + + E = 3 + CODE[STACK[3 + 1] - 1] + 2 + E = 3 + CODE[L1 - 1] + 2 + E = 3 + 1 + 2 + E = 6 + + which overwrites Y2 and Y3 since it uses the 1 from a/0 + +Whereas (**) gives us: + + E = 3 + CODE[CP - 1] + 2 + E = 3 + CODE[L2 - 1] + 2 + E = 3 + 3 + 2 + E = 8 + + which is right since b/0 wants to save 3 locals + +This same problem exists in all the instructions that deal with +allocating new stack frames. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +As described UNIFY_CONSTANT does not increment the S register, this +makes it very hard to read structures with constants which are not the +last argument. Easy fix... + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +When allocating a new choice or environment frame on the stack, one +should use CP (the continuation pointer) insteads of E+1 (the stored +continuation pointer) to find out the number of Y variables to preserve +in the previous environment frame. This is because the continuation +pointer is stored on the stack only if an ALLOCATE instruction is used +and so only CP has the real value. + +Thus, instead of + + if (E > B) + NewB = E + *(((int *) *(E+1))-1) + 2; + else NewB = B + *B + FIXED_CHOICE_FRAME_SIZE; + +one should use code like + + if (E > B) + NewB = E + *(CP-1) + 2; + else NewB = B + *B + FIXED_CHOICE_FRAME_SIZE; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +The try, retry, and trust instructions reset the HB register to an +incorrect value. The problem is that n is computed from the original +value of B (n <- STACK[B]). Hence n is no longer valid when HB is +re-loaded. The correct code is: + + HB <- STACK[B+STACK[B]+6] + +There is a more subtle related bug that usually doesn't matter very +much: both cut and neck_cut should also reset HB. If they do not, +some uneccessary trailing will occur. This normally doesnt matter +too much (aside from a small performance penalty), but it does turn +out to be a problem if you try to implement Older and Rummel's incremental +garbage collection algorithm, because you end up with dangling trail +references to collected heap storage. O&R's algorithm relies on knowing +that there are no trail references below a certain point into the +tip of the heap. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From 9454d670c778d41ca45f45f584fd2f26267f2750 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Mon, 6 Feb 2023 01:23:29 -0700 Subject: [PATCH 098/361] port '$get_from_list' to '$get_from_attr_list' in Rust --- build/instructions_template.rs | 4 +++ src/lib/atts.pl | 4 ++- src/machine/dispatch.rs | 8 +++++ src/machine/system_calls.rs | 65 ++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 48166239..23c96068 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -566,6 +566,8 @@ enum SystemClauseType { GetClauseP, #[strum_discriminants(strum(props(Arity = "6", Name = "$invoke_clause_at_p")))] InvokeClauseAtP, + #[strum_discriminants(strum(props(Arity = "2", Name = "$get_from_attr_list")))] + GetFromAttributedVarList, REPL(REPLCodePtr), } @@ -1626,6 +1628,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallIsExpandedOrInlined(_) | &Instruction::CallGetClauseP(_) | &Instruction::CallInvokeClauseAtP(_) | + &Instruction::CallGetFromAttributedVarList(_) | &Instruction::CallEnqueueAttributedVar(_) | &Instruction::CallFetchGlobalVar(_) | &Instruction::CallFirstStream(_) | @@ -1841,6 +1844,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteIsExpandedOrInlined(_) | &Instruction::ExecuteGetClauseP(_) | &Instruction::ExecuteInvokeClauseAtP(_) | + &Instruction::ExecuteGetFromAttributedVarList(_) | &Instruction::ExecuteEnqueueAttributedVar(_) | &Instruction::ExecuteFetchGlobalVar(_) | &Instruction::ExecuteFirstStream(_) | diff --git a/src/lib/atts.pl b/src/lib/atts.pl index 372a9bdd..752be478 100644 --- a/src/lib/atts.pl +++ b/src/lib/atts.pl @@ -34,8 +34,9 @@ '$get_attr'(V, Attr) :- '$get_attr_list'(V, Ls), nonvar(Ls), - '$get_from_list'(Ls, V, Attr). + '$get_from_attr_list'(Ls, Attr). +/* '$get_from_list'([L|Ls], V, Attr) :- nonvar(L), ( L \= Attr -> @@ -43,6 +44,7 @@ '$get_from_list'(Ls, V, Attr) ; L = Attr ). +*/ '$put_attr'(V, Attr) :- '$get_attr_list'(V, Ls), diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 89fa348a..5ea18ba4 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -5207,6 +5207,14 @@ impl Machine { self.machine_st.execute_at_index(2, p); } + &Instruction::CallGetFromAttributedVarList(_) => { + self.get_from_attributed_variable_list(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteGetFromAttributedVarList(_) => { + self.get_from_attributed_variable_list(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } } } diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 93dcdb09..a41d4aef 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -4328,6 +4328,71 @@ impl Machine { self.machine_st.bind(Ref::heap_cell(attr_var_list), list_addr); } + #[inline(always)] + pub(crate) fn get_from_attributed_variable_list(&mut self) { + let mut attrs_list = self.deref_register(1); + let attr = self.deref_register(2); + + let (name, arity) = match self.machine_st.name_and_arity_from_heap(attr) { + Some(key) => key, + None => { + self.machine_st.fail = true; + return; + } + }; + + while let HeapCellValueTag::Lis = attrs_list.get_tag() { + let mut list_head = self.machine_st.heap[attrs_list.get_value()]; + + loop { + read_heap_cell!(list_head, + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { + if list_head != self.machine_st.heap[h] { + list_head = self.machine_st.heap[h]; + } else { + self.machine_st.fail = true; + return; + } + } + (HeapCellValueTag::Str | HeapCellValueTag::Atom) => { + let (t_name, t_arity) = self.machine_st + .name_and_arity_from_heap(list_head) + .unwrap(); + + if name == t_name && arity == t_arity { + let old_tr = self.machine_st.tr; + + unify!(self.machine_st, list_head, attr); + + if self.machine_st.fail { + let curr_tr = self.machine_st.trail.len(); + + self.unwind_trail(old_tr, curr_tr); + self.machine_st.tr = old_tr; + + self.machine_st.pdl.clear(); + self.machine_st.fail = false; + } else { + return; + } + } + + break; + } + _ => { + break; + } + ); + } + + attrs_list = self.machine_st.store( + self.machine_st.deref(self.machine_st.heap[attrs_list.get_value()+1]) + ); + } + + self.machine_st.fail = true; + } + #[inline(always)] pub(crate) fn get_attr_var_queue_delimiter(&mut self) { let addr = self.deref_register(1); From 359619e0356831beba00e505daa26629b765c4a1 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Fri, 10 Feb 2023 00:09:22 -0700 Subject: [PATCH 099/361] simplify and optimize attributed variables (#1590, #1634, #1730) --- build/instructions_template.rs | 22 +- src/lib/atts.pl | 94 +------- src/machine/attributed_variables.rs | 4 +- src/machine/dispatch.rs | 40 ++-- src/machine/mod.rs | 13 +- src/machine/system_calls.rs | 359 +++++++++++++++++++--------- 6 files changed, 289 insertions(+), 243 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 23c96068..98295308 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -272,16 +272,10 @@ enum SystemClauseType { PathCanonical, #[strum_discriminants(strum(props(Arity = "3", Name = "$file_time")))] FileTime, - #[strum_discriminants(strum(props(Arity = "1", Name = "$del_attr_non_head")))] - DeleteAttribute, - #[strum_discriminants(strum(props(Arity = "1", Name = "$del_attr_head")))] - DeleteHeadAttribute, #[strum_discriminants(strum(props(Arity = "arity", Name = "$module_call")))] DynamicModuleResolution(usize), #[strum_discriminants(strum(props(Arity = "arity", Name = "$prepare_call_clause")))] PrepareCallClause(usize), - #[strum_discriminants(strum(props(Arity = "1", Name = "$enqueue_attr_var")))] - EnqueueAttributedVar, #[strum_discriminants(strum(props(Arity = "2", Name = "$fetch_global_var")))] FetchGlobalVar, #[strum_discriminants(strum(props(Arity = "1", Name = "$first_stream")))] @@ -566,8 +560,12 @@ enum SystemClauseType { GetClauseP, #[strum_discriminants(strum(props(Arity = "6", Name = "$invoke_clause_at_p")))] InvokeClauseAtP, - #[strum_discriminants(strum(props(Arity = "2", Name = "$get_from_attr_list")))] + #[strum_discriminants(strum(props(Arity = "3", Name = "$get_from_attr_list")))] GetFromAttributedVarList, + #[strum_discriminants(strum(props(Arity = "3", Name = "$put_to_attr_list")))] + PutToAttributedVarList, + #[strum_discriminants(strum(props(Arity = "3", Name = "$del_from_attr_list")))] + DeleteFromAttributedVarList, REPL(REPLCodePtr), } @@ -1620,8 +1618,6 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallDeleteDirectory(_) | &Instruction::CallPathCanonical(_) | &Instruction::CallFileTime(_) | - &Instruction::CallDeleteAttribute(_) | - &Instruction::CallDeleteHeadAttribute(_) | &Instruction::CallDynamicModuleResolution(..) | &Instruction::CallPrepareCallClause(..) | &Instruction::CallCompileInlineOrExpandedGoal(..) | @@ -1629,7 +1625,8 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallGetClauseP(_) | &Instruction::CallInvokeClauseAtP(_) | &Instruction::CallGetFromAttributedVarList(_) | - &Instruction::CallEnqueueAttributedVar(_) | + &Instruction::CallPutToAttributedVarList(_) | + &Instruction::CallDeleteFromAttributedVarList(_) | &Instruction::CallFetchGlobalVar(_) | &Instruction::CallFirstStream(_) | &Instruction::CallFlushOutput(_) | @@ -1836,8 +1833,6 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteDeleteDirectory(_) | &Instruction::ExecutePathCanonical(_) | &Instruction::ExecuteFileTime(_) | - &Instruction::ExecuteDeleteAttribute(_) | - &Instruction::ExecuteDeleteHeadAttribute(_) | &Instruction::ExecuteDynamicModuleResolution(..) | &Instruction::ExecutePrepareCallClause(..) | &Instruction::ExecuteCompileInlineOrExpandedGoal(..) | @@ -1845,7 +1840,8 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteGetClauseP(_) | &Instruction::ExecuteInvokeClauseAtP(_) | &Instruction::ExecuteGetFromAttributedVarList(_) | - &Instruction::ExecuteEnqueueAttributedVar(_) | + &Instruction::ExecutePutToAttributedVarList(_) | + &Instruction::ExecuteDeleteFromAttributedVarList(_) | &Instruction::ExecuteFetchGlobalVar(_) | &Instruction::ExecuteFirstStream(_) | &Instruction::ExecuteFlushOutput(_) | diff --git a/src/lib/atts.pl b/src/lib/atts.pl index 752be478..d6ee47a3 100644 --- a/src/lib/atts.pl +++ b/src/lib/atts.pl @@ -19,76 +19,12 @@ '$default_attr_list'(PGs, Module, AttrVar). '$default_attr_list'([], _, _) --> []. -'$absent_attr'(V, Attr) :- - '$get_attr_list'(V, Ls), - '$absent_from_list'(Ls, Attr). - -'$absent_from_list'(X, Attr) :- - ( var(X) -> - true - ; X = [L|Ls], - L \= Attr -> - '$absent_from_list'(Ls, Attr) - ). - -'$get_attr'(V, Attr) :- - '$get_attr_list'(V, Ls), - nonvar(Ls), - '$get_from_attr_list'(Ls, Attr). - -/* -'$get_from_list'([L|Ls], V, Attr) :- - nonvar(L), - ( L \= Attr -> - nonvar(Ls), - '$get_from_list'(Ls, V, Attr) - ; L = Attr - ). -*/ - -'$put_attr'(V, Attr) :- - '$get_attr_list'(V, Ls), - '$add_to_list'(Ls, V, Attr). - -'$add_to_list'(Ls, V, Attr) :- - ( var(Ls) -> - Ls = [Attr | _], - '$enqueue_attr_var'(V) - ; Ls = [_ | Ls0], - '$add_to_list'(Ls0, V, Attr) - ). - -'$del_attr'(Ls0, _, _) :- - var(Ls0), - !. -'$del_attr'(Ls0, V, Attr) :- - Ls0 = [Att | Ls1], - nonvar(Att), - ( Att \= Attr -> - '$del_attr_buried'(Ls0, Ls1, V, Attr) - ; '$del_attr_head'(V), - '$del_attr'(Ls1, V, Attr) - ). - -'$del_attr_step'(Ls1, V, Attr) :- - ( nonvar(Ls1) -> - Ls1 = [_ | Ls2], - '$del_attr_buried'(Ls1, Ls2, V, Attr) +'$absent_attr'(V, Module, Attr) :- + ( '$get_from_attr_list'(V, Module, Attr) -> + false ; true ). -%% assumptions: Ls0 is a list, Ls1 is its tail; -%% the head of Ls0 can be ignored. -'$del_attr_buried'(Ls0, Ls1, V, Attr) :- - ( var(Ls1) -> true - ; Ls1 = [Att | Ls2] -> - ( Att \= Attr -> - '$del_attr_buried'(Ls1, Ls2, V, Attr) - ; '$del_attr_non_head'(Ls0), %% set tail of Ls0 = tail of Ls1. can be undone by backtracking. - '$del_attr_step'(Ls1, V, Attr) - ) - ). - '$copy_attr_list'(L, _Module, []) :- var(L), !. '$copy_attr_list'([Module0:Att|Atts], Module, CopiedAtts) :- ( Module0 == Module -> @@ -144,38 +80,28 @@ put_attr(Name, Arity, Module) --> { functor(Attr, Name, Arity) }, [(put_atts(V, +Attr) :- !, - functor(Attr, Head, Arity), - functor(AttrForm, Head, Arity), - '$get_attr_list'(V, Ls), - atts:'$del_attr'(Ls, V, Module:AttrForm), - atts:'$put_attr'(V, Module:Attr)), - (put_atts(V, Attr) :- + '$put_to_attr_list'(V, Module, Attr)), + (put_atts(V, Attr) :- !, - functor(Attr, Head, Arity), - functor(AttrForm, Head, Arity), - '$get_attr_list'(V, Ls), - atts:'$del_attr'(Ls, V, Module:AttrForm), - atts:'$put_attr'(V, Module:Attr)), + '$put_to_attr_list'(V, Module, Attr)), (put_atts(V, -Attr) :- !, - functor(Attr, _, _), - '$get_attr_list'(V, Ls), - atts:'$del_attr'(Ls, V, Module:Attr))]. + '$del_from_attr_list'(V, Module, Attr))]. get_attr(Name, Arity, Module) --> { functor(Attr, Name, Arity) }, [(get_atts(V, +Attr) :- !, functor(Attr, _, _), - atts:'$get_attr'(V, Module:Attr)), + atts:'$get_from_attr_list'(V, Module, Attr)), (get_atts(V, Attr) :- !, functor(Attr, _, _), - atts:'$get_attr'(V, Module:Attr)), + atts:'$get_from_attr_list'(V, Module, Attr)), (get_atts(V, -Attr) :- !, functor(Attr, _, _), - atts:'$absent_attr'(V, Module:Attr))]. + atts:'$absent_attr'(V, Module, Attr))]. user:goal_expansion(Term, M:put_atts(Var, Attr)) :- nonvar(Term), diff --git a/src/machine/attributed_variables.rs b/src/machine/attributed_variables.rs index 2bab7451..57ea1c22 100644 --- a/src/machine/attributed_variables.rs +++ b/src/machine/attributed_variables.rs @@ -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) } diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 5ea18ba4..1cb1a8fa 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -3570,22 +3570,6 @@ impl Machine { self.file_time(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDeleteAttribute(_) => { - self.delete_attribute(); - self.machine_st.p += 1; - } - &Instruction::ExecuteDeleteAttribute(_) => { - self.delete_attribute(); - self.machine_st.p = self.machine_st.cp; - } - &Instruction::CallDeleteHeadAttribute(_) => { - self.delete_head_attribute(); - self.machine_st.p += 1; - } - &Instruction::ExecuteDeleteHeadAttribute(_) => { - self.delete_head_attribute(); - self.machine_st.p = self.machine_st.cp; - } &Instruction::CallDynamicModuleResolution(arity, _) => { let (module_name, key) = try_or_throw!( self.machine_st, @@ -3616,14 +3600,6 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::CallEnqueueAttributedVar(_) => { - self.enqueue_attributed_var(); - self.machine_st.p += 1; - } - &Instruction::ExecuteEnqueueAttributedVar(_) => { - self.enqueue_attributed_var(); - self.machine_st.p = self.machine_st.cp; - } &Instruction::CallFetchGlobalVar(_) => { self.fetch_global_var(); step_or_fail!(self, self.machine_st.p += 1); @@ -5215,6 +5191,22 @@ impl Machine { self.get_from_attributed_variable_list(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallPutToAttributedVarList(_) => { + self.put_to_attributed_variable_list(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecutePutToAttributedVarList(_) => { + self.put_to_attributed_variable_list(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallDeleteFromAttributedVarList(_) => { + self.delete_from_attributed_variable_list(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteDeleteFromAttributedVarList(_) => { + self.delete_from_attributed_variable_list(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } } } diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 09883b99..2a498b02 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -870,7 +870,18 @@ impl Machine { 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); } diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index a41d4aef..0854a9f8 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -456,7 +456,41 @@ impl BrentAlgState { } } +#[derive(Debug)] +enum MatchSite { + NoMatchVarTail(usize), // no match, we refer to the location of the uninstantiated tail instead. + Match(usize), // a match +} + +#[derive(Debug)] +struct AttrListMatch { + match_site: MatchSite, + prev_tail: Option, +} + impl MachineState { + pub(crate) fn get_attr_var_list(&mut self, attr_var: HeapCellValue) -> Option { + read_heap_cell!(attr_var, + (HeapCellValueTag::AttrVar, h) => { + Some(h + 1) + } + (HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { + // create an AttrVar in the heap. + let h = self.heap.len(); + + self.heap.push(attr_var_as_cell!(h)); + self.heap.push(heap_loc_as_cell!(h+1)); + + self.bind(Ref::attr_var(h), attr_var); + + Some(h + 1) + } + _ => { + None + } + ) + } + pub(crate) fn name_and_arity_from_heap(&self, cell: HeapCellValue) -> Option { read_heap_cell!(self.store(self.deref(cell)), (HeapCellValueTag::Str, s) => { @@ -4306,17 +4340,10 @@ impl Machine { let attr_var = self.deref_register(1); let attr_var_list = read_heap_cell!(attr_var, (HeapCellValueTag::AttrVar, h) => { - h + 1 + h+1 } - (HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { - // create an AttrVar in the heap. - let h = self.machine_st.heap.len(); - - self.machine_st.heap.push(attr_var_as_cell!(h)); - self.machine_st.heap.push(heap_loc_as_cell!(h+1)); - - self.machine_st.bind(Ref::attr_var(h), attr_var); - h + 1 + (HeapCellValueTag::Var, h) => { + h } _ => { self.machine_st.fail = true; @@ -4330,67 +4357,40 @@ impl Machine { #[inline(always)] pub(crate) fn get_from_attributed_variable_list(&mut self) { - let mut attrs_list = self.deref_register(1); - let attr = self.deref_register(2); - - let (name, arity) = match self.machine_st.name_and_arity_from_heap(attr) { - Some(key) => key, - None => { + let attr_var = self.deref_register(1); + let attr = self.deref_register(3); + let attr_var_list = read_heap_cell!(attr_var, + (HeapCellValueTag::AttrVar, h) => { + self.machine_st.heap[h+1] + } + _ => { self.machine_st.fail = true; return; } - }; + ); - while let HeapCellValueTag::Lis = attrs_list.get_tag() { - let mut list_head = self.machine_st.heap[attrs_list.get_value()]; + let module = self.deref_register(2); - loop { - read_heap_cell!(list_head, - (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { - if list_head != self.machine_st.heap[h] { - list_head = self.machine_st.heap[h]; - } else { - self.machine_st.fail = true; - return; - } - } - (HeapCellValueTag::Str | HeapCellValueTag::Atom) => { - let (t_name, t_arity) = self.machine_st - .name_and_arity_from_heap(list_head) - .unwrap(); + match self.match_attribute(attr_var_list, module, attr) { + Some(AttrListMatch { match_site: MatchSite::Match(match_site), .. }) => { + let list_head = self.machine_st.heap[match_site]; - if name == t_name && arity == t_arity { - let old_tr = self.machine_st.tr; + if list_head.get_value() == match_site { + // at the end of the list, no match found in this case. + self.machine_st.fail = true; + } else { + let (_, qualified_goal) = self.machine_st.strip_module( + list_head, + empty_list_as_cell!(), + ); - unify!(self.machine_st, list_head, attr); - - if self.machine_st.fail { - let curr_tr = self.machine_st.trail.len(); - - self.unwind_trail(old_tr, curr_tr); - self.machine_st.tr = old_tr; - - self.machine_st.pdl.clear(); - self.machine_st.fail = false; - } else { - return; - } - } - - break; - } - _ => { - break; - } - ); + unify!(self.machine_st, qualified_goal, attr); + } + } + _ => { + self.machine_st.fail = true; } - - attrs_list = self.machine_st.store( - self.machine_st.deref(self.machine_st.heap[attrs_list.get_value()+1]) - ); } - - self.machine_st.fail = true; } #[inline(always)] @@ -4427,81 +4427,202 @@ impl Machine { } #[inline(always)] - pub(crate) fn enqueue_attributed_var(&mut self) { - let addr = self.deref_register(1); - - read_heap_cell!(addr, + pub(crate) fn delete_from_attributed_variable_list(&mut self) { + let attr_var = self.deref_register(1); + let attr = self.deref_register(3); + let attr_var_list = read_heap_cell!(attr_var, (HeapCellValueTag::AttrVar, h) => { - self.machine_st.attr_var_init.attr_var_queue.push(h); + h + 1 } _ => { + return; } ); - } - #[inline(always)] - pub(crate) fn delete_attribute(&mut self) { - let ls0 = self.deref_register(1); + let module = self.deref_register(2); - if let HeapCellValueTag::Lis = ls0.get_tag() { - let l1 = ls0.get_value(); - let ls1 = self.machine_st.store(self.machine_st.deref(heap_loc_as_cell!(l1 + 1))); - - if let HeapCellValueTag::Lis = ls1.get_tag() { - let l2 = ls1.get_value(); - - let old_addr = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[l1+1])); - let tail = self.machine_st.store(self.machine_st.deref(heap_loc_as_cell!(l2 + 1))); - - let tail = if tail.is_var() { - heap_loc_as_cell!(l1 + 1) + match self.match_attribute(self.machine_st.heap[attr_var_list], module, attr) { + Some(AttrListMatch { prev_tail, match_site: MatchSite::Match(match_site) }) => { + let prev_tail = if let Some(prev_tail) = prev_tail { + // not at the head. + prev_tail } else { - tail + if self.machine_st.heap[match_site + 1].is_var() { + let h = attr_var.get_value(); + + self.machine_st.heap[h] = heap_loc_as_cell!(h); + self.machine_st.trail(TrailRef::Ref(Ref::attr_var(h))); + } + + // at the head. + attr_var_list }; - let trail_ref = read_heap_cell!(old_addr, - (HeapCellValueTag::Var, h) => { - TrailRef::AttrVarHeapLink(h) - } - (HeapCellValueTag::Lis, l) => { - TrailRef::AttrVarListLink(l1 + 1, l) - } - _ => { - unreachable!() - } - ); + if self.machine_st.heap[match_site + 1].get_tag() == HeapCellValueTag::Lis { + let prev_tail_value = self.machine_st.heap[match_site + 1].get_value(); + self.machine_st.heap[prev_tail].set_value(prev_tail_value); + } else { + self.machine_st.heap[prev_tail] = heap_loc_as_cell!(prev_tail); + } - self.machine_st.heap[l1 + 1] = tail; - self.machine_st.trail(trail_ref); + self.machine_st.trail(TrailRef::AttrVarListLink(prev_tail, match_site)); + } + _ => { } } } #[inline(always)] - pub(crate) fn delete_head_attribute(&mut self) { - let addr = self.deref_register(1); - - debug_assert_eq!(addr.get_tag(), HeapCellValueTag::AttrVar); - - let h = addr.get_value(); - let addr = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[h + 1])); - - debug_assert_eq!(addr.get_tag(), HeapCellValueTag::Lis); - - let l = addr.get_value(); - let tail = self.machine_st.store(self.machine_st.deref(self.machine_st.heap[l + 1])); - - let tail = if tail.is_var() { - self.machine_st.heap[h] = heap_loc_as_cell!(h); - self.machine_st.trail(TrailRef::Ref(Ref::attr_var(h))); - - heap_loc_as_cell!(h + 1) - } else { - tail + pub(crate) fn put_to_attributed_variable_list(&mut self) { + let attr_var = self.deref_register(1); + let attr = self.deref_register(3); + let attr_var_list = match self.machine_st.get_attr_var_list(attr_var) { + Some(h) => h, + None => { + self.machine_st.fail = true; + return; + } }; - self.machine_st.heap[h + 1] = tail; - self.machine_st.trail(TrailRef::AttrVarListLink(h + 1, l)); + let module = self.deref_register(2); + + /* + * How to handle attribute trailing using just AttrVarListLink (which + * should be re-named to something more general) in unwind_trail: + * + * Given AttrVarListLink(h, l): + * + * 1. Check cell at offset l. + * 2. If h == l, set heap[h] = heap_loc_as_cell!(h). + * 3. If cell is a Var, set heap[h] = list_loc_as_cell!(l). + * 4. Otherwise, cell points to an element of the list which is therefore + * an atom or str. Set heap[h] accordingly. + * + * For this to work, all elements of attributed variable lists must be + * heap cell locs pointing to later elements in the heap, either atoms (0-arity) + * or str cells (> 0-arity). + */ + + let h = self.machine_st.heap.len(); + + self.machine_st.heap.push(str_loc_as_cell!(h+1)); + self.machine_st.heap.extend(functor!(atom!(":"), [cell(module), cell(attr)])); + + match self.match_attribute(self.machine_st.heap[attr_var_list], module, attr) { + Some(AttrListMatch { match_site, .. }) => { + let (match_site, l) = match match_site { + MatchSite::NoMatchVarTail(match_site) => { + let l = self.machine_st.heap[match_site].get_value(); + + // at the end of the (non-empty) list here. + self.machine_st.heap[match_site] = list_loc_as_cell!(h+4); + self.machine_st.heap.push(heap_loc_as_cell!(h)); + self.machine_st.heap.push(heap_loc_as_cell!(h+5)); + + (match_site, l) + } + MatchSite::Match(match_site) => { + let l = self.machine_st.heap[match_site].get_value(); + self.machine_st.heap[match_site].set_value(h); + + (match_site, l) + } + }; + + self.machine_st.trail(TrailRef::AttrVarListLink(match_site, l)); + } + None => { + // the list is empty. + self.machine_st.heap[attr_var_list] = list_loc_as_cell!(h+4); + self.machine_st.heap.push(heap_loc_as_cell!(h)); + self.machine_st.heap.push(heap_loc_as_cell!(h+5)); + + self.machine_st.attr_var_init.attr_var_queue.push(attr_var_list - 1); + self.machine_st.trail(TrailRef::AttrVarListLink(attr_var_list, attr_var_list)); + } + } + } + + fn match_attribute( + &self, + mut attrs_list: HeapCellValue, + module: HeapCellValue, + attr: HeapCellValue, + ) -> Option { + let (name, arity) = match self.machine_st.name_and_arity_from_heap(attr) { + Some(key) => key, + None => { + return None; + } + }; + + let mut prev_tail = None; + + while let HeapCellValueTag::Lis = attrs_list.get_tag() { + let mut list_head = self.machine_st.heap[attrs_list.get_value()]; + + loop { + read_heap_cell!(list_head, + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { + debug_assert!(list_head != self.machine_st.heap[h]); + list_head = self.machine_st.heap[h]; + } + (HeapCellValueTag::Str | HeapCellValueTag::Atom) => { + let (module_loc, qualified_goal) = self.machine_st.strip_module( + list_head, + empty_list_as_cell!(), + ); + + let (t_name, t_arity) = self.machine_st + .name_and_arity_from_heap(qualified_goal) + .unwrap(); + + if module == module_loc && name == t_name && arity == t_arity { + return Some(AttrListMatch { + match_site: MatchSite::Match(attrs_list.get_value()), + prev_tail, + }); + } + + break; + } + _ => { + break; + } + ); + } + + let tail_loc = attrs_list.get_value() + 1; + prev_tail = Some(tail_loc); + + // do the work of self.store(self.deref(...)) but inline it + // for speed and simplify it. + let mut list_tail = self.machine_st.heap[tail_loc]; + + loop { + read_heap_cell!(list_tail, + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { + if list_tail != self.machine_st.heap[h] { + list_tail = self.machine_st.heap[h]; + } else { + return Some(AttrListMatch { + match_site: MatchSite::NoMatchVarTail(h), + prev_tail, + }); + } + } + (HeapCellValueTag::Lis) => { + attrs_list = list_tail; + break; + } + _ => { + unreachable!() + } + ); + } + } + + None } #[inline(always)] From 491472a8c56bd3a239f6057d9b7fdb6f4a6d139b Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Fri, 10 Feb 2023 22:35:41 -0700 Subject: [PATCH 100/361] retire TrailedAttrVarHeapLink TrailEntry tag --- src/machine/machine_state_impl.rs | 10 ---------- src/machine/mod.rs | 3 --- src/types.rs | 3 --- 3 files changed, 16 deletions(-) diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index 9b69c86b..b486fc31 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -130,16 +130,6 @@ impl MachineState { } } } - TrailRef::AttrVarHeapLink(h) => { - if h < self.hb { - self.trail.push(TrailEntry::build_with( - TrailEntryTag::TrailedAttrVarHeapLink, - h as u64, - )); - - self.tr += 1; - } - } TrailRef::AttrVarListLink(h, l) => { if h < self.hb { self.trail.push(TrailEntry::build_with( diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 2a498b02..480f5a15 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -863,9 +863,6 @@ 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; diff --git a/src/types.rs b/src/types.rs index 168f0e84..a8b66b35 100644 --- a/src/types.rs +++ b/src/types.rs @@ -53,7 +53,6 @@ pub enum HeapCellValueView { // trail elements. TrailedHeapVar = 0b011101, TrailedStackVar = 0b011111, - TrailedAttrVarHeapLink = 0b100001, TrailedAttrVarListLink = 0b100011, TrailedAttachedValue = 0b100101, TrailedBlackboardEntry = 0b100111, @@ -182,7 +181,6 @@ impl Ref { #[derive(Debug, Clone, Copy)] pub enum TrailRef { Ref(Ref), - AttrVarHeapLink(usize), AttrVarListLink(usize, usize), BlackboardEntry(Atom), BlackboardOffset(Atom, HeapCellValue), // key atom, key value @@ -194,7 +192,6 @@ pub(crate) enum TrailEntryTag { TrailedHeapVar = 0b011110, TrailedStackVar = 0b011111, TrailedAttrVar = 0b101110, - TrailedAttrVarHeapLink = 0b100010, TrailedAttrVarListLink = 0b100011, TrailedAttachedValue = 0b101010, TrailedBlackboardEntry = 0b100110, From 326f18ea7511c4ef89edd11526acb41df58bcf01 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 12 Feb 2023 17:16:02 -0700 Subject: [PATCH 101/361] copy attributed variable attribute lists specially via copy_attr_var_list --- src/machine/copier.rs | 70 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/src/machine/copier.rs b/src/machine/copier.rs index 53325332..0d091468 100644 --- a/src/machine/copier.rs +++ b/src/machine/copier.rs @@ -28,7 +28,10 @@ pub(crate) fn copy_term( 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 { old_h: usize, target: T, attr_var_policy: AttrVarPolicy, + attr_var_list_locs: Vec<(usize, HeapCellValue)>, } impl CopyTermState { @@ -48,6 +52,7 @@ impl CopyTermState { old_h: target.threshold(), target, attr_var_policy, + attr_var_list_locs: vec![], } } @@ -86,16 +91,12 @@ impl CopyTermState { 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 CopyTermState { 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 CopyTermState { 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 CopyTermState { } ); } - - self.unwind_trail(); } fn unwind_trail(&mut self) { From 56783b8e4bc20e59f37c98478f1cc85f813ab9fa Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 12 Feb 2023 23:41:25 -0700 Subject: [PATCH 102/361] correct incremental compilation bugs --- src/forms.rs | 5 ++-- src/machine/compile.rs | 52 +++++++++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/forms.rs b/src/forms.rs index 97864370..1c014587 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -812,8 +812,9 @@ impl PredicateInfo { } #[inline] - pub(crate) fn must_retract_local_clauses(&self) -> bool { - self.is_extensible && self.has_clauses && !self.is_discontiguous + pub(crate) fn must_retract_local_clauses(&self, is_cross_module_clause: bool) -> bool { + self.is_extensible && self.has_clauses && !self.is_discontiguous && + !(self.is_multifile && is_cross_module_clause) } } diff --git a/src/machine/compile.rs b/src/machine/compile.rs index db64293d..5f3d8e28 100644 --- a/src/machine/compile.rs +++ b/src/machine/compile.rs @@ -2280,14 +2280,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 @@ -2301,34 +2304,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!( @@ -2343,9 +2349,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")) } @@ -2364,7 +2372,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { self.payload.retraction_info.push_record( RetractionRecord::RemovedSkeleton( - payload_compilation_target, + compilation_target, key, skeleton, ), @@ -2415,9 +2423,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, )?; From 601ff567e3b3175fed08b36c4f078c514daecfb3 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Fri, 17 Feb 2023 00:20:15 -0700 Subject: [PATCH 103/361] keep phrase goal qualified even if qualifier is a variable --- src/lib/dcgs.pl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/dcgs.pl b/src/lib/dcgs.pl index 08870316..33b4c129 100644 --- a/src/lib/dcgs.pl +++ b/src/lib/dcgs.pl @@ -215,6 +215,9 @@ user:goal_expansion(phrase(GRBody, S, S0), GRBody2) :- E, dcgs:error_goal(E, GRBody1) ), - module_call_qualified(M, GRBody1, GRBody2). + ( GRBody = (_:_) -> + GRBody2 = M:GRBody1 + ; GRBody2 = GRBody1 + ). user:goal_expansion(phrase(GRBody, S), phrase(GRBody, S, [])). From a6e416f13d7463e68c4c016263807549538b5826 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Fri, 17 Feb 2023 19:20:28 -0700 Subject: [PATCH 104/361] compile '$atts' and '$project_atts' modules using loader.pl --- src/loader.pl | 2 +- src/machine/mod.rs | 33 +++++++++++-------------------- src/machine/project_attributes.pl | 5 ----- 3 files changed, 13 insertions(+), 27 deletions(-) diff --git a/src/loader.pl b/src/loader.pl index fe3d6fe2..1faba9e3 100644 --- a/src/loader.pl +++ b/src/loader.pl @@ -620,7 +620,7 @@ strip_module(Goal, M, G) :- strip_subst_module(Goal, M1, M2, G) :- '$strip_module'(Goal, M2, G), - ( var(M2) -> + ( var(M2), \+ functor(Goal, (:), 2) -> M2 = M1 ; true ). diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 480f5a15..cad80661 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -255,31 +255,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)) { diff --git a/src/machine/project_attributes.pl b/src/machine/project_attributes.pl index b2f75007..66d6adb5 100644 --- a/src/machine/project_attributes.pl +++ b/src/machine/project_attributes.pl @@ -25,11 +25,6 @@ call_project_attributes([Module|Modules], QueryVars, AttrVars) :- ), 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) From 3f445c76be882e12ccf56b616f23ba385f22b60d Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 18 Feb 2023 02:15:24 -0700 Subject: [PATCH 105/361] add '$delete_all_attributes', use copy_term/3 as defined in #1272 --- build/instructions_template.rs | 4 +++ src/lib/freeze.pl | 2 +- src/loader.pl | 8 +++-- src/machine/dispatch.rs | 8 +++++ src/machine/project_attributes.pl | 56 +++++++++++++++++++------------ src/machine/system_calls.rs | 21 ++++++++++++ 6 files changed, 74 insertions(+), 25 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 98295308..6e21c165 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -566,6 +566,8 @@ enum SystemClauseType { PutToAttributedVarList, #[strum_discriminants(strum(props(Arity = "3", Name = "$del_from_attr_list")))] DeleteFromAttributedVarList, + #[strum_discriminants(strum(props(Arity = "1", Name = "$delete_all_attributes")))] + DeleteAllAttributes, REPL(REPLCodePtr), } @@ -1627,6 +1629,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallGetFromAttributedVarList(_) | &Instruction::CallPutToAttributedVarList(_) | &Instruction::CallDeleteFromAttributedVarList(_) | + &Instruction::CallDeleteAllAttributes(_) | &Instruction::CallFetchGlobalVar(_) | &Instruction::CallFirstStream(_) | &Instruction::CallFlushOutput(_) | @@ -1842,6 +1845,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteGetFromAttributedVarList(_) | &Instruction::ExecutePutToAttributedVarList(_) | &Instruction::ExecuteDeleteFromAttributedVarList(_) | + &Instruction::ExecuteDeleteAllAttributes(_) | &Instruction::ExecuteFetchGlobalVar(_) | &Instruction::ExecuteFirstStream(_) | &Instruction::ExecuteFlushOutput(_) | diff --git a/src/lib/freeze.pl b/src/lib/freeze.pl index c6554fa4..218a2532 100644 --- a/src/lib/freeze.pl +++ b/src/lib/freeze.pl @@ -38,5 +38,5 @@ freeze(X, Goal) :- attribute_goals(Var) --> { get_atts(Var, frozen(Goals)), put_atts(Var, -frozen(_)) }, - [freeze(Var, Goals)]. + [freeze:freeze(Var, Goals)]. diff --git a/src/loader.pl b/src/loader.pl index 1faba9e3..896d6bff 100644 --- a/src/loader.pl +++ b/src/loader.pl @@ -11,7 +11,6 @@ current_module/1 ]). - :- use_module(library(error)). :- use_module(library(lists)). :- use_module(library(pairs)). @@ -221,7 +220,12 @@ complete_partial_goal(N, HeadArg, InnerHeadArgs, SuppArgs, CompleteHeadArg) :- integer(N), N >= 0, HeadArg =.. [Functor | InnerHeadArgs], - length(SuppArgs, N), + % the next two lines are equivalent to length(SuppArgs, N) but + % avoid length/2 so that copy_term/3 (which is invoked by + % length/2) can be bootstrapped without self-reference. + functor(SuppArgsFunctor, '.', N), + SuppArgsFunctor =.. [_ | SuppArgs], + % length(SuppArgs, N), append(InnerHeadArgs, SuppArgs, InnerHeadArgs0), CompleteHeadArg =.. [Functor | InnerHeadArgs0]. diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 1cb1a8fa..e15e8048 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -5207,6 +5207,14 @@ impl Machine { self.delete_from_attributed_variable_list(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallDeleteAllAttributes(_) => { + self.delete_all_attributes(); + self.machine_st.p += 1; + } + &Instruction::ExecuteDeleteAllAttributes(_) => { + self.delete_all_attributes(); + self.machine_st.p = self.machine_st.cp; + } } } diff --git a/src/machine/project_attributes.pl b/src/machine/project_attributes.pl index 66d6adb5..968859cd 100644 --- a/src/machine/project_attributes.pl +++ b/src/machine/project_attributes.pl @@ -1,7 +1,11 @@ :- module('$project_atts', [copy_term/3]). +:- use_module(library(dcgs)). +:- use_module(library(lambda)). +:- use_module(library(lists), [foldl/4]). + 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,9 +21,9 @@ 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 ), @@ -72,25 +76,33 @@ 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([]) --> []. +gather_residual_goals([V|Vs]) --> + { '$get_attr_list'(V, Attrs), + phrase(copy_attribute_modules(Attrs), Modules0), + sort(Modules0, Modules) }, + foldl(V+\M^phrase(M:attribute_goals(V)), Modules), + gather_residual_goals(Vs). +delete_all_attributes(Term) :- '$delete_all_attributes'(Term). -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]). +copy_term(Term, Copy, Gs) :- + '$term_attributed_variables'(Term, Vs), + findall(Term-Gs, + ( phrase(gather_residual_goals(Vs), Gs), + delete_all_attributes(Term) + ), + [Copy-Gs]). diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 0854a9f8..402bcac7 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1036,6 +1036,27 @@ impl MachineState { } impl Machine { + #[inline(always)] + pub(crate) fn delete_all_attributes(&mut self) { + let h = self.machine_st.heap.len(); + + self.machine_st.heap.push(heap_loc_as_cell!(h)); + self.machine_st.registers[2] = heap_loc_as_cell!(h); + + self.term_attributed_variables(); + + let mut list_of_attr_vars = self.deref_register(2); + + while let HeapCellValueTag::Lis = list_of_attr_vars.get_tag() { + let attr_var_loc = list_of_attr_vars.get_value(); + + self.machine_st.heap[attr_var_loc] = heap_loc_as_cell!(attr_var_loc); + self.machine_st.trail(TrailRef::Ref(Ref::attr_var(attr_var_loc))); + + list_of_attr_vars = self.machine_st.heap[attr_var_loc + 1]; + } + } + #[inline(always)] pub(crate) fn get_clause_p(&self, module_name: Atom) -> (usize, usize) { use crate::machine::loader::CompilationTarget; From 92d543b8a8a91ecbce8d74e4fd5c4b40a967e2a7 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 18 Feb 2023 14:12:07 -0700 Subject: [PATCH 106/361] change '$delete_all_attributes' to '$delete_all_attributes_from_var' --- build/instructions_template.rs | 8 ++++---- src/machine/dispatch.rs | 8 ++++---- src/machine/project_attributes.pl | 18 ++++++++++-------- src/machine/system_calls.rs | 18 ++++-------------- 4 files changed, 22 insertions(+), 30 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 6e21c165..0ea3fa59 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -566,8 +566,8 @@ enum SystemClauseType { PutToAttributedVarList, #[strum_discriminants(strum(props(Arity = "3", Name = "$del_from_attr_list")))] DeleteFromAttributedVarList, - #[strum_discriminants(strum(props(Arity = "1", Name = "$delete_all_attributes")))] - DeleteAllAttributes, + #[strum_discriminants(strum(props(Arity = "1", Name = "$delete_all_attributes_from_var")))] + DeleteAllAttributesFromVar, REPL(REPLCodePtr), } @@ -1629,7 +1629,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallGetFromAttributedVarList(_) | &Instruction::CallPutToAttributedVarList(_) | &Instruction::CallDeleteFromAttributedVarList(_) | - &Instruction::CallDeleteAllAttributes(_) | + &Instruction::CallDeleteAllAttributesFromVar(_) | &Instruction::CallFetchGlobalVar(_) | &Instruction::CallFirstStream(_) | &Instruction::CallFlushOutput(_) | @@ -1845,7 +1845,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteGetFromAttributedVarList(_) | &Instruction::ExecutePutToAttributedVarList(_) | &Instruction::ExecuteDeleteFromAttributedVarList(_) | - &Instruction::ExecuteDeleteAllAttributes(_) | + &Instruction::ExecuteDeleteAllAttributesFromVar(_) | &Instruction::ExecuteFetchGlobalVar(_) | &Instruction::ExecuteFirstStream(_) | &Instruction::ExecuteFlushOutput(_) | diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index e15e8048..f0ed0469 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -5207,12 +5207,12 @@ impl Machine { self.delete_from_attributed_variable_list(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDeleteAllAttributes(_) => { - self.delete_all_attributes(); + &Instruction::CallDeleteAllAttributesFromVar(_) => { + self.delete_all_attributes_from_var(); self.machine_st.p += 1; } - &Instruction::ExecuteDeleteAllAttributes(_) => { - self.delete_all_attributes(); + &Instruction::ExecuteDeleteAllAttributesFromVar(_) => { + self.delete_all_attributes_from_var(); self.machine_st.p = self.machine_st.cp; } } diff --git a/src/machine/project_attributes.pl b/src/machine/project_attributes.pl index 968859cd..0c51096e 100644 --- a/src/machine/project_attributes.pl +++ b/src/machine/project_attributes.pl @@ -1,8 +1,9 @@ :- 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]). +:- use_module(library(lists), [foldl/4, maplist/2]). project_attributes(QueryVars, AttrVars) :- phrase(gather_attr_modules(AttrVars), Modules0), @@ -97,12 +98,13 @@ gather_residual_goals([V|Vs]) --> foldl(V+\M^phrase(M:attribute_goals(V)), Modules), gather_residual_goals(Vs). -delete_all_attributes(Term) :- '$delete_all_attributes'(Term). +delete_all_attributes_from_var(V) :- '$delete_all_attributes_from_var'(V). copy_term(Term, Copy, Gs) :- - '$term_attributed_variables'(Term, Vs), - findall(Term-Gs, - ( phrase(gather_residual_goals(Vs), Gs), - delete_all_attributes(Term) - ), - [Copy-Gs]). + can_be(list, Gs), + findall(Term-Rs, term_residual_goals(Term,Rs), [Copy-Gs]). + +term_residual_goals(Term,Rs) :- + '$term_attributed_variables'(Term, Vs), + phrase(gather_residual_goals(Vs), Rs), + maplist(delete_all_attributes_from_var, Vs). diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 402bcac7..e39f3d22 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1037,23 +1037,13 @@ impl MachineState { impl Machine { #[inline(always)] - pub(crate) fn delete_all_attributes(&mut self) { - let h = self.machine_st.heap.len(); - - self.machine_st.heap.push(heap_loc_as_cell!(h)); - self.machine_st.registers[2] = heap_loc_as_cell!(h); - - self.term_attributed_variables(); - - let mut list_of_attr_vars = self.deref_register(2); - - while let HeapCellValueTag::Lis = list_of_attr_vars.get_tag() { - let attr_var_loc = list_of_attr_vars.get_value(); + pub(crate) fn delete_all_attributes_from_var(&mut self) { + let attr_var = self.deref_register(1); + if let HeapCellValueTag::AttrVar = attr_var.get_tag() { + let attr_var_loc = attr_var.get_value(); self.machine_st.heap[attr_var_loc] = heap_loc_as_cell!(attr_var_loc); self.machine_st.trail(TrailRef::Ref(Ref::attr_var(attr_var_loc))); - - list_of_attr_vars = self.machine_st.heap[attr_var_loc + 1]; } } From c9295323f62655bb61985f6cfd30ad9ca9ad30bb Mon Sep 17 00:00:00 2001 From: Robert Jacobson Date: Sat, 18 Feb 2023 15:49:42 -0500 Subject: [PATCH 107/361] Added links to referenced research papers in the Phase 2 and Nice to Have Features sections. --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d421bfa0..4022f26b 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,7 @@ programming, which is itself written in a high-level language. Produce an implementation of the Warren Abstract Machine in Rust, done according to the progression of languages in [Warren's Abstract -Machine: A Tutorial -Reconstruction](https://github.com/mthom/scryer-prolog/blob/master/wambook/wambook.pdf). +Machine: A Tutorial Reconstruction](https://github.com/mthom/scryer-prolog/blob/master/wambook/wambook.pdf). Phase 1 has been completed in that Scryer Prolog implements in some form all of the WAM book, including lists, cuts, Debray allocation, first @@ -52,9 +51,9 @@ Extend Scryer Prolog to include the following, among other features: `bb_put/2` (non-backtrackable) and `bb_b_put/2` (backtrackable). - [x] Delimited continuations based on reset/3, shift/1 (documented in - "Delimited Continuations for Prolog"). + "[Delimited Continuations for Prolog](https://www.swi-prolog.org/download/publications/iclp2013.pdf)"). - [x] Tabling library based on delimited continuations - (documented in "Tabling as a Library with Delimited Control"). + (documented in "[Tabling as a Library with Delimited Control](https://www.ijcai.org/Proceedings/16/Papers/619.pdf)"). - [x] A _redone_ representation of strings as difference lists of characters, using a packed internal representation. - [x] clp(B) and clp(ℤ) as builtin libraries. @@ -69,7 +68,7 @@ Extend Scryer Prolog to include the following, among other features: - [ ] Greatly reducing the number of instructions used to compile disjunctives. - [ ] Storing short atoms to heap cells without writing them to the atom table. - [ ] A compacting garbage collector satisfying the five properties of - "Precise Garbage Collection in Prolog." (_in progress_) + "[Precise Garbage Collection in Prolog](https://www.swi-prolog.org/download/publications/lifegc.pdf)." (_in progress_) - [ ] Mode declarations. ## Phase 3 @@ -88,12 +87,12 @@ nice to have in the future. They'd make a good project for anyone wanting to contribute code to Scryer Prolog. 1. Implement the global analysis techniques described in Peter van -Roy's thesis, "Can Logic Programming Execute as Fast as Imperative -Programming?" +Roy's thesis, "[Can Logic Programming Execute as Fast as Imperative +Programming?](https://www.info.ucl.ac.be/~pvr/Peter.thesis/Peter.thesis.html)" 2. Add unum representation and arithmetic, using either an existing unum implementation or an ad hoc one. Unums are described in -Gustafson's book "The End of Error." +Gustafson's book "[The End of Error](http://www.johngustafson.net/unums.html)." 3. Add concurrent tables to manage shared references to atoms and strings. From 34ec6d3167f6c5b511e2c1e094c5feb3435e25b5 Mon Sep 17 00:00:00 2001 From: Robert Jacobson Date: Sun, 19 Feb 2023 20:10:21 -0500 Subject: [PATCH 108/361] Changed the links for the delimited continuations papers and the precise garbage collection paper. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4022f26b..6b212b6a 100644 --- a/README.md +++ b/README.md @@ -51,9 +51,9 @@ Extend Scryer Prolog to include the following, among other features: `bb_put/2` (non-backtrackable) and `bb_b_put/2` (backtrackable). - [x] Delimited continuations based on reset/3, shift/1 (documented in - "[Delimited Continuations for Prolog](https://www.swi-prolog.org/download/publications/iclp2013.pdf)"). + "[Delimited Continuations for Prolog](https://biblio.ugent.be/publication/5646080/file/5646081)"). - [x] Tabling library based on delimited continuations - (documented in "[Tabling as a Library with Delimited Control](https://www.ijcai.org/Proceedings/16/Papers/619.pdf)"). + (documented in "[Tabling as a Library with Delimited Control](https://biblio.ugent.be/publication/6880648/file/6885145.pdf)"). - [x] A _redone_ representation of strings as difference lists of characters, using a packed internal representation. - [x] clp(B) and clp(ℤ) as builtin libraries. @@ -68,7 +68,7 @@ Extend Scryer Prolog to include the following, among other features: - [ ] Greatly reducing the number of instructions used to compile disjunctives. - [ ] Storing short atoms to heap cells without writing them to the atom table. - [ ] A compacting garbage collector satisfying the five properties of - "[Precise Garbage Collection in Prolog](https://www.swi-prolog.org/download/publications/lifegc.pdf)." (_in progress_) + "[Precise Garbage Collection in Prolog](https://www.complang.tuwien.ac.at/ulrich/papers/PDF/2008-ciclops.pdf)." (_in progress_) - [ ] Mode declarations. ## Phase 3 From 1a01438064157df68841053d1baf4535bc744bc3 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Mon, 20 Feb 2023 20:09:47 +0100 Subject: [PATCH 109/361] add link to newly available homepage Many thanks to @aarroyoc for the documentation system, and for hosting the page! --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6b212b6a..ed002d8d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ source industrial strength production environment that is also a testbed for bleeding edge research in logic and constraint programming, which is itself written in a high-level language. +The homepage of the project is: [**https://www.scryer.pl**](https://www.scryer.pl) + ![Scryer Logo: Cryer](logo/scryer.png) ## Phase 1 From 6e9cd072c5a8244dafca10ac044ca681370d5cab Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 21 Feb 2023 00:50:31 -0700 Subject: [PATCH 110/361] catch attribute_goals errors in copy_term/3, don't discard variable module qualifiers in dcg_body/3 (#1738) --- src/lib/dcgs.pl | 12 ++++-------- src/machine/project_attributes.pl | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/lib/dcgs.pl b/src/lib/dcgs.pl index 33b4c129..1767d2bb 100644 --- a/src/lib/dcgs.pl +++ b/src/lib/dcgs.pl @@ -75,13 +75,6 @@ phrase(GRBody, S0, S) :- ; call(M:GRBody1, S0, S) ). - -module_call_qualified(M, Call, Call1) :- - ( nonvar(M) -> Call1 = M:Call - ; Call = Call1 - ). - - % The same version of the below two dcg_rule clauses, but with module scoping. dcg_rule(( M:NonTerminal, Terminals --> GRBody ), ( M:Head :- Body )) :- dcg_non_terminal(NonTerminal, S0, S, Head), @@ -127,7 +120,10 @@ dcg_body(NonTerminal, S0, S, Goal1) :- NonTerminal \= ( \+ _ ), loader:strip_module(NonTerminal, M, NonTerminal0), dcg_non_terminal(NonTerminal0, S0, S, Goal0), - module_call_qualified(M, Goal0, Goal1). + ( functor(NonTerminal, (:), 2) -> + Goal1 = M:Goal0 + ; Goal1 = Goal0 + ). % The following constructs in a grammar rule body % are defined in the corresponding subclauses. diff --git a/src/machine/project_attributes.pl b/src/machine/project_attributes.pl index 0c51096e..59ad0790 100644 --- a/src/machine/project_attributes.pl +++ b/src/machine/project_attributes.pl @@ -90,12 +90,21 @@ copy_attribute_modules([Module:_|Attrs]) --> [Module], copy_attribute_modules(Attrs). +attribute_goals_or_fail(M, V, V0, V1) :- + ( catch(M:attribute_goals(V, V0, V1), + E, + '$project_atts':'$print_attribute_goals_exception'(M, E) + ) -> + true + ; V0 = V1 + ). + 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^phrase(M:attribute_goals(V)), Modules), + foldl(V+\M^attribute_goals_or_fail(M, V), Modules), gather_residual_goals(Vs). delete_all_attributes_from_var(V) :- '$delete_all_attributes_from_var'(V). @@ -105,6 +114,6 @@ copy_term(Term, Copy, Gs) :- findall(Term-Rs, term_residual_goals(Term,Rs), [Copy-Gs]). term_residual_goals(Term,Rs) :- - '$term_attributed_variables'(Term, Vs), - phrase(gather_residual_goals(Vs), Rs), - maplist(delete_all_attributes_from_var, Vs). + '$term_attributed_variables'(Term, Vs), + phrase(gather_residual_goals(Vs), Rs), + maplist(delete_all_attributes_from_var, Vs). From 95f6ebc0002778b71d402e149e01b2031f74083a Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 21 Feb 2023 20:53:13 -0700 Subject: [PATCH 111/361] assign responsibility for emitting dif goal to the first variable of the left-hand term (#1739) --- src/lib/dif.pl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/lib/dif.pl b/src/lib/dif.pl index 33e3e44b..a59052ac 100644 --- a/src/lib/dif.pl +++ b/src/lib/dif.pl @@ -40,9 +40,6 @@ verify_attributes(Var, Value, Goals) :- ; Goals = [] ). -% Probably the world's worst dif/2 implementation. I'm open to -% suggestions for improvement. - %% dif(?X, ?Y). % % True iff X and Y are different terms. Unlike `\=/2`, `dif/2` is more declarative because if X and Y can @@ -69,12 +66,16 @@ dif(X, Y) :- ) ). -gather_dif_goals([]) --> []. -gather_dif_goals([(X \== Y) | Goals]) --> - [dif:dif(X, Y)], - gather_dif_goals(Goals). +gather_dif_goals(_, []) --> []. +gather_dif_goals(V, [(X \== Y) | Goals]) --> + ( { term_variables(X, [V0 | _]), + V == V0 } -> + [dif:dif(X, Y)] + ; [] + ), + gather_dif_goals(V, Goals). attribute_goals(X) --> { get_atts(X, +dif(Goals)) }, - gather_dif_goals(Goals), + gather_dif_goals(X, Goals), { put_atts(X, -dif(_)) }. From 9d52d2a653c5fe2fb64cd6d9eb555733f46d8d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Wed, 22 Feb 2023 23:10:03 +0100 Subject: [PATCH 112/361] MVP of Foreign Function Interface --- Cargo.lock | 31 +++ Cargo.toml | 2 + build/instructions_template.rs | 12 ++ src/ffi.rs | 369 +++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/lib/ffi.pl | 83 ++++++++ src/machine/dispatch.rs | 24 +++ src/machine/mock_wam.rs | 3 +- src/machine/mod.rs | 3 + src/machine/system_calls.rs | 152 ++++++++++++++ 10 files changed, 679 insertions(+), 1 deletion(-) create mode 100644 src/ffi.rs create mode 100644 src/lib/ffi.pl diff --git a/Cargo.lock b/Cargo.lock index 63784cd9..b7c06dbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -952,6 +952,35 @@ version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +[[package]] +name = "libffi" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb06d5b4c428f3cd682943741c39ed4157ae989fffe1094a08eaf7c4014cf60" +dependencies = [ + "libc", + "libffi-sys", +] + +[[package]] +name = "libffi-sys" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11c6f11e063a27ffe040a9d15f0b661bf41edc2383b7ae0e0ad5a7e7d53d9da3" +dependencies = [ + "cc", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "libsodium-sys" version = "0.2.7" @@ -1833,6 +1862,8 @@ dependencies = [ "lazy_static", "lexical", "libc", + "libffi", + "libloading", "modular-bitfield", "native-tls", "ordered-float", diff --git a/Cargo.toml b/Cargo.toml index 6c98ea43..206b7150 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,8 @@ hyper = { version = "0.14", features = ["full"] } hyper-tls = "0.5.0" tokio = { version = "1.24.2", features = ["full"] } futures = "0.3" +libffi = "3.1.0" +libloading = "0.7" [dev-dependencies] assert_cmd = "1.0.3" diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 48166239..80614815 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -552,6 +552,12 @@ enum SystemClauseType { HttpAccept, #[strum_discriminants(strum(props(Arity = "4", Name = "$http_answer")))] HttpAnswer, + #[strum_discriminants(strum(props(Arity = "2", Name = "$load_foreign_lib")))] + LoadForeignLib, + #[strum_discriminants(strum(props(Arity = "3", Name = "$foreign_call")))] + ForeignCall, + #[strum_discriminants(strum(props(Arity = "2", Name = "$define_foreign_struct")))] + DefineForeignStruct, #[strum_discriminants(strum(props(Arity = "3", Name = "$predicate_defined")))] PredicateDefined, #[strum_discriminants(strum(props(Arity = "3", Name = "$strip_module")))] @@ -1701,6 +1707,9 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallHttpListen(_) | &Instruction::CallHttpAccept(_) | &Instruction::CallHttpAnswer(_) | + &Instruction::CallLoadForeignLib(_) | + &Instruction::CallForeignCall(_) | + &Instruction::CallDefineForeignStruct(_) | &Instruction::CallPredicateDefined(_) | &Instruction::CallStripModule(_) | &Instruction::CallCurrentTime(_) | @@ -1916,6 +1925,9 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteHttpListen(_) | &Instruction::ExecuteHttpAccept(_) | &Instruction::ExecuteHttpAnswer(_) | + &Instruction::ExecuteLoadForeignLib(_) | + &Instruction::ExecuteForeignCall(_) | + &Instruction::ExecuteDefineForeignStruct(_) | &Instruction::ExecutePredicateDefined(_) | &Instruction::ExecuteStripModule(_) | &Instruction::ExecuteCurrentTime(_) | diff --git a/src/ffi.rs b/src/ffi.rs new file mode 100644 index 00000000..e8f2bc4b --- /dev/null +++ b/src/ffi.rs @@ -0,0 +1,369 @@ +use crate::atom_table::Atom; + +use std::alloc::{alloc, Layout}; +use std::any::Any; +use std::collections::HashMap; +use std::error::Error; +use std::ffi::{CString, c_void}; +use std::convert::TryFrom; + +use libffi::low::{ffi_cif, types, CodePtr, ffi_abi_FFI_DEFAULT_ABI, prep_cif, ffi_type, type_tag}; +use libloading::{Symbol, Library}; + +pub struct FunctionDefinition { + pub name: String, + pub return_value: Atom, + pub args: Vec, +} + +#[derive(Debug)] +pub struct FunctionImpl { + cif: ffi_cif, + args: Vec<*mut ffi_type>, + code_ptr: CodePtr, + return_struct_name: Option, +} + +#[derive(Debug, Default)] +pub struct ForeignFunctionTable { + table: HashMap, + structs: HashMap, +} + +#[derive(Debug)] +struct StructImpl { + ffi_type: ffi_type, + fields: Vec<*mut ffi_type>, +} + +struct PointerArgs { + pointers: Vec<*mut c_void>, + memory: Vec>, +} + +impl ForeignFunctionTable { + pub fn merge(&mut self, other: ForeignFunctionTable) { + self.table.extend(other.table); + } + + pub fn define_struct(&mut self, name: &str, fields: Vec) { + let mut fields: Vec<_> = fields.iter().map(|x| self.map_type_ffi(&x)).collect(); + fields.push(std::ptr::null_mut::()); + let mut struct_type: ffi_type = Default::default(); + struct_type.type_ = type_tag::STRUCT; + struct_type.elements = fields.as_mut_ptr(); + self.structs.insert(name.to_string(), StructImpl { ffi_type: struct_type, fields}); + } + + fn map_type_ffi(&mut self, source: &Atom) -> *mut ffi_type { + unsafe { + match source { + atom!("sint64") => &mut types::sint64, + atom!("sint32") => &mut types::sint32, + atom!("sint16") => &mut types::sint16, + atom!("sint8") => &mut types::sint8, + atom!("uint64") => &mut types::uint64, + atom!("uint32") => &mut types::uint32, + atom!("uint16") => &mut types::uint16, + atom!("uint8") => &mut types::uint8, + atom!("bool") => &mut types::sint8, + atom!("void") => &mut types::void, + atom!("cstr") => &mut types::pointer, + atom!("ptr") => &mut types::pointer, + atom!("f32") => &mut types::float, + atom!("f64") => &mut types::double, + struct_name => { + match self.structs.get_mut(struct_name.as_str()) { + Some(ref mut struct_type) => { + &mut struct_type.ffi_type + }, + None => unreachable!() + } + } + } + } + } + + pub(crate) fn load_library(&mut self, library_name: &str, functions: &Vec) -> Result<(), Box> { + let mut ff_table: ForeignFunctionTable = Default::default(); + unsafe { + let library = Library::new(library_name)?; + for function in functions { + let symbol_name: CString = CString::new(function.name.clone())?; + let code_ptr: Symbol<*mut c_void> = library.get(&symbol_name.into_bytes_with_nul())?; + let mut args: Vec<_> = function.args.iter().map(|x| self.map_type_ffi(&x)).collect(); + let mut cif: ffi_cif = Default::default(); + prep_cif( + &mut cif, + ffi_abi_FFI_DEFAULT_ABI, + args.len(), + self.map_type_ffi(&function.return_value), + args.as_mut_ptr() + ).unwrap(); + + let return_struct_name = if (*self.map_type_ffi(&function.return_value)).type_ as u32 == libffi::raw::FFI_TYPE_STRUCT { + Some(function.return_value.as_str().to_string()) + } else { + None + }; + + ff_table.table.insert(function.name.clone(), FunctionImpl { + cif, + args, + code_ptr: CodePtr(code_ptr.into_raw().into_raw() as *mut _), + return_struct_name, + }); + } + std::mem::forget(library); + } + self.merge(ff_table); + Ok(()) + } + + fn build_pointer_args(mut args: &mut Vec, type_args: &Vec<*mut ffi_type>, structs_table: &mut HashMap) -> Result { + let mut pointers = Vec::with_capacity(args.len()); + let mut memory = Vec::new(); + for i in 0..args.len() { + let field_type = type_args[i]; + unsafe { + match (*field_type).type_ as u32 { + libffi::raw::FFI_TYPE_UINT8 => { + let n: u8 = u8::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_SINT8 => { + let n: i8 = i8::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_UINT16 => { + let n: u16 = u16::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_SINT16 => { + let n: i16 = i16::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_UINT32 => { + let n: u32 = u32::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_SINT32 => { + let n: i32 = i32::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_UINT64 => { + let n: u64 = u64::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_SINT64 => { + let n: i64 = args[i].as_int()?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_FLOAT => { + let n: f32 = args[i].as_float()? as f32; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_DOUBLE => { + let n: f64 = args[i].as_float()?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + }, + libffi::raw::FFI_TYPE_POINTER => { + let ptr: *mut c_void = args[i].as_ptr()?; + pointers.push(ptr); + }, + libffi::raw::FFI_TYPE_STRUCT => { + match args[i] { + Value::Struct(ref name, ref struct_args) => { + if let Some(ref mut struct_type) = structs_table.get_mut(name) { + let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap(); + let ptr = alloc(layout) as *mut c_void; + let mut field_ptr = ptr; + for i in 0..(struct_type.fields.len()-1) { + let field = struct_type.fields[i]; + match (*field).type_ as u32 { + libffi::raw::FFI_TYPE_UINT8 => { + let n: u8 = u8::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + std::ptr::write(field_ptr as *mut u8, n); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + libffi::raw::FFI_TYPE_UINT32 => { + let n: u32 = u32::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + std::ptr::write(field_ptr as *mut u32, n); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + libffi::raw::FFI_TYPE_SINT32 => { + let n: u32 = u32::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + std::ptr::write(field_ptr as *mut u32, n); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + libffi::raw::FFI_TYPE_FLOAT => { + let n: f32 = struct_args[i].as_float()? as f32; + std::ptr::write(field_ptr as *mut f32, n); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + _ => { + unreachable!() + } + } + } + pointers.push(ptr); + memory.push(Box::from_raw(ptr)); + } else { + return Err(FFIError::InvalidStructName); + } + } + _ => return Err(FFIError::ValueCast) + } + }, + _ => return Err(FFIError::InvalidFFIType) + } + } + } + Ok(PointerArgs { + pointers, + memory + }) + } + + pub fn exec(&mut self, name: &str, mut args: Vec) -> Result { + let function_impl = self.table.get_mut(name).ok_or(FFIError::FunctionNotFound)?; + let mut pointer_args = Self::build_pointer_args(&mut args, &function_impl.args, &mut self.structs).unwrap(); + return unsafe { + match (*function_impl.cif.rtype).type_ as u32 { + libffi::raw::FFI_TYPE_VOID => { + let mut _n: Box = Box::new(0); + libffi::raw::ffi_call( + &mut function_impl.cif, + Some(*function_impl.code_ptr.as_safe_fun()), + &mut *_n as *mut _ as *mut c_void, + pointer_args.pointers.as_mut_ptr() as *mut *mut c_void + ); + Ok(Value::Int(0)) + }, + libffi::raw::FFI_TYPE_SINT8 => { + let mut n: Box = Box::new(0); + libffi::raw::ffi_call( + &mut function_impl.cif, + Some(*function_impl.code_ptr.as_safe_fun()), + &mut *n as *mut _ as *mut c_void, + pointer_args.pointers.as_mut_ptr() as *mut *mut c_void + ); + Ok(Value::Int(i64::from(*n))) + }, + libffi::raw::FFI_TYPE_SINT32 => { + let mut n: Box = Box::new(0); + libffi::raw::ffi_call( + &mut function_impl.cif, + Some(*function_impl.code_ptr.as_safe_fun()), + &mut *n as *mut _ as *mut c_void, + pointer_args.pointers.as_mut_ptr() as *mut *mut c_void + ); + Ok(Value::Int(i64::from(*n))) + }, + libffi::raw::FFI_TYPE_STRUCT => { + let mut returns = Vec::new(); + let mut struct_type = self.structs.get_mut(&function_impl.return_struct_name.clone().ok_or(FFIError::StructNotFound)?).ok_or(FFIError::StructNotFound)?; + let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap(); + let ptr = alloc(layout) as *mut c_void; + libffi::raw::ffi_call( + &mut function_impl.cif, + Some(*function_impl.code_ptr.as_safe_fun()), + &mut *ptr as *mut _ as *mut c_void, + pointer_args.pointers.as_mut_ptr() as *mut *mut c_void + ); + + let mut field_ptr = ptr; + for i in 0..(struct_type.fields.len()-1) { + let field = struct_type.fields[i]; + match (*field).type_ as u32 { + libffi::raw::FFI_TYPE_UINT8 => { + let n = std::ptr::read(field_ptr as *mut u8); + returns.push(Value::Int(i64::from(n))); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + libffi::raw::FFI_TYPE_SINT32 => { + let n = std::ptr::read(field_ptr as *mut i32); + returns.push(Value::Int(i64::from(n))); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + libffi::raw::FFI_TYPE_UINT32 => { + let n = std::ptr::read(field_ptr as *mut u32); + returns.push(Value::Int(i64::from(n))); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + _ => { + unreachable!() + } + } + } + drop(Box::from_raw(ptr)); + Ok(Value::Struct("texture".into(), returns)) + }, + _ => unreachable!() + } + }; + } +} + +#[derive(Clone, Debug)] +pub enum Value { + Int(i64), + Float(f64), + CString(CString), + Struct(String, Vec), +} + +impl Value { + fn as_int(&self) -> Result { + match self { + Value::Int(n) => Ok(*n), + _ => Err(FFIError::ValueCast), + } + } + + fn as_float(&self) -> Result { + match self { + Value::Float(n) => Ok(*n), + Value::Int(n) => Ok(*n as f64), + _ => Err(FFIError::ValueCast), + } + } + + fn as_ptr(&mut self) -> Result<*mut c_void, FFIError> { + match self { + Value::CString(ref mut cstr) => Ok(&mut *cstr as *mut _ as *mut c_void), + Value::Int(n) => Ok(*n as *mut c_void), + _ => Err(FFIError::ValueCast) + } + } +} + +#[derive(Debug)] +pub enum FFIError { + ValueCast, + ValueDontFit, + InvalidFFIType, + InvalidStructName, + FunctionNotFound, + StructNotFound, +} diff --git a/src/lib.rs b/src/lib.rs index 2846fd0e..45dc2385 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ mod allocator; mod arithmetic; pub mod codegen; mod debray_allocator; +mod ffi; mod fixtures; mod forms; mod heap_iter; diff --git a/src/lib/ffi.pl b/src/lib/ffi.pl new file mode 100644 index 00000000..c34cb4ad --- /dev/null +++ b/src/lib/ffi.pl @@ -0,0 +1,83 @@ +:- module(ffi, [use_foreign_module/2, foreign_struct/2]). + +:- use_module(library(lists)). +:- use_module(library(error)). + +foreign_struct(Name, Elements) :- + '$define_foreign_struct'(Name, Elements). + +use_foreign_module(LibName, Predicates) :- + '$load_foreign_lib'(LibName, Predicates), + maplist(assert_predicate, Predicates). + +assert_predicate(PredicateDefinition) :- + PredicateDefinition =.. [Name, Inputs, void], + length(Inputs, NumInputs), + functor(Head, Name, NumInputs), + term_variables(Head, TermList), + Body = ( + lists:maplist(ffi:check_input, Inputs, TermList), + '$foreign_call'(Name, TermList, _),! + ), + Predicate =.. [:-, Head, Body], + assertz(ffi:Predicate). + +assert_predicate(PredicateDefinition) :- + PredicateDefinition =.. [Name, Inputs, bool], + length(Inputs, NumInputs), + functor(Head, Name, NumInputs), + term_variables(Head, TermList), + Body = ( + lists:maplist(ffi:check_input, Inputs, TermList), + '$foreign_call'(Name, TermList, 1),! + ), + Predicate =.. [:-, Head, Body], + assertz(ffi:Predicate). + +assert_predicate(PredicateDefinition) :- + PredicateDefinition =.. [Name, Inputs, Return], + \+ member(Return, [void, bool]), + length(Inputs, NumInputs), + NumArgs is NumInputs + 1, + functor(Head, Name, NumArgs), + term_variables(Head, TermList), + Body = ( + lists:append(TermListInputs, [TermListReturn], TermList), + lists:maplist(ffi:check_input, Inputs, TermListInputs), + '$foreign_call'(Name, TermListInputs, TermListReturn),! + ), + Predicate =.. [:-, Head, Body], + assertz(ffi:Predicate). + +check_input(sint8, Var) :- + must_be(integer, Var), + ( + (Var > -129, Var < 128) -> + true + ; domain_error(integer_does_not_fit, Var, foreign_call/3) + ). +check_input(sint16, Var) :- + must_be(integer, Var), + ( + (Var > -32769, Var < 32768) -> + true + ; domain_error(integer_does_not_fit, Var, foreign_call/3) + ). +check_input(sint32, Var) :- + must_be(integer, Var), + ( + (Var > -2147483649, Var < 2147483648) -> + true + ; domain_error(integer_does_not_fit, Var, foreign_call/3) + ). +check_input(sint64, Var) :- + must_be(integer, Var). +check_input(f32, _Var). +check_input(f64, _Var). +check_input(cstr, Var) :- + must_be(chars, Var). +check_input(_, Var). +% must_be(list, Var). + +% TODO: assert native predicates. +% They MUST validate types diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 89fa348a..d8ace62d 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4221,6 +4221,30 @@ impl Machine { try_or_throw!(self.machine_st, self.http_answer()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallLoadForeignLib(_) => { + try_or_throw!(self.machine_st, self.load_foreign_lib()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteLoadForeignLib(_) => { + try_or_throw!(self.machine_st, self.load_foreign_lib()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallForeignCall(_) => { + try_or_throw!(self.machine_st, self.foreign_call()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteForeignCall(_) => { + try_or_throw!(self.machine_st, self.foreign_call()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } + &Instruction::CallDefineForeignStruct(_) => { + try_or_throw!(self.machine_st, self.define_foreign_struct()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteDefineForeignStruct(_) => { + try_or_throw!(self.machine_st, self.define_foreign_struct()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } &Instruction::CallCurrentTime(_) => { self.current_time(); step_or_fail!(self, self.machine_st.p += 1); diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index f18d9575..761590fb 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -236,7 +236,8 @@ impl Machine { user_output, user_error, load_contexts: vec![], - runtime + runtime, + foreign_function_table: Default::default(), }; let mut lib_path = current_dir(); diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 09883b99..77eb9c8b 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -26,6 +26,7 @@ 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::*; @@ -65,6 +66,7 @@ pub struct Machine { pub(super) user_error: Stream, pub(super) load_contexts: Vec, pub(super) runtime: Runtime, + pub(super) foreign_function_table: ForeignFunctionTable, } #[derive(Debug)] @@ -443,6 +445,7 @@ impl Machine { user_error, load_contexts: vec![], runtime, + foreign_function_table: Default::default(), }; let mut lib_path = current_dir(); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 93dcdb09..c68a532e 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -6,6 +6,7 @@ use lazy_static::lazy_static; use crate::arena::*; use crate::atom_table::*; use crate::forms::*; +use crate::ffi::*; use crate::heap_iter::*; use crate::heap_print::*; use crate::http::{self, HttpListener, HttpResponse}; @@ -40,6 +41,7 @@ use std::cmp::Ordering; use std::collections::BTreeSet; use std::convert::{TryFrom, Infallible}; use std::env; +use std::ffi::CString; use std::fs; use std::hash::{BuildHasher, BuildHasherDefault}; use std::io::{ErrorKind, Read, Write}; @@ -4172,6 +4174,156 @@ impl Machine { Ok(()) } + #[inline(always)] + pub(crate) fn load_foreign_lib(&mut self) -> CallResult { + let library_name = self.deref_register(1); + let args_reg = self.deref_register(2); + if let Some(library_name) = self.machine_st.value_to_str_like(library_name) { + let stub_gen = || functor_stub(atom!("use_foreign_module"), 2); + match self.machine_st.try_from_list(args_reg, stub_gen) { + Ok(addrs) => { + let mut functions = Vec::new(); + for heap_cell in addrs { + read_heap_cell!(heap_cell, + (HeapCellValueTag::Str, s) => { + let name = cell_as_atom_cell!(self.machine_st.heap[s]).get_name(); + let args: Vec = match self.machine_st.try_from_list(self.machine_st.heap[s + 1], stub_gen) { + Ok(addrs) => { + let mut args = Vec::new(); + for heap_cell in addrs { + args.push(cell_as_atom_cell!(heap_cell).get_name()); + } + args + } + Err(e) => return Err(e) + }; + let return_value = cell_as_atom_cell!(self.machine_st.heap[s + 2]); + functions.push(FunctionDefinition { + name: name.as_str().to_string(), + args, + return_value: return_value.get_name(), + }); + } + _ => { + unreachable!() + } + ) + } + if let Ok(_) = self.foreign_function_table.load_library(library_name.as_str(), &functions) { + return Ok(()); + } + } + Err(e) => return Err(e) + }; + } + self.machine_st.fail = true; + Ok(()) + } + + #[inline(always)] + pub(crate) fn foreign_call(&mut self) -> CallResult { + let function_name = self.deref_register(1); + let args_reg = self.deref_register(2); + let return_value = self.deref_register(3); + if let Some(function_name) = self.machine_st.value_to_str_like(function_name) { + let stub_gen = || functor_stub(atom!("foreign_call"), 3); + fn map_arg(mut machine_st: &mut MachineState, source: HeapCellValue) -> crate::ffi::Value { + match Number::try_from(source) { + Ok(Number::Fixnum(n)) => { + Value::Int(n.get_num()) + }, + Ok(Number::Float(n)) => { + Value::Float(n.into_inner()) + }, + _ => { + let stub_gen = || functor_stub(atom!("foreign_call"), 3); + if let Some(string) = machine_st.value_to_str_like(source) { + Value::CString(CString::new(string.as_str()).unwrap()) + } else { + match machine_st.try_from_list(source, stub_gen) { + Ok(args) => { + let mut iter = args.into_iter(); + if let Some(struct_name) = machine_st.value_to_str_like(iter.next().unwrap()) { + Value::Struct(struct_name.as_str().to_string(), iter.map(|x| map_arg(&mut machine_st, x)).collect()) + } else { + unreachable!() + } + } + _ => { + unreachable!() + } + } + } + } + } + } + match self.machine_st.try_from_list(args_reg, stub_gen) { + Ok(args) => { + let args: Vec<_> = args.into_iter().map(|x| map_arg(&mut self.machine_st, x)).collect(); + match self.foreign_function_table.exec(function_name.as_str(), args) { + Ok(result) => { + match result { + Value::Int(n) => self.machine_st.unify_fixnum(Fixnum::build_with(n), return_value), + Value::Struct(name, mut args) => { + args.insert(0, Value::CString(CString::new(name).unwrap())); + let struct_list = heap_loc_as_cell!( + iter_to_heap_list( + &mut self.machine_st.heap, + args.into_iter() + .map(|val| { + match val { + Value::Int(n) => fixnum_as_cell!(Fixnum::build_with(n)), + Value::CString(cstr) => atom_as_cell!(self.machine_st.atom_tbl.build_with(&cstr.into_string().unwrap())), + _ => unreachable!() + } + }), + ) + ); + unify!(self.machine_st, return_value, struct_list); + } + _ => { + unreachable!(); + } + } + return Ok(()); + }, + Err(e) => { + // throw error + self.machine_st.fail = true; + return Ok(()); + } + } + } + Err(e) => return Err(e) + } + } + self.machine_st.fail = true; + Ok(()) + } + + #[inline(always)] + pub(crate) fn define_foreign_struct(&mut self) -> CallResult { + let struct_name = self.deref_register(1); + let fields_reg = self.deref_register(2); + if let Some(struct_name) = self.machine_st.value_to_str_like(struct_name) { + let stub_gen = || functor_stub(atom!("define_foreign_struct"), 2); + let fields: Vec = match self.machine_st.try_from_list(fields_reg, stub_gen) { + Ok(addrs) => { + let mut args = Vec::new(); + for heap_cell in addrs { + args.push(cell_as_atom_cell!(heap_cell).get_name()); + } + args + } + Err(e) => return Err(e) + }; + self.foreign_function_table.define_struct(struct_name.as_str(), fields); + return Ok(()) + } + self.machine_st.fail = true; + Ok(()) + } + #[inline(always)] pub(crate) fn current_time(&mut self) { let timestamp = self.systemtime_to_timestamp(SystemTime::now()); From 2a04d5e799fbb599931b93b25fc9afd4a52ce6da Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 22 Feb 2023 21:05:31 +0100 Subject: [PATCH 113/361] in projection of residual goals, mark considered propagators as processed This is to avoid duplicated goals with the new projection mechanism. --- src/lib/clpz.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 5823aa42..9404097e 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -7711,7 +7711,7 @@ attributes_goals([]) --> []. attributes_goals([propagator(P, State)|As]) --> ( { ground(State) } -> [] ; { phrase(attribute_goal_(P), Gs) } -> - { % del_attr(State, clpz_aux), State = processed, + { del_attr(State, clpz_aux), State = processed, ( monotonic -> maplist(unwrap_with(bare_integer), Gs, Gs1) ; maplist(unwrap_with(=), Gs, Gs1) @@ -7822,7 +7822,7 @@ conjunction(A, B, G, D) --> original_goal(original_goal(State, Goal)) --> ( { var(State) } -> -% { State = processed }, + { State = processed }, [Goal] ; [] ). From 669242a8ced9ea801a2023115eaa525ce5bb2767 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 23 Feb 2023 00:10:26 +0100 Subject: [PATCH 114/361] DOC: update residual goals --- src/lib/clpb.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/clpb.pl b/src/lib/clpb.pl index 546286ea..1bdbbefc 100644 --- a/src/lib/clpb.pl +++ b/src/lib/clpb.pl @@ -211,7 +211,7 @@ Here is an example session with a few queries and their answers: T = 1, clpb:sat(X=:=X*Y), clpb:sat(Y=:=Y*Z). ?- sat(1#X#a#b). - sat(X=:=a#b). + clpb:sat(X=:=a#b). ``` The pending residual goals constrain remaining variables to Boolean @@ -348,7 +348,7 @@ does compute =|XOR|= as intended: ``` ?- xor(x, y, Z). -sat(Z=:=x#y). + clpb:sat(Z=:=x#y). ``` ## Acknowledgments From 997161c74036f865ea3a98742bc734c9db96bfb9 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 25 Feb 2023 10:17:55 +0100 Subject: [PATCH 115/361] rely on first instantiated argument indexing This great improvement to indexing allows much more natural definitions of virtually all meta-predicates. Many thanks to @notoria! --- src/lib/clpz.pl | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 9404097e..fd5d9d18 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -220,31 +220,23 @@ partition_([X|Xs], Pred, Ls0, Es0, Gs0) :- :- meta_predicate(include(1, ?, ?)). -include(Goal, Ls0, Ls) :- - include_(Ls0, Goal, Ls). - -include_([], _, []). -include_([L|Ls0], Goal, Ls) :- +include(_, [], []). +include(Goal, [L|Ls0], Ls) :- ( call(Goal, L) -> Ls = [L|Rest] ; Ls = Rest ), - include_(Ls0, Goal, Rest). - + include(Goal, Ls0, Rest). :- meta_predicate(exclude(1, ?, ?)). -exclude(Goal, Ls0, Ls) :- - exclude_(Ls0, Goal, Ls). - -exclude_([], _, []). -exclude_([L|Ls0], Goal, Ls) :- +exclude(_, [], []). +exclude(Goal, [L|Ls0], Ls) :- ( call(Goal, L) -> Ls = Rest ; Ls = [L|Rest] ), - exclude_(Ls0, Goal, Rest). - + exclude(Goal, Ls0, Rest). %:- discontiguous clpz:goal_expansion/5. From 92b262d599f83d629e9f75bd492c58aab34a3672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sat, 25 Feb 2023 22:27:28 +0100 Subject: [PATCH 116/361] Macroization of the code --- src/ffi.rs | 211 ++++++++++++++++++---------------- src/machine/machine_errors.rs | 19 +++ src/machine/system_calls.rs | 7 +- 3 files changed, 136 insertions(+), 101 deletions(-) diff --git a/src/ffi.rs b/src/ffi.rs index e8f2bc4b..8b0b5db9 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -120,61 +120,32 @@ impl ForeignFunctionTable { Ok(()) } - fn build_pointer_args(mut args: &mut Vec, type_args: &Vec<*mut ffi_type>, structs_table: &mut HashMap) -> Result { + fn build_pointer_args(args: &mut Vec, type_args: &Vec<*mut ffi_type>, structs_table: &mut HashMap) -> Result { let mut pointers = Vec::with_capacity(args.len()); let mut memory = Vec::new(); for i in 0..args.len() { let field_type = type_args[i]; unsafe { + macro_rules! push_int { + ($type:ty) => { + { + let n: $type = <$type>::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + let mut box_value = Box::new(n) as Box; + pointers.push(&mut *box_value as *mut _ as *mut c_void); + memory.push(box_value); + } + } + } + match (*field_type).type_ as u32 { - libffi::raw::FFI_TYPE_UINT8 => { - let n: u8 = u8::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; - let mut box_value = Box::new(n) as Box; - pointers.push(&mut *box_value as *mut _ as *mut c_void); - memory.push(box_value); - }, - libffi::raw::FFI_TYPE_SINT8 => { - let n: i8 = i8::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; - let mut box_value = Box::new(n) as Box; - pointers.push(&mut *box_value as *mut _ as *mut c_void); - memory.push(box_value); - }, - libffi::raw::FFI_TYPE_UINT16 => { - let n: u16 = u16::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; - let mut box_value = Box::new(n) as Box; - pointers.push(&mut *box_value as *mut _ as *mut c_void); - memory.push(box_value); - }, - libffi::raw::FFI_TYPE_SINT16 => { - let n: i16 = i16::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; - let mut box_value = Box::new(n) as Box; - pointers.push(&mut *box_value as *mut _ as *mut c_void); - memory.push(box_value); - }, - libffi::raw::FFI_TYPE_UINT32 => { - let n: u32 = u32::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; - let mut box_value = Box::new(n) as Box; - pointers.push(&mut *box_value as *mut _ as *mut c_void); - memory.push(box_value); - }, - libffi::raw::FFI_TYPE_SINT32 => { - let n: i32 = i32::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; - let mut box_value = Box::new(n) as Box; - pointers.push(&mut *box_value as *mut _ as *mut c_void); - memory.push(box_value); - }, - libffi::raw::FFI_TYPE_UINT64 => { - let n: u64 = u64::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; - let mut box_value = Box::new(n) as Box; - pointers.push(&mut *box_value as *mut _ as *mut c_void); - memory.push(box_value); - }, - libffi::raw::FFI_TYPE_SINT64 => { - let n: i64 = args[i].as_int()?; - let mut box_value = Box::new(n) as Box; - pointers.push(&mut *box_value as *mut _ as *mut c_void); - memory.push(box_value); - }, + libffi::raw::FFI_TYPE_UINT8 => push_int!(u8), + libffi::raw::FFI_TYPE_SINT8 => push_int!(i8), + libffi::raw::FFI_TYPE_UINT16 => push_int!(u16), + libffi::raw::FFI_TYPE_SINT16 => push_int!(i16), + libffi::raw::FFI_TYPE_UINT32 => push_int!(u32), + libffi::raw::FFI_TYPE_SINT32 => push_int!(i32), + libffi::raw::FFI_TYPE_UINT64 => push_int!(u64), + libffi::raw::FFI_TYPE_SINT64 => push_int!(i64), libffi::raw::FFI_TYPE_FLOAT => { let n: f32 = args[i].as_float()? as f32; let mut box_value = Box::new(n) as Box; @@ -193,34 +164,46 @@ impl ForeignFunctionTable { }, libffi::raw::FFI_TYPE_STRUCT => { match args[i] { - Value::Struct(ref name, ref struct_args) => { + Value::Struct(ref name, ref mut struct_args) => { if let Some(ref mut struct_type) = structs_table.get_mut(name) { let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap(); let ptr = alloc(layout) as *mut c_void; let mut field_ptr = ptr; + for i in 0..(struct_type.fields.len()-1) { + macro_rules! try_write_int { + ($type:ty) => { + { + let n: $type = <$type>::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + std::ptr::write(field_ptr as *mut $type, n); + field_ptr = field_ptr.add(std::mem::size_of::<$type>()); + } + } + } + + macro_rules! write { + ($type:ty, $value:expr) => { + { + let data: $type = $value; + std::ptr::write(field_ptr as *mut $type, data); + field_ptr = field_ptr.add(std::mem::size_of::<$type>()); + } + } + } + let field = struct_type.fields[i]; match (*field).type_ as u32 { - libffi::raw::FFI_TYPE_UINT8 => { - let n: u8 = u8::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; - std::ptr::write(field_ptr as *mut u8, n); - field_ptr = field_ptr.add(std::mem::size_of::()); - }, - libffi::raw::FFI_TYPE_UINT32 => { - let n: u32 = u32::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; - std::ptr::write(field_ptr as *mut u32, n); - field_ptr = field_ptr.add(std::mem::size_of::()); - }, - libffi::raw::FFI_TYPE_SINT32 => { - let n: u32 = u32::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; - std::ptr::write(field_ptr as *mut u32, n); - field_ptr = field_ptr.add(std::mem::size_of::()); - }, - libffi::raw::FFI_TYPE_FLOAT => { - let n: f32 = struct_args[i].as_float()? as f32; - std::ptr::write(field_ptr as *mut f32, n); - field_ptr = field_ptr.add(std::mem::size_of::()); - }, + libffi::raw::FFI_TYPE_UINT8 => try_write_int!(u8), + libffi::raw::FFI_TYPE_SINT8 => try_write_int!(i8), + libffi::raw::FFI_TYPE_UINT16 => try_write_int!(u16), + libffi::raw::FFI_TYPE_SINT16 => try_write_int!(i16), + libffi::raw::FFI_TYPE_UINT32 => try_write_int!(u32), + libffi::raw::FFI_TYPE_SINT32 => try_write_int!(i32), + libffi::raw::FFI_TYPE_UINT64 => try_write_int!(u64), + libffi::raw::FFI_TYPE_SINT64 => try_write_int!(i64), + libffi::raw::FFI_TYPE_POINTER => write!(*mut c_void, struct_args[i].as_ptr()?), + libffi::raw::FFI_TYPE_FLOAT => write!(f32, struct_args[i].as_float()? as f32), + libffi::raw::FFI_TYPE_DOUBLE => write!(f64, struct_args[i].as_float()?), _ => { unreachable!() } @@ -247,42 +230,66 @@ impl ForeignFunctionTable { pub fn exec(&mut self, name: &str, mut args: Vec) -> Result { let function_impl = self.table.get_mut(name).ok_or(FFIError::FunctionNotFound)?; - let mut pointer_args = Self::build_pointer_args(&mut args, &function_impl.args, &mut self.structs).unwrap(); + let mut pointer_args = Self::build_pointer_args(&mut args, &function_impl.args, &mut self.structs)?; + return unsafe { + macro_rules! call_and_return { + ($type:ty) => { + { + let mut n: Box = Box::new(0); + libffi::raw::ffi_call( + &mut function_impl.cif, + Some(*function_impl.code_ptr.as_safe_fun()), + &mut *n as *mut _ as *mut c_void, + pointer_args.pointers.as_mut_ptr() as *mut *mut c_void + ); + Ok(Value::Int(i64::from(*n))) + } + } + } + match (*function_impl.cif.rtype).type_ as u32 { - libffi::raw::FFI_TYPE_VOID => { - let mut _n: Box = Box::new(0); + libffi::raw::FFI_TYPE_VOID => call_and_return!(i32), + libffi::raw::FFI_TYPE_UINT8 => call_and_return!(u8), + libffi::raw::FFI_TYPE_SINT8 => call_and_return!(i8), + libffi::raw::FFI_TYPE_UINT16 => call_and_return!(u16), + libffi::raw::FFI_TYPE_SINT16 => call_and_return!(i16), + libffi::raw::FFI_TYPE_UINT32 => call_and_return!(u32), + libffi::raw::FFI_TYPE_SINT32 => call_and_return!(i32), + libffi::raw::FFI_TYPE_UINT64 => { + let mut n: Box = Box::new(0); libffi::raw::ffi_call( &mut function_impl.cif, Some(*function_impl.code_ptr.as_safe_fun()), - &mut *_n as *mut _ as *mut c_void, + &mut *n as *mut _ as *mut c_void, pointer_args.pointers.as_mut_ptr() as *mut *mut c_void ); - Ok(Value::Int(0)) + Ok(Value::Int(i64::try_from(*n).map_err(|_| FFIError::ValueDontFit)?)) }, - libffi::raw::FFI_TYPE_SINT8 => { - let mut n: Box = Box::new(0); + libffi::raw::FFI_TYPE_SINT64 => call_and_return!(i64), + libffi::raw::FFI_TYPE_FLOAT => { + let mut n: Box = Box::new(0.0); libffi::raw::ffi_call( &mut function_impl.cif, Some(*function_impl.code_ptr.as_safe_fun()), &mut *n as *mut _ as *mut c_void, pointer_args.pointers.as_mut_ptr() as *mut *mut c_void ); - Ok(Value::Int(i64::from(*n))) - }, - libffi::raw::FFI_TYPE_SINT32 => { - let mut n: Box = Box::new(0); + Ok(Value::Float((*n).into())) + }, + libffi::raw::FFI_TYPE_DOUBLE => { + let mut n: Box = Box::new(0.0); libffi::raw::ffi_call( &mut function_impl.cif, Some(*function_impl.code_ptr.as_safe_fun()), &mut *n as *mut _ as *mut c_void, pointer_args.pointers.as_mut_ptr() as *mut *mut c_void ); - Ok(Value::Int(i64::from(*n))) + Ok(Value::Float(*n)) }, libffi::raw::FFI_TYPE_STRUCT => { let mut returns = Vec::new(); - let mut struct_type = self.structs.get_mut(&function_impl.return_struct_name.clone().ok_or(FFIError::StructNotFound)?).ok_or(FFIError::StructNotFound)?; + let struct_type = self.structs.get_mut(&function_impl.return_struct_name.clone().ok_or(FFIError::StructNotFound)?).ok_or(FFIError::StructNotFound)?; let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap(); let ptr = alloc(layout) as *mut c_void; libffi::raw::ffi_call( @@ -292,25 +299,33 @@ impl ForeignFunctionTable { pointer_args.pointers.as_mut_ptr() as *mut *mut c_void ); - let mut field_ptr = ptr; + let mut field_ptr = ptr; + + macro_rules! read_and_push_int { + ($type:ty) => { + { + let n = std::ptr::read(field_ptr as *mut $type); + returns.push(Value::Int(i64::from(n))); + field_ptr = field_ptr.add(std::mem::size_of::<$type>()); + } + } + } + for i in 0..(struct_type.fields.len()-1) { let field = struct_type.fields[i]; match (*field).type_ as u32 { - libffi::raw::FFI_TYPE_UINT8 => { - let n = std::ptr::read(field_ptr as *mut u8); - returns.push(Value::Int(i64::from(n))); - field_ptr = field_ptr.add(std::mem::size_of::()); - }, - libffi::raw::FFI_TYPE_SINT32 => { - let n = std::ptr::read(field_ptr as *mut i32); - returns.push(Value::Int(i64::from(n))); - field_ptr = field_ptr.add(std::mem::size_of::()); - }, - libffi::raw::FFI_TYPE_UINT32 => { - let n = std::ptr::read(field_ptr as *mut u32); - returns.push(Value::Int(i64::from(n))); - field_ptr = field_ptr.add(std::mem::size_of::()); + libffi::raw::FFI_TYPE_UINT8 => read_and_push_int!(u8), + libffi::raw::FFI_TYPE_SINT8 => read_and_push_int!(i8), + libffi::raw::FFI_TYPE_UINT16 => read_and_push_int!(u16), + libffi::raw::FFI_TYPE_SINT16 => read_and_push_int!(i16), + libffi::raw::FFI_TYPE_UINT32 => read_and_push_int!(u32), + libffi::raw::FFI_TYPE_SINT32 => read_and_push_int!(i32), + libffi::raw::FFI_TYPE_UINT64 => { + let n = std::ptr::read(field_ptr as *mut u64); + returns.push(Value::Int(i64::try_from(n).map_err(|_| FFIError::ValueDontFit)?)); + field_ptr = field_ptr.add(std::mem::size_of::()); }, + libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64), _ => { unreachable!() } diff --git a/src/machine/machine_errors.rs b/src/machine/machine_errors.rs index ca2af22e..194d3ccc 100644 --- a/src/machine/machine_errors.rs +++ b/src/machine/machine_errors.rs @@ -1,6 +1,7 @@ use crate::atom_table::*; use crate::parser::ast::*; +use crate::ffi::FFIError; use crate::forms::*; use crate::machine::heap::*; use crate::machine::loader::CompilationTarget; @@ -515,6 +516,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; diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index c68a532e..e8536035 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -4288,9 +4288,10 @@ impl Machine { return Ok(()); }, Err(e) => { - // throw error - self.machine_st.fail = true; - return Ok(()); + let stub = functor_stub(atom!("current_input"), 1); + let err = self.machine_st.ffi_error(e); + + return Err(self.machine_st.error_form(err, stub)); } } } From 04ba58067aedc8e79f6220a5ea0b28d9656b5051 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 25 Feb 2023 21:52:18 -0700 Subject: [PATCH 117/361] add, implement and use the Unifier trait --- Cargo.lock | 12 + Cargo.toml | 1 + src/machine/machine_state_impl.rs | 1205 ++--------------------------- src/machine/mod.rs | 1 + src/machine/system_calls.rs | 1 - src/machine/unify.rs | 763 ++++++++++++++++++ 6 files changed, 850 insertions(+), 1133 deletions(-) create mode 100644 src/machine/unify.rs diff --git a/Cargo.lock b/Cargo.lock index 63784cd9..3b817784 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -367,6 +367,17 @@ dependencies = [ "syn 1.0.103", ] +[[package]] +name = "derive_deref" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcdbcee2d9941369faba772587a565f4f534e42cb8d17e5295871de730163b2b" +dependencies = [ + "proc-macro2 1.0.47", + "quote 1.0.21", + "syn 1.0.103", +] + [[package]] name = "difflib" version = "0.4.0" @@ -1821,6 +1832,7 @@ dependencies = [ "crossterm", "crrl", "ctrlc", + "derive_deref", "dirs-next", "divrem", "futures", diff --git a/Cargo.toml b/Cargo.toml index 6c98ea43..b21d4921 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,7 @@ hyper = { version = "0.14", features = ["full"] } hyper-tls = "0.5.0" tokio = { version = "1.24.2", features = ["full"] } futures = "0.3" +derive_deref = "1.1.1" [dev-dependencies] assert_cmd = "1.0.3" diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index b486fc31..b457ecae 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -11,10 +11,10 @@ use crate::machine::machine_indices::*; use crate::machine::machine_state::*; use crate::machine::partial_string::*; use crate::machine::stack::*; +use crate::machine::unify::*; use crate::parser::ast::*; use crate::parser::rug::{Integer, Rational}; -use fxhash::FxBuildHasher; use indexmap::IndexSet; use std::cmp::Ordering; @@ -235,634 +235,96 @@ impl 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)) => { - if !(a1 == 0 && a2 == 0 && n1 == n2) { - self.fail = true; - } - } - (HeapCellValueTag::AttrVar, h) => { - self.bind(Ref::attr_var(h), str_loc_as_cell!(s1)); - } - (HeapCellValueTag::Var, h) => { - self.bind(Ref::heap_cell(h), str_loc_as_cell!(s1)); - } - (HeapCellValueTag::StackVar, s) => { - self.bind(Ref::stack_cell(s), str_loc_as_cell!(s1)); - } - _ => { - self.fail = true; - } - ) + #[inline] + pub(super) fn bind_with_occurs_check_wrapper(&mut self, r: Ref, value: HeapCellValue) { + let mut unifier = CompositeUnifierForOccursCheck::from(DefaultUnifier::from(self)); + unifier.bind(r, value); } - fn unify_list(&mut self, l1: usize, d2: HeapCellValue) { - read_heap_cell!(d2, - (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(list_loc_as_cell!(l1), d2) - } - (HeapCellValueTag::AttrVar, h) => { - self.bind(Ref::attr_var(h), list_loc_as_cell!(l1)); - } - (HeapCellValueTag::Var, h) => { - self.bind(Ref::heap_cell(h), list_loc_as_cell!(l1)); - } - (HeapCellValueTag::StackVar, s) => { - self.bind(Ref::stack_cell(s), list_loc_as_cell!(l1)); - } - _ => { - self.fail = true; - } - ) - } - - pub fn unify_complete_string(&mut self, atom: Atom, value: HeapCellValue) { - if let Some(r) = value.as_var() { - if atom == atom!("") { - self.bind(r, atom_as_cell!(atom!("[]"))); - } else { - self.bind(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(atom_as_cstr_cell!(atom), value); - - if !self.pdl.is_empty() { - self.unify(); - } - } - } - (HeapCellValueTag::CStr, cstr_atom) => { - self.fail = atom != cstr_atom; - } - (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => { - self.unify_partial_string(atom_as_cstr_cell!(atom), value); - - if !self.pdl.is_empty() { - self.unify(); - } - } - _ => { - self.fail = true; - } + #[inline] + pub(super) fn bind_with_occurs_check_with_error_wrapper( + &mut self, + r: Ref, + value: HeapCellValue, + ) { + let mut unifier = CompositeUnifierForOccursCheckWithError::from( + DefaultUnifier::from(self), ); - } - // d1's tag is LIS, STR or PSTRLOC. - pub fn unify_partial_string(&mut self, d1: HeapCellValue, d2: HeapCellValue) { - if let Some(r) = d2.as_var() { - self.bind(r, d1); - return; - } - - let s1 = self.heap.len(); - - self.heap.push(d1); - self.heap.push(d2); - - let mut pstr_iter1 = HeapPStrIter::new(&self.heap, s1); - let mut pstr_iter2 = HeapPStrIter::new(&self.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() { - self.fail = true; - } else { - self.pdl.push(empty_list_as_cell!()); - self.pdl.push(pstr_iter2.focus); - } - } - PStrCmpResult::Ordered(Ordering::Greater) => { - if pstr_iter1.focus.as_var().is_none() { - self.fail = true; - } else { - self.pdl.push(empty_list_as_cell!()); - self.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]; - - self.pdl.push(val); - self.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 { - self.pdl.push(pstr_iter2.heap[s+1]); - self.pdl.push(char_as_cell!(c)); - - focus = pstr_iter2.heap[s+2]; - } else { - self.fail = true; - break 'outer; - } - } - (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { - match chars_iter.item.unwrap() { - PStrIteratee::Char(focus, _) => { - self.pdl.push(self.heap[focus]); - self.pdl.push(heap_loc_as_cell!(h)); - } - PStrIteratee::PStrSegment(focus, _, n) => { - read_heap_cell!(self.heap[focus], - (HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => { - if focus < self.heap.len() - 2 { - self.heap.pop(); - self.heap.pop(); - } - - if n == 0 { - let target_cell = match self.heap[focus].get_tag() { - HeapCellValueTag::CStr => { - atom_as_cstr_cell!(pstr_atom) - } - HeapCellValueTag::PStr => { - pstr_loc_as_cell!(focus) - } - _ => { - unreachable!() - } - }; - - self.pdl.push(target_cell); - self.pdl.push(heap_loc_as_cell!(h)); - } else { - let h_len = self.heap.len(); - - self.heap.push(pstr_offset_as_cell!(focus)); - self.heap.push(fixnum_as_cell!( - Fixnum::build_with(n as i64) - )); - - self.pdl.push(pstr_loc_as_cell!(h_len)); - self.pdl.push(heap_loc_as_cell!(h)); - } - - return; - } - (HeapCellValueTag::PStrOffset, pstr_loc) => { - let n0 = cell_as_fixnum!(self.heap[focus+1]) - .get_num() as usize; - - if pstr_loc < self.heap.len() - 2 { - self.heap.pop(); - self.heap.pop(); - } - - if n == n0 { - self.pdl.push(pstr_loc_as_cell!(focus)); - self.pdl.push(heap_loc_as_cell!(h)); - } else { - let h_len = self.heap.len(); - - self.heap.push(pstr_offset_as_cell!(pstr_loc)); - self.heap.push(fixnum_as_cell!( - Fixnum::build_with(n as i64) - )); - - self.pdl.push(pstr_loc_as_cell!(h_len)); - self.pdl.push(heap_loc_as_cell!(h)); - } - - return; - } - _ => { - } - ); - - if focus < self.heap.len() - 2 { - self.heap.pop(); - self.heap.pop(); - } - - self.pdl.push(self.heap[focus]); - self.pdl.push(heap_loc_as_cell!(h)); - - return; - } - } - - break 'outer; - } - _ => { - self.fail = true; - break 'outer; - } - ); - - chars_iter.next(); - } - - chars_iter.iter.next(); - - self.pdl.push(focus); - self.pdl.push(chars_iter.iter.focus); - - break; - } - } - PStrCmpResult::Unordered => { - self.pdl.push(pstr_iter1.focus); - self.pdl.push(pstr_iter2.focus); - } - } - - self.heap.pop(); - self.heap.pop(); - } - - pub 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(Ref::attr_var(h), atom_as_cell!(atom)); - } - (HeapCellValueTag::Var, h) => { - self.bind(Ref::heap_cell(h), atom_as_cell!(atom)); - } - (HeapCellValueTag::StackVar, s) => { - self.bind(Ref::stack_cell(s), atom_as_cell!(atom)); - } - _ => { - self.fail = true; - } - ); - } - - pub 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(Ref::attr_var(h), char_as_cell!(c)); - } - (HeapCellValueTag::Var, h) => { - self.bind(Ref::heap_cell(h), char_as_cell!(c)); - } - (HeapCellValueTag::StackVar, s) => { - self.bind(Ref::stack_cell(s), char_as_cell!(c)); - } - _ => { - self.fail = true; - } - ); - } - - pub fn unify_fixnum(&mut self, n1: Fixnum, value: HeapCellValue) { - if let Some(r) = value.as_var() { - self.bind(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; - } - } - } - - pub fn unify_big_int(&mut self, n1: TypedArenaPtr, value: HeapCellValue) { - if let Some(r) = value.as_var() { - self.bind(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; - } - } - } - - pub fn unify_rational(&mut self, n1: TypedArenaPtr, value: HeapCellValue) { - if let Some(r) = value.as_var() { - self.bind(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; - } - } - } - - pub fn unify_f64(&mut self, f1: F64Ptr, value: HeapCellValue) { - if let Some(r) = value.as_var() { - self.bind(r, HeapCellValue::from(f1)); - return; - } - - read_heap_cell!(value, - (HeapCellValueTag::F64, f2) => { - self.fail = **f1 != **f2; - } - _ => { - self.fail = true; - } - ); - } - - pub 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_int(int_ptr, value); - } - (ArenaHeaderTag::Rational, rat_ptr) => { - self.unify_rational(rat_ptr, value); - } - _ => { - if let Some(r) = value.as_var() { - self.bind(r, untyped_arena_ptr_as_cell!(ptr)); - } else { - self.fail = true; - } - } - ); + unifier.bind(r, value); } pub fn unify(&mut self) { - let mut tabu_list = IndexSet::with_hasher(FxBuildHasher::default()); + let mut unifier = DefaultUnifier::from(self); + unifier.unify_internal(); + } - while !(self.pdl.is_empty() || self.fail) { - let s1 = self.pdl.pop().unwrap(); - let s1 = self.deref(s1); + pub fn unify_structure(&mut self, s1: usize, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_structure(s1, value); + } - let s2 = self.pdl.pop().unwrap(); - let s2 = self.deref(s2); + pub fn unify_atom(&mut self, atom: Atom, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_atom(atom, value); + } - if s1 != s2 { - let d1 = self.store(s1); - let d2 = self.store(s2); + pub fn unify_list(&mut self, l1: usize, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_list(l1, value); + } - read_heap_cell!(d1, - (HeapCellValueTag::AttrVar, h) => { - self.bind(Ref::attr_var(h), d2); - } - (HeapCellValueTag::Var, h) => { - self.bind(Ref::heap_cell(h), d2); - } - (HeapCellValueTag::StackVar, s) => { - self.bind(Ref::stack_cell(s), d2); - } - (HeapCellValueTag::Atom, (name, arity)) => { - debug_assert!(arity == 0); - self.unify_atom(name, d2); - } - (HeapCellValueTag::Str, s1) => { - if tabu_list.contains(&(d1, d2)) { - continue; - } + pub fn unify_complete_string(&mut self, atom: Atom, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_complete_string(atom, value); + } - self.unify_structure(s1, d2); + pub fn unify_partial_string(&mut self, value_1: HeapCellValue, value_2: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_partial_string(value_1, value_2); + } - 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; - } - } + pub fn unify_char(&mut self, c: char, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_char(c, value); + } - self.unify_list(l1, d2); + pub fn unify_fixnum(&mut self, n1: Fixnum, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_fixnum(n1, value); + } - 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; - } - ); + pub fn unify_big_int(&mut self, n1: TypedArenaPtr, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_big_num(n1, value); + } - self.unify_partial_string(d1, d2); + pub fn unify_rational(&mut self, n1: TypedArenaPtr, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_big_num(n1, value); + } - 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(Ref::attr_var(h), d1); - continue; - } - (HeapCellValueTag::Var, h) => { - self.bind(Ref::heap_cell(h), d1); - continue; - } - (HeapCellValueTag::StackVar, s) => { - self.bind(Ref::stack_cell(s), d1); - continue; - } - (HeapCellValueTag::Str | - HeapCellValueTag::Lis | - HeapCellValueTag::PStrLoc) => { - } - (HeapCellValueTag::CStr) => { - self.fail = d1 != d2; - continue; - } - _ => { - self.fail = true; - return; - } - ); + pub fn unify_f64(&mut self, f1: F64Ptr, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_f64(f1, value); + } - self.unify_partial_string(d2, d1); - } - (HeapCellValueTag::F64, f1) => { - self.unify_f64(f1, d2); - } - (HeapCellValueTag::Fixnum, n1) => { - self.unify_fixnum(n1, d2); - } - (HeapCellValueTag::Char, c1) => { - self.unify_char(c1, d2); - } - (HeapCellValueTag::Cons, ptr_1) => { - self.unify_constant(ptr_1, d2); - } - _ => { - unreachable!(); - } - ); - } - } + pub fn unify_constant(&mut self, ptr: UntypedArenaPtr, value: HeapCellValue) { + let mut unifier = DefaultUnifier::from(self); + unifier.unify_constant(ptr, value); + } + + pub(super) fn unify_with_occurs_check_with_error(&mut self) { + let mut unifier = CompositeUnifierForOccursCheckWithError::from( + DefaultUnifier::from(self), + ); + + unifier.unify_internal(); + } + + pub(super) fn unify_with_occurs_check(&mut self) { + let mut unifier = CompositeUnifierForOccursCheck::from(DefaultUnifier::from(self)); + unifier.unify_internal(); } pub(super) fn set_ball(&mut self) { @@ -883,527 +345,6 @@ impl MachineState { self.fail = true; } - #[inline] - pub fn bind_with_occurs_check(&mut self, 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. - self.bind(r, value); - return false; - } - - let mut occurs_triggered = false; - - if !value.is_constant() { - for addr in stackful_preorder_iter(&mut self.heap, value) { - let addr = unmark_cell_bits!(addr); - - if let Some(inner_r) = addr.as_var() { - if r == inner_r { - occurs_triggered = true; - break; - } - } - } - } - - if occurs_triggered { - self.fail = true; - } else { - self.bind(r, value); - } - - return occurs_triggered; - } - - #[inline] - pub(super) fn bind_with_occurs_check_wrapper(&mut self, r: Ref, value: HeapCellValue) { - self.bind_with_occurs_check(r, value); - } - - #[inline] - pub(super) fn bind_with_occurs_check_with_error_wrapper( - &mut self, - r: Ref, - value: HeapCellValue, - ) { - if self.bind_with_occurs_check(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); - } - } - - pub(super) fn unify_with_occurs_check_with_error(&mut self) { - let mut throw_error = false; - self.unify_with_occurs_check_loop(|| throw_error = true); - - if throw_error { - 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); - } - } - - pub(super) fn unify_with_occurs_check(&mut self) { - self.unify_with_occurs_check_loop(|| {}) - } - - fn unify_structure_with_occurs_check( - &mut self, - s1: usize, - value: HeapCellValue, - mut occurs_trigger: impl FnMut(), - ) { - // 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+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) => { - if self.bind_with_occurs_check(Ref::attr_var(h), str_loc_as_cell!(s1)) { - occurs_trigger(); - } - } - (HeapCellValueTag::Var, h) => { - if self.bind_with_occurs_check(Ref::heap_cell(h), str_loc_as_cell!(s1)) { - occurs_trigger(); - } - } - (HeapCellValueTag::StackVar, s) => { - if self.bind_with_occurs_check(Ref::stack_cell(s), str_loc_as_cell!(s1)) { - occurs_trigger(); - } - } - _ => { - self.fail = true; - } - ) - } - - // the return value of unify_partial_string_with_occurs_check 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. - pub fn unify_partial_string_with_occurs_check( - &mut self, - d1: HeapCellValue, - d2: HeapCellValue, - mut occurs_trigger: impl FnMut(), - ) { - if let Some(r) = d2.as_var() { - if self.bind_with_occurs_check(r, d1) { - occurs_trigger(); - } - - return; - } - - let s1 = self.heap.len(); - - self.heap.push(d1); - self.heap.push(d2); - - let mut pstr_iter1 = HeapPStrIter::new(&self.heap, s1); - let mut pstr_iter2 = HeapPStrIter::new(&self.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() { - self.fail = true; - } else { - self.pdl.push(empty_list_as_cell!()); - self.pdl.push(pstr_iter2.focus); - } - } - PStrCmpResult::Ordered(Ordering::Greater) => { - if pstr_iter1.focus.as_var().is_none() { - self.fail = true; - } else { - self.pdl.push(empty_list_as_cell!()); - self.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]; - - self.pdl.push(val); - self.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 { - self.pdl.push(pstr_iter2.heap[s+1]); - self.pdl.push(char_as_cell!(c)); - - focus = pstr_iter2.heap[s+2]; - } else { - self.fail = true; - break 'outer; - } - } - (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { - match chars_iter.item.unwrap() { - PStrIteratee::Char(focus, _) => { - self.pdl.push(self.heap[focus]); - self.pdl.push(heap_loc_as_cell!(h)); - } - PStrIteratee::PStrSegment(focus, _, n) => { - read_heap_cell!(self.heap[focus], - (HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => { - if focus < self.heap.len() - 2 { - self.heap.pop(); - self.heap.pop(); - } - - if n == 0 { - let target_cell = match self.heap[focus].get_tag() { - HeapCellValueTag::CStr => { - atom_as_cstr_cell!(pstr_atom) - } - HeapCellValueTag::PStr => { - pstr_loc_as_cell!(focus) - } - _ => { - unreachable!() - } - }; - - self.pdl.push(target_cell); - self.pdl.push(heap_loc_as_cell!(h)); - } else { - let h_len = self.heap.len(); - - self.heap.push(pstr_offset_as_cell!(focus)); - self.heap.push(fixnum_as_cell!( - Fixnum::build_with(n as i64) - )); - - self.pdl.push(pstr_loc_as_cell!(h_len)); - self.pdl.push(heap_loc_as_cell!(h)); - } - - return; - } - (HeapCellValueTag::PStrOffset, pstr_loc) => { - let n0 = cell_as_fixnum!(self.heap[focus+1]) - .get_num() as usize; - - if pstr_loc < self.heap.len() - 2 { - self.heap.pop(); - self.heap.pop(); - } - - if n == n0 { - self.pdl.push(pstr_loc_as_cell!(focus)); - self.pdl.push(heap_loc_as_cell!(h)); - } else { - let h_len = self.heap.len(); - - self.heap.push(pstr_offset_as_cell!(pstr_loc)); - self.heap.push(fixnum_as_cell!( - Fixnum::build_with(n as i64) - )); - - self.pdl.push(pstr_loc_as_cell!(h_len)); - self.pdl.push(heap_loc_as_cell!(h)); - } - - return; - } - _ => { - } - ); - - if focus < self.heap.len() - 2 { - self.heap.pop(); - self.heap.pop(); - } - - self.pdl.push(self.heap[focus]); - self.pdl.push(heap_loc_as_cell!(h)); - - return; - } - } - - break 'outer; - } - _ => { - self.fail = true; - break 'outer; - } - ); - - chars_iter.next(); - } - - chars_iter.iter.next(); - - self.pdl.push(chars_iter.iter.focus); - self.pdl.push(focus); - - break; - } - } - PStrCmpResult::Unordered => { - self.pdl.push(pstr_iter1.focus); - self.pdl.push(pstr_iter2.focus); - } - } - - self.heap.pop(); - self.heap.pop(); - } - - fn unify_list_with_occurs_trigger( - &mut self, - l1: usize, - d2: HeapCellValue, - mut occurs_trigger: impl FnMut(), - ) { - read_heap_cell!(d2, - (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_with_occurs_check( - list_loc_as_cell!(l1), - d2, - &mut occurs_trigger, - ) - } - (HeapCellValueTag::AttrVar, h) => { - if self.bind_with_occurs_check(Ref::attr_var(h), list_loc_as_cell!(l1)) { - occurs_trigger(); - } - } - (HeapCellValueTag::Var, h) => { - if self.bind_with_occurs_check(Ref::heap_cell(h), list_loc_as_cell!(l1)) { - occurs_trigger(); - } - } - (HeapCellValueTag::StackVar, s) => { - if self.bind_with_occurs_check(Ref::stack_cell(s), list_loc_as_cell!(l1)) { - occurs_trigger(); - } - } - _ => { - self.fail = true; - } - ) - } - - pub(super) fn unify_with_occurs_check_loop(&mut self, mut occurs_trigger: impl FnMut()) { - let mut tabu_list = IndexSet::with_hasher(FxBuildHasher::default()); - - // self.fail = false; - - while !(self.pdl.is_empty() || self.fail) { - let s1 = self.pdl.pop().unwrap(); - let s1 = self.deref(s1); - - let s2 = self.pdl.pop().unwrap(); - let s2 = self.deref(s2); - - if s1 != s2 { - let d1 = self.store(s1); - let d2 = self.store(s2); - - read_heap_cell!(d1, - (HeapCellValueTag::AttrVar, h) => { - if self.bind_with_occurs_check(Ref::attr_var(h), d2) { - occurs_trigger(); - } - } - (HeapCellValueTag::Var, h) => { - if self.bind_with_occurs_check(Ref::heap_cell(h), d2) { - occurs_trigger(); - } - } - (HeapCellValueTag::StackVar, s) => { - if self.bind_with_occurs_check(Ref::stack_cell(s), d2) { - occurs_trigger(); - } - } - (HeapCellValueTag::Atom, (name, arity)) => { - debug_assert!(arity == 0); - self.unify_atom(name, d2); - } - (HeapCellValueTag::Str, s1) => { - if tabu_list.contains(&(d1, d2)) { - continue; - } - - self.unify_structure_with_occurs_check(s1, d2, &mut occurs_trigger); - - 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_with_occurs_trigger(l1, d2, &mut occurs_trigger); - - 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_with_occurs_check( - d1, - d2, - &mut occurs_trigger, - ); - - 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(Ref::attr_var(h), d1); - continue; - } - (HeapCellValueTag::Var, h) => { - self.bind(Ref::heap_cell(h), d1); - continue; - } - (HeapCellValueTag::StackVar, s) => { - self.bind(Ref::stack_cell(s), d1); - continue; - } - (HeapCellValueTag::Str | - HeapCellValueTag::Lis | - HeapCellValueTag::PStrLoc) => { - } - _ => { - self.fail = true; - return; - } - ); - - self.unify_partial_string(d2, d1); - } - (HeapCellValueTag::F64, f1) => { - self.unify_f64(f1, d2); - } - (HeapCellValueTag::Fixnum, n1) => { - self.unify_fixnum(n1, d2); - } - (HeapCellValueTag::Char, c1) => { - self.unify_char(c1, d2); - } - (HeapCellValueTag::Cons, ptr_1) => { - self.unify_constant(ptr_1, d2); - } - _ => { - unreachable!(); - } - ); - } - } - } - pub(crate) fn read_s(&mut self) -> HeapCellValue { match &mut self.s { &mut HeapPtr::HeapCell(h) => self.deref(self.heap[h + self.s_offset]), diff --git a/src/machine/mod.rs b/src/machine/mod.rs index cad80661..21fc3e54 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -21,6 +21,7 @@ pub mod stack; pub mod streams; pub mod system_calls; pub mod term_stream; +pub mod unify; use crate::arena::*; use crate::arithmetic::*; diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index e39f3d22..e9ce7731 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -7213,4 +7213,3 @@ impl hkdf::KeyType for MyKey { self.0 } } - diff --git a/src/machine/unify.rs b/src/machine/unify.rs new file mode 100644 index 00000000..19445fe4 --- /dev/null +++ b/src/machine/unify.rs @@ -0,0 +1,763 @@ +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 { + 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(&mut self, n1: TypedArenaPtr, value: HeapCellValue) + where N: PartialEq + + PartialEq + + PartialEq + + 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); + } + _ => { + 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(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() { + for addr in stackful_preorder_iter(&mut unifier.heap, value) { + let addr = unmark_cell_bits!(addr); + + if let Some(inner_r) = addr.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 { + unifier: U, +} + +impl Deref for CompositeUnifierForOccursCheck { + type Target = MachineState; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.unifier.deref() + } +} + +impl DerefMut for CompositeUnifierForOccursCheck { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + self.unifier.deref_mut() + } +} + +impl From for CompositeUnifierForOccursCheck { + #[inline(always)] + fn from(unifier: U) -> Self { + Self { unifier } + } +} + +impl Unifier for CompositeUnifierForOccursCheck { + fn bind(&mut self, r: Ref, value: HeapCellValue) { + bind_with_occurs_check(&mut self.unifier, r, value); + } +} + +pub(crate) struct CompositeUnifierForOccursCheckWithError { + unifier: U, +} + +impl Deref for CompositeUnifierForOccursCheckWithError { + type Target = MachineState; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.unifier.deref() + } +} + +impl DerefMut for CompositeUnifierForOccursCheckWithError { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + self.unifier.deref_mut() + } +} + +impl From for CompositeUnifierForOccursCheckWithError { + #[inline(always)] + fn from(unifier: U) -> Self { + Self { unifier } + } +} + +impl Unifier for CompositeUnifierForOccursCheckWithError { + 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); + } + } +} From f94294dbd91111849a40ade6797a546ab62fb48e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sun, 26 Feb 2023 20:48:00 +0100 Subject: [PATCH 118/361] FFI: Nested structs --- src/ffi.rs | 283 ++++++++++++++++++++---------------- src/machine/system_calls.rs | 40 +++-- 2 files changed, 184 insertions(+), 139 deletions(-) diff --git a/src/ffi.rs b/src/ffi.rs index 8b0b5db9..2910a20d 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -30,15 +30,16 @@ pub struct ForeignFunctionTable { structs: HashMap, } -#[derive(Debug)] +#[derive(Debug, Clone)] struct StructImpl { ffi_type: ffi_type, fields: Vec<*mut ffi_type>, + atom_fields: Vec, } struct PointerArgs { pointers: Vec<*mut c_void>, - memory: Vec>, + _memory: Vec>, } impl ForeignFunctionTable { @@ -46,42 +47,42 @@ impl ForeignFunctionTable { self.table.extend(other.table); } - pub fn define_struct(&mut self, name: &str, fields: Vec) { - let mut fields: Vec<_> = fields.iter().map(|x| self.map_type_ffi(&x)).collect(); + pub fn define_struct(&mut self, name: &str, atom_fields: Vec) { + let mut fields: Vec<_> = atom_fields.iter().map(|x| self.map_type_ffi(&x)).collect(); fields.push(std::ptr::null_mut::()); let mut struct_type: ffi_type = Default::default(); struct_type.type_ = type_tag::STRUCT; struct_type.elements = fields.as_mut_ptr(); - self.structs.insert(name.to_string(), StructImpl { ffi_type: struct_type, fields}); + self.structs.insert(name.to_string(), StructImpl { ffi_type: struct_type, fields, atom_fields}); } fn map_type_ffi(&mut self, source: &Atom) -> *mut ffi_type { unsafe { - match source { - atom!("sint64") => &mut types::sint64, - atom!("sint32") => &mut types::sint32, - atom!("sint16") => &mut types::sint16, - atom!("sint8") => &mut types::sint8, - atom!("uint64") => &mut types::uint64, - atom!("uint32") => &mut types::uint32, - atom!("uint16") => &mut types::uint16, - atom!("uint8") => &mut types::uint8, - atom!("bool") => &mut types::sint8, - atom!("void") => &mut types::void, - atom!("cstr") => &mut types::pointer, - atom!("ptr") => &mut types::pointer, - atom!("f32") => &mut types::float, - atom!("f64") => &mut types::double, - struct_name => { - match self.structs.get_mut(struct_name.as_str()) { - Some(ref mut struct_type) => { - &mut struct_type.ffi_type - }, - None => unreachable!() + match source { + atom!("sint64") => &mut types::sint64, + atom!("sint32") => &mut types::sint32, + atom!("sint16") => &mut types::sint16, + atom!("sint8") => &mut types::sint8, + atom!("uint64") => &mut types::uint64, + atom!("uint32") => &mut types::uint32, + atom!("uint16") => &mut types::uint16, + atom!("uint8") => &mut types::uint8, + atom!("bool") => &mut types::sint8, + atom!("void") => &mut types::void, + atom!("cstr") => &mut types::pointer, + atom!("ptr") => &mut types::pointer, + atom!("f32") => &mut types::float, + atom!("f64") => &mut types::double, + struct_name => { + match self.structs.get_mut(struct_name.as_str()) { + Some(ref mut struct_type) => { + &mut struct_type.ffi_type + }, + None => unreachable!() + } } } } - } } pub(crate) fn load_library(&mut self, library_name: &str, functions: &Vec) -> Result<(), Box> { @@ -122,7 +123,7 @@ impl ForeignFunctionTable { fn build_pointer_args(args: &mut Vec, type_args: &Vec<*mut ffi_type>, structs_table: &mut HashMap) -> Result { let mut pointers = Vec::with_capacity(args.len()); - let mut memory = Vec::new(); + let mut _memory = Vec::new(); for i in 0..args.len() { let field_type = type_args[i]; unsafe { @@ -132,7 +133,7 @@ impl ForeignFunctionTable { let n: $type = <$type>::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; let mut box_value = Box::new(n) as Box; pointers.push(&mut *box_value as *mut _ as *mut c_void); - memory.push(box_value); + _memory.push(box_value); } } } @@ -150,73 +151,22 @@ impl ForeignFunctionTable { let n: f32 = args[i].as_float()? as f32; let mut box_value = Box::new(n) as Box; pointers.push(&mut *box_value as *mut _ as *mut c_void); - memory.push(box_value); + _memory.push(box_value); }, libffi::raw::FFI_TYPE_DOUBLE => { let n: f64 = args[i].as_float()?; let mut box_value = Box::new(n) as Box; pointers.push(&mut *box_value as *mut _ as *mut c_void); - memory.push(box_value); + _memory.push(box_value); }, libffi::raw::FFI_TYPE_POINTER => { let ptr: *mut c_void = args[i].as_ptr()?; pointers.push(ptr); }, libffi::raw::FFI_TYPE_STRUCT => { - match args[i] { - Value::Struct(ref name, ref mut struct_args) => { - if let Some(ref mut struct_type) = structs_table.get_mut(name) { - let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap(); - let ptr = alloc(layout) as *mut c_void; - let mut field_ptr = ptr; - - for i in 0..(struct_type.fields.len()-1) { - macro_rules! try_write_int { - ($type:ty) => { - { - let n: $type = <$type>::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; - std::ptr::write(field_ptr as *mut $type, n); - field_ptr = field_ptr.add(std::mem::size_of::<$type>()); - } - } - } - - macro_rules! write { - ($type:ty, $value:expr) => { - { - let data: $type = $value; - std::ptr::write(field_ptr as *mut $type, data); - field_ptr = field_ptr.add(std::mem::size_of::<$type>()); - } - } - } - - let field = struct_type.fields[i]; - match (*field).type_ as u32 { - libffi::raw::FFI_TYPE_UINT8 => try_write_int!(u8), - libffi::raw::FFI_TYPE_SINT8 => try_write_int!(i8), - libffi::raw::FFI_TYPE_UINT16 => try_write_int!(u16), - libffi::raw::FFI_TYPE_SINT16 => try_write_int!(i16), - libffi::raw::FFI_TYPE_UINT32 => try_write_int!(u32), - libffi::raw::FFI_TYPE_SINT32 => try_write_int!(i32), - libffi::raw::FFI_TYPE_UINT64 => try_write_int!(u64), - libffi::raw::FFI_TYPE_SINT64 => try_write_int!(i64), - libffi::raw::FFI_TYPE_POINTER => write!(*mut c_void, struct_args[i].as_ptr()?), - libffi::raw::FFI_TYPE_FLOAT => write!(f32, struct_args[i].as_float()? as f32), - libffi::raw::FFI_TYPE_DOUBLE => write!(f64, struct_args[i].as_float()?), - _ => { - unreachable!() - } - } - } - pointers.push(ptr); - memory.push(Box::from_raw(ptr)); - } else { - return Err(FFIError::InvalidStructName); - } - } - _ => return Err(FFIError::ValueCast) - } + let (mut ptr, _size, _align) = Self::build_struct(&mut args[i], structs_table)?; + pointers.push(&mut *ptr as *mut _ as *mut c_void); + _memory.push(ptr); }, _ => return Err(FFIError::InvalidFFIType) } @@ -224,10 +174,78 @@ impl ForeignFunctionTable { } Ok(PointerArgs { pointers, - memory + _memory }) } + fn build_struct(arg: &mut Value, structs_table: &mut HashMap) -> Result<(Box, usize, usize), FFIError> { + unsafe { + match arg { + Value::Struct(ref name, ref mut struct_args) => { + if let Some(ref mut struct_type) = structs_table.clone().get_mut(name) { + let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap(); + let align = struct_type.ffi_type.alignment as usize; + let size = struct_type.ffi_type.size; + let ptr = alloc(layout) as *mut c_void; + let mut field_ptr = ptr; + + for i in 0..(struct_type.fields.len()-1) { + macro_rules! try_write_int { + ($type:ty) => { + { + field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<$type>())); + let n: $type = <$type>::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?; + std::ptr::write(field_ptr as *mut $type, n); + field_ptr = field_ptr.add(std::mem::size_of::<$type>()); + } + } + } + + macro_rules! write { + ($type:ty, $value:expr) => { + { + let data: $type = $value; + std::ptr::write(field_ptr as *mut $type, data); + field_ptr = field_ptr.add(align); + } + } + } + + let field = struct_type.fields[i]; + match (*field).type_ as u32 { + libffi::raw::FFI_TYPE_UINT8 => try_write_int!(u8), + libffi::raw::FFI_TYPE_SINT8 => try_write_int!(i8), + libffi::raw::FFI_TYPE_UINT16 => try_write_int!(u16), + libffi::raw::FFI_TYPE_SINT16 => try_write_int!(i16), + libffi::raw::FFI_TYPE_UINT32 => try_write_int!(u32), + libffi::raw::FFI_TYPE_SINT32 => try_write_int!(i32), + libffi::raw::FFI_TYPE_UINT64 => try_write_int!(u64), + libffi::raw::FFI_TYPE_SINT64 => try_write_int!(i64), + libffi::raw::FFI_TYPE_POINTER => write!(*mut c_void, struct_args[i].as_ptr()?), + libffi::raw::FFI_TYPE_FLOAT => write!(f32, struct_args[i].as_float()? as f32), + libffi::raw::FFI_TYPE_DOUBLE => write!(f64, struct_args[i].as_float()?), + libffi::raw::FFI_TYPE_STRUCT => { + let (struct_ptr, struct_size, struct_align) = Self::build_struct(&mut struct_args[i], structs_table)?; + field_ptr = field_ptr.add(field_ptr.align_offset(struct_align)); + + std::ptr::copy(& *struct_ptr as *const _ as *const c_void, field_ptr as *mut c_void, struct_size); + field_ptr = field_ptr.add(struct_size); + }, + _ => { + unreachable!() + } + } + } + return Ok((Box::from_raw(ptr), size, align)); + } else { + return Err(FFIError::InvalidStructName); + } + } + _ => return Err(FFIError::ValueCast) + } + } + } + pub fn exec(&mut self, name: &str, mut args: Vec) -> Result { let function_impl = self.table.get_mut(name).ok_or(FFIError::FunctionNotFound)?; let mut pointer_args = Self::build_pointer_args(&mut args, &function_impl.args, &mut self.structs)?; @@ -288,56 +306,75 @@ impl ForeignFunctionTable { Ok(Value::Float(*n)) }, libffi::raw::FFI_TYPE_STRUCT => { - let mut returns = Vec::new(); - let struct_type = self.structs.get_mut(&function_impl.return_struct_name.clone().ok_or(FFIError::StructNotFound)?).ok_or(FFIError::StructNotFound)?; + let name = &function_impl.return_struct_name.clone().ok_or(FFIError::StructNotFound)?; + let struct_type = self.structs.get(name).ok_or(FFIError::StructNotFound)?; let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap(); let ptr = alloc(layout) as *mut c_void; + libffi::raw::ffi_call( &mut function_impl.cif, Some(*function_impl.code_ptr.as_safe_fun()), &mut *ptr as *mut _ as *mut c_void, pointer_args.pointers.as_mut_ptr() as *mut *mut c_void ); - - let mut field_ptr = ptr; - - macro_rules! read_and_push_int { - ($type:ty) => { - { - let n = std::ptr::read(field_ptr as *mut $type); - returns.push(Value::Int(i64::from(n))); - field_ptr = field_ptr.add(std::mem::size_of::<$type>()); - } - } - } - - for i in 0..(struct_type.fields.len()-1) { - let field = struct_type.fields[i]; - match (*field).type_ as u32 { - libffi::raw::FFI_TYPE_UINT8 => read_and_push_int!(u8), - libffi::raw::FFI_TYPE_SINT8 => read_and_push_int!(i8), - libffi::raw::FFI_TYPE_UINT16 => read_and_push_int!(u16), - libffi::raw::FFI_TYPE_SINT16 => read_and_push_int!(i16), - libffi::raw::FFI_TYPE_UINT32 => read_and_push_int!(u32), - libffi::raw::FFI_TYPE_SINT32 => read_and_push_int!(i32), - libffi::raw::FFI_TYPE_UINT64 => { - let n = std::ptr::read(field_ptr as *mut u64); - returns.push(Value::Int(i64::try_from(n).map_err(|_| FFIError::ValueDontFit)?)); - field_ptr = field_ptr.add(std::mem::size_of::()); - }, - libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64), - _ => { - unreachable!() - } - } - } + let struct_val = self.read_struct(ptr, name, struct_type); drop(Box::from_raw(ptr)); - Ok(Value::Struct("texture".into(), returns)) - }, + struct_val + } _ => unreachable!() } }; } + + fn read_struct(&self, ptr: *mut c_void, name: &str, struct_type: &StructImpl) -> Result { + unsafe { + let mut returns = Vec::new(); + let mut field_ptr = ptr; + + for i in 0..(struct_type.fields.len()-1) { + let field = struct_type.fields[i]; + + macro_rules! read_and_push_int { + ($type:ty) => { + { + field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<$type>())); + let n = std::ptr::read(field_ptr as *mut $type); + returns.push(Value::Int(i64::from(n))); + field_ptr = field_ptr.add(std::mem::size_of::<$type>()); + } + } + } + + match (*field).type_ as u32 { + libffi::raw::FFI_TYPE_UINT8 => read_and_push_int!(u8), + libffi::raw::FFI_TYPE_SINT8 => read_and_push_int!(i8), + libffi::raw::FFI_TYPE_UINT16 => read_and_push_int!(u16), + libffi::raw::FFI_TYPE_SINT16 => read_and_push_int!(i16), + libffi::raw::FFI_TYPE_UINT32 => read_and_push_int!(u32), + libffi::raw::FFI_TYPE_SINT32 => read_and_push_int!(i32), + libffi::raw::FFI_TYPE_UINT64 => { + field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::())); + let n = std::ptr::read(field_ptr as *mut u64); + returns.push(Value::Int(i64::try_from(n).map_err(|_| FFIError::ValueDontFit)?)); + field_ptr = field_ptr.add(std::mem::size_of::()); + }, + libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64), + libffi::raw::FFI_TYPE_STRUCT => { + let substruct = struct_type.atom_fields[i].as_str(); + let struct_type = self.structs.get(substruct).ok_or(FFIError::StructNotFound)?; + field_ptr = field_ptr.add(field_ptr.align_offset(struct_type.ffi_type.alignment as usize)); + let struct_val = self.read_struct(field_ptr, substruct, struct_type); + returns.push(struct_val?); + field_ptr = field_ptr.add(struct_type.ffi_type.size); + }, + _ => { + unreachable!() + } + } + } + Ok(Value::Struct(name.into(), returns)) + } + } } #[derive(Clone, Debug)] diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index e8536035..288b457b 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -4257,6 +4257,7 @@ impl Machine { } } } + match self.machine_st.try_from_list(args_reg, stub_gen) { Ok(args) => { let args: Vec<_> = args.into_iter().map(|x| map_arg(&mut self.machine_st, x)).collect(); @@ -4264,22 +4265,9 @@ impl Machine { Ok(result) => { match result { Value::Int(n) => self.machine_st.unify_fixnum(Fixnum::build_with(n), return_value), - Value::Struct(name, mut args) => { - args.insert(0, Value::CString(CString::new(name).unwrap())); - let struct_list = heap_loc_as_cell!( - iter_to_heap_list( - &mut self.machine_st.heap, - args.into_iter() - .map(|val| { - match val { - Value::Int(n) => fixnum_as_cell!(Fixnum::build_with(n)), - Value::CString(cstr) => atom_as_cell!(self.machine_st.atom_tbl.build_with(&cstr.into_string().unwrap())), - _ => unreachable!() - } - }), - ) - ); - unify!(self.machine_st, return_value, struct_list); + Value::Struct(name, args) => { + let struct_value = self.build_struct(&name, args); + unify!(self.machine_st, return_value, struct_value); } _ => { unreachable!(); @@ -4302,6 +4290,26 @@ impl Machine { Ok(()) } + fn build_struct(&mut self, name: &str, mut args: Vec) -> HeapCellValue { + args.insert(0, Value::CString(CString::new(name).unwrap())); + let cells: Vec<_> = args.into_iter() + .map(|val| { + match val { + Value::Int(n) => fixnum_as_cell!(Fixnum::build_with(n)), + Value::CString(cstr) => atom_as_cell!(self.machine_st.atom_tbl.build_with(&cstr.into_string().unwrap())), + Value::Struct(name, struct_args) => self.build_struct(&name, struct_args), + _ => unreachable!() + } + }).collect(); + + heap_loc_as_cell!( + iter_to_heap_list( + &mut self.machine_st.heap, + cells.into_iter() + ) + ) + } + #[inline(always)] pub(crate) fn define_foreign_struct(&mut self) -> CallResult { let struct_name = self.deref_register(1); From 3dc6ed79d2379bfa00fb658209f267a1a8f5e352 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 26 Feb 2023 22:27:06 +0100 Subject: [PATCH 119/361] ENHANCED: must_be/2: prefer type error over instantiation error This addresses #1594. --- src/lib/error.pl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/error.pl b/src/lib/error.pl index 7efb170e..38a93214 100644 --- a/src/lib/error.pl +++ b/src/lib/error.pl @@ -1,5 +1,5 @@ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Written 2018-2022 by Markus Triska (triska@metalevel.at) + Written 2018-2023 by Markus Triska (triska@metalevel.at) I place this code in the public domain. Use it in any way you want. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ @@ -85,11 +85,11 @@ must_be_(list, Term) :- check_(error:ilist, list, Term). must_be_(type, Term) :- check_(error:type, type, Term). must_be_(boolean, Term) :- check_(error:boolean, boolean, Term). must_be_(term, Term) :- - ( \+ ground(Term) -> - instantiation_error(must_be/2) - ; \+ acyclic_term(Term) -> - type_error(term, Term, must_be/2) - ; true + ( acyclic_term(Term) -> + ( ground(Term) -> true + ; instantiation_error(must_be/2) + ) + ; type_error(term, Term, must_be/2) ). % We cannot use maplist(must_be(character), Cs), because library(lists) From 3286e78cd2057a407dcfc54a74835854f0abb9dc Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 26 Feb 2023 16:29:39 -0700 Subject: [PATCH 120/361] third argument of copy_term should be instantiated as a list (#1747) --- src/machine/project_attributes.pl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/machine/project_attributes.pl b/src/machine/project_attributes.pl index 59ad0790..20a19e97 100644 --- a/src/machine/project_attributes.pl +++ b/src/machine/project_attributes.pl @@ -111,7 +111,11 @@ 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]). + findall(Term-Rs, term_residual_goals(Term,Rs), [Copy-Gs]), + ( var(Gs) -> + Gs = [] + ; true + ). term_residual_goals(Term,Rs) :- '$term_attributed_variables'(Term, Vs), From 400ca21213ed25ebc902899569921329ab0e3913 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 26 Feb 2023 22:41:38 -0700 Subject: [PATCH 121/361] invoke '$default_attr_list' in project_attributes.pl (#1748) --- src/machine/project_attributes.pl | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/machine/project_attributes.pl b/src/machine/project_attributes.pl index 20a19e97..f54797c8 100644 --- a/src/machine/project_attributes.pl +++ b/src/machine/project_attributes.pl @@ -38,20 +38,6 @@ call_project_attributes([Module|Modules], QueryVars, 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), @@ -90,21 +76,26 @@ copy_attribute_modules([Module:_|Attrs]) --> [Module], copy_attribute_modules(Attrs). -attribute_goals_or_fail(M, V, V0, V1) :- +gather_residual_goals_(M, V, V0, V1) :- ( catch(M:attribute_goals(V, V0, V1), E, - '$project_atts':'$print_attribute_goals_exception'(M, 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). + 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^attribute_goals_or_fail(M, V), 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). From 0ac93751d0054c1ba1e86d71031b41e94e073a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Mon, 27 Feb 2023 20:02:21 +0100 Subject: [PATCH 122/361] FFI: Documentation --- src/ffi.rs | 21 ++++++++++ src/lib/ffi.pl | 101 +++++++++++++++++++++++++++++-------------------- 2 files changed, 82 insertions(+), 40 deletions(-) diff --git a/src/ffi.rs b/src/ffi.rs index 2910a20d..afc52339 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -1,3 +1,24 @@ +/* How does FFI work? + +Each WAM machine has a ForeignFunctionTable instance that contains a table of functions and structs. + +Structs are defined via foreign_struct/2. Basic types are defined by libffi, but struct types need to +be manually defined to get an ffi_type. Additionally, to recover structs from return arguments, we store +fields and atom_fields, as a way to lookup the content of the struct (fields) and the nested structs (atom_fields). + +Functions are defined via use_foreign_module/2. It opens a library and leaks the memory of the library, +to prevent Rust freeing the memory. There's no way to recover that memory at the moment. We get a pointer for +each function and we build a CIF for each one, with the input arguments and the return argument. + +Exec happens via '$foreign_call', we find the function, we try to cast the values that we have to the definition +of the function, we reserve memory for them and we build an array of pointers. To get the return argument, we +reserve enough memory for the return and we build the Scryer values from them. + +Structs are a bit tricky as they need to be aligned. For that, we reserve enough memory (libffi calculates that) +and for each field: we add to the pointer until we're aligned to the next data type we're going to write, we write it, +and finally we add the pointer the size of what we've written. +*/ + use crate::atom_table::Atom; use std::alloc::{alloc, Layout}; diff --git a/src/lib/ffi.pl b/src/lib/ffi.pl index c34cb4ad..cce60ff6 100644 --- a/src/lib/ffi.pl +++ b/src/lib/ffi.pl @@ -1,8 +1,65 @@ :- module(ffi, [use_foreign_module/2, foreign_struct/2]). +/** Foreign Function Interface + +This module contains predicates used to call native code (exposed by the C ABI). +It uses [libffi](https://sourceware.org/libffi/) under the hood. The bridge is very simple +and is very unsafe and should be used with care. FFI isn't the only way to communicate with +the outside world in Prolog: sockets, pipes and HTTP may be good enough for your use case. + +The main predicate is `use_foreign_module/2`. It takes a library name (which depending on the +operating system could be a `.so`, `.dylib` or `.dll` file). and a list of functions. Each +function is defined by its name, a list of the type of the arguments, and the return argument. + +Types available are: `sint8`, `uint8`, `sint16`, `uint16`, `sint32`, `uint32`, `sint64`, +`uint64`, `f32`, `f64`, `cstr`, `void`, `bool`, `ptr` and custom structs, which can be defined +with `foreign_struct/2`. + +After that, each function on the lists maps to a predicate created in the ffi module which +are used to call the native code. +The predicate takes the functor name after the function name. Then, the arguments are the input +arguments followed by a return argument. However, functions with return type `void` or `bool` +don't have that return argument. Predicates with `void` always succeed and `bool` predicates depend +on the return value on the native side. + +``` +ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN, -ReturnArg). % for all return types except void and bool +ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN). % for void and bool +``` + +## Example + +For example, let's see how to define a function from the [raylib](https://www.raylib.com/) library. + +``` +?- use_foreign_module("./libraylib.so", ['InitWindow'([sint32, sint32, cstr], void)]). +``` + +This creates a `'InitWindow'` predicate under the ffi module. Now, we can call it: + +``` +?- ffi:'InitWindow'(800, 600, "Scryer Prolog + Raylib"). +``` + +And a new window should pop up! +*/ + :- use_module(library(lists)). :- use_module(library(error)). - + +%% foreign_struct(+Name, +Elements). +% +% Defines a new struct type with name Name, composed of the elements Elements, which is a list +% of other types. +% +% The name of the types doesn't matter, but the order of Elements must match the ones in the +% native code. +% +% Example: +% +% ``` +% ?- foreign_struct(color, [uint8, uint8, uint8, uint8]). +% ``` foreign_struct(Name, Elements) :- '$define_foreign_struct'(Name, Elements). @@ -16,10 +73,9 @@ assert_predicate(PredicateDefinition) :- functor(Head, Name, NumInputs), term_variables(Head, TermList), Body = ( - lists:maplist(ffi:check_input, Inputs, TermList), '$foreign_call'(Name, TermList, _),! ), - Predicate =.. [:-, Head, Body], + Predicate = (Head:-Body), assertz(ffi:Predicate). assert_predicate(PredicateDefinition) :- @@ -28,10 +84,9 @@ assert_predicate(PredicateDefinition) :- functor(Head, Name, NumInputs), term_variables(Head, TermList), Body = ( - lists:maplist(ffi:check_input, Inputs, TermList), '$foreign_call'(Name, TermList, 1),! ), - Predicate =.. [:-, Head, Body], + Predicate = (Head:-Body), assertz(ffi:Predicate). assert_predicate(PredicateDefinition) :- @@ -43,41 +98,7 @@ assert_predicate(PredicateDefinition) :- term_variables(Head, TermList), Body = ( lists:append(TermListInputs, [TermListReturn], TermList), - lists:maplist(ffi:check_input, Inputs, TermListInputs), '$foreign_call'(Name, TermListInputs, TermListReturn),! ), - Predicate =.. [:-, Head, Body], + Predicate = (Head:-Body), assertz(ffi:Predicate). - -check_input(sint8, Var) :- - must_be(integer, Var), - ( - (Var > -129, Var < 128) -> - true - ; domain_error(integer_does_not_fit, Var, foreign_call/3) - ). -check_input(sint16, Var) :- - must_be(integer, Var), - ( - (Var > -32769, Var < 32768) -> - true - ; domain_error(integer_does_not_fit, Var, foreign_call/3) - ). -check_input(sint32, Var) :- - must_be(integer, Var), - ( - (Var > -2147483649, Var < 2147483648) -> - true - ; domain_error(integer_does_not_fit, Var, foreign_call/3) - ). -check_input(sint64, Var) :- - must_be(integer, Var). -check_input(f32, _Var). -check_input(f64, _Var). -check_input(cstr, Var) :- - must_be(chars, Var). -check_input(_, Var). -% must_be(list, Var). - -% TODO: assert native predicates. -% They MUST validate types From 73df96244dca8b12fb9d02f5047ee55a40ed20ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Wed, 1 Mar 2023 22:10:26 +0100 Subject: [PATCH 123/361] Fill more cases --- src/ffi.rs | 2 ++ src/machine/system_calls.rs | 17 +++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/ffi.rs b/src/ffi.rs index afc52339..a48ed62e 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -306,6 +306,7 @@ impl ForeignFunctionTable { Ok(Value::Int(i64::try_from(*n).map_err(|_| FFIError::ValueDontFit)?)) }, libffi::raw::FFI_TYPE_SINT64 => call_and_return!(i64), + libffi::raw::FFI_TYPE_POINTER => call_and_return!(*mut c_void), libffi::raw::FFI_TYPE_FLOAT => { let mut n: Box = Box::new(0.0); libffi::raw::ffi_call( @@ -380,6 +381,7 @@ impl ForeignFunctionTable { field_ptr = field_ptr.add(std::mem::size_of::()); }, libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64), + libffi::raw::FFI_TYPE_POINTER => read_and_push_int!(i64), libffi::raw::FFI_TYPE_STRUCT => { let substruct = struct_type.atom_fields[i].as_str(); let struct_type = self.structs.get(substruct).ok_or(FFIError::StructNotFound)?; diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 1c367a80..39311276 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -4310,12 +4310,17 @@ impl Machine { Ok(result) => { match result { Value::Int(n) => self.machine_st.unify_fixnum(Fixnum::build_with(n), return_value), + Value::Float(n) => { + let n = float_alloc!(n, self.machine_st.arena); + self.machine_st.unify_f64(n, return_value) + }, Value::Struct(name, args) => { let struct_value = self.build_struct(&name, args); unify!(self.machine_st, return_value, struct_value); } - _ => { - unreachable!(); + Value::CString(cstr) => { + let cstr = self.machine_st.atom_tbl.build_with(cstr.to_str().unwrap()); + self.machine_st.unify_complete_string(cstr, return_value); } } return Ok(()); @@ -4340,10 +4345,10 @@ impl Machine { let cells: Vec<_> = args.into_iter() .map(|val| { match val { - Value::Int(n) => fixnum_as_cell!(Fixnum::build_with(n)), - Value::CString(cstr) => atom_as_cell!(self.machine_st.atom_tbl.build_with(&cstr.into_string().unwrap())), - Value::Struct(name, struct_args) => self.build_struct(&name, struct_args), - _ => unreachable!() + Value::Int(n) => fixnum_as_cell!(Fixnum::build_with(n)), + Value::Float(n) => HeapCellValue::from(float_alloc!(n, self.machine_st.arena)), + Value::CString(cstr) => atom_as_cell!(self.machine_st.atom_tbl.build_with(&cstr.into_string().unwrap())), + Value::Struct(name, struct_args) => self.build_struct(&name, struct_args), } }).collect(); From c9ecfb11d9d2c39eb519d42f8d24cc51b2d4475e Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 8 Mar 2023 20:50:33 +0100 Subject: [PATCH 124/361] ENHANCED: more compact definition of dif/2 As outlined in #1753. --- src/lib/dif.pl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/lib/dif.pl b/src/lib/dif.pl index a59052ac..20842b27 100644 --- a/src/lib/dif.pl +++ b/src/lib/dif.pl @@ -59,11 +59,8 @@ verify_attributes(Var, Value, Goals) :- dif(X, Y) :- X \== Y, ( X \= Y -> true - ; ( term_variables(X, XVars), - term_variables(Y, YVars), - dif_set_variables(XVars, X, Y), - dif_set_variables(YVars, X, Y) - ) + ; term_variables(dif(X,Y), Vars), + dif_set_variables(Vars, X, Y) ). gather_dif_goals(_, []) --> []. From 884b0ca10eac28e300dbfddb1455f3cfd7ad0aa6 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 8 Mar 2023 21:16:54 +0100 Subject: [PATCH 125/361] FIXED: Take all variables into account during goal projection. This addresses #1751. --- src/lib/dif.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dif.pl b/src/lib/dif.pl index 20842b27..73d65518 100644 --- a/src/lib/dif.pl +++ b/src/lib/dif.pl @@ -65,7 +65,7 @@ dif(X, Y) :- gather_dif_goals(_, []) --> []. gather_dif_goals(V, [(X \== Y) | Goals]) --> - ( { term_variables(X, [V0 | _]), + ( { term_variables(X-Y, [V0 | _]), V == V0 } -> [dif:dif(X, Y)] ; [] From c3477d8476dde6341b279c6399cb7a798df04190 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 8 Mar 2023 23:31:02 +0100 Subject: [PATCH 126/361] items --> elements This addresses #1740. --- src/lib/lists.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/lists.pl b/src/lib/lists.pl index 1bdaf61e..5eef37c2 100644 --- a/src/lib/lists.pl +++ b/src/lib/lists.pl @@ -63,7 +63,7 @@ resource_error(Resource, Context) :- %% length(?Xs, ?N). % -% Relates a list to its length (number of items). It can be used to count the elements of a current list or +% Relates a list to its length (number of elements). It can be used to count the elements of a current list or % to create a list full of free variables with N length. % % ``` From 21acb9361a624a190b3256b8d18759afedc358af Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 8 Mar 2023 23:32:05 +0100 Subject: [PATCH 127/361] use string notation as discussed on #scryer IRC --- src/lib/lists.pl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib/lists.pl b/src/lib/lists.pl index 5eef37c2..a037fd85 100644 --- a/src/lib/lists.pl +++ b/src/lib/lists.pl @@ -67,14 +67,12 @@ resource_error(Resource, Context) :- % to create a list full of free variables with N length. % % ``` -% ?- length([a,b,c], 3). +% ?- length("abc", 3). % true. -% ?- length([a,b,c], N). +% ?- length("abc", N). % N = 3. % ?- length(Xs, 3). % Xs = [_A, _B, _C]. -% ?- length("chars", N). -% N = 5. % ``` length(Xs0, N) :- From 56bd596af3c33123e8dccf6d16075e92503d0005 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 8 Mar 2023 23:35:11 +0100 Subject: [PATCH 128/361] use actual toplevel answers --- src/lib/lists.pl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib/lists.pl b/src/lib/lists.pl index a037fd85..792c2be2 100644 --- a/src/lib/lists.pl +++ b/src/lib/lists.pl @@ -72,7 +72,7 @@ resource_error(Resource, Context) :- % ?- length("abc", N). % N = 3. % ?- length(Xs, 3). -% Xs = [_A, _B, _C]. +% Xs = [_A,_B,_C]. % ``` length(Xs0, N) :- @@ -131,7 +131,8 @@ member(X, [_|Xs]) :- member(X, Xs). % % ``` % ?- select(c, "abcd", X). -% X = "abd". +% X = "abd" +% ; false. % ``` select(X, [X|Xs], Xs). select(X, [Y|Xs], [Y|Ys]) :- select(X, Xs, Ys). @@ -142,7 +143,7 @@ select(X, [Y|Xs], [Y|Ys]) :- select(X, Xs, Ys). % % ``` % ?- append([[1, 2], [3]], Xs). -% Xs = [1, 2, 3]. +% Xs = [1,2,3]. % ``` append([], []). append([L0|Ls0], Ls) :- @@ -155,7 +156,7 @@ append([L0|Ls0], Ls) :- % % ``` % ?- append([1,2,3], [4,5,6], Xs). -% Xs = [1, 2, 3, 4, 5, 6]. +% Xs = [1,2,3,4,5,6]. % ``` append([], R, R). append([X|L], R, [X|S]) :- append(L, R, S). From 4c448591323f4c805c63673c5b40da247e2ee094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sun, 12 Mar 2023 16:47:20 +0100 Subject: [PATCH 129/361] Fixes #1756 --- src/lib/builtins.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 878919cf..759c60b6 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -124,6 +124,7 @@ call(_, _, _, _, _, _, _, _, _). % while others can be set with `set_prolog_flag/2`. % % The flags that Scryer Prolog support are: +% % * `max_arity`: The max arity a predicate can have in Prolog. On Scryer is set to 1023. Read only. % * `bounded`: `true` if integer arithmethic is bounded between some min/max values. On Scryer is always set % to `false` since it supports unbounded integer arithmethic. Read only. From 9b35a316c9a01e7897490b58c222093959a21de4 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 12 Mar 2023 14:14:29 -0600 Subject: [PATCH 130/361] correct call_residue_vars/3 using new copy_term_3 (#1239) --- build/instructions_template.rs | 4 ++++ src/lib/atts.pl | 4 +++- src/machine/dispatch.rs | 12 ++++++++++-- src/machine/system_calls.rs | 28 ++++++++++++++++++++-------- src/types.rs | 5 +++++ 5 files changed, 42 insertions(+), 11 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index d2cee3bb..937e1c15 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -574,6 +574,8 @@ enum SystemClauseType { DeleteFromAttributedVarList, #[strum_discriminants(strum(props(Arity = "1", Name = "$delete_all_attributes_from_var")))] DeleteAllAttributesFromVar, + #[strum_discriminants(strum(props(Arity = "2", Name = "$term_attributed_variables_without_attrs")))] + TermAttributedVariablesWithoutAttrs, REPL(REPLCodePtr), } @@ -1636,6 +1638,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallPutToAttributedVarList(_) | &Instruction::CallDeleteFromAttributedVarList(_) | &Instruction::CallDeleteAllAttributesFromVar(_) | + &Instruction::CallTermAttributedVariablesWithoutAttrs(_) | &Instruction::CallFetchGlobalVar(_) | &Instruction::CallFirstStream(_) | &Instruction::CallFlushOutput(_) | @@ -1855,6 +1858,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecutePutToAttributedVarList(_) | &Instruction::ExecuteDeleteFromAttributedVarList(_) | &Instruction::ExecuteDeleteAllAttributesFromVar(_) | + &Instruction::ExecuteTermAttributedVariablesWithoutAttrs(_) | &Instruction::ExecuteFetchGlobalVar(_) | &Instruction::ExecuteFirstStream(_) | &Instruction::ExecuteFlushOutput(_) | diff --git a/src/lib/atts.pl b/src/lib/atts.pl index d6ee47a3..a5cd9582 100644 --- a/src/lib/atts.pl +++ b/src/lib/atts.pl @@ -115,7 +115,9 @@ user:goal_expansion(Term, M:get_atts(Var, Attr)) :- call_residue_vars(Goal, Vars) :- '$get_attr_var_queue_delim'(B), call(Goal), - '$get_attr_var_queue_beyond'(B, Vars). + '$get_attr_var_queue_beyond'(B, AttrVars), + '$project_atts':copy_term(AttrVars, AttrVars, Gs), + '$term_attributed_variables_without_attrs'(Gs, Vars). term_attributed_variables(Term, Vars) :- '$term_attributed_variables'(Term, Vars). diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index e2e144c5..b24cbf76 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4392,11 +4392,11 @@ impl Machine { step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallTermVariables(_) => { - self.term_variables(); + self.term_variables(|value| value.is_var()); step_or_fail!(self, self.machine_st.p += 1); } &Instruction::ExecuteTermVariables(_) => { - self.term_variables(); + self.term_variables(|value| value.is_var()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallTermVariablesUnderMaxDepth(_) => { @@ -5239,6 +5239,14 @@ impl Machine { self.delete_all_attributes_from_var(); self.machine_st.p = self.machine_st.cp; } + &Instruction::CallTermAttributedVariablesWithoutAttrs(_) => { + self.term_variables(|value| value.is_attr_var()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteTermAttributedVariablesWithoutAttrs(_) => { + self.term_variables(|value| value.is_attr_var()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } } } diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 39311276..9cd28d1e 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -508,23 +508,24 @@ impl MachineState { } #[inline] - pub(crate) fn variable_set( + pub(crate) fn filter_cell_set( &mut self, seen_set: &mut IndexSet, value: HeapCellValue, + filter_fn: impl Fn(HeapCellValue) -> bool, ) { let mut iter = stackful_preorder_iter(&mut self.heap, value); while let Some(value) = iter.next() { let value = unmark_cell_bits!(value); - if value.is_var() { + if filter_fn(value) { let value = unmark_cell_bits!(heap_bound_store( iter.heap, heap_bound_deref(iter.heap, value) )); - if value.is_var() { + if filter_fn(value) { seen_set.insert(value); } } @@ -1243,7 +1244,11 @@ impl Machine { // complete_partial_goal prior to goal_expansion. let mut supp_vars = IndexSet::with_hasher(FxBuildHasher::default()); - self.machine_st.variable_set(&mut supp_vars, self.machine_st.registers[2]); + self.machine_st.filter_cell_set( + &mut supp_vars, + self.machine_st.registers[2], + |value| value.is_var(), + ); struct GoalAnalysisResult { is_simple_goal: bool, @@ -1263,7 +1268,11 @@ impl Machine { // fill expanded_vars with variables of the partial // goal pre-completion by complete_partial_goal. for idx in s + 1 .. s + arity - supp_vars.len() + 1 { - self.machine_st.variable_set(&mut expanded_vars, self.machine_st.heap[idx]); + self.machine_st.filter_cell_set( + &mut expanded_vars, + self.machine_st.heap[idx], + |value| value.is_var(), + ); } let is_simple_goal = if arity >= supp_vars.len() { @@ -4637,7 +4646,9 @@ impl Machine { if self.machine_st.heap[match_site + 1].get_tag() == HeapCellValueTag::Lis { let prev_tail_value = self.machine_st.heap[match_site + 1].get_value(); + self.machine_st.heap[prev_tail].set_value(prev_tail_value); + self.machine_st.attr_var_init.attr_var_queue.push(attr_var_list - 1); } else { self.machine_st.heap[prev_tail] = heap_loc_as_cell!(prev_tail); } @@ -4685,6 +4696,8 @@ impl Machine { self.machine_st.heap.push(str_loc_as_cell!(h+1)); self.machine_st.heap.extend(functor!(atom!(":"), [cell(module), cell(attr)])); + self.machine_st.attr_var_init.attr_var_queue.push(attr_var_list - 1); + match self.match_attribute(self.machine_st.heap[attr_var_list], module, attr) { Some(AttrListMatch { match_site, .. }) => { let (match_site, l) = match match_site { @@ -4714,7 +4727,6 @@ impl Machine { self.machine_st.heap.push(heap_loc_as_cell!(h)); self.machine_st.heap.push(heap_loc_as_cell!(h+5)); - self.machine_st.attr_var_init.attr_var_queue.push(attr_var_list - 1); self.machine_st.trail(TrailRef::AttrVarListLink(attr_var_list, attr_var_list)); } } @@ -6124,7 +6136,7 @@ impl Machine { } #[inline(always)] - pub(crate) fn term_variables(&mut self) { + pub(crate) fn term_variables(&mut self, filter_fn: impl Fn(HeapCellValue) -> bool) { let stored_v = self.deref_register(1); let a2 = self.deref_register(2); @@ -6135,7 +6147,7 @@ impl Machine { let mut seen_set = IndexSet::with_hasher(FxBuildHasher::default()); - self.machine_st.variable_set(&mut seen_set, stored_v); + self.machine_st.filter_cell_set(&mut seen_set, stored_v, filter_fn); let outcome = heap_loc_as_cell!( iter_to_heap_list(&mut self.machine_st.heap, seen_set.into_iter()) diff --git a/src/types.rs b/src/types.rs index a8b66b35..24015fe8 100644 --- a/src/types.rs +++ b/src/types.rs @@ -469,6 +469,11 @@ impl HeapCellValue { ) } + #[inline] + pub fn is_attr_var(self) -> bool { + self.get_tag() == HeapCellValueTag::AttrVar + } + #[inline] pub(crate) fn as_var(self) -> Option { read_heap_cell!(self, From 04ba9bc11af31780cd8fa259d78d151262c3c756 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 12 Mar 2023 16:59:03 -0600 Subject: [PATCH 131/361] use new call_residue_vars/2 in toplevel.pl (#847) --- src/lib/atts.pl | 6 +++--- src/toplevel.pl | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/atts.pl b/src/lib/atts.pl index a5cd9582..0521d429 100644 --- a/src/lib/atts.pl +++ b/src/lib/atts.pl @@ -110,6 +110,9 @@ user:goal_expansion(Term, M:get_atts(Var, Attr)) :- nonvar(Term), Term = get_atts(Var, M, Attr). +term_attributed_variables(Term, Vars) :- + '$term_attributed_variables'(Term, Vars). + :- meta_predicate call_residue_vars(0, ?). call_residue_vars(Goal, Vars) :- @@ -118,6 +121,3 @@ call_residue_vars(Goal, Vars) :- '$get_attr_var_queue_beyond'(B, AttrVars), '$project_atts':copy_term(AttrVars, AttrVars, Gs), '$term_attributed_variables_without_attrs'(Gs, Vars). - -term_attributed_variables(Term, Vars) :- - '$term_attributed_variables'(Term, Vars). diff --git a/src/toplevel.pl b/src/toplevel.pl index 8caea7ba..0bab4415 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -1,6 +1,7 @@ :- module('$toplevel', [argv/1, copy_term/3]). +:- use_module(library(atts), [call_residue_vars/2]). :- use_module(library(charsio)). :- use_module(library(error)). :- use_module(library(files)). @@ -180,8 +181,8 @@ submit_query_and_print_results_(Term, VarList) :- '$get_b_value'(B), bb_put('$report_all', false), bb_put('$report_n_more', 0), - call(user:Term), - write_eqs_and_read_input(B, VarList), + atts:call_residue_vars(user:Term, AttrVars), + write_eqs_and_read_input(B, VarList, AttrVars), !. submit_query_and_print_results_(_, _) :- ( bb_get('$answer_count', 0) -> @@ -286,11 +287,10 @@ trailing_period_is_ambiguous(Value) :- term_variables_under_max_depth(Term, MaxDepth, Vars) :- '$term_variables_under_max_depth'(Term, MaxDepth, Vars). -write_eqs_and_read_input(B, VarList) :- +write_eqs_and_read_input(B, VarList, AttrVars) :- gather_query_vars(VarList, OrigVars), % one layer of depth added for (=/2) functor '$term_variables_under_max_depth'(OrigVars, 22, Vars0), - '$term_attributed_variables'(VarList, AttrVars), '$project_atts':project_attributes(Vars0, AttrVars), copy_term(AttrVars, AttrVars, AttrGoals), term_variables(AttrGoals, AttrGoalVars), From cc8bb38abc6c5f2dc4fca68ef3991b7c5453bf90 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 14 Mar 2023 21:34:48 -0600 Subject: [PATCH 132/361] Revert "use new call_residue_vars/2 in toplevel.pl (#847)" This reverts commit 04ba9bc11af31780cd8fa259d78d151262c3c756. --- src/lib/atts.pl | 6 +++--- src/toplevel.pl | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/atts.pl b/src/lib/atts.pl index 0521d429..a5cd9582 100644 --- a/src/lib/atts.pl +++ b/src/lib/atts.pl @@ -110,9 +110,6 @@ user:goal_expansion(Term, M:get_atts(Var, Attr)) :- nonvar(Term), Term = get_atts(Var, M, Attr). -term_attributed_variables(Term, Vars) :- - '$term_attributed_variables'(Term, Vars). - :- meta_predicate call_residue_vars(0, ?). call_residue_vars(Goal, Vars) :- @@ -121,3 +118,6 @@ call_residue_vars(Goal, Vars) :- '$get_attr_var_queue_beyond'(B, AttrVars), '$project_atts':copy_term(AttrVars, AttrVars, Gs), '$term_attributed_variables_without_attrs'(Gs, Vars). + +term_attributed_variables(Term, Vars) :- + '$term_attributed_variables'(Term, Vars). diff --git a/src/toplevel.pl b/src/toplevel.pl index 0bab4415..8caea7ba 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -1,7 +1,6 @@ :- module('$toplevel', [argv/1, copy_term/3]). -:- use_module(library(atts), [call_residue_vars/2]). :- use_module(library(charsio)). :- use_module(library(error)). :- use_module(library(files)). @@ -181,8 +180,8 @@ submit_query_and_print_results_(Term, VarList) :- '$get_b_value'(B), bb_put('$report_all', false), bb_put('$report_n_more', 0), - atts:call_residue_vars(user:Term, AttrVars), - write_eqs_and_read_input(B, VarList, AttrVars), + call(user:Term), + write_eqs_and_read_input(B, VarList), !. submit_query_and_print_results_(_, _) :- ( bb_get('$answer_count', 0) -> @@ -287,10 +286,11 @@ trailing_period_is_ambiguous(Value) :- term_variables_under_max_depth(Term, MaxDepth, Vars) :- '$term_variables_under_max_depth'(Term, MaxDepth, Vars). -write_eqs_and_read_input(B, VarList, AttrVars) :- +write_eqs_and_read_input(B, VarList) :- gather_query_vars(VarList, OrigVars), % one layer of depth added for (=/2) functor '$term_variables_under_max_depth'(OrigVars, 22, Vars0), + '$term_attributed_variables'(VarList, AttrVars), '$project_atts':project_attributes(Vars0, AttrVars), copy_term(AttrVars, AttrVars, AttrGoals), term_variables(AttrGoals, AttrGoalVars), From 4da646252b46aa2cd1b46603acca7a57b9348e1e Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 14 Mar 2023 21:34:49 -0600 Subject: [PATCH 133/361] Revert "correct call_residue_vars/3 using new copy_term_3 (#1239)" This reverts commit 9b35a316c9a01e7897490b58c222093959a21de4. --- build/instructions_template.rs | 4 ---- src/lib/atts.pl | 4 +--- src/machine/dispatch.rs | 12 ++---------- src/machine/system_calls.rs | 28 ++++++++-------------------- src/types.rs | 5 ----- 5 files changed, 11 insertions(+), 42 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 937e1c15..d2cee3bb 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -574,8 +574,6 @@ enum SystemClauseType { DeleteFromAttributedVarList, #[strum_discriminants(strum(props(Arity = "1", Name = "$delete_all_attributes_from_var")))] DeleteAllAttributesFromVar, - #[strum_discriminants(strum(props(Arity = "2", Name = "$term_attributed_variables_without_attrs")))] - TermAttributedVariablesWithoutAttrs, REPL(REPLCodePtr), } @@ -1638,7 +1636,6 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallPutToAttributedVarList(_) | &Instruction::CallDeleteFromAttributedVarList(_) | &Instruction::CallDeleteAllAttributesFromVar(_) | - &Instruction::CallTermAttributedVariablesWithoutAttrs(_) | &Instruction::CallFetchGlobalVar(_) | &Instruction::CallFirstStream(_) | &Instruction::CallFlushOutput(_) | @@ -1858,7 +1855,6 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecutePutToAttributedVarList(_) | &Instruction::ExecuteDeleteFromAttributedVarList(_) | &Instruction::ExecuteDeleteAllAttributesFromVar(_) | - &Instruction::ExecuteTermAttributedVariablesWithoutAttrs(_) | &Instruction::ExecuteFetchGlobalVar(_) | &Instruction::ExecuteFirstStream(_) | &Instruction::ExecuteFlushOutput(_) | diff --git a/src/lib/atts.pl b/src/lib/atts.pl index a5cd9582..d6ee47a3 100644 --- a/src/lib/atts.pl +++ b/src/lib/atts.pl @@ -115,9 +115,7 @@ user:goal_expansion(Term, M:get_atts(Var, Attr)) :- call_residue_vars(Goal, Vars) :- '$get_attr_var_queue_delim'(B), call(Goal), - '$get_attr_var_queue_beyond'(B, AttrVars), - '$project_atts':copy_term(AttrVars, AttrVars, Gs), - '$term_attributed_variables_without_attrs'(Gs, Vars). + '$get_attr_var_queue_beyond'(B, Vars). term_attributed_variables(Term, Vars) :- '$term_attributed_variables'(Term, Vars). diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index b24cbf76..e2e144c5 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4392,11 +4392,11 @@ impl Machine { step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallTermVariables(_) => { - self.term_variables(|value| value.is_var()); + self.term_variables(); step_or_fail!(self, self.machine_st.p += 1); } &Instruction::ExecuteTermVariables(_) => { - self.term_variables(|value| value.is_var()); + self.term_variables(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallTermVariablesUnderMaxDepth(_) => { @@ -5239,14 +5239,6 @@ impl Machine { self.delete_all_attributes_from_var(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallTermAttributedVariablesWithoutAttrs(_) => { - self.term_variables(|value| value.is_attr_var()); - step_or_fail!(self, self.machine_st.p += 1); - } - &Instruction::ExecuteTermAttributedVariablesWithoutAttrs(_) => { - self.term_variables(|value| value.is_attr_var()); - step_or_fail!(self, self.machine_st.p = self.machine_st.cp); - } } } diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 9cd28d1e..39311276 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -508,24 +508,23 @@ impl MachineState { } #[inline] - pub(crate) fn filter_cell_set( + pub(crate) fn variable_set( &mut self, seen_set: &mut IndexSet, value: HeapCellValue, - filter_fn: impl Fn(HeapCellValue) -> bool, ) { let mut iter = stackful_preorder_iter(&mut self.heap, value); while let Some(value) = iter.next() { let value = unmark_cell_bits!(value); - if filter_fn(value) { + if value.is_var() { let value = unmark_cell_bits!(heap_bound_store( iter.heap, heap_bound_deref(iter.heap, value) )); - if filter_fn(value) { + if value.is_var() { seen_set.insert(value); } } @@ -1244,11 +1243,7 @@ impl Machine { // complete_partial_goal prior to goal_expansion. let mut supp_vars = IndexSet::with_hasher(FxBuildHasher::default()); - self.machine_st.filter_cell_set( - &mut supp_vars, - self.machine_st.registers[2], - |value| value.is_var(), - ); + self.machine_st.variable_set(&mut supp_vars, self.machine_st.registers[2]); struct GoalAnalysisResult { is_simple_goal: bool, @@ -1268,11 +1263,7 @@ impl Machine { // fill expanded_vars with variables of the partial // goal pre-completion by complete_partial_goal. for idx in s + 1 .. s + arity - supp_vars.len() + 1 { - self.machine_st.filter_cell_set( - &mut expanded_vars, - self.machine_st.heap[idx], - |value| value.is_var(), - ); + self.machine_st.variable_set(&mut expanded_vars, self.machine_st.heap[idx]); } let is_simple_goal = if arity >= supp_vars.len() { @@ -4646,9 +4637,7 @@ impl Machine { if self.machine_st.heap[match_site + 1].get_tag() == HeapCellValueTag::Lis { let prev_tail_value = self.machine_st.heap[match_site + 1].get_value(); - self.machine_st.heap[prev_tail].set_value(prev_tail_value); - self.machine_st.attr_var_init.attr_var_queue.push(attr_var_list - 1); } else { self.machine_st.heap[prev_tail] = heap_loc_as_cell!(prev_tail); } @@ -4696,8 +4685,6 @@ impl Machine { self.machine_st.heap.push(str_loc_as_cell!(h+1)); self.machine_st.heap.extend(functor!(atom!(":"), [cell(module), cell(attr)])); - self.machine_st.attr_var_init.attr_var_queue.push(attr_var_list - 1); - match self.match_attribute(self.machine_st.heap[attr_var_list], module, attr) { Some(AttrListMatch { match_site, .. }) => { let (match_site, l) = match match_site { @@ -4727,6 +4714,7 @@ impl Machine { self.machine_st.heap.push(heap_loc_as_cell!(h)); self.machine_st.heap.push(heap_loc_as_cell!(h+5)); + self.machine_st.attr_var_init.attr_var_queue.push(attr_var_list - 1); self.machine_st.trail(TrailRef::AttrVarListLink(attr_var_list, attr_var_list)); } } @@ -6136,7 +6124,7 @@ impl Machine { } #[inline(always)] - pub(crate) fn term_variables(&mut self, filter_fn: impl Fn(HeapCellValue) -> bool) { + pub(crate) fn term_variables(&mut self) { let stored_v = self.deref_register(1); let a2 = self.deref_register(2); @@ -6147,7 +6135,7 @@ impl Machine { let mut seen_set = IndexSet::with_hasher(FxBuildHasher::default()); - self.machine_st.filter_cell_set(&mut seen_set, stored_v, filter_fn); + self.machine_st.variable_set(&mut seen_set, stored_v); let outcome = heap_loc_as_cell!( iter_to_heap_list(&mut self.machine_st.heap, seen_set.into_iter()) diff --git a/src/types.rs b/src/types.rs index 24015fe8..a8b66b35 100644 --- a/src/types.rs +++ b/src/types.rs @@ -469,11 +469,6 @@ impl HeapCellValue { ) } - #[inline] - pub fn is_attr_var(self) -> bool { - self.get_tag() == HeapCellValueTag::AttrVar - } - #[inline] pub(crate) fn as_var(self) -> Option { read_heap_cell!(self, From cd586aab8c69376b680992c40d172af4005f8565 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 15 Mar 2023 19:56:02 +0100 Subject: [PATCH 134/361] show remaining queue/2 attributes as residual goals This lets us verify that all attributes are correctly removed earlier. --- src/lib/clpz.pl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index fd5d9d18..afc6f8cd 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -7683,10 +7683,6 @@ intervals_to_drep([A0-B0|Rest], Drep0, Drep) :- ), intervals_to_drep(Rest, Drep0 \/ D1, Drep). -attribute_goals(X) --> - { get_atts(X, queue(_,_)) }, - !, - { put_atts(X, -queue(_,_)) }. attribute_goals(X) --> % { get_attr(X, clpz, Attr), format("A: ~w\n", [Attr]) }, { get_attr(X, clpz, clpz_attr(_,_,_,Dom,fd_props(Gs,Bs,Os),_)), From 01285f12c3ddca4666f5b70163fe3d1ec31a38aa Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 15 Mar 2023 21:17:06 +0100 Subject: [PATCH 135/361] remove no longer needed queue attributes after propagation --- src/lib/clpz.pl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index afc6f8cd..acf4738f 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3943,10 +3943,7 @@ put_terminating(X, Dom, Ps) --> ) ). -new_queue(queue(Goals,Fast,Slow,_Aux)) :- - put_atts(Goals, +queue([],_)), - put_atts(Fast, +queue([],_)), - put_atts(Slow, +queue([],_)). +new_queue(queue(_Goals,_Fast,_Slow,_Aux)). queue_goal(Goal) --> insert_queue(Goal, 1). queue_fast(Prop) --> insert_queue(Prop, 2). @@ -3955,11 +3952,10 @@ queue_slow(Prop) --> insert_queue(Prop, 3). insert_queue(Element, Which) --> state(Queue), { arg(Which, Queue, Arg), - get_atts(Arg, queue(Head0,Tail0)), - ( Head0 == [] -> - Head = [Element|Tail] - ; Head = Head0, + ( get_atts(Arg, queue(Head0,Tail0)) -> + Head = Head0, Tail0 = [Element|Tail] + ; Head = [Element|Tail] ), put_atts(Arg, +queue(Head,Tail)) }. @@ -4187,11 +4183,15 @@ do_queue --> ; true ). +:- meta_predicate(ignore(0)). + +ignore(Goal) :- ( Goal -> true ; true ). + print_queue --> state(queue(Goal,Fast,Slow,_)), - { get_atts(Goal, +queue(GHs,_)), - get_atts(Fast, +queue(FHs,_)), - get_atts(Slow, +queue(SHs,_)), + { ignore(get_atts(Goal, +queue(GHs,_))), + ignore(get_atts(Fast, +queue(FHs,_))), + ignore(get_atts(Slow, +queue(SHs,_))), format("Current queue:~n goal: ~q~n fast: ~q~n slow: ~q~n~n", [GHs,FHs,SHs]) }. @@ -4208,7 +4208,7 @@ queue_get_arg_(Queue, Which, Element) :- arg(Which, Queue, Arg), get_atts(Arg, +queue([Element|Elements],Tail)), ( var(Elements) -> - put_atts(Arg, +queue([],_)) + put_atts(Arg, -queue(_,_)) ; put_atts(Arg, +queue(Elements,Tail)) ). From 4ee6a7bfb828474cbcab51695f24b120b1e35314 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 18 Mar 2023 23:08:09 -0600 Subject: [PATCH 136/361] add '$unattributed_var' builtin (#1758) --- build/instructions_template.rs | 6 +++++- src/lib/lists.pl | 4 ++-- src/machine/dispatch.rs | 8 ++++++++ src/machine/system_calls.rs | 19 +++++++++++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index d2cee3bb..93615dec 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -574,6 +574,8 @@ enum SystemClauseType { DeleteFromAttributedVarList, #[strum_discriminants(strum(props(Arity = "1", Name = "$delete_all_attributes_from_var")))] DeleteAllAttributesFromVar, + #[strum_discriminants(strum(props(Arity = "1", Name = "$unattributed_var")))] + UnattributedVar, REPL(REPLCodePtr), } @@ -1621,7 +1623,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallMakeDirectoryPath(_) | &Instruction::CallDeleteFile(_) | &Instruction::CallRenameFile(_) | - &Instruction::CallFileCopy(_) | + &Instruction::CallFileCopy(_) | &Instruction::CallWorkingDirectory(_) | &Instruction::CallDeleteDirectory(_) | &Instruction::CallPathCanonical(_) | @@ -1636,6 +1638,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallPutToAttributedVarList(_) | &Instruction::CallDeleteFromAttributedVarList(_) | &Instruction::CallDeleteAllAttributesFromVar(_) | + &Instruction::CallUnattributedVar(_) | &Instruction::CallFetchGlobalVar(_) | &Instruction::CallFirstStream(_) | &Instruction::CallFlushOutput(_) | @@ -1855,6 +1858,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecutePutToAttributedVarList(_) | &Instruction::ExecuteDeleteFromAttributedVarList(_) | &Instruction::ExecuteDeleteAllAttributesFromVar(_) | + &Instruction::ExecuteUnattributedVar(_) | &Instruction::ExecuteFetchGlobalVar(_) | &Instruction::ExecuteFirstStream(_) | &Instruction::ExecuteFlushOutput(_) | diff --git a/src/lib/lists.pl b/src/lib/lists.pl index 792c2be2..92bc202d 100644 --- a/src/lib/lists.pl +++ b/src/lib/lists.pl @@ -92,7 +92,7 @@ length(_, N) :- length_rundown(Xs, 0) :- !, Xs = []. length_rundown(Vs, N) :- - \+ \+ '$project_atts':copy_term(Vs,Vs,[]), % unconstrained + '$unattributed_var'(Vs), % unconstrained !, '$det_length_rundown'(Vs, N). length_rundown([_|Xs], N) :- % force unification @@ -100,7 +100,7 @@ length_rundown([_|Xs], N) :- % force unification length(Xs, N1). % maybe some new info on Xs failingvarskip(Xs) :- - \+ \+ '$project_atts':copy_term(Xs,Xs,[]), % unconstrained + '$unattributed_var'(Xs), % unconstrained !. failingvarskip([_|Xs0]) :- % force unification '$skip_max_list'(_, _, Xs0,Xs), diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index e2e144c5..39ca74c6 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -5239,6 +5239,14 @@ impl Machine { self.delete_all_attributes_from_var(); self.machine_st.p = self.machine_st.cp; } + &Instruction::CallUnattributedVar(_) => { + self.machine_st.unattributed_var(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteUnattributedVar(_) => { + self.machine_st.unattributed_var(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } } } diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 39311276..a84b78c7 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -471,6 +471,25 @@ struct AttrListMatch { } impl MachineState { + #[inline(always)] + pub(crate) fn unattributed_var(&mut self) { + let attr_var = self.store(self.deref(self.registers[1])); + + if !attr_var.is_var() { + self.fail = true; + return; + } + + read_heap_cell!(attr_var, + (HeapCellValueTag::AttrVar, h) => { + let list_cell = self.store(self.deref(self.heap[h+1])); + self.fail = list_cell.get_tag() == HeapCellValueTag::Lis; + } + _ => { + } + ); + } + pub(crate) fn get_attr_var_list(&mut self, attr_var: HeapCellValue) -> Option { read_heap_cell!(attr_var, (HeapCellValueTag::AttrVar, h) => { From 41b083c962c8c081fb84b89b14e6e117263c4869 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Mar 2023 01:27:40 +0000 Subject: [PATCH 137/361] Bump openssl from 0.10.42 to 0.10.48 Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.42 to 0.10.48. - [Release notes](https://github.com/sfackler/rust-openssl/releases) - [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.42...openssl-v0.10.48) --- updated-dependencies: - dependency-name: openssl dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b504f1e..ad74c77c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1245,9 +1245,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "518915b97df115dd36109bfa429a48b8f737bd05508cf9588977b599648926d2" dependencies = [ "bitflags", "cfg-if", @@ -1277,9 +1277,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.77" +version = "0.9.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a" +checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b" dependencies = [ "autocfg 1.1.0", "cc", From adc77985d7fc69cea354c4aaa6a833c2fc50ab7d Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sat, 25 Mar 2023 17:22:16 -0600 Subject: [PATCH 138/361] broaden the definition of alpha_char! (#1749, #1515, #1591) --- src/parser/macros.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/parser/macros.rs b/src/parser/macros.rs index 326772cf..27b106fc 100644 --- a/src/parser/macros.rs +++ b/src/parser/macros.rs @@ -7,7 +7,13 @@ macro_rules! char_class { #[macro_export] macro_rules! alpha_char { ($c: expr) => { - $c.is_alphabetic() || $c == '_' + (!$c.is_numeric() && + !$c.is_whitespace() && + !$c.is_control() && + !$crate::graphic_token_char!($c) && + !$crate::layout_char!($c) && + !$crate::meta_char!($c) && + !$crate::solo_char!($c)) || $c == '_' }; } From 8ab1155fc56c8dbc826fba374c844bdacf0653a5 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 30 Mar 2023 23:29:49 +0200 Subject: [PATCH 139/361] ENHANCED: use call_residue_vars/2 to show all pending constraints Example: ?- freeze(_, false). freeze:freeze(_A,false). This was originally added in 04ba9bc11af31780cd8fa259d78d151262c3c756, then reverted, and is now restored. --- src/toplevel.pl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/toplevel.pl b/src/toplevel.pl index 8caea7ba..0bab4415 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -1,6 +1,7 @@ :- module('$toplevel', [argv/1, copy_term/3]). +:- use_module(library(atts), [call_residue_vars/2]). :- use_module(library(charsio)). :- use_module(library(error)). :- use_module(library(files)). @@ -180,8 +181,8 @@ submit_query_and_print_results_(Term, VarList) :- '$get_b_value'(B), bb_put('$report_all', false), bb_put('$report_n_more', 0), - call(user:Term), - write_eqs_and_read_input(B, VarList), + atts:call_residue_vars(user:Term, AttrVars), + write_eqs_and_read_input(B, VarList, AttrVars), !. submit_query_and_print_results_(_, _) :- ( bb_get('$answer_count', 0) -> @@ -286,11 +287,10 @@ trailing_period_is_ambiguous(Value) :- term_variables_under_max_depth(Term, MaxDepth, Vars) :- '$term_variables_under_max_depth'(Term, MaxDepth, Vars). -write_eqs_and_read_input(B, VarList) :- +write_eqs_and_read_input(B, VarList, AttrVars) :- gather_query_vars(VarList, OrigVars), % one layer of depth added for (=/2) functor '$term_variables_under_max_depth'(OrigVars, 22, Vars0), - '$term_attributed_variables'(VarList, AttrVars), '$project_atts':project_attributes(Vars0, AttrVars), copy_term(AttrVars, AttrVars, AttrGoals), term_variables(AttrGoals, AttrGoalVars), From 3df0806017ec8de173bedb2ca63ab089c39dfe81 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 1 Apr 2023 10:50:32 +0200 Subject: [PATCH 140/361] change "run" to "build", since "run" leads to a Scryer prompt which can be unexpected --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ed002d8d..97830007 100644 --- a/README.md +++ b/README.md @@ -115,12 +115,13 @@ distribution should be uninstalled from your system before rustup is used. Currently the only way to install the latest version of Scryer is to -clone directly from this git repository, which can be done as follows: +clone directly from this git repository, and compile the system. This +can be done as follows: ``` $> git clone https://github.com/mthom/scryer-prolog $> cd scryer-prolog -$> cargo run [--release] +$> cargo build [--release] ``` The optional `--release` flag will perform various optimizations, From b79d8732ea9da51ec76520193ae5056a28191f29 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 1 Apr 2023 10:59:32 +0200 Subject: [PATCH 141/361] use the release flag so that the instructions can be used verbatim Also, the location of the executable depends on this flag. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 97830007..a91ce449 100644 --- a/README.md +++ b/README.md @@ -121,11 +121,11 @@ can be done as follows: ``` $> git clone https://github.com/mthom/scryer-prolog $> cd scryer-prolog -$> cargo build [--release] +$> cargo build --release ``` -The optional `--release` flag will perform various optimizations, -producing a faster executable. +The `--release` flag performs various optimizations, producing a +faster executable. On Windows, Scryer Prolog is easier to build inside a [MSYS2](https://www.msys2.org/) environment as some crates may require native C compilation. However, From d6ac125425b3b26f6be1136e79ed8428edc084da Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 1 Apr 2023 10:42:13 +0200 Subject: [PATCH 142/361] DOC: explain location of scryer-prolog after compilation This question was recently raised on the #scryer IRC channel. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index a91ce449..7cf8c129 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,9 @@ $> cargo build --release The `--release` flag performs various optimizations, producing a faster executable. +After compilation, the executable `scryer-prolog` is available in the +directory `target/release` and can be invoked to run the system. + On Windows, Scryer Prolog is easier to build inside a [MSYS2](https://www.msys2.org/) environment as some crates may require native C compilation. However, the resulting binary does not need MSYS2 to run. When executing Scryer in a shell, it is recommended to use a more advanced shell than mintty (the default MSYS2 shell). The [Windows Terminal](https://github.com/microsoft/terminal) works correctly. From b87fe1e21f2c509d85f9c2510146a7031c33f746 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 1 Apr 2023 10:42:58 +0200 Subject: [PATCH 143/361] DOC: link to "Indexing dif/2" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7cf8c129..12ec6934 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Extend Scryer Prolog to include the following, among other features: - [x] Support for `attribute_goals/2` and `project_attributes/2` - [x] `call_residue_vars/2` - [x] `if_/3` and related predicates, following the developments of the - paper "Indexing `dif/2`". + paper "[Indexing `dif/2`](https://arxiv.org/abs/1607.01590)". - [x] All-solutions predicates (`findall/{3,4}`, `bagof/3`, `setof/3`, `forall/2`). - [x] Clause creation and destruction (`asserta/1`, `assertz/1`, `retract/1`, `abolish/1`) with logical update semantics. From 8792ee438c3a6d1319ec06a65460b76396e42e64 Mon Sep 17 00:00:00 2001 From: infogulch Date: Sat, 1 Apr 2023 18:11:35 -0500 Subject: [PATCH 144/361] Add cache step to test workflow --- .github/workflows/test.yml | 110 +++++++++++++++++++------------------ Cargo.toml | 6 +- README.md | 6 +- scryer-prolog.wxs | 13 ++--- 4 files changed, 67 insertions(+), 68 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index edcd1993..aeffd7e9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,66 +6,68 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-20.04, macos-10.15] - rust-version: [stable, beta] - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{ matrix.rust-version }} - override: true - - name: Build lib - uses: actions-rs/cargo@v1 - with: - command: rustc - args: --verbose --lib -- -D warnings - - name: Build bin - uses: actions-rs/cargo@v1 - with: - command: rustc - args: --verbose --bin scryer-prolog -- -D warnings - - name: Test - uses: actions-rs/cargo@v1 - with: - command: test - args: --verbose --all - - name: Num tests - uses: actions-rs/cargo@v1 - continue-on-error: true - with: - command: test - args: --verbose --all --no-default-features --features num - msrv: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-20.04, macos-10.15] - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - name: Install cargo-msrv - uses: baptiste0928/cargo-install@v1.1.0 - with: - crate: cargo-msrv - - name: Verify MSRV - run: cargo msrv --verify - windows: - runs-on: windows-latest + include: + - { os: windows-latest, rust-version: stable, shell: 'msys2 {0}' } + - { os: macos-10.15, rust-version: stable, shell: bash } + - { os: ubuntu-20.04, rust-version: stable, shell: bash } + - { os: ubuntu-20.04, rust-version: 1.63, shell: bash } + - { os: ubuntu-20.04, rust-version: beta, shell: bash } + - { os: ubuntu-20.04, rust-version: nightly, shell: bash } defaults: run: - shell: msys2 {0} + shell: ${{ matrix.shell }} + outputs: + os: ${{ matrix.os }} + rust-version: ${{ matrix.rust-version }} steps: - - name: Setup MSYS2 - uses: msys2/setup-msys2@v2 + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + if: "!startsWith(matrix.os, 'windows')" + id: toolchain + with: + toolchain: ${{ matrix.rust-version }} + components: clippy, rustfmt + - uses: msys2/setup-msys2@v2 + if: startsWith(matrix.os,'windows') with: update: true install: >- base-devel mingw-w64-x86_64-rust - - name: Checkout sources - uses: actions/checkout@v3 - - name: Test on Windows + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ matrix.os }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }} + + # Build and test. + - name: Build library + run: cargo rustc --verbose --lib -- -D warnings + - name: Test run: cargo test --verbose --all + + # Only run formatting & style check on one job to not spam warnings. + - name: Check formatting + if: startsWith(matrix.os,'ubuntu') && matrix.rust-version == 'stable' + run: cargo fmt --check || echo "::warning ::cargo fmt found some formatting changes that may improve readability" + - name: Check clippy + if: startsWith(matrix.os,'ubuntu') && matrix.rust-version == 'stable' + run: cargo clippy || echo "::warning ::cargo clippy found some code style changes that may be more idiomatic" + + # On stable rust builds, build a binary and publish as a github actions + # artifact. These binaries could be useful for testing the pipeline but + # are only retained by github for 90 days. + # TODO: Check that they actually work. + - name: Build release binary + if: matrix.rust-version == 'stable' + run: cargo rustc --verbose --bin scryer-prolog --release -- -D warnings + - name: Publish artifact + if: matrix.rust-version == 'stable' + uses: actions/upload-artifact@v3 + with: + path: target/release/scryer-prolog* + name: scryer-prolog_${{ matrix.os }} diff --git a/Cargo.toml b/Cargo.toml index 9e2ca883..975ba64f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ license = "BSD-3-Clause" keywords = ["prolog", "prolog-interpreter", "prolog-system"] categories = ["command-line-utilities"] build = "build/main.rs" -rust-version = "1.61" +rust-version = "1.63" [features] default = ["rug"] @@ -41,7 +41,7 @@ libc = "0.2.62" modular-bitfield = "0.11.2" ctrlc = "3.2.2" ordered-float = "2.6.0" -phf = { version = "0.9", features = ["macros"] } +phf = { version = "0.9", features = ["macros"] } ref_thread_local = "0.0.0" rug = { version = "1.15.0", optional = true } rustyline = "9.0.0" @@ -49,7 +49,7 @@ ring = "0.16.13" ripemd160 = "0.8.0" sha3 = "0.8.2" blake2 = "0.8.1" -crrl ="0.2.0" +crrl = "0.2.0" native-tls = "0.2.4" chrono = "0.4.11" select = "0.4.3" diff --git a/README.md b/README.md index 12ec6934..93d7a43c 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Extend Scryer Prolog to include the following, among other features: characters, using a packed internal representation. - [x] clp(B) and clp(ℤ) as builtin libraries. - [x] Streams and predicates for stream control. - - [x] A simple sockets library representing TCP connections as streams. + - [x] A simple sockets library representing TCP connections as streams. - [x] Incremental compilation and loading process, newly written, primarily in Prolog. - [ ] Improvements to the WAM compiler and heap representation: @@ -131,7 +131,7 @@ After compilation, the executable `scryer-prolog` is available in the directory `target/release` and can be invoked to run the system. On Windows, Scryer Prolog is easier to build inside a [MSYS2](https://www.msys2.org/) -environment as some crates may require native C compilation. However, +environment as some crates may require native C compilation. However, the resulting binary does not need MSYS2 to run. When executing Scryer in a shell, it is recommended to use a more advanced shell than mintty (the default MSYS2 shell). The [Windows Terminal](https://github.com/microsoft/terminal) works correctly. To build a Windows Installer, you'll need first Scryer Prolog compiled in release mode, then, with WiX Toolset installed, execute: @@ -141,7 +141,7 @@ light.exe scryer-prolog.wixobj ``` It will generate a very basic MSI file which installs the main executable and a shortcut in the Start Menu. It can be installed with a double-click. To uninstall, go to the Control Panel and uninstall as usual. -Scryer Prolog must be built with **Rust 1.61 and up**. +Scryer Prolog must be built with **Rust 1.63 and up**. ### Docker Install diff --git a/scryer-prolog.wxs b/scryer-prolog.wxs index 53b21242..b69b1dff 100644 --- a/scryer-prolog.wxs +++ b/scryer-prolog.wxs @@ -4,7 +4,6 @@ - @@ -14,18 +13,16 @@ - + - - - + + + - - - + \ No newline at end of file From fe2760549731f63ba2fb01558f20d192844ea99b Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 1 Apr 2023 20:29:02 +0200 Subject: [PATCH 145/361] FIXED: number_chars(N, "0' "), addressing #1580. There may be a more elegant way to solve this. --- src/machine/system_calls.rs | 45 +++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index a84b78c7..70dd649f 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -834,19 +834,40 @@ impl MachineState { ) -> CallResult { let nx = self.store(self.deref(self.registers[2])); - if let Some(c) = string.chars().last() { - if layout_char!(c) { - let (line_num, col_num) = string.chars().fold((0, 0), |(line_num, col_num), c| { - if new_line_char!(c) { - (1 + line_num, 0) - } else { - (line_num, col_num + 1) - } - }); - let err = ParserError::UnexpectedChar(c, line_num, col_num); - let err = self.syntax_error(err); + let mut charcode_space = false; + let mut cs = string.chars(); - return Err(self.error_form(err, stub_gen())); + loop { + let c = cs.next(); + + if c == None { + break; + } + + if c == Some('0') + && cs.next() == Some('\'') + && cs.next() == Some(' ') + && cs.next() == None { + charcode_space = true; + break; + } + } + + if !charcode_space { + if let Some(c) = string.chars().last() { + if layout_char!(c) { + let (line_num, col_num) = string.chars().fold((0, 0), |(line_num, col_num), c| { + if new_line_char!(c) { + (1 + line_num, 0) + } else { + (line_num, col_num + 1) + } + }); + let err = ParserError::UnexpectedChar(c, line_num, col_num); + let err = self.syntax_error(err); + + return Err(self.error_form(err, stub_gen())); + } } } From ebf8091dbac4bd11a508479a16c172fcf9990ddb Mon Sep 17 00:00:00 2001 From: infogulch Date: Sun, 2 Apr 2023 14:47:52 -0500 Subject: [PATCH 146/361] Test workflow cleanup; switch to macos-11 --- .github/workflows/test.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index aeffd7e9..4767f7b3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: matrix: include: - { os: windows-latest, rust-version: stable, shell: 'msys2 {0}' } - - { os: macos-10.15, rust-version: stable, shell: bash } + - { os: macos-11, rust-version: stable, shell: bash } - { os: ubuntu-20.04, rust-version: stable, shell: bash } - { os: ubuntu-20.04, rust-version: 1.63, shell: bash } - { os: ubuntu-20.04, rust-version: beta, shell: bash } @@ -16,9 +16,6 @@ jobs: defaults: run: shell: ${{ matrix.shell }} - outputs: - os: ${{ matrix.os }} - rust-version: ${{ matrix.rust-version }} steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master @@ -56,7 +53,7 @@ jobs: run: cargo fmt --check || echo "::warning ::cargo fmt found some formatting changes that may improve readability" - name: Check clippy if: startsWith(matrix.os,'ubuntu') && matrix.rust-version == 'stable' - run: cargo clippy || echo "::warning ::cargo clippy found some code style changes that may be more idiomatic" + run: cargo clippy --no-deps || echo "::warning ::cargo clippy found some code style changes that may be more idiomatic" # On stable rust builds, build a binary and publish as a github actions # artifact. These binaries could be useful for testing the pipeline but From 9de8456c7fda15f527164460bd3bb32d0e74ebd2 Mon Sep 17 00:00:00 2001 From: infogulch Date: Sun, 2 Apr 2023 15:47:02 -0500 Subject: [PATCH 147/361] Add logtalk test suite --- .github/workflows/test.yml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4767f7b3..2bdfe64d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,7 +47,7 @@ jobs: - name: Test run: cargo test --verbose --all - # Only run formatting & style check on one job to not spam warnings. + # Run code formatting and style checks on one job to not spam warnings. - name: Check formatting if: startsWith(matrix.os,'ubuntu') && matrix.rust-version == 'stable' run: cargo fmt --check || echo "::warning ::cargo fmt found some formatting changes that may improve readability" @@ -61,10 +61,26 @@ jobs: # TODO: Check that they actually work. - name: Build release binary if: matrix.rust-version == 'stable' - run: cargo rustc --verbose --bin scryer-prolog --release -- -D warnings + run: | + cargo rustc --verbose --bin scryer-prolog --release -- -D warnings + echo "$PWD/target/release" >> $GITHUB_PATH - name: Publish artifact if: matrix.rust-version == 'stable' uses: actions/upload-artifact@v3 with: path: target/release/scryer-prolog* name: scryer-prolog_${{ matrix.os }} + + # Run iso compliance tests. + - name: Install Logtalk + if: startsWith(matrix.os,'ubuntu') && matrix.rust-version == 'stable' + uses: logtalk-actions/setup-logtalk@master + with: + logtalk-version: git + logtalk-tool-dependencies: false + - name: Run compliance test suite + if: startsWith(matrix.os,'ubuntu') && matrix.rust-version == 'stable' + working-directory: /home/runner/logtalk/tests/prolog + run: | + scryer-prolog -v + logtalk_tester -p scryer -g "set_logtalk_flag(clean,off)" -w -t 360 From a197bb68154fc19b9d895661d6700cd75cd981bb Mon Sep 17 00:00:00 2001 From: infogulch Date: Thu, 6 Apr 2023 09:40:03 -0500 Subject: [PATCH 148/361] Allow the test suite to fail without failing the build --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2bdfe64d..1f883ec7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -80,6 +80,7 @@ jobs: logtalk-tool-dependencies: false - name: Run compliance test suite if: startsWith(matrix.os,'ubuntu') && matrix.rust-version == 'stable' + continue-on-error: true working-directory: /home/runner/logtalk/tests/prolog run: | scryer-prolog -v From fd1e902492e3b490320f582ace1fb2860c4310f0 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 11 Apr 2023 21:14:18 +0200 Subject: [PATCH 149/361] do not leave an attribute when (re-)enabling a queue --- src/lib/clpz.pl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index acf4738f..588c3b37 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -147,7 +147,7 @@ clpz_gcc_num/1, clpz_gcc_occurred/1, queue/2, - enabled/1. + disabled/0. :- dynamic(monotonic/0). :- dynamic(clpz_equal_/2). @@ -2684,7 +2684,7 @@ clear_queue(queue(Goals,Fast,Slow,Aux)) :- put_atts(Goals, -queue(_,_)), put_atts(Fast, -queue(_,_)), put_atts(Slow, -queue(_,_)), - put_atts(Aux, -enabled(_)). + put_atts(Aux, -disabled). collect_goal(Qs) --> collect_arg(Qs, 1). collect_fast(Qs) --> collect_arg(Qs, 2). @@ -4212,9 +4212,9 @@ queue_get_arg_(Queue, Which, Element) :- ; put_atts(Arg, +queue(Elements,Tail)) ). -queue_enabled --> state(queue(_,_,_,Aux)), { \+ get_atts(Aux, +enabled(false)) }. -disable_queue --> state(queue(_,_,_,Aux)), { put_atts(Aux, +enabled(false)) }. -enable_queue --> state(queue(_,_,_,Aux)), { put_atts(Aux, +enabled(true)) }. +queue_enabled --> state(queue(_,_,_,Aux)), { \+ get_atts(Aux, disabled) }. +disable_queue --> state(queue(_,_,_,Aux)), { put_atts(Aux, +disabled) }. +enable_queue --> state(queue(_,_,_,Aux)), { put_atts(Aux, -disabled) }. portray_propagator(propagator(P,_), F) :- functor(P, F, _). From 94efb9ffe33fb059d4529e04372f4993b4503f88 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 11 Apr 2023 21:16:02 +0200 Subject: [PATCH 150/361] remove no longer needed clpz_relation attributes --- src/lib/clpz.pl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 588c3b37..ab042152 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -4490,14 +4490,20 @@ run_propagator(pgeq(A,B), MState) --> run_propagator(rel_tuple(R, Tuple), MState) --> { get_attr(R, clpz_relation, Relation) }, - ( { ground(Tuple) } -> kill(MState), { memberchk(Tuple, Relation) } + ( { ground(Tuple) } -> + kill(MState), + { del_attr(R, clpz_relation), + memberchk(Tuple, Relation) } ; { relation_unifiable(Relation, Tuple, Us, false, Changed), Us = [_|_] }, ( { Tuple = [First,Second], ( ground(First) ; ground(Second) ) } -> kill(MState) ; [] ), - ( { Us = [Single] } -> kill(MState), Single = Tuple + ( { Us = [Single] } -> + kill(MState), + { del_attr(R, clpz_relation) }, + Single = Tuple ; { Changed } -> { put_attr(R, clpz_relation, Us), disable_queue }, From be45672e223a86c4a85919a1be5c155281e7a566 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 11 Apr 2023 21:19:10 +0200 Subject: [PATCH 151/361] actually disable and reenable the queue --- src/lib/clpz.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index ab042152..2c412ac3 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -4505,10 +4505,10 @@ run_propagator(rel_tuple(R, Tuple), MState) --> { del_attr(R, clpz_relation) }, Single = Tuple ; { Changed } -> - { put_attr(R, clpz_relation, Us), - disable_queue }, + { put_attr(R, clpz_relation, Us) }, + disable_queue, tuple_domain(Tuple, Us), - { enable_queue } + enable_queue ; [] ) ). From f08f539768bf21e7e63209fbbc1d7a263b67dcb2 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 11 Apr 2023 21:24:34 +0200 Subject: [PATCH 152/361] do not create attributed variables for ground tuples --- src/lib/clpz.pl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 2c412ac3..daed1471 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -4369,9 +4369,11 @@ tuple_domain([T|Ts], Relation0) --> tuple_domain(Ts, Relation1). tuple_freeze(Tuple, Relation) :- - put_attr(R, clpz_relation, Relation), - make_propagator(rel_tuple(R, Tuple), Prop), - tuple_freeze_(Tuple, Prop). + ( ground(Tuple) -> true + ; put_attr(R, clpz_relation, Relation), + make_propagator(rel_tuple(R, Tuple), Prop), + tuple_freeze_(Tuple, Prop) + ). tuple_freeze_([], _). tuple_freeze_([T|Ts], Prop) :- From 5dce7d907572f7646a26214f129c9200340939f7 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 11 Apr 2023 22:31:09 +0200 Subject: [PATCH 153/361] FIXED: enforce equality also for ground elements in tuples Example: ?- tuples_in([[A,B]], [[1,2],[3,4]]), tuples_in([[A,B]], [[3,2]]). false. See https://github.com/SWI-Prolog/swipl-devel/issues/1160. --- src/lib/clpz.pl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index daed1471..0e42bd92 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -4357,14 +4357,14 @@ list_first_rest([L|Ls], L, Ls). tuple_domain([], _) --> []. tuple_domain([T|Ts], Relation0) --> { maplist(list_first_rest, Relation0, Firsts, Relation1) }, - ( var(T) -> - ( Firsts = [Unique] -> T = Unique - ; { list_to_domain(Firsts, FDom), + ( Firsts = [Unique] -> T = Unique + ; ( var(T) -> + { list_to_domain(Firsts, FDom), fd_get(T, TDom, TPs), domains_intersection(TDom, FDom, TDom1) }, fd_put(T, TDom1, TPs) + ; [] ) - ; [] ), tuple_domain(Ts, Relation1). From 82200a21eb1baee42df5f0c12886635e9e000a5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Apr 2023 17:03:50 +0000 Subject: [PATCH 154/361] Bump h2 from 0.3.15 to 0.3.17 Bumps [h2](https://github.com/hyperium/h2) from 0.3.15 to 0.3.17. - [Release notes](https://github.com/hyperium/h2/releases) - [Changelog](https://github.com/hyperium/h2/blob/master/CHANGELOG.md) - [Commits](https://github.com/hyperium/h2/compare/v0.3.15...v0.3.17) --- updated-dependencies: - dependency-name: h2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad74c77c..46c5a14f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -708,9 +708,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" dependencies = [ "bytes", "fnv", From 5763a4b9df76a097d1b0c045cefe72800d9177e8 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 13 Apr 2023 23:38:55 +0200 Subject: [PATCH 155/361] FIXED: propagation for ground tuples Example: ?- tuples_in([[A,A]],[[0,1],[2,0]]). false. See https://github.com/triska/clpz/issues/22. --- src/lib/clpz.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 0e42bd92..b6ad08b3 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -4369,7 +4369,7 @@ tuple_domain([T|Ts], Relation0) --> tuple_domain(Ts, Relation1). tuple_freeze(Tuple, Relation) :- - ( ground(Tuple) -> true + ( ground(Tuple) -> memberchk(Tuple, Relation) ; put_attr(R, clpz_relation, Relation), make_propagator(rel_tuple(R, Tuple), Prop), tuple_freeze_(Tuple, Prop) From 5a3e2899dd7d480c195eaf4ad77eac3a9f405a5f Mon Sep 17 00:00:00 2001 From: infogulch Date: Wed, 12 Apr 2023 19:15:06 -0500 Subject: [PATCH 156/361] Refactor CI Workflow * Rename the workflow from Test to CI, since it does more than tests * Run logtalk tests in a separate job to improve isolation * Publish all xunit/junit test files as build artifacts to be consumed by a separate publishing workflow. * Add "job summary" feature to show a formatted summary of the test results on the job summary status page * Run the CI job once every Wed to ensure that there are always some recent builds on master that haven't expired. --- .github/workflows/ci.yml | 141 +++++++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 87 ----------------------- 2 files changed, 141 insertions(+), 87 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..5bf7a5a7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,141 @@ +name: CI +on: + push: + branches: [master] + pull_request: + schedule: + - cron: '0 0 * * 3' # At 12:00 AM, only on Wednesday + +jobs: + build-test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - { os: windows-latest, rust-version: stable, shell: 'msys2 {0}' } + - { os: macos-11, rust-version: stable, shell: bash } + - { os: ubuntu-20.04, rust-version: stable, shell: bash, extra: true } + - { os: ubuntu-20.04, rust-version: 1.63, shell: bash } + - { os: ubuntu-20.04, rust-version: beta, shell: bash } + - { os: ubuntu-20.04, rust-version: nightly, shell: bash } + defaults: + run: + shell: ${{ matrix.shell }} + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + if: "!contains(matrix.os,'windows')" + id: toolchain + with: + toolchain: ${{ matrix.rust-version }} + components: clippy, rustfmt + - uses: msys2/setup-msys2@v2 + if: contains(matrix.os,'windows') + with: + update: true + install: >- + base-devel + mingw-w64-x86_64-rust + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ matrix.os }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }} + + # Build and test. + - name: Build library + run: cargo rustc --verbose --lib -- -D warnings + - name: Test + if: "!matrix.extra" + run: cargo test --all --verbose + + # Extra steps + - name: Test and report + if: matrix.extra + run: | + cargo install cargo2junit + cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml + - name: Publish cargo test results artifact + if: matrix.extra + uses: actions/upload-artifact@v3 + with: + name: cargo-test-results + path: cargo_test_results.xml + - name: Publish cargo test summary + if: matrix.extra + uses: EnricoMi/publish-unit-test-result-action/composite@branch-publish-summary-on-fork + with: + check_name: Cargo test summary + files: cargo_test_results.xml + fail_on: nothing + comment_mode: off + - name: Check formatting + if: matrix.extra + run: cargo fmt --check || echo "::warning ::cargo fmt found some formatting changes that may improve readability" + - name: Check clippy + if: matrix.extra + run: cargo clippy --no-deps || echo "::warning ::cargo clippy found some code style changes that may be more idiomatic" + + # On stable rust builds, build a binary and publish as a github actions + # artifact. These binaries could be useful for testing the pipeline but + # are only retained by github for 90 days. + - name: Build release binary + if: contains(matrix.rust-version,'stable') + run: | + cargo rustc --verbose --bin scryer-prolog --release -- -D warnings + echo "$PWD/target/release" >> $GITHUB_PATH + - name: Publish release binary artifact + if: contains(matrix.rust-version,'stable') + uses: actions/upload-artifact@v3 + with: + path: target/release/scryer-prolog* + name: scryer-prolog_${{ matrix.os }} + + logtalk-test: + runs-on: ubuntu-20.04 + needs: [build-test] + steps: + - uses: actions/download-artifact@v3 + with: + name: scryer-prolog_ubuntu-20.04 + - run: | + chmod +x scryer-prolog + echo "$PWD" >> "$GITHUB_PATH" + - name: Install Logtalk + uses: logtalk-actions/setup-logtalk@master + with: + logtalk-version: git + logtalk-tool-dependencies: false + + # Run logtalk tests. + - name: Run Logtalk's prolog compliance test suite + working-directory: ${{ env.LOGTALKUSER }}/tests/prolog/ + run: | + pwd + scryerlgt -g '{ack(tester)},halt.' + logtalk_tester -p scryer -g "set_logtalk_flag(clean,off)" -w -t 360 \ + -f xunit \ + -s "$LOGTALKUSER/tests/prolog" \ + || echo "::warning ::logtalk compliance suite failed" + # -u "https://github.com/LogtalkDotOrg/logtalk3/tree/$LOGTALK_GIT_HASH/tests/prolog/" \ + - name: Publish Logtalk test logs + uses: actions/upload-artifact@v3 + with: + name: logtalk-test-logs + path: '${{ env.LOGTALKUSER }}/tests/prolog/logtalk_tester_logs' + - name: Publish Logtalk test results artifact + uses: actions/upload-artifact@v3 + with: + name: logtalk-test-results + path: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml' + - name: Publish Logtalk test summary + uses: EnricoMi/publish-unit-test-result-action/composite@branch-publish-summary-on-fork + with: + check_name: Logtalk test summary + files: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml' + fail_on: nothing + comment_mode: off diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 1f883ec7..00000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Test -on: [push, pull_request] - -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - include: - - { os: windows-latest, rust-version: stable, shell: 'msys2 {0}' } - - { os: macos-11, rust-version: stable, shell: bash } - - { os: ubuntu-20.04, rust-version: stable, shell: bash } - - { os: ubuntu-20.04, rust-version: 1.63, shell: bash } - - { os: ubuntu-20.04, rust-version: beta, shell: bash } - - { os: ubuntu-20.04, rust-version: nightly, shell: bash } - defaults: - run: - shell: ${{ matrix.shell }} - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@master - if: "!startsWith(matrix.os, 'windows')" - id: toolchain - with: - toolchain: ${{ matrix.rust-version }} - components: clippy, rustfmt - - uses: msys2/setup-msys2@v2 - if: startsWith(matrix.os,'windows') - with: - update: true - install: >- - base-devel - mingw-w64-x86_64-rust - - uses: actions/cache@v3 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ matrix.os }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }} - - # Build and test. - - name: Build library - run: cargo rustc --verbose --lib -- -D warnings - - name: Test - run: cargo test --verbose --all - - # Run code formatting and style checks on one job to not spam warnings. - - name: Check formatting - if: startsWith(matrix.os,'ubuntu') && matrix.rust-version == 'stable' - run: cargo fmt --check || echo "::warning ::cargo fmt found some formatting changes that may improve readability" - - name: Check clippy - if: startsWith(matrix.os,'ubuntu') && matrix.rust-version == 'stable' - run: cargo clippy --no-deps || echo "::warning ::cargo clippy found some code style changes that may be more idiomatic" - - # On stable rust builds, build a binary and publish as a github actions - # artifact. These binaries could be useful for testing the pipeline but - # are only retained by github for 90 days. - # TODO: Check that they actually work. - - name: Build release binary - if: matrix.rust-version == 'stable' - run: | - cargo rustc --verbose --bin scryer-prolog --release -- -D warnings - echo "$PWD/target/release" >> $GITHUB_PATH - - name: Publish artifact - if: matrix.rust-version == 'stable' - uses: actions/upload-artifact@v3 - with: - path: target/release/scryer-prolog* - name: scryer-prolog_${{ matrix.os }} - - # Run iso compliance tests. - - name: Install Logtalk - if: startsWith(matrix.os,'ubuntu') && matrix.rust-version == 'stable' - uses: logtalk-actions/setup-logtalk@master - with: - logtalk-version: git - logtalk-tool-dependencies: false - - name: Run compliance test suite - if: startsWith(matrix.os,'ubuntu') && matrix.rust-version == 'stable' - continue-on-error: true - working-directory: /home/runner/logtalk/tests/prolog - run: | - scryer-prolog -v - logtalk_tester -p scryer -g "set_logtalk_flag(clean,off)" -w -t 360 From 4b882c465c0169e3f11420db6a44751b0468ae75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Tue, 18 Apr 2023 18:48:59 +0200 Subject: [PATCH 157/361] library(charsio): add to_upper and to_lower --- src/lib/charsio.pl | 27 +++++++ src/machine/system_calls.rs | 149 ++++++++++++++++++++++-------------- 2 files changed, 117 insertions(+), 59 deletions(-) diff --git a/src/lib/charsio.pl b/src/lib/charsio.pl index 6b43cc7d..0f19a8db 100644 --- a/src/lib/charsio.pl +++ b/src/lib/charsio.pl @@ -104,7 +104,32 @@ extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :- % - `symbolic_control` % - `symbolic_hexadecimal` % - `upper` +% - `to_lower(Lower)` +% - `to_upper(Upper)` % - `whitespace` +% +% An example: +% +% ``` +% ?- char_type(a, Type). +% Type = alnum +% ; Type = alpha +% ; Type = alphabetic +% ; Type = alphanumeric +% ; Type = ascii +% ; Type = ascii_graphic +% ; Type = hexadecimal_digit +% ; Type = lower +% ; Type = octet +% ; Type = prolog +% ; Type = symbolic_control +% ; Type = to_lower("a") +% ; Type = to_upper("A") +% ; false. +% ``` +% +% Note that uppercase and lowercase transformations use a string. This is because +% some characters do not map 1:1 between lowercase and uppercase. char_type(Char, Type) :- must_be(character, Char), ( ground(Type) -> @@ -142,6 +167,8 @@ ctype(sign). ctype(solo). ctype(symbolic_control). ctype(symbolic_hexadecimal). +ctype(to_lower(_)). +ctype(to_upper(_)). ctype(upper). ctype(whitespace). diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 70dd649f..21f3057c 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -2723,69 +2723,100 @@ impl Machine { unreachable!() } ); - - let chars = cell_as_atom!(a2); + self.machine_st.fail = true; // This predicate fails by default. - macro_rules! macro_check { - ($id:ident, $name:expr) => { - if $id!(c) && chars == $name { - self.machine_st.fail = false; - return; - } - }; - } + read_heap_cell!(a2, + (HeapCellValueTag::Atom, (chars, _arity)) => { + macro_rules! macro_check { + ($id:ident, $name:expr) => { + if $id!(c) && chars == $name { + self.machine_st.fail = false; + return; + } + }; + } - macro_rules! method_check { - ($id:ident, $name:expr) => { - if c.$id() && chars == $name { - self.machine_st.fail = false; - return; - } - }; - } + macro_rules! method_check { + ($id:ident, $name:expr) => { + if c.$id() && chars == $name { + self.machine_st.fail = false; + return; + } + }; + } - macro_check!(alpha_char, atom!("alpha")); - method_check!(is_alphabetic, atom!("alphabetic")); - method_check!(is_alphanumeric, atom!("alphanumeric")); - macro_check!(alpha_numeric_char, atom!("alnum")); - method_check!(is_ascii, atom!("ascii")); - method_check!(is_ascii_punctuation, atom!("ascii_ponctuaction")); - method_check!(is_ascii_graphic, atom!("ascii_graphic")); - // macro_check!(backslash_char, atom!("backslash")); - // macro_check!(back_quote_char, atom!("back_quote")); - macro_check!(binary_digit_char, atom!("binary_digit")); - // macro_check!(capital_letter_char, atom!("upper")); - // macro_check!(comment_1_char, "comment_1"); - // macro_check!(comment_2_char, "comment_2"); - method_check!(is_control, atom!("control")); - // macro_check!(cut_char, atom!("cut")); - macro_check!(decimal_digit_char, atom!("decimal_digit")); - // macro_check!(decimal_point_char, atom!("decimal_point")); - // macro_check!(double_quote_char, atom!("double_quote")); - macro_check!(exponent_char, atom!("exponent")); - macro_check!(graphic_char, atom!("graphic")); - macro_check!(graphic_token_char, atom!("graphic_token")); - macro_check!(hexadecimal_digit_char, atom!("hexadecimal_digit")); - macro_check!(layout_char, atom!("layout")); - method_check!(is_lowercase, atom!("lower")); - macro_check!(meta_char, atom!("meta")); - // macro_check!(new_line_char, atom!("new_line")); - method_check!(is_numeric, atom!("numeric")); - macro_check!(octal_digit_char, atom!("octal_digit")); - macro_check!(octet_char, atom!("octet")); - macro_check!(prolog_char, atom!("prolog")); - // macro_check!(semicolon_char, atom!("semicolon")); - macro_check!(sign_char, atom!("sign")); - // macro_check!(single_quote_char, atom!("single_quote")); - // macro_check!(small_letter_char, atom!("lower")); - macro_check!(solo_char, atom!("solo")); - // macro_check!(space_char, atom!("space")); - macro_check!(symbolic_hexadecimal_char, atom!("symbolic_hexadecimal")); - macro_check!(symbolic_control_char, atom!("symbolic_control")); - method_check!(is_uppercase, atom!("upper")); - // macro_check!(variable_indicator_char, atom!("variable_indicator")); - method_check!(is_whitespace, atom!("whitespace")); + macro_check!(alpha_char, atom!("alpha")); + method_check!(is_alphabetic, atom!("alphabetic")); + method_check!(is_alphanumeric, atom!("alphanumeric")); + macro_check!(alpha_numeric_char, atom!("alnum")); + method_check!(is_ascii, atom!("ascii")); + method_check!(is_ascii_punctuation, atom!("ascii_ponctuaction")); + method_check!(is_ascii_graphic, atom!("ascii_graphic")); + // macro_check!(backslash_char, atom!("backslash")); + // macro_check!(back_quote_char, atom!("back_quote")); + macro_check!(binary_digit_char, atom!("binary_digit")); + // macro_check!(capital_letter_char, atom!("upper")); + // macro_check!(comment_1_char, "comment_1"); + // macro_check!(comment_2_char, "comment_2"); + method_check!(is_control, atom!("control")); + // macro_check!(cut_char, atom!("cut")); + macro_check!(decimal_digit_char, atom!("decimal_digit")); + // macro_check!(decimal_point_char, atom!("decimal_point")); + // macro_check!(double_quote_char, atom!("double_quote")); + macro_check!(exponent_char, atom!("exponent")); + macro_check!(graphic_char, atom!("graphic")); + macro_check!(graphic_token_char, atom!("graphic_token")); + macro_check!(hexadecimal_digit_char, atom!("hexadecimal_digit")); + macro_check!(layout_char, atom!("layout")); + method_check!(is_lowercase, atom!("lower")); + macro_check!(meta_char, atom!("meta")); + // macro_check!(new_line_char, atom!("new_line")); + method_check!(is_numeric, atom!("numeric")); + macro_check!(octal_digit_char, atom!("octal_digit")); + macro_check!(octet_char, atom!("octet")); + macro_check!(prolog_char, atom!("prolog")); + // macro_check!(semicolon_char, atom!("semicolon")); + macro_check!(sign_char, atom!("sign")); + // macro_check!(single_quote_char, atom!("single_quote")); + // macro_check!(small_letter_char, atom!("lower")); + macro_check!(solo_char, atom!("solo")); + // macro_check!(space_char, atom!("space")); + macro_check!(symbolic_hexadecimal_char, atom!("symbolic_hexadecimal")); + macro_check!(symbolic_control_char, atom!("symbolic_control")); + method_check!(is_uppercase, atom!("upper")); + // macro_check!(variable_indicator_char, atom!("variable_indicator")); + method_check!(is_whitespace, atom!("whitespace")); + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]) + .get_name_and_arity(); + + match (name, arity) { + (atom!("to_upper"), 1) => { + let reg = self.machine_st.heap[s+1]; + let upper_str = self.machine_st.atom_tbl.build_with(&c.to_uppercase().to_string()); + self.machine_st.unify_complete_string(upper_str, reg); + self.machine_st.fail = false; + } + (atom!("to_lower"), 1) => { + let reg = self.machine_st.heap[s+1]; + let lower_str = self.machine_st.atom_tbl.build_with(&c.to_lowercase().to_string()); + self.machine_st.unify_complete_string(lower_str, reg); + self.machine_st.fail = false; + } + _ => { + unreachable!() + } + }; + } + _ => { + unreachable!() + } + ); + + + } #[inline(always)] From 9c037d6028f601ee50cbab2b33e89383c95daad2 Mon Sep 17 00:00:00 2001 From: infogulch Date: Sat, 22 Apr 2023 10:18:10 -0500 Subject: [PATCH 158/361] Change ref for `publish-unit-test-result-action` --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5bf7a5a7..f44c8203 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,7 +67,7 @@ jobs: path: cargo_test_results.xml - name: Publish cargo test summary if: matrix.extra - uses: EnricoMi/publish-unit-test-result-action/composite@branch-publish-summary-on-fork + uses: EnricoMi/publish-unit-test-result-action/composite@master with: check_name: Cargo test summary files: cargo_test_results.xml @@ -133,7 +133,7 @@ jobs: name: logtalk-test-results path: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml' - name: Publish Logtalk test summary - uses: EnricoMi/publish-unit-test-result-action/composite@branch-publish-summary-on-fork + uses: EnricoMi/publish-unit-test-result-action/composite@master with: check_name: Logtalk test summary files: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml' From 7a0f4e5787bc1df0aaa7f117b78451755f972384 Mon Sep 17 00:00:00 2001 From: infogulch Date: Sat, 22 Apr 2023 15:42:10 -0500 Subject: [PATCH 159/361] Use --force to install cargo2junit This probably appeared now because it's the first time this tool was cached from a previous run. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f44c8203..73296baf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: - name: Test and report if: matrix.extra run: | - cargo install cargo2junit + cargo install cargo2junit --force cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml - name: Publish cargo test results artifact if: matrix.extra From 8b7281fad08db58cc00e5c7cb5fdc43b4dc3f24f Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 23 Apr 2023 00:37:03 +0200 Subject: [PATCH 160/361] ADDED: dif_si/2 Source: https://stackoverflow.com/questions/20223390/prolog-a-person-is-a-sibling-of-himself In Scryer Prolog, this is actually not needed, since Scryer Prolog provides dif/2 in library(dif). However, it is still useful to provide dif_si/2 for two reasons: 1) to more easily port code from systems where only dif_si/2 is available 2) to provide correct disequality in other systems that adopt this library --- src/lib/si.pl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib/si.pl b/src/lib/si.pl index a1705343..4c3b6c65 100644 --- a/src/lib/si.pl +++ b/src/lib/si.pl @@ -36,7 +36,8 @@ integer_si/1, atomic_si/1, list_si/1, - chars_si/1]). + chars_si/1, + dif_si/2]). :- use_module(library(lists)). @@ -64,3 +65,9 @@ list_si(L0) :- chars_si(Cs) :- list_si(Cs), '$is_partial_string'(Cs). + +dif_si(X, Y) :- + X \== Y, + ( X \= Y -> true + ; throw(error(instantiation_error,dif_si/2)) + ). From f35298a2277ad95f433a363dca139786c4f62392 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 22 Apr 2023 17:13:24 -0600 Subject: [PATCH 161/361] add and document inlined_instructions/2 to/in diag.pl (#1791) --- build/instructions_template.rs | 10 ++- src/lib/diag.pl | 126 ++++++++++++++++++++++++++++++++- src/machine/dispatch.rs | 8 +++ src/machine/system_calls.rs | 90 ++++++++++++++--------- 4 files changed, 195 insertions(+), 39 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 93615dec..e9ba3a38 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -470,6 +470,8 @@ enum SystemClauseType { UnwindStack, #[strum_discriminants(strum(props(Arity = "4", Name = "$wam_instructions")))] WAMInstructions, + #[strum_discriminants(strum(props(Arity = "2", Name = "$inlined_instructions")))] + InlinedInstructions, #[strum_discriminants(strum(props(Arity = "7", Name = "$write_term")))] WriteTerm, #[strum_discriminants(strum(props(Arity = "7", Name = "$write_term_to_chars")))] @@ -1747,6 +1749,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallUnwindEnvironments(_) | &Instruction::CallUnwindStack(_) | &Instruction::CallWAMInstructions(_) | + &Instruction::CallInlinedInstructions(_) | &Instruction::CallWriteTerm(_) | &Instruction::CallWriteTermToChars(_) | &Instruction::CallScryerPrologVersion(_) | @@ -1933,9 +1936,9 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteHttpListen(_) | &Instruction::ExecuteHttpAccept(_) | &Instruction::ExecuteHttpAnswer(_) | - &Instruction::ExecuteLoadForeignLib(_) | - &Instruction::ExecuteForeignCall(_) | - &Instruction::ExecuteDefineForeignStruct(_) | + &Instruction::ExecuteLoadForeignLib(_) | + &Instruction::ExecuteForeignCall(_) | + &Instruction::ExecuteDefineForeignStruct(_) | &Instruction::ExecutePredicateDefined(_) | &Instruction::ExecuteStripModule(_) | &Instruction::ExecuteCurrentTime(_) | @@ -1967,6 +1970,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteUnwindEnvironments(_) | &Instruction::ExecuteUnwindStack(_) | &Instruction::ExecuteWAMInstructions(_) | + &Instruction::ExecuteInlinedInstructions(_) | &Instruction::ExecuteWriteTerm(_) | &Instruction::ExecuteWriteTermToChars(_) | &Instruction::ExecuteScryerPrologVersion(_) | diff --git a/src/lib/diag.pl b/src/lib/diag.pl index 636ed0a4..fc989b34 100644 --- a/src/lib/diag.pl +++ b/src/lib/diag.pl @@ -1,4 +1,4 @@ -:- module(diag, [wam_instructions/2]). +:- module(diag, [wam_instructions/2, inlined_instructions/2]). /** Diagnostics library @@ -33,6 +33,120 @@ unify_variable(x(3)). execute(append,3). Is = [switch_on_term(1,external(1),external(2),external(6),fail)|...]. ``` + + `inlined_instructions/2` decompiles predicates at the code offset in + its first argument. + + For example, given the program + +``` +?- [user]. +:- use_module(library(clpz)). + +all_eq(Vs, E) :- maplist(#=(E), Vs). + +``` + + we inspect the code of `all_eqs/2` using `wam_instructions/2`, + revealing: + +``` +?- wam_instructions(all_eq/2, Is), + maplist(portray_clause, Is). +put_structure('$aux',2,x(3)). +set_local_value(x(2)). +set_void(1). +set_constant('$index_ptr'(115334)). +get_variable(x(4),1). +put_structure(:,2,x(1)). +set_constant(user). +set_local_value(x(3)). +get_variable(x(5),2). +put_value(x(4),2). +execute(maplist,2). + Is = [put_structure('$aux',2,x(3)),set_local_value(x(2)),set_void(1),set_constant('$index_ptr'(115334)),get_variable(x(4),1),put_structure(:,2,x(1)),set_constant(user),set_local_value(x(3)),get_variable(x(5),2),put_value(x(4),2),execute(maplist,2)]. +``` + + The `'$index_ptr(115334)` functor gives a code offset to an inlined + predicate compiled for the use of maplist/2. `inlined_instructions/2` + can be used to decompile its source code: + +``` +?- inlined_instructions(115334, Is), + maplist(portray_clause, Is). +allocate(1). +get_level(y(1)). +get_variable(x(5),2). +put_value(x(3),2). +get_variable(x(6),3). +put_value(x(5),3). +put_unsafe_value(1,4). +deallocate. +jmp_by_execute(1). +try_me_else(8). +call(integer,1). +neck_cut. +get_variable(x(5),1). +put_value(x(2),1). +get_variable(x(6),2). +put_value(x(5),2). +jmp_by_execute(7). +try_me_else(12). +allocate(3). +get_level(y(1)). +get_variable(y(3),1). +get_variable(y(2),2). +call_default(true,0). +call(var,1). +cut(y(1)). +put_unsafe_value(3,1). +put_unsafe_value(2,2). +deallocate. +execute_default(is,2). +default_retry_me_else(4). +call(integer,1). +neck_cut. +execute(=:=,2). +default_trust_me(0). +allocate(2). +get_variable(y(1),1). +get_variable(y(2),3). +put_value(y(2),1). +call_default(is,2). +put_unsafe_value(2,1). +put_unsafe_value(1,2). +deallocate. +execute_default(clpz_equal,2). +default_retry_me_else(4). +call(integer,1). +neck_cut. +jmp_by_execute(29). +try_me_else(12). +allocate(3). +get_level(y(1)). +get_variable(y(3),1). +get_variable(y(2),2). +call_default(true,0). +call(var,1). +cut(y(1)). +put_unsafe_value(3,1). +put_unsafe_value(2,2). +deallocate. +execute_default(is,2). +default_trust_me(0). +allocate(2). +get_variable(y(2),1). +get_variable(y(1),3). +put_value(y(1),1). +call_default(is,2). +put_unsafe_value(2,1). +put_unsafe_value(1,2). +deallocate. +execute_default(clpz_equal,2). +default_trust_me(0). +execute_default(clpz_equal,2). + Is = [allocate(1),get_level(y(1)),get_variable(x(5),2),put_value(x(3),2),get_variable(x(6),3),put_value(x(5),3),put_unsafe_value(1,4),deallocate,jmp_by_execute(1),try_me_else(8),call(integer,1),neck_cut,get_variable(x(5),1),put_value(x(2),1),get_variable(x(6),2),put_value(x(5),2),jmp_by_execute(7),try_me_else(12),allocate(3),get_level(...),...]. +``` */ @@ -52,6 +166,16 @@ wam_instructions(Clause, Listing) :- ; throw(error(instantiation_error, wam_instructions/2)) ). +%% inlined_instructions(+IndexPtr, -Instrs) +% +% _Instrs_ are the WAM instructions corresponding to code offset _IndexPtr_. + +inlined_instructions(IndexPtr, Listing) :- + must_be(integer, IndexPtr), + ( IndexPtr >= 0 -> + '$inlined_instructions'(IndexPtr, Listing) + ; throw(error(domain_error(not_less_than_zero, IndexPtr), inlined_instructions/2)) + ). fetch_instructions(Module, Name, Arity, Listing) :- must_be(atom, Module), diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 39ca74c6..e366dc81 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4445,6 +4445,14 @@ impl Machine { try_or_throw!(self.machine_st, self.wam_instructions()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallInlinedInstructions(_) => { + self.inlined_instructions(); + self.machine_st.p += 1; + } + &Instruction::ExecuteInlinedInstructions(_) => { + self.inlined_instructions(); + self.machine_st.p = self.machine_st.cp; + } &Instruction::CallWriteTerm(_) => { try_or_throw!(self.machine_st, self.write_term()); step_or_fail!(self, self.machine_st.p += 1); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 21f3057c..cc5bc536 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -6267,6 +6267,43 @@ impl Machine { false } + fn walk_code_at_ptr(&mut self, index_ptr: usize) -> HeapCellValue { + let mut h = self.machine_st.heap.len(); + + let mut functors = vec![]; + let mut functor_list = vec![]; + + walk_code(&self.code, index_ptr, |instr| { + let old_len = functors.len(); + instr.enqueue_functors(h, &mut self.machine_st.arena, &mut functors); + let new_len = functors.len(); + + for index in old_len..new_len { + let functor_len = functors[index].len(); + + match functor_len { + 0 => {} + 1 => { + functor_list.push(heap_loc_as_cell!(h)); + h += functor_len; + } + _ => { + functor_list.push(str_loc_as_cell!(h)); + h += functor_len; + } + } + } + }); + + for functor in functors { + self.machine_st.heap.extend(functor.into_iter()); + } + + heap_loc_as_cell!( + iter_to_heap_list(&mut self.machine_st.heap, functor_list.into_iter()) + ) + } + #[inline(always)] pub(crate) fn wam_instructions(&mut self) -> CallResult { let module_name = cell_as_atom!(self.deref_register(1)); @@ -6318,47 +6355,30 @@ impl Machine { } }; - let mut h = self.machine_st.heap.len(); - - let mut functors = vec![]; - let mut functor_list = vec![]; - - walk_code(&self.code, first_idx, |instr| { - let old_len = functors.len(); - instr.enqueue_functors(h, &mut self.machine_st.arena, &mut functors); - let new_len = functors.len(); - - for index in old_len..new_len { - let functor_len = functors[index].len(); - - match functor_len { - 0 => {} - 1 => { - functor_list.push(heap_loc_as_cell!(h)); - h += functor_len; - } - _ => { - functor_list.push(str_loc_as_cell!(h)); - h += functor_len; - } - } - } - }); - - for functor in functors { - self.machine_st.heap.extend(functor.into_iter()); - } - - let listing = heap_loc_as_cell!( - iter_to_heap_list(&mut self.machine_st.heap, functor_list.into_iter()) - ); - + let listing = self.walk_code_at_ptr(first_idx); let listing_var = self.machine_st.registers[4]; unify!(self.machine_st, listing, listing_var); Ok(()) } + #[inline(always)] + pub(crate) fn inlined_instructions(&mut self) { + let index_ptr = self.deref_register(1); + let index_ptr = match Number::try_from(index_ptr) { + Ok(Number::Fixnum(n)) => n.get_num() as usize, + Ok(Number::Integer(n)) => n.to_usize().unwrap(), + _ => { + unreachable!() + } + }; + + let listing = self.walk_code_at_ptr(index_ptr); + let listing_var = self.machine_st.registers[2]; + + unify!(self.machine_st, listing, listing_var); + } + #[inline(always)] pub(crate) fn write_term(&mut self) -> CallResult { let mut stream = self.machine_st.get_stream_or_alias( From 7d2e59ab64fd36ee90195b61a3b6bceb071576a0 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 22 Apr 2023 17:14:00 -0600 Subject: [PATCH 162/361] discard CodeIndex literals from unfolded control operators in preprocessor (#1791) --- src/machine/preprocessor.rs | 6 +++--- src/parser/ast.rs | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/machine/preprocessor.rs b/src/machine/preprocessor.rs index 37871bb0..7f0cc264 100644 --- a/src/machine/preprocessor.rs +++ b/src/machine/preprocessor.rs @@ -399,7 +399,7 @@ fn check_for_internal_if_then(terms: &mut Vec) { } if let Some(Term::Clause(_, name, ref subterms)) = terms.last() { - if *name != atom!("->") || subterms.len() != 2 { + if *name != atom!("->") || source_arity(subterms) != 2 { return; } } else { @@ -770,7 +770,7 @@ impl Preprocessor { Term::Var(_, ref v) if v.as_str() == "!" => { Ok(QueryTerm::UnblockedCut(Cell::default())) } - Term::Clause(r, name, mut terms) => match (name, terms.len()) { + Term::Clause(r, name, mut terms) => match (name, source_arity(&terms)) { (atom!(";"), 2) => { let term = Term::Clause(r, name, terms); @@ -900,7 +900,7 @@ impl Preprocessor { let mut term = term; if let Term::Clause(cell, name, terms) = term { - if name == atom!(",") && terms.len() == 2 { + if name == atom!(",") && source_arity(&terms) == 2 { let term = Term::Clause(cell, name, terms); let mut subterms = unfold_by_str(term, atom!(",")); diff --git a/src/parser/ast.rs b/src/parser/ast.rs index b1c0f5d9..0933b11c 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -626,8 +626,25 @@ impl Term { } } +#[inline] +pub fn source_arity(terms: &[Term]) -> usize { + if let Some(last_arg) = terms.last() { + if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg { + return terms.len() - 1; + } + } + + terms.len() +} + fn unfold_by_str_once(term: &mut Term, s: Atom) -> Option<(Term, Term)> { if let Term::Clause(_, ref name, ref mut subterms) = term { + if let Some(last_arg) = subterms.last() { + if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg { + subterms.pop(); + } + } + if name == &s && subterms.len() == 2 { let snd = subterms.pop().unwrap(); let fst = subterms.pop().unwrap(); From 2a1b8f37eca21a80ad22b22dc23304379e3d77c5 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 23 Apr 2023 09:20:46 +0200 Subject: [PATCH 163/361] remove residual goal for ground BDD Example: ?- sat(X). X = 1. --- src/lib/clpb.pl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/clpb.pl b/src/lib/clpb.pl index 1bdbbefc..3b7ac007 100644 --- a/src/lib/clpb.pl +++ b/src/lib/clpb.pl @@ -1628,6 +1628,7 @@ skip_to_var_(Var, Weight, [Var0-Weight0|VWs0], VWs) --> attribute_goals(Var) --> { var_index_root(Var, _, Root) }, + !, ( { root_get_formula_bdd(Root, Formula, BDD) } -> { del_bdd(Root) }, ( { clpb_residuals(bdd) } -> @@ -1655,6 +1656,10 @@ attribute_goals(Var) --> booleans(RestVs) ; boolean(Var) % the variable may have occurred only in taut/2 ). +attribute_goals(Var) --> + { get_atts(Var, clpb_bdd(BDD)), + ground(BDD), + put_atts(Var, -clpb_bdd(_)) }. del_clpb(Var) :- del_attr(Var, clpb), From b162c40007540f77f14e2f5cf2bf4aac3e3f428e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Sat, 22 Apr 2023 10:54:35 +0200 Subject: [PATCH 164/361] Fix to_upper/to_lower when string is instantiated --- src/machine/system_calls.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index cc5bc536..674fc54f 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -2794,15 +2794,17 @@ impl Machine { match (name, arity) { (atom!("to_upper"), 1) => { - let reg = self.machine_st.heap[s+1]; - let upper_str = self.machine_st.atom_tbl.build_with(&c.to_uppercase().to_string()); - self.machine_st.unify_complete_string(upper_str, reg); + let reg = self.machine_st.deref(self.machine_st.heap[s+1]); + let atom = self.machine_st.atom_tbl.build_with(&c.to_uppercase().to_string()); + let upper_str = string_as_cstr_cell!(atom); + unify!(self.machine_st, reg, upper_str); self.machine_st.fail = false; } (atom!("to_lower"), 1) => { - let reg = self.machine_st.heap[s+1]; - let lower_str = self.machine_st.atom_tbl.build_with(&c.to_lowercase().to_string()); - self.machine_st.unify_complete_string(lower_str, reg); + let reg = self.machine_st.deref(self.machine_st.heap[s+1]); + let atom = self.machine_st.atom_tbl.build_with(&c.to_lowercase().to_string()); + let lower_str = string_as_cstr_cell!(atom); + unify!(self.machine_st, reg, lower_str); self.machine_st.fail = false; } _ => { From c5a3ec3ba8afb3504bf0a955f6e5078667d4ba6d Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 24 Apr 2023 23:23:19 -0600 Subject: [PATCH 165/361] fix current_predicate/1 (#1761) --- build/instructions_template.rs | 12 +++-- src/lib/builtins.pl | 37 ++++++--------- src/machine/dispatch.rs | 16 +++++-- src/machine/system_calls.rs | 84 +++++++++++++++++++++++----------- 4 files changed, 92 insertions(+), 57 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index e9ba3a38..bec70e7e 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -306,10 +306,10 @@ enum SystemClauseType { GetBValue, #[strum_discriminants(strum(props(Arity = "3", Name = "$get_cont_chunk")))] GetContinuationChunk, - #[strum_discriminants(strum(props(Arity = "4", Name = "$get_next_db_ref")))] - GetNextDBRef, #[strum_discriminants(strum(props(Arity = "7", Name = "$get_next_op_db_ref")))] GetNextOpDBRef, + #[strum_discriminants(strum(props(Arity = "2", Name = "$lookup_db_ref")))] + LookupDBRef, #[strum_discriminants(strum(props(Arity = "1", Name = "$is_partial_string")))] IsPartialString, #[strum_discriminants(strum(props(Arity = "1", Name = "$halt")))] @@ -578,6 +578,8 @@ enum SystemClauseType { DeleteAllAttributesFromVar, #[strum_discriminants(strum(props(Arity = "1", Name = "$unattributed_var")))] UnattributedVar, + #[strum_discriminants(strum(props(Arity = "3", Name = "$get_db_refs")))] + GetDBRefs, REPL(REPLCodePtr), } @@ -1641,6 +1643,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallDeleteFromAttributedVarList(_) | &Instruction::CallDeleteAllAttributesFromVar(_) | &Instruction::CallUnattributedVar(_) | + &Instruction::CallGetDBRefs(_) | &Instruction::CallFetchGlobalVar(_) | &Instruction::CallFirstStream(_) | &Instruction::CallFlushOutput(_) | @@ -1656,8 +1659,8 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallGetAttrVarQueueBeyond(_) | &Instruction::CallGetBValue(_) | &Instruction::CallGetContinuationChunk(_) | - &Instruction::CallGetNextDBRef(_) | &Instruction::CallGetNextOpDBRef(_) | + &Instruction::CallLookupDBRef(_) | &Instruction::CallIsPartialString(_) | &Instruction::CallHalt(_) | &Instruction::CallGetLiftedHeapFromOffset(_) | @@ -1862,6 +1865,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteDeleteFromAttributedVarList(_) | &Instruction::ExecuteDeleteAllAttributesFromVar(_) | &Instruction::ExecuteUnattributedVar(_) | + &Instruction::ExecuteGetDBRefs(_) | &Instruction::ExecuteFetchGlobalVar(_) | &Instruction::ExecuteFirstStream(_) | &Instruction::ExecuteFlushOutput(_) | @@ -1877,8 +1881,8 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteGetAttrVarQueueBeyond(_) | &Instruction::ExecuteGetBValue(_) | &Instruction::ExecuteGetContinuationChunk(_) | - &Instruction::ExecuteGetNextDBRef(_) | &Instruction::ExecuteGetNextOpDBRef(_) | + &Instruction::ExecuteLookupDBRef(_) | &Instruction::ExecuteIsPartialString(_) | &Instruction::ExecuteHalt(_) | &Instruction::ExecuteGetLiftedHeapFromOffset(_) | diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 759c60b6..0005d395 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -1205,7 +1205,6 @@ module_abolish(Pred, Module) :- ; throw(error(type_error(predicate_indicator, Module:Pred), abolish/1)) ). - :- meta_predicate abolish(:). %% abolish(Pred). @@ -1246,13 +1245,6 @@ abolish(Pred) :- ; throw(error(type_error(predicate_indicator, Pred), abolish/1)) ). - -'$iterate_db_refs'(Name, Arity, Name/Arity). % :- -% '$lookup_db_ref'(Ref, Name, Arity). -'$iterate_db_refs'(RName, RArity, Name/Arity) :- - '$get_next_db_ref'(RName, RArity, RRName, RRArity), - '$iterate_db_refs'(RRName, RRArity, Name/Arity). - %% current_predicate(Pred). % % Pred must satisfy: `Pred = Name/Arity`. @@ -1260,27 +1252,28 @@ abolish(Pred) :- % It can be used to check for existence of a predicate or to enumerate all loaded predicates current_predicate(Pred) :- ( var(Pred) -> - '$get_next_db_ref'(RN, RA, _, _), - '$iterate_db_refs'(RN, RA, Pred) - ; Pred \= _/_ -> - throw(error(type_error(predicate_indicator, Pred), current_predicate/1)) - ; Pred = Name/Arity, - ( nonvar(Name), \+ atom(Name) - ; nonvar(Arity), \+ integer(Arity) - ; integer(Arity), Arity < 0 - ) -> - throw(error(type_error(predicate_indicator, Pred), current_predicate/1)) - ; '$get_next_db_ref'(RN, RA, _, _), - '$iterate_db_refs'(RN, RA, Pred) + '$get_db_refs'(_, _, PIs), + lists:member(Pred, PIs) + ; Pred = Name/Arity -> + ( ( nonvar(Name), \+ atom(Name) + ; nonvar(Arity), \+ integer(Arity) + ; integer(Arity), Arity < 0 + ) -> + throw(error(type_error(predicate_indicator, Pred), current_predicate/1)) + ; nonvar(Name), + nonvar(Arity) -> + '$lookup_db_ref'(Name, Arity) + ; '$get_db_refs'(Name, Arity, PIs), + lists:member(Pred, PIs) + ) + ; throw(error(type_error(predicate_indicator, Pred), current_predicate/1)) ). - '$iterate_op_db_refs'(RPriority, RSpec, ROp, _, RPriority, RSpec, ROp). '$iterate_op_db_refs'(RPriority, RSpec, ROp, OssifiedOpDir, Priority, Spec, Op) :- '$get_next_op_db_ref'(RPriority, RSpec, ROp, OssifiedOpDir, RRPriority, RRSpec, RROp), '$iterate_op_db_refs'(RRPriority, RRSpec, RROp, OssifiedOpDir, Priority, Spec, Op). - can_be_op_priority(Priority) :- var(Priority). can_be_op_priority(Priority) :- op_priority(Priority). diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index e366dc81..3067ecb9 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -3720,12 +3720,12 @@ impl Machine { self.get_continuation_chunk(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetNextDBRef(_) => { - self.get_next_db_ref(); + &Instruction::CallLookupDBRef(_) => { + self.lookup_db_ref(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetNextDBRef(_) => { - self.get_next_db_ref(); + &Instruction::ExecuteLookupDBRef(_) => { + self.lookup_db_ref(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallGetNextOpDBRef(_) => { @@ -5255,6 +5255,14 @@ impl Machine { self.machine_st.unattributed_var(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallGetDBRefs(_) => { + self.get_db_refs(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteGetDBRefs(_) => { + self.get_db_refs(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } } } diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 674fc54f..e457a611 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -3660,44 +3660,74 @@ impl Machine { } #[inline(always)] - pub(crate) fn get_next_db_ref(&mut self) { - let a1 = self.deref_register(1); + pub(crate) fn lookup_db_ref(&mut self) { + let name = cell_as_atom!(self.deref_register(1)); + let arity = cell_as_fixnum!(self.deref_register(2)).get_num() as usize; - if let Some(name_var) = a1.as_var() { - let mut iter = self.indices.code_dir.iter(); + if self.indices.code_dir.get(&(name, arity)).is_none() { + self.machine_st.fail = true; + } + } - while let Some(((name, arity), _)) = iter.next() { - let arity_var = self.machine_st.deref(self.machine_st.registers[2]) - .as_var().unwrap(); + #[inline(always)] + pub(crate) fn get_db_refs(&mut self) { + let name_match: fn(Atom, Atom) -> bool; + let arity_match: fn(usize, usize) -> bool; - self.machine_st.bind(name_var, atom_as_cell!(name)); - self.machine_st.bind(arity_var, fixnum_as_cell!(Fixnum::build_with(*arity as i64))); + let atom = self.deref_register(1); + let pred_atom = if atom.is_var() { + name_match = |_, _| true; + atom!("") + } else { + name_match = |atom_1, atom_2| atom_1 == atom_2; + cell_as_atom!(atom) + }; + + let arity = self.deref_register(2); + + let pred_arity = if arity.is_var() { + arity_match = |_, _| true; + 0 + } else { + arity_match = |arity_1, arity_2| arity_1 == arity_2; + + let arity = match Number::try_from(arity) { + Ok(Number::Fixnum(n)) => Some(n.get_num() as usize), + Ok(Number::Integer(n)) => n.to_usize(), + _ => None, + }; + + if let Some(arity) = arity { + arity + } else { + self.machine_st.fail = true; return; } + }; - self.machine_st.fail = true; - } else if a1.get_tag() == HeapCellValueTag::Atom { - let name = cell_as_atom!(a1); - let arity = cell_as_fixnum!(self.deref_register(2)).get_num() as usize; + let h = self.machine_st.heap.len(); + let mut num_functors = 0; - match self.machine_st.get_next_db_ref(&self.indices, &DBRef::NamedPred(name, arity)) { - Some(DBRef::NamedPred(name, arity)) => { - let atom_var = self.machine_st.deref(self.machine_st.registers[3]) - .as_var().unwrap(); + for (name, arity) in self.indices.code_dir.keys() { + if name_match(pred_atom, *name) && arity_match(pred_arity, *arity) { + self.machine_st.heap.extend( + functor!(atom!("/"), [cell(atom_as_cell!(name)), fixnum(*arity)]), + ); - let arity_var = self.machine_st.deref(self.machine_st.registers[4]) - .as_var().unwrap(); - - self.machine_st.bind(atom_var, atom_as_cell!(name)); - self.machine_st.bind(arity_var, fixnum_as_cell!(Fixnum::build_with(arity as i64))); - } - Some(DBRef::Op(..)) | None => { - self.machine_st.fail = true; - } + num_functors += 1; } + } + + if num_functors > 0 { + let h = iter_to_heap_list( + &mut self.machine_st.heap, + (0 .. num_functors).map(|i| str_loc_as_cell!(h + 3 * i)), + ); + + unify!(self.machine_st, heap_loc_as_cell!(h), self.machine_st.registers[3]); } else { - self.machine_st.fail = true; + unify!(self.machine_st, empty_list_as_cell!(), self.machine_st.registers[3]); } } From e951db662d95725377a349964cb0d8cb6f3b38b7 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 3 May 2023 21:56:04 +0200 Subject: [PATCH 166/361] ENHANCED: allow Roman numerals in strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example: ?- X = "ↁ". X = "ↁ". This addresses #1790. --- src/parser/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/macros.rs b/src/parser/macros.rs index 27b106fc..3e6826c9 100644 --- a/src/parser/macros.rs +++ b/src/parser/macros.rs @@ -20,7 +20,7 @@ macro_rules! alpha_char { #[macro_export] macro_rules! alpha_numeric_char { ($c: expr) => { - $crate::alpha_char!($c) || $crate::decimal_digit_char!($c) + $crate::alpha_char!($c) || $c.is_numeric() }; } From d8edf7bfff53c3979c84531ac5d6efd38c510f34 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 3 May 2023 23:23:53 +0200 Subject: [PATCH 167/361] FIXED: consistent read/write of further control characters, and non-breaking space Example: ?- X = '\xa0\'. X = '\xa0\'. This addresses #1768. --- src/heap_print.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 54f52cd6..d05e8455 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -172,7 +172,9 @@ fn char_to_string(is_quoted: bool, c: char) -> String { '\'' | '\n' | '\r' | '\t' | '\u{0b}' | '\u{0c}' | '\u{08}' | '\u{07}' | '"' | '\\' => { c.to_string() } - '\u{0}'..='\u{1f}' => format!("\\x{:x}\\", c as u32), // print all other control characters in hex. + '\u{0}'..='\u{1f}' | '\u{7f}' ..= '\u{a0}' + // print all other control characters, and also non-breaking space, in hex. + => format!("\\x{:x}\\", c as u32), _ => c.to_string(), } } From 4e60cc46a2ef81e9f6ea8640252458e64c77741a Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 4 May 2023 00:50:27 +0200 Subject: [PATCH 168/361] rely on first instantiated argument indexing in the definitions of foldl/N This allows shorter and more natural definitions. --- src/lib/lists.pl | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/lib/lists.pl b/src/lib/lists.pl index 92bc202d..815e2b2a 100644 --- a/src/lib/lists.pl +++ b/src/lib/lists.pl @@ -295,25 +295,19 @@ same_length([_|As], [_|Bs]) :- % sum_list(Ls, S) :- foldl(sum_, Ls, 0, S). % ``` -foldl(Goal_3, Ls, A0, A) :- - foldl_(Ls, Goal_3, A0, A). - -foldl_([], _, A, A). -foldl_([L|Ls], G_3, A0, A) :- +foldl(_, [], A, A). +foldl(G_3, [L|Ls], A0, A) :- call(G_3, L, A0, A1), - foldl_(Ls, G_3, A1, A). + foldl(G_3, Ls, A1, A). %% foldl(+Predicate, ?Ls0, ?Ls1, +A0, ?A). % % Same as `foldl/4` but with an extra list -foldl(Goal_4, Xs, Ys, A0, A) :- - foldl_(Xs, Ys, Goal_4, A0, A). - -foldl_([], [], _, A, A). -foldl_([X|Xs], [Y|Ys], G_4, A0, A) :- +foldl(_, [], [], A, A). +foldl(G_4, [X|Xs], [Y|Ys], A0, A) :- call(G_4, X, Y, A0, A1), - foldl_(Xs, Ys, G_4, A1, A). + foldl(G_4, Xs, Ys, A1, A). %% transpose(?Ls, ?Ts). % From 2f9996f9ac807f91bd523b8d33c4eac356c123fb Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 10 May 2023 00:04:35 -0600 Subject: [PATCH 169/361] use same logic to print Chars and Atoms (#1804) --- src/heap_print.rs | 123 ++++++++++++++++++++--------------- src/machine/machine_state.rs | 1 + src/machine/mock_wam.rs | 1 + 3 files changed, 73 insertions(+), 52 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index d05e8455..635ae13c 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -470,6 +470,7 @@ pub fn fmt_float(mut fl: f64) -> String { pub struct HCPrinter<'a, Outputter> { outputter: Outputter, iter: StackfulPreOrderHeapIter<'a>, + atom_tbl: &'a mut AtomTable, op_dir: &'a OpDir, state_stack: Vec, toplevel_spec: Option, @@ -534,6 +535,7 @@ pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { pub fn new( heap: &'a mut Heap, + atom_tbl: &'a mut AtomTable, op_dir: &'a OpDir, output: Outputter, cell: HeapCellValue, @@ -541,6 +543,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { HCPrinter { outputter: output, iter: stackful_preorder_iter(heap, cell), + atom_tbl, op_dir, state_stack: vec![], toplevel_spec: None, @@ -888,7 +891,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } } - fn print_atom(&mut self, atom: Atom) { + fn print_impromptu_atom(&mut self, atom: Atom) { let result = self.print_op_addendum(atom.as_str()); push_space_if_amb!(self, result.as_str(), { @@ -1404,7 +1407,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { fn print_stream(&mut self, stream: Stream, max_depth: usize) { if let Some(alias) = stream.options().get_alias() { - self.print_atom(alias); + self.print_impromptu_atom(alias); } else { let stream_atom = atom!("$stream"); @@ -1440,53 +1443,62 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { None => return, }; - read_heap_cell!(addr, - (HeapCellValueTag::Atom, (name, arity)) => { - if name == atom!("[]") && arity == 0 { - if !self.at_cdr("") { - append_str!(self, "[]"); - } - } else if arity > 0 { - if let Some(spec) = fetch_op_spec(name, arity, self.op_dir) { - self.handle_op_as_struct( - name, - arity, - &op, - is_functor_redirect, - spec, - negated_operand, - max_depth, - ); - } else { - push_space_if_amb!(self, name.as_str(), { - self.format_clause(max_depth, arity, name, None); - }); - } - } else if fetch_op_spec(name, arity, self.op_dir).is_some() { - let mut result = String::new(); - - if let Some(ref op) = op { - if self.outputter.ends_with(&format!(" {}", op.as_atom().as_str())) { - result.push(' '); - } - - result.push('('); - } - - result += &self.print_op_addendum(name.as_str()); - - if op.is_some() { - result.push(')'); - } - - push_space_if_amb!(self, &result, { - append_str!(self, &result); - }); + let print_atom = |printer: &mut Self, name: Atom, arity: usize| { + if name == atom!("[]") && arity == 0 { + if !printer.at_cdr("") { + append_str!(printer, "[]"); + } + } else if arity > 0 { + if let Some(spec) = fetch_op_spec(name, arity, printer.op_dir) { + printer.handle_op_as_struct( + name, + arity, + &op, + is_functor_redirect, + spec, + negated_operand, + max_depth, + ); } else { - push_space_if_amb!(self, name.as_str(), { - self.print_atom(name); + push_space_if_amb!(printer, name.as_str(), { + printer.format_clause(max_depth, arity, name, None); }); } + } else if fetch_op_spec(name, arity, printer.op_dir).is_some() { + let mut result = String::new(); + + if let Some(ref op) = op { + if printer.outputter.ends_with(&format!(" {}", op.as_atom().as_str())) { + result.push(' '); + } + + result.push('('); + } + + result += &printer.print_op_addendum(name.as_str()); + + if op.is_some() { + result.push(')'); + } + + push_space_if_amb!(printer, &result, { + append_str!(printer, &result); + }); + } else { + push_space_if_amb!(printer, name.as_str(), { + printer.print_impromptu_atom(name); + }); + } + }; + + read_heap_cell!(addr, + (HeapCellValueTag::Atom, (name, arity)) => { + print_atom(self, name, arity); + } + (HeapCellValueTag::Char, c) => { + let name = self.atom_tbl.build_with(&String::from(c)); + print_atom(self, name, 0); + // print_char!(self, self.quoted, c); } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.iter.heap[s]) @@ -1534,9 +1546,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { }) } } - (HeapCellValueTag::Char, c) => { - print_char!(self, self.quoted, c); - } (HeapCellValueTag::Cons, c) => { match_untyped_arena_ptr!(c, (ArenaHeaderTag::Integer, n) => { @@ -1549,10 +1558,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.print_stream(stream, max_depth); } (ArenaHeaderTag::OssifiedOpDir, _op_dir) => { - self.print_atom(atom!("$ossified_op_dir")); + self.print_impromptu_atom(atom!("$ossified_op_dir")); } (ArenaHeaderTag::Dropped, _value) => { - self.print_atom(atom!("$dropped_value")); + self.print_impromptu_atom(atom!("$dropped_value")); } (ArenaHeaderTag::IndexPtr, index_ptr) => { self.print_index_ptr(*index_ptr, max_depth); @@ -1586,7 +1595,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { while let Some(loc_data) = self.state_stack.pop() { match loc_data { - TokenOrRedirect::Atom(atom) => self.print_atom(atom), + TokenOrRedirect::Atom(atom) => self.print_impromptu_atom(atom), TokenOrRedirect::BarAsOp => append_str!(self, " | "), TokenOrRedirect::Char(c) => print_char!(self, self.quoted, c), TokenOrRedirect::Op(atom, _) => self.print_op(atom.as_str()), @@ -1652,6 +1661,7 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1679,6 +1689,7 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1701,6 +1712,7 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1712,6 +1724,7 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1743,6 +1756,7 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), @@ -1760,6 +1774,7 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), @@ -1775,6 +1790,7 @@ mod tests { { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1805,6 +1821,7 @@ mod tests { { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1826,6 +1843,7 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), pstr_loc_as_cell!(0) @@ -1852,6 +1870,7 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index bdaf048c..e489742a 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -765,6 +765,7 @@ impl MachineState { let mut printer = HCPrinter::new( &mut self.heap, + &mut self.atom_tbl, op_dir, PrinterOutputter::new(), term_to_be_printed, diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index 761590fb..2ddde129 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -61,6 +61,7 @@ impl MockWAM { let mut printer = HCPrinter::new( &mut self.machine_st.heap, + &mut self.machine_st.atom_tbl, &self.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(term_write_result.heap_loc), From 49addc7b04a88038586e88c7c1d10ff496870a79 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 14 May 2023 09:14:10 +0200 Subject: [PATCH 170/361] extend logic to all control and whitespace characters This addresses #1802. --- src/heap_print.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 635ae13c..61477b1a 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -169,13 +169,16 @@ fn char_to_string(is_quoted: bool, c: char) -> String { '\u{08}' if is_quoted => "\\b".to_string(), // UTF-8 backspace '\u{07}' if is_quoted => "\\a".to_string(), // UTF-8 alert '\\' if is_quoted => "\\\\".to_string(), - '\'' | '\n' | '\r' | '\t' | '\u{0b}' | '\u{0c}' | '\u{08}' | '\u{07}' | '"' | '\\' => { + ' ' | '\'' | '\n' | '\r' | '\t' | '\u{0b}' | '\u{0c}' | '\u{08}' | '\u{07}' | '"' | '\\' => { c.to_string() } - '\u{0}'..='\u{1f}' | '\u{7f}' ..= '\u{a0}' - // print all other control characters, and also non-breaking space, in hex. - => format!("\\x{:x}\\", c as u32), - _ => c.to_string(), + _ => + if c.is_whitespace() || c.is_control() { + // print all other control and whitespace characters in hex. + format!("\\x{:x}\\", c as u32) + } else { + c.to_string() + } } } From 30f222b837afae40fc239e3a70a4b6dccbafc110 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 16 May 2023 22:41:20 +0200 Subject: [PATCH 171/361] shorten gensym/2 --- src/lib/gensym.pl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/lib/gensym.pl b/src/lib/gensym.pl index 92cd4d1f..86e7ad5b 100644 --- a/src/lib/gensym.pl +++ b/src/lib/gensym.pl @@ -19,13 +19,12 @@ gensym(Base, Unique) :- must_be(var, Unique), atom_si(Base), gensym_key(Base, BaseKey), - ( bb_get(BaseKey, UniqueID0) -> - UniqueID is UniqueID0 + 1, - bb_put(BaseKey, UniqueID), - append_id(Base, UniqueID, Unique) - ; bb_put(BaseKey, 1), - append_id(Base, 1, Unique) - ). + ( bb_get(BaseKey, UniqueID0) -> true + ; UniqueID0 = 0 + ), + UniqueID is UniqueID0 + 1, + append_id(Base, UniqueID, Unique), + bb_put(BaseKey, UniqueID). reset_gensym(Base) :- atom_si(Base), From 021c01dfd03699a62b556c893245dedbd6d126c1 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 16 May 2023 22:42:10 +0200 Subject: [PATCH 172/361] FIXED: correctly reset counter in reset_gensym/2 (#1807) Many thanks to @infradig for detecting this issue and suggesting this correction! --- src/lib/gensym.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/gensym.pl b/src/lib/gensym.pl index 86e7ad5b..272e68bd 100644 --- a/src/lib/gensym.pl +++ b/src/lib/gensym.pl @@ -28,4 +28,5 @@ gensym(Base, Unique) :- reset_gensym(Base) :- atom_si(Base), - bb_put(Base, 0). + gensym_key(Base, BaseKey), + bb_put(BaseKey, 0). From b54a4afad63bbd6a95dc5b66446516f42309d16d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Wed, 17 May 2023 18:19:19 +0200 Subject: [PATCH 173/361] Update select crate to 0.6.0 and remove warning --- Cargo.lock | 471 ++++++++++++++++------------------------------------- Cargo.toml | 2 +- 2 files changed, 142 insertions(+), 331 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46c5a14f..63887d6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,15 +31,6 @@ dependencies = [ "wait-timeout", ] -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -203,15 +194,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - [[package]] name = "codespan-reporting" version = "0.11.1" @@ -289,7 +271,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2db40892a506901e4e8281f00e42687df82d1d3448cb0289ae9183a60cb42ec1" dependencies = [ "blake2 0.10.4", - "rand_core 0.6.4", + "rand_core", "sha2", ] @@ -344,10 +326,10 @@ dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2", + "quote", "scratch", - "syn 1.0.103", + "syn", ] [[package]] @@ -362,9 +344,9 @@ version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -373,9 +355,9 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcdbcee2d9941369faba772587a565f4f534e42cb8d17e5295871de730163b2b" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -530,12 +512,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "futf" version = "0.1.5" @@ -600,9 +576,9 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -691,9 +667,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe69f1cbdb6e28af2bac214e943b99ce8a0a06b447d15d3e61161b0423139f3f" dependencies = [ "proc-macro-hack", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -762,16 +738,16 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.23.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce65ac8028cf5a287a7dbf6c4e0a6cf2dcf022ed5b167a81bae66ebf599a8b7" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" dependencies = [ "log", "mac", "markup5ever", - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -875,7 +851,7 @@ version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ - "autocfg 1.1.0", + "autocfg", "hashbrown", ] @@ -1025,7 +1001,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ - "autocfg 1.1.0", + "autocfg", "scopeguard", ] @@ -1046,21 +1022,30 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "markup5ever" -version = "0.8.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1af46a727284117e09780d05038b1ce6fc9c76cc6df183c3dae5a8955a25e21" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" dependencies = [ "log", - "phf 0.7.24", + "phf 0.10.1", "phf_codegen", - "serde", - "serde_derive", - "serde_json", "string_cache", "string_cache_codegen", "tendril", ] +[[package]] +name = "markup5ever_rcdom" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9521dd6750f8e80ee6c53d65e2e4656d7de37064f3a7a5d2d11d05df93839c2" +dependencies = [ + "html5ever", + "markup5ever", + "tendril", + "xml5ever", +] + [[package]] name = "match_cfg" version = "0.1.0" @@ -1079,7 +1064,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -1130,9 +1115,9 @@ name = "modular-bitfield-impl" version = "0.11.2" source = "git+https://github.com/mthom/modular-bitfield#213535c684af277563678179d8496f11b84a283f" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1187,7 +1172,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" dependencies = [ - "autocfg 1.1.0", + "autocfg", "bitflags", "cfg-if", "libc", @@ -1208,7 +1193,7 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-traits", ] @@ -1218,7 +1203,7 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -1264,9 +1249,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1281,7 +1266,7 @@ version = "0.9.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b" dependencies = [ - "autocfg 1.1.0", + "autocfg", "cc", "libc", "pkg-config", @@ -1345,15 +1330,6 @@ dependencies = [ "windows-sys 0.42.0", ] -[[package]] -name = "phf" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" -dependencies = [ - "phf_shared 0.7.24", -] - [[package]] name = "phf" version = "0.9.0" @@ -1366,23 +1342,22 @@ dependencies = [ ] [[package]] -name = "phf_codegen" -version = "0.7.24" +name = "phf" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ - "phf_generator 0.7.24", - "phf_shared 0.7.24", + "phf_shared 0.10.0", ] [[package]] -name = "phf_generator" -version = "0.7.24" +name = "phf_codegen" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" dependencies = [ - "phf_shared 0.7.24", - "rand 0.6.5", + "phf_generator 0.10.0", + "phf_shared 0.10.0", ] [[package]] @@ -1392,7 +1367,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" dependencies = [ "phf_shared 0.9.0", - "rand 0.8.5", + "rand", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand", ] [[package]] @@ -1404,18 +1389,9 @@ dependencies = [ "phf_generator 0.9.1", "phf_shared 0.9.0", "proc-macro-hack", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", -] - -[[package]] -name = "phf_shared" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" -dependencies = [ - "siphasher 0.2.3", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1424,7 +1400,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" dependencies = [ - "siphasher 0.3.10", + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", ] [[package]] @@ -1490,15 +1475,6 @@ version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid", -] - [[package]] name = "proc-macro2" version = "1.0.47" @@ -1508,22 +1484,13 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -dependencies = [ - "proc-macro2 0.4.30", -] - [[package]] name = "quote" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ - "proc-macro2 1.0.47", + "proc-macro2", ] [[package]] @@ -1536,25 +1503,6 @@ dependencies = [ "nibble_vec", ] -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift", - "winapi", -] - [[package]] name = "rand" version = "0.8.5" @@ -1562,18 +1510,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -1583,24 +1521,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.6.4" @@ -1610,77 +1533,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "redox_syscall" version = "0.2.16" @@ -1881,8 +1733,8 @@ dependencies = [ "ordered-float", "phf 0.9.0", "predicates-core", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2", + "quote", "ref_thread_local", "ring", "ripemd160", @@ -1898,7 +1750,7 @@ dependencies = [ "static_assertions", "strum", "strum_macros", - "syn 1.0.103", + "syn", "to-syn-value", "to-syn-value_derive", "tokio", @@ -1930,12 +1782,13 @@ dependencies = [ [[package]] name = "select" -version = "0.4.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac645958c62108d11f90f8d34e4dc2799c838fc995ed4c2075867a2a8d5be76b" +checksum = "6f9da09dc3f4dfdb6374cbffff7a2cffcec316874d4429899eefdc97b3b94dcd" dependencies = [ "bit-set", "html5ever", + "markup5ever_rcdom", ] [[package]] @@ -1944,28 +1797,6 @@ version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" -[[package]] -name = "serde_derive" -version = "1.0.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" -dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", -] - -[[package]] -name = "serde_json" -version = "1.0.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "serial_test" version = "0.5.1" @@ -1983,9 +1814,9 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2048,12 +1879,6 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -[[package]] -name = "siphasher" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" - [[package]] name = "siphasher" version = "0.3.10" @@ -2066,7 +1891,7 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -2117,38 +1942,30 @@ checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" [[package]] name = "string_cache" -version = "0.7.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c058a82f9fd69b1becf8c274f412281038877c553182f1d02eb027045a2d67" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ - "lazy_static", "new_debug_unreachable", - "phf_shared 0.7.24", + "once_cell", + "parking_lot 0.12.1", + "phf_shared 0.10.0", "precomputed-hash", "serde", - "string_cache_codegen", - "string_cache_shared", ] [[package]] name = "string_cache_codegen" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f45ed1b65bf9a4bf2f7b7dc59212d1926e9eaf00fa998988e420fd124467c6" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" dependencies = [ - "phf_generator 0.7.24", - "phf_shared 0.7.24", - "proc-macro2 1.0.47", - "quote 1.0.21", - "string_cache_shared", + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", ] -[[package]] -name = "string_cache_shared" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" - [[package]] name = "strum" version = "0.23.0" @@ -2162,10 +1979,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38" dependencies = [ "heck", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2", + "quote", "rustversion", - "syn 1.0.103", + "syn", ] [[package]] @@ -2180,25 +1997,14 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" -[[package]] -name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid", -] - [[package]] name = "syn" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2", + "quote", "unicode-ident", ] @@ -2257,9 +2063,9 @@ version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2279,7 +2085,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45dcb7b4108a4793bdd74aa3714296c6eaf43663edf73fa8625d0d7621e68447" dependencies = [ - "syn 1.0.103", + "syn", "to-syn-value_derive", ] @@ -2289,9 +2095,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd4fdec6de01b568c1d3721c9d46a352623c536cd55a8a5acfefb63d1fccccbc" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2300,7 +2106,7 @@ version = "1.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" dependencies = [ - "autocfg 1.1.0", + "autocfg", "bytes", "libc", "memchr", @@ -2320,9 +2126,9 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2405,12 +2211,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - [[package]] name = "untrusted" version = "0.7.1" @@ -2502,9 +2302,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", "wasm-bindgen-shared", ] @@ -2514,7 +2314,7 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ - "quote 1.0.21", + "quote", "wasm-bindgen-macro-support", ] @@ -2524,9 +2324,9 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2678,6 +2478,17 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +[[package]] +name = "xml5ever" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4034e1d05af98b51ad7214527730626f019682d797ba38b51689212118d8e650" +dependencies = [ + "log", + "mac", + "markup5ever", +] + [[package]] name = "xmlparser" version = "0.13.5" diff --git a/Cargo.toml b/Cargo.toml index 975ba64f..df90f142 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ blake2 = "0.8.1" crrl = "0.2.0" native-tls = "0.2.4" chrono = "0.4.11" -select = "0.4.3" +select = "0.6.0" roxmltree = "0.11.0" base64 = "0.12.3" smallvec = "1.8.0" From 0e374c2e96cdece4e16bc74eaa3454c05e37a40f Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 24 May 2023 13:43:52 -0600 Subject: [PATCH 174/361] affirm integers as rational/1 (#1810) --- src/machine/dispatch.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 3067ecb9..9599766d 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -2601,7 +2601,7 @@ impl Machine { read_heap_cell!(d, (HeapCellValueTag::Cons, ptr) => { match_untyped_arena_ptr!(ptr, - (ArenaHeaderTag::Rational, _r) => { + (ArenaHeaderTag::Rational | ArenaHeaderTag::Integer, _r) => { self.machine_st.p += 1; } _ => { @@ -2609,6 +2609,9 @@ impl Machine { } ); } + (HeapCellValueTag::Fixnum) => { + self.machine_st.p += 1; + } _ => { self.machine_st.backtrack(); } @@ -2620,7 +2623,7 @@ impl Machine { read_heap_cell!(d, (HeapCellValueTag::Cons, ptr) => { match_untyped_arena_ptr!(ptr, - (ArenaHeaderTag::Rational, _r) => { + (ArenaHeaderTag::Rational | ArenaHeaderTag::Integer, _r) => { self.machine_st.p = self.machine_st.cp; } _ => { @@ -2628,6 +2631,9 @@ impl Machine { } ); } + (HeapCellValueTag::Fixnum) => { + self.machine_st.p = self.machine_st.cp; + } _ => { self.machine_st.backtrack(); } From 462097d95615181d45d20cfed4daac28d0a6a815 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 26 May 2023 15:19:07 -0600 Subject: [PATCH 175/361] optionally read from machine stack in stackful pre-order iterator (#1812) --- src/heap_iter.rs | 40 ++++++++++++++-- src/heap_print.rs | 93 +++++++++++++++++++++--------------- src/machine/machine_state.rs | 1 + src/machine/mock_wam.rs | 1 + 4 files changed, 93 insertions(+), 42 deletions(-) diff --git a/src/heap_iter.rs b/src/heap_iter.rs index 9760be9e..e957225c 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -1,8 +1,9 @@ #[cfg(test)] pub(crate) use crate::machine::gc::{IteratorUMP, StacklessPreOrderHeapIter}; -use crate::machine::heap::*; use crate::atom_table::*; +use crate::machine::heap::*; +use crate::machine::stack::*; use crate::types::*; use modular_bitfield::prelude::*; @@ -72,6 +73,7 @@ fn forward_if_referent_marked(heap: &mut [HeapCellValue], h: usize) { #[derive(Debug)] pub struct StackfulPreOrderHeapIter<'a> { pub heap: &'a mut Vec, + machine_stack: Option<&'a Stack>, stack: Vec, h: usize, } @@ -109,10 +111,15 @@ impl<'a> StackfulPreOrderHeapIter<'a> { Self { heap, h, + machine_stack: None, stack: vec![IterStackLoc::iterable_heap_loc(h)], } } + pub fn iterate_over_machine_stack(&mut self, stack: &'a Stack) { + self.machine_stack = Some(stack); + } + #[inline] pub fn push_stack(&mut self, h: usize) { self.stack.push(IterStackLoc::iterable_heap_loc(h)); @@ -166,6 +173,26 @@ impl<'a> StackfulPreOrderHeapIter<'a> { } } + fn stack_deref(&self, s: usize) -> Option { + if let Some(stack) = &self.machine_stack { + let mut cell = stack[s]; + + while cell.is_stack_var() { + let s = cell.get_value(); + + if cell == stack[s] { + break; + } + + cell = stack[s]; + } + + return Some(cell); + } + + None + } + fn follow(&mut self) -> Option { while let Some(h) = self.stack.pop() { if h.is_pending_mark() { @@ -193,7 +220,14 @@ impl<'a> StackfulPreOrderHeapIter<'a> { continue; } - read_heap_cell!(*cell, + let cell = if cell.get_tag() == HeapCellValueTag::StackVar { + let cell = *cell; + self.stack_deref(cell.get_value()).unwrap_or(cell) + } else { + *cell + }; + + read_heap_cell!(cell, (HeapCellValueTag::Str | HeapCellValueTag::PStrLoc, vh) => { self.push_if_unmarked(vh); self.stack.push(IterStackLoc::mark_heap_loc(vh)); @@ -241,7 +275,7 @@ impl<'a> StackfulPreOrderHeapIter<'a> { return Some(self.heap[h]); } _ => { - return Some(*cell); + return Some(cell); } ) } diff --git a/src/heap_print.rs b/src/heap_print.rs index 61477b1a..c0ea5fa3 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -14,6 +14,7 @@ use crate::machine::heap::*; use crate::machine::machine_indices::*; use crate::machine::machine_state::pstr_loc_and_offset; use crate::machine::partial_string::*; +use crate::machine::stack::*; use crate::machine::streams::*; use crate::types::*; @@ -474,6 +475,7 @@ pub struct HCPrinter<'a, Outputter> { outputter: Outputter, iter: StackfulPreOrderHeapIter<'a>, atom_tbl: &'a mut AtomTable, + stack: &'a Stack, op_dir: &'a OpDir, state_stack: Vec, toplevel_spec: Option, @@ -539,6 +541,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { pub fn new( heap: &'a mut Heap, atom_tbl: &'a mut AtomTable, + stack: &'a Stack, op_dir: &'a OpDir, output: Outputter, cell: HeapCellValue, @@ -547,6 +550,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { outputter: output, iter: stackful_preorder_iter(heap, cell), atom_tbl, + stack, op_dir, state_stack: vec![], toplevel_spec: None, @@ -1441,12 +1445,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { ) { let negated_operand = negated_op_needs_bracketing(&self.iter, self.op_dir, &op); - let addr = match self.check_for_seen() { - Some(addr) => addr, - None => return, - }; - - let print_atom = |printer: &mut Self, name: Atom, arity: usize| { + let print_struct = |printer: &mut Self, name: Atom, arity: usize| { if name == atom!("[]") && arity == 0 { if !printer.at_cdr("") { append_str!(printer, "[]"); @@ -1494,29 +1493,33 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } }; + let addr = match self.check_for_seen() { + Some(addr) => addr, + None => return, + }; + read_heap_cell!(addr, (HeapCellValueTag::Atom, (name, arity)) => { - print_atom(self, name, arity); + print_struct(self, name, arity); } (HeapCellValueTag::Char, c) => { let name = self.atom_tbl.build_with(&String::from(c)); - print_atom(self, name, 0); - // print_char!(self, self.quoted, c); + print_struct(self, name, 0); } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.iter.heap[s]) .get_name_and_arity(); if let Some(spec) = fetch_op_spec(name, arity, self.op_dir) { - self.handle_op_as_struct( - name, - arity, - &op, - is_functor_redirect, - spec, - negated_operand, - max_depth, - ); + self.handle_op_as_struct( + name, + arity, + &op, + is_functor_redirect, + spec, + negated_operand, + max_depth, + ); } else { push_space_if_amb!(self, name.as_str(), { self.format_clause(max_depth, arity, name, None); @@ -1551,27 +1554,27 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } (HeapCellValueTag::Cons, c) => { match_untyped_arena_ptr!(c, - (ArenaHeaderTag::Integer, n) => { - self.print_number(max_depth, NumberFocus::Unfocused(Number::Integer(n)), &op); - } - (ArenaHeaderTag::Rational, r) => { - self.print_number(max_depth, NumberFocus::Unfocused(Number::Rational(r)), &op); - } - (ArenaHeaderTag::Stream, stream) => { - self.print_stream(stream, max_depth); - } - (ArenaHeaderTag::OssifiedOpDir, _op_dir) => { - self.print_impromptu_atom(atom!("$ossified_op_dir")); - } - (ArenaHeaderTag::Dropped, _value) => { - self.print_impromptu_atom(atom!("$dropped_value")); - } - (ArenaHeaderTag::IndexPtr, index_ptr) => { - self.print_index_ptr(*index_ptr, max_depth); - } - _ => { - } - ); + (ArenaHeaderTag::Integer, n) => { + self.print_number(max_depth, NumberFocus::Unfocused(Number::Integer(n)), &op); + } + (ArenaHeaderTag::Rational, r) => { + self.print_number(max_depth, NumberFocus::Unfocused(Number::Rational(r)), &op); + } + (ArenaHeaderTag::Stream, stream) => { + self.print_stream(stream, max_depth); + } + (ArenaHeaderTag::OssifiedOpDir, _op_dir) => { + self.print_impromptu_atom(atom!("$ossified_op_dir")); + } + (ArenaHeaderTag::Dropped, _value) => { + self.print_impromptu_atom(atom!("$dropped_value")); + } + (ArenaHeaderTag::IndexPtr, index_ptr) => { + self.print_index_ptr(*index_ptr, max_depth); + } + _ => { + } + ); } _ => { unreachable!() @@ -1594,6 +1597,8 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { pub fn print(mut self) -> Outputter { let spec = self.toplevel_spec.take(); + + self.iter.iterate_over_machine_stack(self.stack); self.handle_heap_term(spec, false, self.max_depth); while let Some(loc_data) = self.state_stack.pop() { @@ -1665,6 +1670,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1693,6 +1699,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1716,6 +1723,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1728,6 +1736,7 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1760,6 +1769,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), @@ -1778,6 +1788,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), @@ -1794,6 +1805,7 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1825,6 +1837,7 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1847,6 +1860,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), pstr_loc_as_cell!(0) @@ -1874,6 +1888,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index e489742a..6deab8d1 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -766,6 +766,7 @@ 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, diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index 2ddde129..f71f32e3 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -62,6 +62,7 @@ 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), From dc02be494484af62b2b36804630b416824695be4 Mon Sep 17 00:00:00 2001 From: notoria Date: Sat, 27 May 2023 13:08:25 +0200 Subject: [PATCH 176/361] Remove and move comments --- src/lib/clpz.pl | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index b6ad08b3..43b8f26e 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -4990,7 +4990,6 @@ run_propagator(ptzdiv(X,Y,Z), MState) --> run_propagator(pmod(X,Y,Z), MState) --> ( Y == 0 -> { false } ; Y == Z -> { false } - % ; nonvar(Y), Z == X -> true ; X == Y -> kill(MState), queue_goal(Z = 0) ; true ), @@ -5008,7 +5007,7 @@ run_propagator(pmod(X,Y,Z), MState) --> ), { fd_get(X, XD0, XPs), domain_remove_smaller_than(XD0, XMin, XD2) }, - fd_put(X, XD2, XPs) + fd_put(X, XD2, XPs) % queue_goal(X #>= XMin) ; true ), @@ -5016,7 +5015,7 @@ run_propagator(pmod(X,Y,Z), MState) --> XMax is Z + Y * ((XU - Z) div Y), { fd_get(X, XD1, XPs), domain_remove_greater_than(XD1, XMax, XD3) }, - fd_put(X, XD3, XPs) + fd_put(X, XD3, XPs) % queue_goal(X #=< XMax) ; true ) @@ -5041,13 +5040,13 @@ run_propagator(pmod(X,Y,Z), MState) --> { fd_get(Y, YD, YPs), YMin is Z + 1, domain_remove_smaller_than(YD, YMin, YD1) }, - fd_put(Y, YD1, YPs) + fd_put(Y, YD1, YPs) % queue_goal(Y #> Z) ; Z < 0 -> { fd_get(Y, YD, YPs), YMax is Z - 1, domain_remove_greater_than(YD, YMax, YD1) }, - fd_put(Y, YD1, YPs) + fd_put(Y, YD1, YPs) % queue_goal(Y #< Z) ; true ) @@ -5067,7 +5066,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> queue_goal(Z = X) ; { fd_get(Z, ZD0, ZPs), domain_remove_greater_than(ZD0, X, ZD2) }, - fd_put(Z, ZD2, ZPs) + fd_put(Z, ZD2, ZPs) % queue_goal(Z #=< X) ) ; X < 0 -> @@ -5076,7 +5075,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> queue_goal(Z = X) ; { fd_get(Z, ZD0, ZPs), domain_remove_smaller_than(ZD0, X, ZD2) }, - fd_put(Z, ZD2, ZPs) + fd_put(Z, ZD2, ZPs) % queue_goal(Z #>= X) ) ), @@ -5085,14 +5084,14 @@ run_propagator(pmodz(X,Y,Z), MState) --> { fd_get(Z, ZD1, ZPs), domain_remove_smaller_than(ZD1, 0, ZD3), domain_remove_greater_than(ZD3, ZMax, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in 0..ZMax) ; { fd_get(Y, _, n(YL), n(YU), _), YU < 0 } -> ZMin is YL + 1, { fd_get(Z, ZD1, ZPs), domain_remove_greater_than(ZD1, 0, ZD3), domain_remove_smaller_than(ZD3, ZMin, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in ZMin..0) ; true ) @@ -5107,7 +5106,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> { fd_get(Z, ZD1, ZPs), domain_remove_greater_than(ZD1, 0, ZD3), domain_remove_smaller_than(ZD3, ZMin, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in ZMin..0) ) ; Y > 0 -> @@ -5118,7 +5117,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> { fd_get(Z, ZD1, ZPs), domain_remove_smaller_than(ZD1, 0, ZD3), domain_remove_greater_than(ZD3, ZMax, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in 0..ZMax) ) ) @@ -5133,12 +5132,12 @@ run_propagator(pmodz(X,Y,Z), MState) --> ; ( { fd_get(X, _, n(XL), n(XU), _), XL >= 0 } -> { fd_get(Z, ZD0, ZPs), domain_remove_greater_than(ZD0, XU, ZD2) }, - fd_put(Z, ZD2, ZPs) + fd_put(Z, ZD2, ZPs) % queue_goal(Z #=< XU) ; { fd_get(X, _, n(XL), n(XU), _), XU =< 0 } -> { fd_get(Z, ZD0, ZPs), domain_remove_smaller_than(ZD0, XL, ZD2) }, - fd_put(Z, ZD2, ZPs) + fd_put(Z, ZD2, ZPs) % queue_goal(Z #>= XL) ; true ), @@ -5147,14 +5146,14 @@ run_propagator(pmodz(X,Y,Z), MState) --> { fd_get(Z, ZD1, ZPs), domain_remove_smaller_than(ZD1, 0, ZD3), domain_remove_greater_than(ZD3, ZMax, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in 0..ZMax) ; { fd_get(Y, _, n(YL), n(YU), _), YU < 0 } -> ZMin is YL + 1, { fd_get(Z, ZD1, ZPs), domain_remove_greater_than(ZD1, 0, ZD3), domain_remove_smaller_than(ZD3, ZMin, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in ZMin..0) ; { fd_get(Y, _, n(YL), n(YU), _) } -> ZMin is YL + 1, @@ -5162,19 +5161,19 @@ run_propagator(pmodz(X,Y,Z), MState) --> { fd_get(Z, ZD1, ZPs), domain_remove_greater_than(ZD1, ZMax, ZD3), domain_remove_smaller_than(ZD3, ZMin, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in ZMin..ZMax) ; { fd_get(Y, _, _, n(YU), _), YU > 0 } -> { fd_get(Z, ZD1, ZPs), ZMax is YU - 1, domain_remove_greater_than(ZD1, ZMax, ZD3) }, - fd_put(Z, ZD3, ZPs) + fd_put(Z, ZD3, ZPs) % queue_goal(Z #< YU) ; { fd_get(Y, _, n(YL), _, _), YL < 0 } -> { fd_get(Z, ZD1, ZPs), ZMin is YL + 1, domain_remove_smaller_than(ZD1, ZMin, ZD3) }, - fd_put(Z, ZD3, ZPs) + fd_put(Z, ZD3, ZPs) % queue_goal(Z #> YL) ; true ) @@ -5185,29 +5184,31 @@ run_propagator(pmody(X,Y,Z), MState) --> ( nonvar(Y) -> true % Nothing to do. % ; nonvar(X) -> true ; nonvar(Z) -> - ( Z > 0 -> % queue_goal(Y #> Z) + ( Z > 0 -> { fd_get(Y, YD, YPs), YMin is Z + 1, domain_remove_smaller_than(YD, YMin, YD1) }, - fd_put(Y, YD1, YPs) - ; Z < 0 -> % queue_goal(Y #< Z) + fd_put(Y, YD1, YPs) + % queue_goal(Y #> Z) + ; Z < 0 -> { fd_get(Y, YD, YPs), YMax is Z - 1, domain_remove_greater_than(YD, YMax, YD1) }, - fd_put(Y, YD1, YPs) + fd_put(Y, YD1, YPs) + % queue_goal(Y #< Z) ; Z =:= 0 -> kill(MState), queue_goal(X / Y #= _) ) ; ( { fd_get(Z, _, n(ZL), _, _), ZL > 0 } -> { fd_get(Y, YD, YPs), YMin is ZL + 1, domain_remove_smaller_than(YD, YMin, YD1) }, - fd_put(Y, YD1, YPs) + fd_put(Y, YD1, YPs) % queue_goal(Y #> ZL) ; { fd_get(Z, _, _, n(ZU), _), ZU < 0 } -> { fd_get(Y, YD, YPs), YMax is ZU - 1, domain_remove_greater_than(YD, YMax, YD1) }, - fd_put(Y, YD1, YPs) + fd_put(Y, YD1, YPs) % queue_goal(Y #< ZU) ; true ) From 05d48cdcc397ac870e955146e02a5ef7645a05df Mon Sep 17 00:00:00 2001 From: notoria Date: Sat, 27 May 2023 13:19:19 +0200 Subject: [PATCH 177/361] Don't add variable ?- Z #= 0, Z #= X mod Y. Z = 0, clpz:(_A*Y#=X), clpz:(Y in inf.. -1\/1..sup) % Unexpected. The expected result: Z = 0, clpz:(X mod Y#=0), clpz:(Y in inf.. -1\/1..sup). --- src/lib/clpz.pl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 43b8f26e..4cdcdc9a 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -5019,8 +5019,6 @@ run_propagator(pmod(X,Y,Z), MState) --> % queue_goal(X #=< XMax) ; true ) - % kill(MState), - % queue_goal(X #= Z + Y * _) % Add a variable to be efficient. ; nonvar(Z), nonvar(X) -> ( Z > 0 -> ( X < 0 -> true @@ -5180,7 +5178,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> ) ). -run_propagator(pmody(X,Y,Z), MState) --> +run_propagator(pmody(_X,Y,Z), _MState) --> ( nonvar(Y) -> true % Nothing to do. % ; nonvar(X) -> true ; nonvar(Z) -> @@ -5196,7 +5194,7 @@ run_propagator(pmody(X,Y,Z), MState) --> domain_remove_greater_than(YD, YMax, YD1) }, fd_put(Y, YD1, YPs) % queue_goal(Y #< Z) - ; Z =:= 0 -> kill(MState), queue_goal(X / Y #= _) + ; Z =:= 0 % Multiple solutions so do nothing special. ) ; ( { fd_get(Z, _, n(ZL), _, _), ZL > 0 } -> { fd_get(Y, YD, YPs), From 495df8846addb8581f20c6b212dca1a89351f717 Mon Sep 17 00:00:00 2001 From: notoria Date: Sat, 27 May 2023 13:47:14 +0200 Subject: [PATCH 178/361] Compute correctly the domain of the remainder --- src/lib/clpz.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 4cdcdc9a..8f9c6bf8 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -5153,7 +5153,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> domain_remove_smaller_than(ZD3, ZMin, ZD5) }, fd_put(Z, ZD5, ZPs) % queue_goal(Z in ZMin..0) - ; { fd_get(Y, _, n(YL), n(YU), _) } -> + ; { fd_get(Y, _, n(YL), n(YU), _), YL < 0, YU > 0 } -> ZMin is YL + 1, ZMax is YU - 1, { fd_get(Z, ZD1, ZPs), From b656700294444142a6cb70071ec6c8c8d9cc0871 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 26 May 2023 15:19:07 -0600 Subject: [PATCH 179/361] read from machine stack in stackful pre-order iterator (#1812) --- src/heap_iter.rs | 454 +++++++++++++++++++--------- src/heap_print.rs | 80 +++-- src/machine/arithmetic_ops.rs | 2 +- src/machine/attributed_variables.rs | 6 +- src/machine/gc.rs | 6 +- src/machine/loader.rs | 4 +- src/machine/machine_state.rs | 6 +- src/machine/machine_state_impl.rs | 4 +- src/machine/system_calls.rs | 4 +- src/machine/unify.rs | 8 +- 10 files changed, 368 insertions(+), 206 deletions(-) diff --git a/src/heap_iter.rs b/src/heap_iter.rs index e957225c..96aef41d 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -19,28 +19,45 @@ enum IterStackLocTag { PendingMark, } +#[derive(BitfieldSpecifier, Clone, Copy, Debug, PartialEq, Eq)] +#[bits = 1] +pub enum HeapOrStackTag { + Heap, + Stack, +} + #[bitfield] #[repr(u64)] #[derive(Clone, Copy, Debug)] pub struct IterStackLoc { - value: B62, + pub value: B61, tag: IterStackLocTag, + heap_or_stack: HeapOrStackTag, } impl IterStackLoc { #[inline] - pub fn iterable_heap_loc(h: usize) -> Self { - IterStackLoc::new().with_tag(IterStackLocTag::Iterable).with_value(h as u64) + pub fn iterable_loc(h: usize, heap_or_stack: HeapOrStackTag) -> Self { + IterStackLoc::new() + .with_tag(IterStackLocTag::Iterable) + .with_heap_or_stack(heap_or_stack) + .with_value(h as u64) } #[inline] - pub fn mark_heap_loc(h: usize) -> Self { - IterStackLoc::new().with_tag(IterStackLocTag::Marked).with_value(h as u64) + fn mark_loc(h: usize, heap_or_stack: HeapOrStackTag) -> Self { + IterStackLoc::new() + .with_tag(IterStackLocTag::Marked) + .with_heap_or_stack(heap_or_stack) + .with_value(h as u64) } #[inline] - pub fn pending_mark_heap_loc(h: usize) -> Self { - IterStackLoc::new().with_tag(IterStackLocTag::PendingMark).with_value(h as u64) + fn pending_mark_loc(h: usize, heap_or_stack: HeapOrStackTag) -> Self { + IterStackLoc::new() + .with_tag(IterStackLocTag::PendingMark) + .with_heap_or_stack(heap_or_stack) + .with_value(h as u64) } #[inline] @@ -52,39 +69,35 @@ impl IterStackLoc { pub fn is_pending_mark(self) -> bool { self.tag() == IterStackLocTag::PendingMark } -} -#[inline] -fn forward_if_referent_marked(heap: &mut [HeapCellValue], h: usize) { - read_heap_cell!(heap[h], - (HeapCellValueTag::Str - | HeapCellValueTag::Lis - | HeapCellValueTag::AttrVar - | HeapCellValueTag::Var - | HeapCellValueTag::PStrLoc, vh) => { - if heap[vh].get_mark_bit() { - heap[h].set_forwarding_bit(true); + #[inline] + pub fn as_ref(self) -> Ref { + match self.heap_or_stack() { + HeapOrStackTag::Heap => { + Ref::heap_cell(self.value() as usize) + } + HeapOrStackTag::Stack => { + Ref::stack_cell(self.value() as usize) } } - _ => {} - ) + } } #[derive(Debug)] pub struct StackfulPreOrderHeapIter<'a> { pub heap: &'a mut Vec, - machine_stack: Option<&'a Stack>, + pub machine_stack: &'a mut Stack, stack: Vec, - h: usize, + h: IterStackLoc, } impl<'a> Drop for StackfulPreOrderHeapIter<'a> { fn drop(&mut self) { while let Some(h) = self.stack.pop() { - let h = h.value() as usize; + let cell = self.read_cell_mut(h); - self.heap[h].set_forwarding_bit(false); - self.heap[h].set_mark_bit(false); + cell.set_forwarding_bit(false); + cell.set_mark_bit(false); } self.heap.pop(); @@ -92,53 +105,93 @@ impl<'a> Drop for StackfulPreOrderHeapIter<'a> { } pub trait FocusedHeapIter: Iterator { - fn focus(&self) -> usize; + fn focus(&self) -> IterStackLoc; } impl<'a> FocusedHeapIter for StackfulPreOrderHeapIter<'a> { #[inline] - fn focus(&self) -> usize { + fn focus(&self) -> IterStackLoc { self.h } } impl<'a> StackfulPreOrderHeapIter<'a> { #[inline] - fn new(heap: &'a mut Vec, cell: HeapCellValue) -> Self { - let h = heap.len(); + fn new(heap: &'a mut Vec, stack: &'a mut Stack, cell: HeapCellValue) -> Self { + let h = IterStackLoc::iterable_loc(heap.len(), HeapOrStackTag::Heap); heap.push(cell); Self { heap, h, - machine_stack: None, - stack: vec![IterStackLoc::iterable_heap_loc(h)], + machine_stack: stack, + stack: vec![h], } } - pub fn iterate_over_machine_stack(&mut self, stack: &'a Stack) { - self.machine_stack = Some(stack); + #[inline] + fn forward_if_referent_marked(&mut self, loc: IterStackLoc) { + read_heap_cell!(self.read_cell(loc), + (HeapCellValueTag::Str | + HeapCellValueTag::Lis | + HeapCellValueTag::AttrVar | + HeapCellValueTag::Var | + HeapCellValueTag::PStrLoc, vh) => { + if self.heap[vh].get_mark_bit() { + self.read_cell_mut(loc).set_forwarding_bit(true); + } + } + (HeapCellValueTag::StackVar, vs) => { + if self.machine_stack[vs].get_mark_bit() { + self.read_cell_mut(loc).set_forwarding_bit(true); + } + } + _ => {} + ); } #[inline] - pub fn push_stack(&mut self, h: usize) { - self.stack.push(IterStackLoc::iterable_heap_loc(h)); + pub fn push_stack(&mut self, h: IterStackLoc) { + self.stack.push(h); } #[inline] - pub fn stack_last(&self) -> Option { + pub fn read_cell_mut(&mut self, loc: IterStackLoc) -> &mut HeapCellValue { + match loc.heap_or_stack() { + HeapOrStackTag::Heap => { + &mut self.heap[loc.value() as usize] + } + HeapOrStackTag::Stack => { + &mut self.machine_stack[loc.value() as usize] + } + } + } + + #[inline] + pub fn read_cell(&self, loc: IterStackLoc) -> HeapCellValue { + match loc.heap_or_stack() { + HeapOrStackTag::Heap => { + self.heap[loc.value() as usize] + } + HeapOrStackTag::Stack => { + self.machine_stack[loc.value() as usize] + } + } + } + + #[inline] + pub fn stack_last(&self) -> Option { for h in self.stack.iter().rev() { let is_readable_marked = h.is_marked(); - let h = h.value() as usize; - let cell = self.heap[h]; + let cell = self.read_cell(*h); if cell.get_forwarding_bit() { - return Some(h); + return Some(*h); } else if cell.get_mark_bit() && !is_readable_marked { continue; } - return Some(h); + return Some(*h); } None @@ -148,10 +201,9 @@ impl<'a> StackfulPreOrderHeapIter<'a> { pub fn pop_stack(&mut self) -> Option { while let Some(h) = self.stack.pop() { let is_readable_marked = h.is_marked(); - let h = h.value() as usize; - self.h = h; - let cell = &mut self.heap[h]; + self.h = h; + let cell = self.read_cell_mut(h); if cell.get_forwarding_bit() { cell.set_forwarding_bit(false); @@ -166,50 +218,29 @@ impl<'a> StackfulPreOrderHeapIter<'a> { None } - fn push_if_unmarked(&mut self, h: usize) { - if !self.heap[h].get_mark_bit() { - self.heap[h].set_mark_bit(true); - self.stack.push(IterStackLoc::iterable_heap_loc(h)); + fn push_if_unmarked(&mut self, loc: IterStackLoc) { + let cell = self.read_cell_mut(loc); + + if !cell.get_mark_bit() { + cell.set_mark_bit(true); + self.stack.push(IterStackLoc::iterable_loc(loc.value() as usize, loc.heap_or_stack())); } } - fn stack_deref(&self, s: usize) -> Option { - if let Some(stack) = &self.machine_stack { - let mut cell = stack[s]; - - while cell.is_stack_var() { - let s = cell.get_value(); - - if cell == stack[s] { - break; - } - - cell = stack[s]; - } - - return Some(cell); - } - - None - } - fn follow(&mut self) -> Option { while let Some(h) = self.stack.pop() { if h.is_pending_mark() { - let h = h.value() as usize; - self.push_if_unmarked(h); - self.stack.push(IterStackLoc::mark_heap_loc(h)); + self.stack.push(IterStackLoc::mark_loc(h.value() as usize, h.heap_or_stack())); - forward_if_referent_marked(&mut self.heap, h); + self.forward_if_referent_marked(h); continue; } - let is_readable_marked = h.is_marked(); - let h = h.value() as usize; - self.h = h; - let cell = &mut self.heap[h]; + + let is_readable_marked = h.is_marked(); + let cell = self.read_cell_mut(h); if cell.get_forwarding_bit() { let copy = *cell; @@ -220,62 +251,73 @@ impl<'a> StackfulPreOrderHeapIter<'a> { continue; } - let cell = if cell.get_tag() == HeapCellValueTag::StackVar { - let cell = *cell; - self.stack_deref(cell.get_value()).unwrap_or(cell) - } else { - *cell - }; - - read_heap_cell!(cell, + read_heap_cell!(*cell, (HeapCellValueTag::Str | HeapCellValueTag::PStrLoc, vh) => { - self.push_if_unmarked(vh); - self.stack.push(IterStackLoc::mark_heap_loc(vh)); + let loc = IterStackLoc::iterable_loc(vh, HeapOrStackTag::Heap); + + self.push_if_unmarked(loc); + self.stack.push(IterStackLoc::mark_loc(vh, HeapOrStackTag::Heap)); } (HeapCellValueTag::Lis, vh) => { - self.push_if_unmarked(vh); + let loc = IterStackLoc::iterable_loc(vh, HeapOrStackTag::Heap); - self.stack.push(IterStackLoc::pending_mark_heap_loc(vh + 1)); - self.stack.push(IterStackLoc::mark_heap_loc(vh)); + self.push_if_unmarked(loc); - forward_if_referent_marked(&mut self.heap, vh); + self.stack.push(IterStackLoc::pending_mark_loc(vh + 1, HeapOrStackTag::Heap)); + self.stack.push(IterStackLoc::mark_loc(vh, HeapOrStackTag::Heap)); - return Some(self.heap[h]); + self.forward_if_referent_marked(loc); + + return Some(self.read_cell(h)); } (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, vh) => { - self.push_if_unmarked(vh); - self.stack.push(IterStackLoc::mark_heap_loc(vh)); - forward_if_referent_marked(&mut self.heap, vh); + let loc = IterStackLoc::iterable_loc(vh, HeapOrStackTag::Heap); + + self.push_if_unmarked(loc); + self.stack.push(IterStackLoc::mark_loc(vh, HeapOrStackTag::Heap)); + self.forward_if_referent_marked(loc); + } + (HeapCellValueTag::StackVar, vs) => { + let loc = IterStackLoc::iterable_loc(vs, HeapOrStackTag::Stack); + + self.push_if_unmarked(loc); + self.stack.push(IterStackLoc::mark_loc(vs, HeapOrStackTag::Stack)); + self.forward_if_referent_marked(loc); } (HeapCellValueTag::PStrOffset, offset) => { - self.push_if_unmarked(offset); - self.stack.push(IterStackLoc::iterable_heap_loc(h+1)); + self.push_if_unmarked(IterStackLoc::iterable_loc(offset, HeapOrStackTag::Heap)); + self.stack.push(IterStackLoc::iterable_loc((h.value()+1) as usize, HeapOrStackTag::Heap)); - return Some(self.heap[h]); + return Some(self.read_cell(h)); } (HeapCellValueTag::PStr) => { - self.push_if_unmarked(h); + let tail_loc = IterStackLoc::iterable_loc((h.value()+1) as usize, HeapOrStackTag::Heap); - self.stack.push(IterStackLoc::iterable_heap_loc(h+1)); - forward_if_referent_marked(&mut self.heap, h+1); + self.push_if_unmarked(IterStackLoc::iterable_loc(h.value() as usize, HeapOrStackTag::Heap)); + self.stack.push(tail_loc); + self.forward_if_referent_marked(tail_loc); - return Some(self.heap[h]); + return Some(self.read_cell(h)); } (HeapCellValueTag::Atom, (_name, arity)) => { - for h in (h + 2 .. h + arity + 1).rev() { - self.stack.push(IterStackLoc::pending_mark_heap_loc(h)); + let l = h.value() as usize; + + for l in (l + 2 .. l + arity + 1).rev() { + self.stack.push(IterStackLoc::pending_mark_loc(l, HeapOrStackTag::Heap)); } if arity > 0 { - self.push_if_unmarked(h+1); - self.stack.push(IterStackLoc::mark_heap_loc(h+1)); - forward_if_referent_marked(&mut self.heap, h+1); + let first_arg_loc = IterStackLoc::iterable_loc(l+1, HeapOrStackTag::Heap); + + self.push_if_unmarked(first_arg_loc); + self.stack.push(IterStackLoc::mark_loc(l+1, HeapOrStackTag::Heap)); + self.forward_if_referent_marked(first_arg_loc); } - return Some(self.heap[h]); + return Some(self.read_cell(h)); } _ => { - return Some(cell); + return Some(*cell); } ) } @@ -303,19 +345,20 @@ pub(crate) fn stackless_preorder_iter( } #[inline(always)] -pub(crate) fn stackful_preorder_iter( - heap: &mut Vec, +pub(crate) fn stackful_preorder_iter<'a>( + heap: &'a mut Vec, + stack: &'a mut Stack, cell: HeapCellValue, -) -> StackfulPreOrderHeapIter { - StackfulPreOrderHeapIter::new(heap, cell) +) -> StackfulPreOrderHeapIter<'a> { + StackfulPreOrderHeapIter::new(heap, stack, cell) } #[derive(Debug)] pub(crate) struct PostOrderIterator { - focus: usize, + focus: IterStackLoc, base_iter: Iter, base_iter_valid: bool, - parent_stack: Vec<(usize, HeapCellValue, usize)>, // number of children, parent node, focus. + parent_stack: Vec<(usize, HeapCellValue, IterStackLoc)>, // number of children, parent node, focus. } impl Deref for PostOrderIterator { @@ -329,7 +372,7 @@ impl Deref for PostOrderIterator { impl PostOrderIterator { pub(crate) fn new(base_iter: Iter) -> Self { PostOrderIterator { - focus: 0, + focus: IterStackLoc::iterable_loc(0, HeapOrStackTag::Heap), base_iter, base_iter_valid: true, parent_stack: vec![], @@ -386,7 +429,7 @@ impl Iterator for PostOrderIterator { impl FocusedHeapIter for PostOrderIterator { #[inline(always)] - fn focus(&self) -> usize { + fn focus(&self) -> IterStackLoc { self.focus } } @@ -402,7 +445,8 @@ impl PostOrderIterator { if let Some((_child_count, item, focus)) = self.parent_stack.last() { read_heap_cell!(item, (HeapCellValueTag::Atom, (_name, arity)) => { - return focus + arity >= idx_loc && *focus < idx_loc; + let focus = focus.value() as usize; + return focus + arity >= idx_loc && focus < idx_loc; } _ => {} ); @@ -435,9 +479,10 @@ impl<'a> LeftistPostOrderHeapIter<'a> { #[inline] pub(crate) fn stackful_post_order_iter<'a>( heap: &'a mut Heap, + stack: &'a mut Stack, cell: HeapCellValue, ) -> LeftistPostOrderHeapIter<'a> { - PostOrderIterator::new(StackfulPreOrderHeapIter::new(heap, cell)) + PostOrderIterator::new(StackfulPreOrderHeapIter::new(heap, stack, cell)) } #[cfg(test)] @@ -1416,7 +1461,11 @@ mod tests { .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + str_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1447,7 +1496,11 @@ mod tests { )); for _ in 0..20 { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + str_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1475,7 +1528,12 @@ mod tests { { wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); + let mut var = heap_loc_as_cell!(0); // self-referencing variables are copied with their forwarding @@ -1497,7 +1555,11 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1517,7 +1579,11 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1549,7 +1615,11 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(0)); { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); // the cycle will be iterated twice before being detected. assert_eq!( @@ -1577,7 +1647,11 @@ mod tests { } { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); // cut the iteration short to check that all cells are // unmarked and unforwarded by the Drop instance of @@ -1611,7 +1685,11 @@ mod tests { let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!( @@ -1631,7 +1709,11 @@ mod tests { let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; { - let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_preorder_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); @@ -1650,7 +1732,12 @@ mod tests { wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0i64))); { - let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_preorder_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); + let pstr_offset_cell = pstr_offset_as_cell!(0); // pstr_offset_cell.set_forwarding_bit(true); @@ -1675,7 +1762,12 @@ mod tests { wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(1i64))); { - let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_preorder_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); + let pstr_offset_cell = pstr_offset_as_cell!(0); // pstr_offset_cell.set_forwarding_bit(true); @@ -1688,7 +1780,7 @@ mod tests { let h = iter.focus(); - assert_eq!(h, 5); + assert_eq!(h.value(), 5); assert_eq!(unmark_cell_bits!(iter.heap[4]), pstr_offset_as_cell!(0)); assert_eq!(unmark_cell_bits!(iter.heap[5]), fixnum_as_cell!(Fixnum::build_with(1i64))); @@ -1708,7 +1800,11 @@ mod tests { wam.machine_st.heap.extend(functor); { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1767,7 +1863,11 @@ mod tests { wam.machine_st.heap[4] = list_loc_as_cell!(1); { - let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_preorder_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1834,6 +1934,7 @@ mod tests { { let mut iter = StackfulPreOrderHeapIter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.stack, heap_loc_as_cell!(0), ); @@ -1865,6 +1966,7 @@ mod tests { { let mut iter = stackful_preorder_iter( &mut wam.machine_st.heap, + &mut wam.machine_st.stack, heap_loc_as_cell!(0), ); @@ -1899,6 +2001,7 @@ mod tests { { let mut iter = stackful_preorder_iter( &mut wam.machine_st.heap, + &mut wam.machine_st.stack, heap_loc_as_cell!(0), ); @@ -1933,7 +2036,11 @@ mod tests { .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + str_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1964,7 +2071,11 @@ mod tests { )); for _ in 0..20 { // 0000 { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + str_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1994,7 +2105,12 @@ mod tests { { wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); + let mut var = heap_loc_as_cell!(0); // self-referencing variables are copied with their forwarding @@ -2016,7 +2132,11 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2036,7 +2156,11 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2068,7 +2192,11 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(0)); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); // the cycle will be iterated twice before being detected. assert_eq!( @@ -2098,6 +2226,7 @@ mod tests { { let mut iter = stackful_post_order_iter( &mut wam.machine_st.heap, + &mut wam.machine_st.stack, heap_loc_as_cell!(0), ); @@ -2133,7 +2262,11 @@ mod tests { let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2152,7 +2285,11 @@ mod tests { let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2171,7 +2308,11 @@ mod tests { wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0i64))); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(0i64))); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0)); @@ -2186,7 +2327,11 @@ mod tests { wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(1i64))); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(1i64))); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0)); @@ -2210,7 +2355,11 @@ mod tests { wam.machine_st.heap.extend(functor); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2270,7 +2419,11 @@ mod tests { wam.machine_st.heap[4] = list_loc_as_cell!(1); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2377,7 +2530,10 @@ mod tests { )); for _ in 0..20 { - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter( + &mut wam.machine_st.heap, + str_loc_as_cell!(0), + ); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(0)); @@ -2407,7 +2563,10 @@ mod tests { { wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter( + &mut wam.machine_st.heap, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2423,7 +2582,10 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter( + &mut wam.machine_st.heap, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), diff --git a/src/heap_print.rs b/src/heap_print.rs index c0ea5fa3..910600ab 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -116,7 +116,9 @@ impl<'a> StackfulPreOrderHeapIter<'a> { let mut parent_spec = DirectedOp::Left(atom!("-"), OpDesc::build_with(200, FY as u8)); loop { - read_heap_cell!(self.heap[h], + let cell = self.read_cell(h); + + read_heap_cell!(cell, (HeapCellValueTag::Str, s) => { read_heap_cell!(self.heap[s], (HeapCellValueTag::Atom, (name, _arity)) => { @@ -125,7 +127,7 @@ impl<'a> StackfulPreOrderHeapIter<'a> { if needs_bracketing(spec, &parent_spec) { return false; } else { - h = s + 1; + h = IterStackLoc::iterable_loc(s + 1, HeapOrStackTag::Heap); parent_spec = DirectedOp::Right(name, spec); continue; } @@ -140,7 +142,7 @@ impl<'a> StackfulPreOrderHeapIter<'a> { ) } _ => { - return property_check(self.heap[h]); + return property_check(cell); } ) } @@ -150,12 +152,12 @@ impl<'a> StackfulPreOrderHeapIter<'a> { where P: Fn(HeapCellValue) -> bool, { - let addr = match self.stack_last() { - Some(h) => self.heap[h], + let cell = match self.stack_last() { + Some(h) => self.read_cell(h), None => return false, }; - property_check(addr) + property_check(cell) } } @@ -475,7 +477,6 @@ pub struct HCPrinter<'a, Outputter> { outputter: Outputter, iter: StackfulPreOrderHeapIter<'a>, atom_tbl: &'a mut AtomTable, - stack: &'a Stack, op_dir: &'a OpDir, state_stack: Vec, toplevel_spec: Option, @@ -541,16 +542,15 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { pub fn new( heap: &'a mut Heap, atom_tbl: &'a mut AtomTable, - stack: &'a Stack, + stack: &'a mut Stack, op_dir: &'a OpDir, output: Outputter, cell: HeapCellValue, ) -> Self { HCPrinter { outputter: output, - iter: stackful_preorder_iter(heap, cell), + iter: stackful_preorder_iter(heap, stack, cell), atom_tbl, - stack, op_dir, state_stack: vec![], toplevel_spec: None, @@ -758,14 +758,14 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { fn format_numbered_vars(&mut self) -> bool { let h = self.iter.stack_last().unwrap(); - let addr = self.iter.heap[h]; - let addr = heap_bound_store( + let cell = self.iter.read_cell(h); + let cell = heap_bound_store( &self.iter.heap, - heap_bound_deref(&self.iter.heap, addr), + heap_bound_deref(&self.iter.heap, cell), ); // 7.10.4 - if let Some(var) = numbervar(&self.numbervars_offset, addr) { + if let Some(var) = numbervar(&self.numbervars_offset, cell) { self.iter.pop_stack(); self.state_stack.push(TokenOrRedirect::NumberedVar(var)); return true; @@ -809,11 +809,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { }; } - fn offset_as_string(&mut self, h: usize) -> Option { - let addr = self.iter.heap[h]; + fn offset_as_string(&mut self, h: IterStackLoc) -> Option { + let cell = self.iter.read_cell(h); - if let Some(var) = self.var_names.get(&addr) { - read_heap_cell!(addr, + if let Some(var) = self.var_names.get(&cell) { + read_heap_cell!(cell, (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { return Some(format!("{}", var.as_str())); } @@ -824,7 +824,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { ); } - read_heap_cell!(addr, + read_heap_cell!(cell, (HeapCellValueTag::Lis | HeapCellValueTag::Str, h) => { Some(format!("{}", h)) } @@ -1167,7 +1167,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { fn print_list_like(&mut self, mut max_depth: usize) { let focus = self.iter.focus(); - let mut heap_pstr_iter = HeapPStrIter::new(self.iter.heap, focus); + let mut heap_pstr_iter = HeapPStrIter::new(self.iter.heap, focus.value() as usize); if heap_pstr_iter.next().is_some() { while let Some(_) = heap_pstr_iter.next() {} @@ -1179,7 +1179,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let end_cell = heap_pstr_iter.focus; if self.check_max_depth(&mut max_depth) { - self.remove_list_children(focus); + self.remove_list_children(focus.value() as usize); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); return; } @@ -1187,26 +1187,26 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let at_cdr = self.outputter.ends_with("|"); if !at_cdr && !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) { - self.remove_list_children(focus); - return self.print_proper_string(focus, max_depth); + self.remove_list_children(focus.value() as usize); + return self.print_proper_string(focus.value() as usize, max_depth); } if self.ignore_ops { self.at_cdr(","); - self.remove_list_children(focus); + self.remove_list_children(focus.value() as usize); - if !self.print_string_as_functor(focus, max_depth) { + if !self.print_string_as_functor(focus.value() as usize, max_depth) { if end_cell == empty_list_as_cell!() { append_str!(self, "[]"); } else { self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); - self.iter.push_stack(end_h); + self.iter.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap)); } } } else { let value = heap_bound_store( self.iter.heap, - heap_bound_deref(self.iter.heap, self.iter.heap[focus]), + heap_bound_deref(self.iter.heap, self.iter.read_cell(focus)), ); read_heap_cell!(value, @@ -1217,7 +1217,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let switch = Rc::new(Cell::new((!at_cdr, 0))); self.state_stack.push(TokenOrRedirect::CloseList(switch.clone())); - let (h, offset) = pstr_loc_and_offset(self.iter.heap, focus); + let (h, offset) = pstr_loc_and_offset(self.iter.heap, focus.value() as usize); let pstr = cell_as_string!(self.iter.heap[h]); let pstr = pstr.as_str_from(offset.get_num() as usize); @@ -1239,7 +1239,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.state_stack.push(TokenOrRedirect::HeadTailSeparator); } else if end_cell != empty_list_as_cell!() { if tag == HeapCellValueTag::PStrOffset { - self.iter.push_stack(end_h); + self.iter.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap)); } self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); @@ -1597,8 +1597,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { pub fn print(mut self) -> Outputter { let spec = self.toplevel_spec.take(); - - self.iter.iterate_over_machine_stack(self.stack); self.handle_heap_term(spec, false, self.max_depth); while let Some(loc_data) = self.state_stack.pop() { @@ -1670,7 +1668,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1699,7 +1697,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1723,7 +1721,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1736,7 +1734,7 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1769,7 +1767,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), @@ -1788,7 +1786,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), @@ -1805,7 +1803,7 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1837,7 +1835,7 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1860,7 +1858,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), pstr_loc_as_cell!(0) @@ -1888,7 +1886,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index 774b848f..f5aa982f 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -1106,7 +1106,7 @@ impl MachineState { pub(crate) fn arith_eval_by_metacall(&mut self, value: HeapCellValue) -> Result { 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() { diff --git a/src/machine/attributed_variables.rs b/src/machine/attributed_variables.rs index 57ea1c22..633378a0 100644 --- a/src/machine/attributed_variables.rs +++ b/src/machine/attributed_variables.rs @@ -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,7 +147,7 @@ impl MachineState { let value = unmark_cell_bits!(value); - if h != iter.focus() { + 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) { @@ -167,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; } diff --git a/src/machine/gc.rs b/src/machine/gc.rs index 8a884950..1de28ffe 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -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) } } diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 616fe7ee..989b963b 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -536,7 +536,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { 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); + let mut iter = stackful_post_order_iter(&mut machine_st.heap, &mut machine_st.stack, term_addr); while let Some(addr) = iter.next() { let addr = unmark_cell_bits!(addr); @@ -568,7 +568,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { term_stack.push(Term::Literal(Cell::default(), Literal::try_from(addr).unwrap())); } (HeapCellValueTag::Atom, (name, arity)) => { - let h = iter.focus(); + let h = iter.focus().value() as usize; let mut arity = arity; if iter.heap.len() > h + arity + 1 { diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 6deab8d1..08c12ccd 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -558,10 +558,10 @@ impl MachineState { let mut singleton_var_set: IndexMap = IndexMap::new(); - for addr in stackful_preorder_iter(&mut self.heap, term) { - let addr = unmark_cell_bits!(addr); + for cell in stackful_preorder_iter(&mut self.heap, &mut self.stack, term) { + let cell = unmark_cell_bits!(cell); - if let Some(var) = addr.as_var() { + if let Some(var) = cell.as_var() { if !singleton_var_set.contains_key(&var) { singleton_var_set.insert(var, true); } else { diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index b457ecae..b4dc3fea 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -1125,7 +1125,7 @@ impl MachineState { return false; } - let mut iter = stackful_preorder_iter(&mut self.heap, value); + let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, value); while let Some(value) = iter.next() { if value.get_forwarding_bit() { @@ -1626,7 +1626,7 @@ impl MachineState { return true; } - let mut iter = stackful_preorder_iter(&mut self.heap, value); + let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, value); while let Some(value) = iter.next() { let value = unmark_cell_bits!(value); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index e457a611..61bd9b67 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -532,7 +532,7 @@ impl MachineState { seen_set: &mut IndexSet, value: HeapCellValue, ) { - let mut iter = stackful_preorder_iter(&mut self.heap, value); + let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, value); while let Some(value) = iter.next() { let value = unmark_cell_bits!(value); @@ -722,7 +722,7 @@ impl MachineState { let mut seen_set = IndexSet::new(); { - let mut iter = stackful_post_order_iter(&mut self.heap, term); + let mut iter = stackful_post_order_iter(&mut self.heap, &mut self.stack, term); while let Some(value) = iter.next() { if iter.parent_stack_len() >= max_depth { diff --git a/src/machine/unify.rs b/src/machine/unify.rs index 19445fe4..d6401b92 100644 --- a/src/machine/unify.rs +++ b/src/machine/unify.rs @@ -651,10 +651,12 @@ fn bind_with_occurs_check(unifier: &mut U, r: Ref, value: HeapCellVa let mut occurs_triggered = false; if !value.is_constant() { - for addr in stackful_preorder_iter(&mut unifier.heap, value) { - let addr = unmark_cell_bits!(addr); + let machine_st: &mut MachineState = unifier.deref_mut(); - if let Some(inner_r) = addr.as_var() { + 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; From 07115ce4f5516a3fd3b50c0e4695545af53dd6fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Mon, 29 May 2023 00:29:53 +0200 Subject: [PATCH 180/361] Rename INDEX.md to INDEX.dj and add banner about Scryer Prolog Meetup --- INDEX.md => INDEX.dj | 7 +++++++ 1 file changed, 7 insertions(+) rename INDEX.md => INDEX.dj (87%) diff --git a/INDEX.md b/INDEX.dj similarity index 87% rename from INDEX.md rename to INDEX.dj index 907d1e9c..c7cf6a11 100644 --- a/INDEX.md +++ b/INDEX.dj @@ -5,6 +5,13 @@ X = "Scryer Prolog!". ``` +``` =html +
+

Scryer Prolog Meetup 2023

+

The first annual Scryer Prolog meetup is going to happen in Düsseldorf (Germany) on the 9th and 10th of November 2023. Join us to discover the present and future of Scryer Prolog! Participation is free, registration not required. More details here.

+
+``` + ![scryer](scryer.png){width=128 style=float:right;} [Scryer Prolog](https://github.com/mthom/scryer-prolog) is a free software ISO Prolog system intended to be an industrial strength production environment *and* a testbed for bleeding edge research in logic and constraint programming. From 5154314786f0921e6ca65d1d99fc88e6ed6d49b5 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Mon, 29 May 2023 11:12:00 +0200 Subject: [PATCH 181/361] FIXED: correct dereferencing in atom_codes/2 and number_codes/2. This addresses #1818. Test case: run :- length(Ls, L), portray_clause(L), maplist(=(X), Ls), X = Y, Y = 12, atom_codes(_, Ls), false. --- src/machine/system_calls.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 61bd9b67..84714436 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1011,6 +1011,8 @@ impl MachineState { let mut string = String::new(); for addr in addrs { + let addr = self.store(self.deref(addr)); + match Number::try_from(addr) { Ok(Number::Fixnum(n)) => { match u32::try_from(n.get_num()) { From 6093c2858dfe313cdebe5941baf843006edaf958 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 29 May 2023 20:49:54 -0600 Subject: [PATCH 182/361] read set_value args from temp regs of put_unsafe_value (#1812) --- src/fixtures.rs | 10 +++++++--- src/heap_print.rs | 20 ++++++++++---------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/fixtures.rs b/src/fixtures.rs index 65340da0..01a5e385 100644 --- a/src/fixtures.rs +++ b/src/fixtures.rs @@ -258,6 +258,7 @@ pub(crate) struct UnsafeVarMarker { pub(crate) safe_perm_vars: IndexSet, pub(crate) safe_temp_vars: IndexSet, pub(crate) temp_vars_to_perm_vars: IndexMap, + pub(crate) perm_vars_to_temp_vars: IndexMap, } impl UnsafeVarMarker { @@ -268,6 +269,7 @@ impl UnsafeVarMarker { safe_perm_vars: IndexSet::new(), safe_temp_vars: IndexSet::new(), temp_vars_to_perm_vars: IndexMap::new(), + perm_vars_to_temp_vars: IndexMap::new(), } } @@ -344,14 +346,16 @@ impl UnsafeVarMarker { if let Some(ph) = self.unsafe_perm_vars.swap_remove(&p) { if ph == phase { *query_instr = Instruction::PutUnsafeValue(p, arg); - self.safe_perm_vars.insert(p); + self.perm_vars_to_temp_vars.insert(p, arg); } else { self.unsafe_perm_vars.insert(p, ph); } } } - &mut Instruction::SetValue(r @ RegType::Perm(p)) - if !self.safe_perm_vars.contains(&p) => { + &mut Instruction::SetValue(r @ RegType::Perm(p)) => + if let Some(t) = self.perm_vars_to_temp_vars.get(&p) { + *query_instr = Instruction::SetValue(RegType::Temp(*t)); + } else { *query_instr = Instruction::SetLocalValue(r); self.safe_perm_vars.insert(p); diff --git a/src/heap_print.rs b/src/heap_print.rs index 910600ab..7eba8aca 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -841,19 +841,19 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } fn check_for_seen(&mut self) -> Option { - if let Some(addr) = self.iter.next() { - let is_cyclic = addr.get_forwarding_bit(); + if let Some(cell) = self.iter.next() { + let is_cyclic = cell.get_forwarding_bit(); - let addr = heap_bound_store( + let cell = heap_bound_store( self.iter.heap, - heap_bound_deref(self.iter.heap, addr), + heap_bound_deref(self.iter.heap, cell), ); - let addr = unmark_cell_bits!(addr); + let cell = unmark_cell_bits!(cell); - match self.var_names.get(&addr).cloned() { - Some(var) if addr.is_var() => { - // If addr is an unbound variable and maps to + match self.var_names.get(&cell).cloned() { + Some(var) if cell.is_var() => { + // If cell is an unbound variable and maps to // a name via heap_locs, append the name to // the current output, and return None. None // short-circuits handle_heap_term. @@ -868,7 +868,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { None } var_opt => { - if is_cyclic && addr.is_compound(self.iter.heap) { + if is_cyclic && cell.is_compound(self.iter.heap) { // self-referential variables are marked "cyclic". match var_opt { Some(var) => { @@ -889,7 +889,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { return None; } - Some(addr) + Some(cell) } } } else { From 3c344b176b87594ade5bb22b1b2d6489edb3a130 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 6 Jun 2023 00:58:44 -0600 Subject: [PATCH 183/361] set_local_value does not make values safe (#1812) --- src/fixtures.rs | 3 --- src/heap_print.rs | 1 - 2 files changed, 4 deletions(-) diff --git a/src/fixtures.rs b/src/fixtures.rs index 01a5e385..1f812e2c 100644 --- a/src/fixtures.rs +++ b/src/fixtures.rs @@ -357,9 +357,6 @@ impl UnsafeVarMarker { *query_instr = Instruction::SetValue(RegType::Temp(*t)); } else { *query_instr = Instruction::SetLocalValue(r); - - self.safe_perm_vars.insert(p); - self.unsafe_perm_vars.remove(&p); } _ => {} } diff --git a/src/heap_print.rs b/src/heap_print.rs index 7eba8aca..21835d49 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -848,7 +848,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.iter.heap, heap_bound_deref(self.iter.heap, cell), ); - let cell = unmark_cell_bits!(cell); match self.var_names.get(&cell).cloned() { From 98b0ab3409c43b51f2dd61993edf50bdd6ecf5bf Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 10 Jun 2023 01:25:47 -0600 Subject: [PATCH 184/361] improve call/N implementation (#1829) --- build/instructions_template.rs | 12 +- src/loader.pl | 1225 +++++++++++--------------------- src/machine/dispatch.rs | 52 +- src/machine/loader.rs | 15 + src/machine/machine_indices.rs | 10 +- src/machine/system_calls.rs | 273 +++---- src/macros.rs | 1 + src/toplevel.pl | 3 +- 8 files changed, 598 insertions(+), 993 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index bec70e7e..1fd2a60a 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -560,8 +560,8 @@ enum SystemClauseType { StripModule, #[strum_discriminants(strum(props(Arity = "4", Name = "$compile_inline_or_expanded_goal")))] CompileInlineOrExpandedGoal, - #[strum_discriminants(strum(props(Arity = "arity", Name = "$call_inline")))] - InlineCallN(usize), + #[strum_discriminants(strum(props(Arity = "arity", Name = "$fast_call")))] + FastCallN(usize), #[strum_discriminants(strum(props(Arity = "1", Name = "$is_expanded_or_inlined")))] IsExpandedOrInlined, #[strum_discriminants(strum(props(Arity = "3", Name = "$get_clause_p")))] @@ -1467,11 +1467,11 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::DefaultExecuteN(arity, _) => { functor!(atom!("execute_default_n"), [fixnum(arity)]) } - &Instruction::CallInlineCallN(arity, _) => { - functor!(atom!("call_n_inline"), [fixnum(arity)]) + &Instruction::CallFastCallN(arity, _) => { + functor!(atom!("call_fast_call_n"), [fixnum(arity)]) } - &Instruction::ExecuteInlineCallN(arity, _) => { - functor!(atom!("call_n_inline"), [fixnum(arity)]) + &Instruction::ExecuteFastCallN(arity, _) => { + functor!(atom!("execute_fast_call_n"), [fixnum(arity)]) } &Instruction::CallTermGreaterThan(_) | &Instruction::CallTermLessThan(_) | diff --git a/src/loader.pl b/src/loader.pl index 896d6bff..754ed96f 100644 --- a/src/loader.pl +++ b/src/loader.pl @@ -758,7 +758,6 @@ expand_goal(UnexpandedGoals, Module, ExpandedGoals) :- UnexpandedGoals = ExpandedGoals), !. - :- non_counted_backtracking expand_goal/4. expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars) :- @@ -779,7 +778,6 @@ expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars) :- ) ). - /* * private predicate for use in call/N. it doesn't specially consider * control predicates as expand_goal does with expand_goal_cases. @@ -790,27 +788,20 @@ expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars) :- expand_call_goal(UnexpandedGoals, Module, ExpandedGoals) :- % if a goal isn't callable, defer to call/N to report the error. - catch(loader:expand_call_goal_(UnexpandedGoals, Module, ExpandedGoals), + catch('$call'(loader:expand_call_goal_(UnexpandedGoals, Module, ExpandedGoals)), error(type_error(callable, _), _), - UnexpandedGoals = ExpandedGoals), + '$call'(UnexpandedGoals = ExpandedGoals)), !. - :- non_counted_backtracking expand_call_goal_/3. expand_call_goal_(UnexpandedGoals, Module, ExpandedGoals) :- ( var(UnexpandedGoals) -> - expand_module_names(call(UnexpandedGoals), [0], Module, ExpandedGoals, []) + UnexpandedGoals = ExpandedGoals ; goal_expansion(UnexpandedGoals, Module, UnexpandedGoals1), ( Module \== user -> - goal_expansion(UnexpandedGoals1, user, Goals) - ; Goals = UnexpandedGoals1 - ), - ( predicate_property(Module:Goals, meta_predicate(MetaSpecs0)), - MetaSpecs0 =.. [_ | MetaSpecs] -> - expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, []) - ; thread_goals(Goals, ExpandedGoals, (',')) - ; Goals = ExpandedGoals + goal_expansion(UnexpandedGoals1, user, ExpandedGoals) + ; ExpandedGoals = UnexpandedGoals1 ) ). @@ -838,7 +829,6 @@ expand_goal_cases((Module:Goals0), _, ExpandedGoals, HeadVars) :- expand_goal(Goals0, Module, Goals1, HeadVars), ExpandedGoals = (Module:Goals1). - :- non_counted_backtracking thread_goals/3. thread_goals(Goals0, Goals1, Functor) :- @@ -853,7 +843,6 @@ thread_goals(Goals0, Goals1, Functor) :- ; Goals1 = Goals0 ). - :- non_counted_backtracking thread_goals/4. thread_goals(Goals0, Goals1, Hole, Functor) :- @@ -872,8 +861,6 @@ thread_goals(Goals0, Goals1, Hole, Functor) :- % % call/{1-64} with dynamic goal expansion. % -% The program used to generate the call/N predicates: -% % :- use_module(library(between)). % :- use_module(library(error)). % :- use_module(library(lists)). @@ -884,22 +871,18 @@ thread_goals(Goals0, Goals1, Hole, Functor) :- % Head =.. [call, G | Args], % CallNHead =.. [call, '$call'(G) | Args], % N1 is N + 1, -% InlineCall =.. ['$call_inline', G0 | Args], -% CallClause =.. ['$prepare_call_clause', G1, M1, G | Args], -% ModuleCallClause0 =.. ['$module_call', M1, G1], -% ModuleCallClause1 =.. ['$module_call', M2, G3], +% StripModule =.. ['$strip_module', G, M1, G1], +% FastCall =.. ['$fast_call', G | Args], +% PrepareCallClause =.. [ '$prepare_call_clause', G2, G1 | Args], +% ModuleCall =.. ['$module_call', M2, G4], % Clauses = [(Head :- var(G), % instantiation_error(call/N1)), -% (Head :- '$strip_module'(G, _, G0), InlineCall), -% (CallNHead :- !, -% CallClause, -% '$call_with_inference_counting'(ModuleCallClause0)), -% (Head :- CallClause, -% ( '$call_inline'(G1) -% ; expand_call_goal(G1, M1, G2), -% strip_subst_module(G2, M1, M2, G3), -% '$call_with_inference_counting'(ModuleCallClause1) -% ))]. +% (Head :- FastCall), +% (Head :- StripModule, +% PrepareCallClause, +% expand_call_goal(G2, M1, G3), +% strip_subst_module(G3, M1, M2, G4), +% '$call_with_inference_counting'(ModuleCall))]. % % generate_call_forms :- % between(1, 64, N), @@ -915,1237 +898,847 @@ thread_goals(Goals0, Goals1, Hole, Functor) :- % The '$call' functor is an escape hatch from goal expansion. So far, % it is used only to avoid infinite recursion into expand_call_goal/3. -:-non_counted_backtracking call/1. +:- non_counted_backtracking call/1. + call(G) :- - var(G), - instantiation_error(call/1). + var(G), + instantiation_error(call/1). call(G) :- - '$strip_module'(G, _, G0), - '$call_inline'(G0). -call('$call'(G0)) :- - !, - '$prepare_call_clause'(G,M,G0), - '$call_with_inference_counting'('$module_call'(M, G)). -call(G) :- - '$prepare_call_clause'(G0,M1,G), - ( '$call_inline'(G0) %% '$call_inline' cuts (only) after succeeding. - ; expand_call_goal(G0, M1, G1), - strip_subst_module(G1, M1, M2, G2), - '$call_with_inference_counting'('$module_call'(M2, G2)) - ). + '$fast_call'(G). +call(G0) :- + '$strip_module'(G0, M0, G1), + expand_call_goal(G1, M0, G2), + strip_subst_module(G2, M0, M1, G3), + '$call_with_inference_counting'('$module_call'(M1, G3)). :-non_counted_backtracking call/2. call(A,B) :- var(A), instantiation_error(call/2). call(A,B) :- - '$strip_module'(A,C,D), - '$call_inline'(D,B). -call('$call'(A),B) :- - !, - '$prepare_call_clause'(C,D,A,B), - '$call_with_inference_counting'('$module_call'(D,C)). + '$fast_call'(A,B). call(A,B) :- - '$prepare_call_clause'(C,D,A,B), - ( '$call_inline'(C) - ; expand_call_goal(C,D,E), - strip_subst_module(E,D,F,G), - '$call_with_inference_counting'('$module_call'(F,G)) - ). + '$strip_module'(A,C,D), + '$prepare_call_clause'(E,D,B), + expand_call_goal(E,C,F), + strip_subst_module(F,C,G,H), + '$call_with_inference_counting'('$module_call'(G,H)). :-non_counted_backtracking call/3. call(A,B,C) :- var(A), instantiation_error(call/3). call(A,B,C) :- - '$strip_module'(A,D,E), - '$call_inline'(E,B,C). -call('$call'(A),B,C) :- - !, - '$prepare_call_clause'(D,E,A,B,C), - '$call_with_inference_counting'('$module_call'(E,D)). + '$fast_call'(A,B,C). call(A,B,C) :- - '$prepare_call_clause'(D,E,A,B,C), - ( '$call_inline'(D) - ; expand_call_goal(D,E,F), - strip_subst_module(F,E,G,H), - '$call_with_inference_counting'('$module_call'(G,H)) - ). + '$strip_module'(A,D,E), + '$prepare_call_clause'(F,E,B,C), + expand_call_goal(F,D,G), + strip_subst_module(G,D,H,I), + '$call_with_inference_counting'('$module_call'(H,I)). :-non_counted_backtracking call/4. call(A,B,C,D) :- var(A), instantiation_error(call/4). call(A,B,C,D) :- - '$strip_module'(A,E,F), - '$call_inline'(F,B,C,D). -call('$call'(A),B,C,D) :- - !, - '$prepare_call_clause'(E,F,A,B,C,D), - '$call_with_inference_counting'('$module_call'(F,E)). + '$fast_call'(A,B,C,D). call(A,B,C,D) :- - '$prepare_call_clause'(E,F,A,B,C,D), - ( '$call_inline'(E) - ; expand_call_goal(E,F,G), - strip_subst_module(G,F,H,I), - '$call_with_inference_counting'('$module_call'(H,I)) - ). + '$strip_module'(A,E,F), + '$prepare_call_clause'(G,F,B,C,D), + expand_call_goal(G,E,H), + strip_subst_module(H,E,I,J), + '$call_with_inference_counting'('$module_call'(I,J)). :-non_counted_backtracking call/5. call(A,B,C,D,E) :- var(A), instantiation_error(call/5). call(A,B,C,D,E) :- - '$strip_module'(A,F,G), - '$call_inline'(G,B,C,D,E). -call('$call'(A),B,C,D,E) :- - !, - '$prepare_call_clause'(F,G,A,B,C,D,E), - '$call_with_inference_counting'('$module_call'(G,F)). + '$fast_call'(A,B,C,D,E). call(A,B,C,D,E) :- - '$prepare_call_clause'(F,G,A,B,C,D,E), - ( '$call_inline'(F) - ; expand_call_goal(F,G,H), - strip_subst_module(H,G,I,J), - '$call_with_inference_counting'('$module_call'(I,J)) - ). + '$strip_module'(A,F,G), + '$prepare_call_clause'(H,G,B,C,D,E), + expand_call_goal(H,F,I), + strip_subst_module(I,F,J,K), + '$call_with_inference_counting'('$module_call'(J,K)). :-non_counted_backtracking call/6. call(A,B,C,D,E,F) :- var(A), instantiation_error(call/6). call(A,B,C,D,E,F) :- - '$strip_module'(A,G,H), - '$call_inline'(H,B,C,D,E,F). -call('$call'(A),B,C,D,E,F) :- - !, - '$prepare_call_clause'(G,H,A,B,C,D,E,F), - '$call_with_inference_counting'('$module_call'(H,G)). + '$fast_call'(A,B,C,D,E,F). call(A,B,C,D,E,F) :- - '$prepare_call_clause'(G,H,A,B,C,D,E,F), - ( '$call_inline'(G) - ; expand_call_goal(G,H,I), - strip_subst_module(I,H,J,K), - '$call_with_inference_counting'('$module_call'(J,K)) - ). + '$strip_module'(A,G,H), + '$prepare_call_clause'(I,H,B,C,D,E,F), + expand_call_goal(I,G,J), + strip_subst_module(J,G,K,L), + '$call_with_inference_counting'('$module_call'(K,L)). :-non_counted_backtracking call/7. call(A,B,C,D,E,F,G) :- var(A), instantiation_error(call/7). call(A,B,C,D,E,F,G) :- - '$strip_module'(A,H,I), - '$call_inline'(I,B,C,D,E,F,G). -call('$call'(A),B,C,D,E,F,G) :- - !, - '$prepare_call_clause'(H,I,A,B,C,D,E,F,G), - '$call_with_inference_counting'('$module_call'(I,H)). + '$fast_call'(A,B,C,D,E,F,G). call(A,B,C,D,E,F,G) :- - '$prepare_call_clause'(H,I,A,B,C,D,E,F,G), - ( '$call_inline'(H) - ; expand_call_goal(H,I,J), - strip_subst_module(J,I,K,L), - '$call_with_inference_counting'('$module_call'(K,L)) - ). + '$strip_module'(A,H,I), + '$prepare_call_clause'(J,I,B,C,D,E,F,G), + expand_call_goal(J,H,K), + strip_subst_module(K,H,L,M), + '$call_with_inference_counting'('$module_call'(L,M)). :-non_counted_backtracking call/8. call(A,B,C,D,E,F,G,H) :- var(A), instantiation_error(call/8). call(A,B,C,D,E,F,G,H) :- - '$strip_module'(A,I,J), - '$call_inline'(J,B,C,D,E,F,G,H). -call('$call'(A),B,C,D,E,F,G,H) :- - !, - '$prepare_call_clause'(I,J,A,B,C,D,E,F,G,H), - '$call_with_inference_counting'('$module_call'(J,I)). + '$fast_call'(A,B,C,D,E,F,G,H). call(A,B,C,D,E,F,G,H) :- - '$prepare_call_clause'(I,J,A,B,C,D,E,F,G,H), - ( '$call_inline'(I) - ; expand_call_goal(I,J,K), - strip_subst_module(K,J,L,M), - '$call_with_inference_counting'('$module_call'(L,M)) - ). + '$strip_module'(A,I,J), + '$prepare_call_clause'(K,J,B,C,D,E,F,G,H), + expand_call_goal(K,I,L), + strip_subst_module(L,I,M,N), + '$call_with_inference_counting'('$module_call'(M,N)). :-non_counted_backtracking call/9. call(A,B,C,D,E,F,G,H,I) :- var(A), instantiation_error(call/9). call(A,B,C,D,E,F,G,H,I) :- - '$strip_module'(A,J,K), - '$call_inline'(K,B,C,D,E,F,G,H,I). -call('$call'(A),B,C,D,E,F,G,H,I) :- - !, - '$prepare_call_clause'(J,K,A,B,C,D,E,F,G,H,I), - '$call_with_inference_counting'('$module_call'(K,J)). + '$fast_call'(A,B,C,D,E,F,G,H,I). call(A,B,C,D,E,F,G,H,I) :- - '$prepare_call_clause'(J,K,A,B,C,D,E,F,G,H,I), - ( '$call_inline'(J) - ; expand_call_goal(J,K,L), - strip_subst_module(L,K,M,N), - '$call_with_inference_counting'('$module_call'(M,N)) - ). + '$strip_module'(A,J,K), + '$prepare_call_clause'(L,K,B,C,D,E,F,G,H,I), + expand_call_goal(L,J,M), + strip_subst_module(M,J,N,O), + '$call_with_inference_counting'('$module_call'(N,O)). :-non_counted_backtracking call/10. call(A,B,C,D,E,F,G,H,I,J) :- var(A), instantiation_error(call/10). call(A,B,C,D,E,F,G,H,I,J) :- - '$strip_module'(A,K,L), - '$call_inline'(L,B,C,D,E,F,G,H,I,J). -call('$call'(A),B,C,D,E,F,G,H,I,J) :- - !, - '$prepare_call_clause'(K,L,A,B,C,D,E,F,G,H,I,J), - '$call_with_inference_counting'('$module_call'(L,K)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J). call(A,B,C,D,E,F,G,H,I,J) :- - '$prepare_call_clause'(K,L,A,B,C,D,E,F,G,H,I,J), - ( '$call_inline'(K) - ; expand_call_goal(K,L,M), - strip_subst_module(M,L,N,O), - '$call_with_inference_counting'('$module_call'(N,O)) - ). + '$strip_module'(A,K,L), + '$prepare_call_clause'(M,L,B,C,D,E,F,G,H,I,J), + expand_call_goal(M,K,N), + strip_subst_module(N,K,O,P), + '$call_with_inference_counting'('$module_call'(O,P)). :-non_counted_backtracking call/11. call(A,B,C,D,E,F,G,H,I,J,K) :- var(A), instantiation_error(call/11). call(A,B,C,D,E,F,G,H,I,J,K) :- - '$strip_module'(A,L,M), - '$call_inline'(M,B,C,D,E,F,G,H,I,J,K). -call('$call'(A),B,C,D,E,F,G,H,I,J,K) :- - !, - '$prepare_call_clause'(L,M,A,B,C,D,E,F,G,H,I,J,K), - '$call_with_inference_counting'('$module_call'(M,L)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K). call(A,B,C,D,E,F,G,H,I,J,K) :- - '$prepare_call_clause'(L,M,A,B,C,D,E,F,G,H,I,J,K), - ( '$call_inline'(L) - ; expand_call_goal(L,M,N), - strip_subst_module(N,M,O,P), - '$call_with_inference_counting'('$module_call'(O,P)) - ). + '$strip_module'(A,L,M), + '$prepare_call_clause'(N,M,B,C,D,E,F,G,H,I,J,K), + expand_call_goal(N,L,O), + strip_subst_module(O,L,P,Q), + '$call_with_inference_counting'('$module_call'(P,Q)). :-non_counted_backtracking call/12. call(A,B,C,D,E,F,G,H,I,J,K,L) :- var(A), instantiation_error(call/12). call(A,B,C,D,E,F,G,H,I,J,K,L) :- - '$strip_module'(A,M,N), - '$call_inline'(N,B,C,D,E,F,G,H,I,J,K,L). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L) :- - !, - '$prepare_call_clause'(M,N,A,B,C,D,E,F,G,H,I,J,K,L), - '$call_with_inference_counting'('$module_call'(N,M)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L). call(A,B,C,D,E,F,G,H,I,J,K,L) :- - '$prepare_call_clause'(M,N,A,B,C,D,E,F,G,H,I,J,K,L), - ( '$call_inline'(M) - ; expand_call_goal(M,N,O), - strip_subst_module(O,N,P,Q), - '$call_with_inference_counting'('$module_call'(P,Q)) - ). + '$strip_module'(A,M,N), + '$prepare_call_clause'(O,N,B,C,D,E,F,G,H,I,J,K,L), + expand_call_goal(O,M,P), + strip_subst_module(P,M,Q,R), + '$call_with_inference_counting'('$module_call'(Q,R)). :-non_counted_backtracking call/13. call(A,B,C,D,E,F,G,H,I,J,K,L,M) :- var(A), instantiation_error(call/13). call(A,B,C,D,E,F,G,H,I,J,K,L,M) :- - '$strip_module'(A,N,O), - '$call_inline'(O,B,C,D,E,F,G,H,I,J,K,L,M). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M) :- - !, - '$prepare_call_clause'(N,O,A,B,C,D,E,F,G,H,I,J,K,L,M), - '$call_with_inference_counting'('$module_call'(O,N)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M). call(A,B,C,D,E,F,G,H,I,J,K,L,M) :- - '$prepare_call_clause'(N,O,A,B,C,D,E,F,G,H,I,J,K,L,M), - ( '$call_inline'(N) - ; expand_call_goal(N,O,P), - strip_subst_module(P,O,Q,R), - '$call_with_inference_counting'('$module_call'(Q,R)) - ). + '$strip_module'(A,N,O), + '$prepare_call_clause'(P,O,B,C,D,E,F,G,H,I,J,K,L,M), + expand_call_goal(P,N,Q), + strip_subst_module(Q,N,R,S), + '$call_with_inference_counting'('$module_call'(R,S)). :-non_counted_backtracking call/14. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N) :- var(A), instantiation_error(call/14). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N) :- - '$strip_module'(A,O,P), - '$call_inline'(P,B,C,D,E,F,G,H,I,J,K,L,M,N). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N) :- - !, - '$prepare_call_clause'(O,P,A,B,C,D,E,F,G,H,I,J,K,L,M,N), - '$call_with_inference_counting'('$module_call'(P,O)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N) :- - '$prepare_call_clause'(O,P,A,B,C,D,E,F,G,H,I,J,K,L,M,N), - ( '$call_inline'(O) - ; expand_call_goal(O,P,Q), - strip_subst_module(Q,P,R,S), - '$call_with_inference_counting'('$module_call'(R,S)) - ). + '$strip_module'(A,O,P), + '$prepare_call_clause'(Q,P,B,C,D,E,F,G,H,I,J,K,L,M,N), + expand_call_goal(Q,O,R), + strip_subst_module(R,O,S,T), + '$call_with_inference_counting'('$module_call'(S,T)). :-non_counted_backtracking call/15. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) :- var(A), instantiation_error(call/15). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) :- - '$strip_module'(A,P,Q), - '$call_inline'(Q,B,C,D,E,F,G,H,I,J,K,L,M,N,O). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O) :- - !, - '$prepare_call_clause'(P,Q,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O), - '$call_with_inference_counting'('$module_call'(Q,P)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) :- - '$prepare_call_clause'(P,Q,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O), - ( '$call_inline'(P) - ; expand_call_goal(P,Q,R), - strip_subst_module(R,Q,S,T), - '$call_with_inference_counting'('$module_call'(S,T)) - ). + '$strip_module'(A,P,Q), + '$prepare_call_clause'(R,Q,B,C,D,E,F,G,H,I,J,K,L,M,N,O), + expand_call_goal(R,P,S), + strip_subst_module(S,P,T,U), + '$call_with_inference_counting'('$module_call'(T,U)). :-non_counted_backtracking call/16. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :- var(A), instantiation_error(call/16). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :- - '$strip_module'(A,Q,R), - '$call_inline'(R,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :- - !, - '$prepare_call_clause'(Q,R,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P), - '$call_with_inference_counting'('$module_call'(R,Q)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :- - '$prepare_call_clause'(Q,R,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P), - ( '$call_inline'(Q) - ; expand_call_goal(Q,R,S), - strip_subst_module(S,R,T,U), - '$call_with_inference_counting'('$module_call'(T,U)) - ). + '$strip_module'(A,Q,R), + '$prepare_call_clause'(S,R,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P), + expand_call_goal(S,Q,T), + strip_subst_module(T,Q,U,V), + '$call_with_inference_counting'('$module_call'(U,V)). :-non_counted_backtracking call/17. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :- var(A), instantiation_error(call/17). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :- - '$strip_module'(A,R,S), - '$call_inline'(S,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :- - !, - '$prepare_call_clause'(R,S,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q), - '$call_with_inference_counting'('$module_call'(S,R)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :- - '$prepare_call_clause'(R,S,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q), - ( '$call_inline'(R) - ; expand_call_goal(R,S,T), - strip_subst_module(T,S,U,V), - '$call_with_inference_counting'('$module_call'(U,V)) - ). + '$strip_module'(A,R,S), + '$prepare_call_clause'(T,S,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q), + expand_call_goal(T,R,U), + strip_subst_module(U,R,V,W), + '$call_with_inference_counting'('$module_call'(V,W)). :-non_counted_backtracking call/18. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :- var(A), instantiation_error(call/18). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :- - '$strip_module'(A,S,T), - '$call_inline'(T,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :- - !, - '$prepare_call_clause'(S,T,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R), - '$call_with_inference_counting'('$module_call'(T,S)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :- - '$prepare_call_clause'(S,T,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R), - ( '$call_inline'(S) - ; expand_call_goal(S,T,U), - strip_subst_module(U,T,V,W), - '$call_with_inference_counting'('$module_call'(V,W)) - ). + '$strip_module'(A,S,T), + '$prepare_call_clause'(U,T,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R), + expand_call_goal(U,S,V), + strip_subst_module(V,S,W,X), + '$call_with_inference_counting'('$module_call'(W,X)). :-non_counted_backtracking call/19. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :- var(A), instantiation_error(call/19). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :- - '$strip_module'(A,T,U), - '$call_inline'(U,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :- - !, - '$prepare_call_clause'(T,U,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S), - '$call_with_inference_counting'('$module_call'(U,T)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :- - '$prepare_call_clause'(T,U,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S), - ( '$call_inline'(T) - ; expand_call_goal(T,U,V), - strip_subst_module(V,U,W,X), - '$call_with_inference_counting'('$module_call'(W,X)) - ). + '$strip_module'(A,T,U), + '$prepare_call_clause'(V,U,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S), + expand_call_goal(V,T,W), + strip_subst_module(W,T,X,Y), + '$call_with_inference_counting'('$module_call'(X,Y)). :-non_counted_backtracking call/20. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :- var(A), instantiation_error(call/20). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :- - '$strip_module'(A,U,V), - '$call_inline'(V,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :- - !, - '$prepare_call_clause'(U,V,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T), - '$call_with_inference_counting'('$module_call'(V,U)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :- - '$prepare_call_clause'(U,V,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T), - ( '$call_inline'(U) - ; expand_call_goal(U,V,W), - strip_subst_module(W,V,X,Y), - '$call_with_inference_counting'('$module_call'(X,Y)) - ). + '$strip_module'(A,U,V), + '$prepare_call_clause'(W,V,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T), + expand_call_goal(W,U,X), + strip_subst_module(X,U,Y,Z), + '$call_with_inference_counting'('$module_call'(Y,Z)). :-non_counted_backtracking call/21. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :- var(A), instantiation_error(call/21). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :- - '$strip_module'(A,V,W), - '$call_inline'(W,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :- - !, - '$prepare_call_clause'(V,W,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U), - '$call_with_inference_counting'('$module_call'(W,V)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :- - '$prepare_call_clause'(V,W,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U), - ( '$call_inline'(V) - ; expand_call_goal(V,W,X), - strip_subst_module(X,W,Y,Z), - '$call_with_inference_counting'('$module_call'(Y,Z)) - ). + '$strip_module'(A,V,W), + '$prepare_call_clause'(X,W,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U), + expand_call_goal(X,V,Y), + strip_subst_module(Y,V,Z,A1), + '$call_with_inference_counting'('$module_call'(Z,A1)). :-non_counted_backtracking call/22. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :- var(A), instantiation_error(call/22). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :- - '$strip_module'(A,W,X), - '$call_inline'(X,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :- - !, - '$prepare_call_clause'(W,X,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V), - '$call_with_inference_counting'('$module_call'(X,W)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :- - '$prepare_call_clause'(W,X,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V), - ( '$call_inline'(W) - ; expand_call_goal(W,X,Y), - strip_subst_module(Y,X,Z,A1), - '$call_with_inference_counting'('$module_call'(Z,A1)) - ). + '$strip_module'(A,W,X), + '$prepare_call_clause'(Y,X,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V), + expand_call_goal(Y,W,Z), + strip_subst_module(Z,W,A1,B1), + '$call_with_inference_counting'('$module_call'(A1,B1)). :-non_counted_backtracking call/23. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :- var(A), instantiation_error(call/23). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :- - '$strip_module'(A,X,Y), - '$call_inline'(Y,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :- - !, - '$prepare_call_clause'(X,Y,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W), - '$call_with_inference_counting'('$module_call'(Y,X)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :- - '$prepare_call_clause'(X,Y,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W), - ( '$call_inline'(X) - ; expand_call_goal(X,Y,Z), - strip_subst_module(Z,Y,A1,B1), - '$call_with_inference_counting'('$module_call'(A1,B1)) - ). + '$strip_module'(A,X,Y), + '$prepare_call_clause'(Z,Y,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W), + expand_call_goal(Z,X,A1), + strip_subst_module(A1,X,B1,C1), + '$call_with_inference_counting'('$module_call'(B1,C1)). :-non_counted_backtracking call/24. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :- var(A), instantiation_error(call/24). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :- - '$strip_module'(A,Y,Z), - '$call_inline'(Z,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :- - !, - '$prepare_call_clause'(Y,Z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X), - '$call_with_inference_counting'('$module_call'(Z,Y)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :- - '$prepare_call_clause'(Y,Z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X), - ( '$call_inline'(Y) - ; expand_call_goal(Y,Z,A1), - strip_subst_module(A1,Z,B1,C1), - '$call_with_inference_counting'('$module_call'(B1,C1)) - ). + '$strip_module'(A,Y,Z), + '$prepare_call_clause'(A1,Z,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X), + expand_call_goal(A1,Y,B1), + strip_subst_module(B1,Y,C1,D1), + '$call_with_inference_counting'('$module_call'(C1,D1)). :-non_counted_backtracking call/25. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :- var(A), instantiation_error(call/25). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :- - '$strip_module'(A,Z,A1), - '$call_inline'(A1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :- - !, - '$prepare_call_clause'(Z,A1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y), - '$call_with_inference_counting'('$module_call'(A1,Z)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :- - '$prepare_call_clause'(Z,A1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y), - ( '$call_inline'(Z) - ; expand_call_goal(Z,A1,B1), - strip_subst_module(B1,A1,C1,D1), - '$call_with_inference_counting'('$module_call'(C1,D1)) - ). + '$strip_module'(A,Z,A1), + '$prepare_call_clause'(B1,A1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y), + expand_call_goal(B1,Z,C1), + strip_subst_module(C1,Z,D1,E1), + '$call_with_inference_counting'('$module_call'(D1,E1)). :-non_counted_backtracking call/26. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :- var(A), instantiation_error(call/26). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :- - '$strip_module'(A,A1,B1), - '$call_inline'(B1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :- - !, - '$prepare_call_clause'(A1,B1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z), - '$call_with_inference_counting'('$module_call'(B1,A1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :- - '$prepare_call_clause'(A1,B1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z), - ( '$call_inline'(A1) - ; expand_call_goal(A1,B1,C1), - strip_subst_module(C1,B1,D1,E1), - '$call_with_inference_counting'('$module_call'(D1,E1)) - ). + '$strip_module'(A,A1,B1), + '$prepare_call_clause'(C1,B1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z), + expand_call_goal(C1,A1,D1), + strip_subst_module(D1,A1,E1,F1), + '$call_with_inference_counting'('$module_call'(E1,F1)). :-non_counted_backtracking call/27. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :- var(A), instantiation_error(call/27). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :- - '$strip_module'(A,B1,C1), - '$call_inline'(C1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :- - !, - '$prepare_call_clause'(B1,C1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1), - '$call_with_inference_counting'('$module_call'(C1,B1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :- - '$prepare_call_clause'(B1,C1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1), - ( '$call_inline'(B1) - ; expand_call_goal(B1,C1,D1), - strip_subst_module(D1,C1,E1,F1), - '$call_with_inference_counting'('$module_call'(E1,F1)) - ). + '$strip_module'(A,B1,C1), + '$prepare_call_clause'(D1,C1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1), + expand_call_goal(D1,B1,E1), + strip_subst_module(E1,B1,F1,G1), + '$call_with_inference_counting'('$module_call'(F1,G1)). :-non_counted_backtracking call/28. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :- var(A), instantiation_error(call/28). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :- - '$strip_module'(A,C1,D1), - '$call_inline'(D1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :- - !, - '$prepare_call_clause'(C1,D1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1), - '$call_with_inference_counting'('$module_call'(D1,C1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :- - '$prepare_call_clause'(C1,D1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1), - ( '$call_inline'(C1) - ; expand_call_goal(C1,D1,E1), - strip_subst_module(E1,D1,F1,G1), - '$call_with_inference_counting'('$module_call'(F1,G1)) - ). + '$strip_module'(A,C1,D1), + '$prepare_call_clause'(E1,D1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1), + expand_call_goal(E1,C1,F1), + strip_subst_module(F1,C1,G1,H1), + '$call_with_inference_counting'('$module_call'(G1,H1)). :-non_counted_backtracking call/29. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :- var(A), instantiation_error(call/29). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :- - '$strip_module'(A,D1,E1), - '$call_inline'(E1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :- - !, - '$prepare_call_clause'(D1,E1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1), - '$call_with_inference_counting'('$module_call'(E1,D1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :- - '$prepare_call_clause'(D1,E1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1), - ( '$call_inline'(D1) - ; expand_call_goal(D1,E1,F1), - strip_subst_module(F1,E1,G1,H1), - '$call_with_inference_counting'('$module_call'(G1,H1)) - ). + '$strip_module'(A,D1,E1), + '$prepare_call_clause'(F1,E1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1), + expand_call_goal(F1,D1,G1), + strip_subst_module(G1,D1,H1,I1), + '$call_with_inference_counting'('$module_call'(H1,I1)). :-non_counted_backtracking call/30. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :- var(A), instantiation_error(call/30). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :- - '$strip_module'(A,E1,F1), - '$call_inline'(F1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :- - !, - '$prepare_call_clause'(E1,F1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1), - '$call_with_inference_counting'('$module_call'(F1,E1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :- - '$prepare_call_clause'(E1,F1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1), - ( '$call_inline'(E1) - ; expand_call_goal(E1,F1,G1), - strip_subst_module(G1,F1,H1,I1), - '$call_with_inference_counting'('$module_call'(H1,I1)) - ). + '$strip_module'(A,E1,F1), + '$prepare_call_clause'(G1,F1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1), + expand_call_goal(G1,E1,H1), + strip_subst_module(H1,E1,I1,J1), + '$call_with_inference_counting'('$module_call'(I1,J1)). :-non_counted_backtracking call/31. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :- var(A), instantiation_error(call/31). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :- - '$strip_module'(A,F1,G1), - '$call_inline'(G1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :- - !, - '$prepare_call_clause'(F1,G1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1), - '$call_with_inference_counting'('$module_call'(G1,F1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :- - '$prepare_call_clause'(F1,G1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1), - ( '$call_inline'(F1) - ; expand_call_goal(F1,G1,H1), - strip_subst_module(H1,G1,I1,J1), - '$call_with_inference_counting'('$module_call'(I1,J1)) - ). + '$strip_module'(A,F1,G1), + '$prepare_call_clause'(H1,G1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1), + expand_call_goal(H1,F1,I1), + strip_subst_module(I1,F1,J1,K1), + '$call_with_inference_counting'('$module_call'(J1,K1)). :-non_counted_backtracking call/32. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :- var(A), instantiation_error(call/32). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :- - '$strip_module'(A,G1,H1), - '$call_inline'(H1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :- - !, - '$prepare_call_clause'(G1,H1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1), - '$call_with_inference_counting'('$module_call'(H1,G1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :- - '$prepare_call_clause'(G1,H1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1), - ( '$call_inline'(G1) - ; expand_call_goal(G1,H1,I1), - strip_subst_module(I1,H1,J1,K1), - '$call_with_inference_counting'('$module_call'(J1,K1)) - ). + '$strip_module'(A,G1,H1), + '$prepare_call_clause'(I1,H1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1), + expand_call_goal(I1,G1,J1), + strip_subst_module(J1,G1,K1,L1), + '$call_with_inference_counting'('$module_call'(K1,L1)). :-non_counted_backtracking call/33. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :- var(A), instantiation_error(call/33). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :- - '$strip_module'(A,H1,I1), - '$call_inline'(I1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :- - !, - '$prepare_call_clause'(H1,I1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1), - '$call_with_inference_counting'('$module_call'(I1,H1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :- - '$prepare_call_clause'(H1,I1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1), - ( '$call_inline'(H1) - ; expand_call_goal(H1,I1,J1), - strip_subst_module(J1,I1,K1,L1), - '$call_with_inference_counting'('$module_call'(K1,L1)) - ). + '$strip_module'(A,H1,I1), + '$prepare_call_clause'(J1,I1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1), + expand_call_goal(J1,H1,K1), + strip_subst_module(K1,H1,L1,M1), + '$call_with_inference_counting'('$module_call'(L1,M1)). :-non_counted_backtracking call/34. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :- var(A), instantiation_error(call/34). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :- - '$strip_module'(A,I1,J1), - '$call_inline'(J1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :- - !, - '$prepare_call_clause'(I1,J1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1), - '$call_with_inference_counting'('$module_call'(J1,I1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :- - '$prepare_call_clause'(I1,J1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1), - ( '$call_inline'(I1) - ; expand_call_goal(I1,J1,K1), - strip_subst_module(K1,J1,L1,M1), - '$call_with_inference_counting'('$module_call'(L1,M1)) - ). + '$strip_module'(A,I1,J1), + '$prepare_call_clause'(K1,J1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1), + expand_call_goal(K1,I1,L1), + strip_subst_module(L1,I1,M1,N1), + '$call_with_inference_counting'('$module_call'(M1,N1)). :-non_counted_backtracking call/35. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :- var(A), instantiation_error(call/35). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :- - '$strip_module'(A,J1,K1), - '$call_inline'(K1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :- - !, - '$prepare_call_clause'(J1,K1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1), - '$call_with_inference_counting'('$module_call'(K1,J1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :- - '$prepare_call_clause'(J1,K1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1), - ( '$call_inline'(J1) - ; expand_call_goal(J1,K1,L1), - strip_subst_module(L1,K1,M1,N1), - '$call_with_inference_counting'('$module_call'(M1,N1)) - ). + '$strip_module'(A,J1,K1), + '$prepare_call_clause'(L1,K1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1), + expand_call_goal(L1,J1,M1), + strip_subst_module(M1,J1,N1,O1), + '$call_with_inference_counting'('$module_call'(N1,O1)). :-non_counted_backtracking call/36. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :- var(A), instantiation_error(call/36). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :- - '$strip_module'(A,K1,L1), - '$call_inline'(L1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :- - !, - '$prepare_call_clause'(K1,L1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1), - '$call_with_inference_counting'('$module_call'(L1,K1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :- - '$prepare_call_clause'(K1,L1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1), - ( '$call_inline'(K1) - ; expand_call_goal(K1,L1,M1), - strip_subst_module(M1,L1,N1,O1), - '$call_with_inference_counting'('$module_call'(N1,O1)) - ). + '$strip_module'(A,K1,L1), + '$prepare_call_clause'(M1,L1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1), + expand_call_goal(M1,K1,N1), + strip_subst_module(N1,K1,O1,P1), + '$call_with_inference_counting'('$module_call'(O1,P1)). :-non_counted_backtracking call/37. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :- var(A), instantiation_error(call/37). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :- - '$strip_module'(A,L1,M1), - '$call_inline'(M1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :- - !, - '$prepare_call_clause'(L1,M1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1), - '$call_with_inference_counting'('$module_call'(M1,L1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :- - '$prepare_call_clause'(L1,M1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1), - ( '$call_inline'(L1) - ; expand_call_goal(L1,M1,N1), - strip_subst_module(N1,M1,O1,P1), - '$call_with_inference_counting'('$module_call'(O1,P1)) - ). + '$strip_module'(A,L1,M1), + '$prepare_call_clause'(N1,M1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1), + expand_call_goal(N1,L1,O1), + strip_subst_module(O1,L1,P1,Q1), + '$call_with_inference_counting'('$module_call'(P1,Q1)). :-non_counted_backtracking call/38. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :- var(A), instantiation_error(call/38). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :- - '$strip_module'(A,M1,N1), - '$call_inline'(N1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :- - !, - '$prepare_call_clause'(M1,N1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1), - '$call_with_inference_counting'('$module_call'(N1,M1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :- - '$prepare_call_clause'(M1,N1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1), - ( '$call_inline'(M1) - ; expand_call_goal(M1,N1,O1), - strip_subst_module(O1,N1,P1,Q1), - '$call_with_inference_counting'('$module_call'(P1,Q1)) - ). + '$strip_module'(A,M1,N1), + '$prepare_call_clause'(O1,N1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1), + expand_call_goal(O1,M1,P1), + strip_subst_module(P1,M1,Q1,R1), + '$call_with_inference_counting'('$module_call'(Q1,R1)). :-non_counted_backtracking call/39. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :- var(A), instantiation_error(call/39). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :- - '$strip_module'(A,N1,O1), - '$call_inline'(O1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :- - !, - '$prepare_call_clause'(N1,O1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1), - '$call_with_inference_counting'('$module_call'(O1,N1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :- - '$prepare_call_clause'(N1,O1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1), - ( '$call_inline'(N1) - ; expand_call_goal(N1,O1,P1), - strip_subst_module(P1,O1,Q1,R1), - '$call_with_inference_counting'('$module_call'(Q1,R1)) - ). + '$strip_module'(A,N1,O1), + '$prepare_call_clause'(P1,O1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1), + expand_call_goal(P1,N1,Q1), + strip_subst_module(Q1,N1,R1,S1), + '$call_with_inference_counting'('$module_call'(R1,S1)). :-non_counted_backtracking call/40. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :- var(A), instantiation_error(call/40). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :- - '$strip_module'(A,O1,P1), - '$call_inline'(P1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :- - !, - '$prepare_call_clause'(O1,P1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1), - '$call_with_inference_counting'('$module_call'(P1,O1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :- - '$prepare_call_clause'(O1,P1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1), - ( '$call_inline'(O1) - ; expand_call_goal(O1,P1,Q1), - strip_subst_module(Q1,P1,R1,S1), - '$call_with_inference_counting'('$module_call'(R1,S1)) - ). + '$strip_module'(A,O1,P1), + '$prepare_call_clause'(Q1,P1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1), + expand_call_goal(Q1,O1,R1), + strip_subst_module(R1,O1,S1,T1), + '$call_with_inference_counting'('$module_call'(S1,T1)). :-non_counted_backtracking call/41. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :- var(A), instantiation_error(call/41). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :- - '$strip_module'(A,P1,Q1), - '$call_inline'(Q1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :- - !, - '$prepare_call_clause'(P1,Q1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1), - '$call_with_inference_counting'('$module_call'(Q1,P1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :- - '$prepare_call_clause'(P1,Q1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1), - ( '$call_inline'(P1) - ; expand_call_goal(P1,Q1,R1), - strip_subst_module(R1,Q1,S1,T1), - '$call_with_inference_counting'('$module_call'(S1,T1)) - ). + '$strip_module'(A,P1,Q1), + '$prepare_call_clause'(R1,Q1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1), + expand_call_goal(R1,P1,S1), + strip_subst_module(S1,P1,T1,U1), + '$call_with_inference_counting'('$module_call'(T1,U1)). :-non_counted_backtracking call/42. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :- var(A), instantiation_error(call/42). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :- - '$strip_module'(A,Q1,R1), - '$call_inline'(R1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :- - !, - '$prepare_call_clause'(Q1,R1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1), - '$call_with_inference_counting'('$module_call'(R1,Q1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :- - '$prepare_call_clause'(Q1,R1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1), - ( '$call_inline'(Q1) - ; expand_call_goal(Q1,R1,S1), - strip_subst_module(S1,R1,T1,U1), - '$call_with_inference_counting'('$module_call'(T1,U1)) - ). + '$strip_module'(A,Q1,R1), + '$prepare_call_clause'(S1,R1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1), + expand_call_goal(S1,Q1,T1), + strip_subst_module(T1,Q1,U1,V1), + '$call_with_inference_counting'('$module_call'(U1,V1)). :-non_counted_backtracking call/43. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :- var(A), instantiation_error(call/43). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :- - '$strip_module'(A,R1,S1), - '$call_inline'(S1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :- - !, - '$prepare_call_clause'(R1,S1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1), - '$call_with_inference_counting'('$module_call'(S1,R1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :- - '$prepare_call_clause'(R1,S1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1), - ( '$call_inline'(R1) - ; expand_call_goal(R1,S1,T1), - strip_subst_module(T1,S1,U1,V1), - '$call_with_inference_counting'('$module_call'(U1,V1)) - ). + '$strip_module'(A,R1,S1), + '$prepare_call_clause'(T1,S1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1), + expand_call_goal(T1,R1,U1), + strip_subst_module(U1,R1,V1,W1), + '$call_with_inference_counting'('$module_call'(V1,W1)). :-non_counted_backtracking call/44. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :- var(A), instantiation_error(call/44). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :- - '$strip_module'(A,S1,T1), - '$call_inline'(T1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :- - !, - '$prepare_call_clause'(S1,T1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1), - '$call_with_inference_counting'('$module_call'(T1,S1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :- - '$prepare_call_clause'(S1,T1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1), - ( '$call_inline'(S1) - ; expand_call_goal(S1,T1,U1), - strip_subst_module(U1,T1,V1,W1), - '$call_with_inference_counting'('$module_call'(V1,W1)) - ). + '$strip_module'(A,S1,T1), + '$prepare_call_clause'(U1,T1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1), + expand_call_goal(U1,S1,V1), + strip_subst_module(V1,S1,W1,X1), + '$call_with_inference_counting'('$module_call'(W1,X1)). :-non_counted_backtracking call/45. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :- var(A), instantiation_error(call/45). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :- - '$strip_module'(A,T1,U1), - '$call_inline'(U1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :- - !, - '$prepare_call_clause'(T1,U1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1), - '$call_with_inference_counting'('$module_call'(U1,T1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :- - '$prepare_call_clause'(T1,U1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1), - ( '$call_inline'(T1) - ; expand_call_goal(T1,U1,V1), - strip_subst_module(V1,U1,W1,X1), - '$call_with_inference_counting'('$module_call'(W1,X1)) - ). + '$strip_module'(A,T1,U1), + '$prepare_call_clause'(V1,U1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1), + expand_call_goal(V1,T1,W1), + strip_subst_module(W1,T1,X1,Y1), + '$call_with_inference_counting'('$module_call'(X1,Y1)). :-non_counted_backtracking call/46. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :- var(A), instantiation_error(call/46). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :- - '$strip_module'(A,U1,V1), - '$call_inline'(V1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :- - !, - '$prepare_call_clause'(U1,V1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1), - '$call_with_inference_counting'('$module_call'(V1,U1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :- - '$prepare_call_clause'(U1,V1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1), - ( '$call_inline'(U1) - ; expand_call_goal(U1,V1,W1), - strip_subst_module(W1,V1,X1,Y1), - '$call_with_inference_counting'('$module_call'(X1,Y1)) - ). + '$strip_module'(A,U1,V1), + '$prepare_call_clause'(W1,V1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1), + expand_call_goal(W1,U1,X1), + strip_subst_module(X1,U1,Y1,Z1), + '$call_with_inference_counting'('$module_call'(Y1,Z1)). :-non_counted_backtracking call/47. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :- var(A), instantiation_error(call/47). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :- - '$strip_module'(A,V1,W1), - '$call_inline'(W1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :- - !, - '$prepare_call_clause'(V1,W1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1), - '$call_with_inference_counting'('$module_call'(W1,V1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :- - '$prepare_call_clause'(V1,W1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1), - ( '$call_inline'(V1) - ; expand_call_goal(V1,W1,X1), - strip_subst_module(X1,W1,Y1,Z1), - '$call_with_inference_counting'('$module_call'(Y1,Z1)) - ). + '$strip_module'(A,V1,W1), + '$prepare_call_clause'(X1,W1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1), + expand_call_goal(X1,V1,Y1), + strip_subst_module(Y1,V1,Z1,A2), + '$call_with_inference_counting'('$module_call'(Z1,A2)). :-non_counted_backtracking call/48. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :- var(A), instantiation_error(call/48). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :- - '$strip_module'(A,W1,X1), - '$call_inline'(X1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :- - !, - '$prepare_call_clause'(W1,X1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1), - '$call_with_inference_counting'('$module_call'(X1,W1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :- - '$prepare_call_clause'(W1,X1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1), - ( '$call_inline'(W1) - ; expand_call_goal(W1,X1,Y1), - strip_subst_module(Y1,X1,Z1,A2), - '$call_with_inference_counting'('$module_call'(Z1,A2)) - ). + '$strip_module'(A,W1,X1), + '$prepare_call_clause'(Y1,X1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1), + expand_call_goal(Y1,W1,Z1), + strip_subst_module(Z1,W1,A2,B2), + '$call_with_inference_counting'('$module_call'(A2,B2)). :-non_counted_backtracking call/49. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :- var(A), instantiation_error(call/49). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :- - '$strip_module'(A,X1,Y1), - '$call_inline'(Y1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :- - !, - '$prepare_call_clause'(X1,Y1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1), - '$call_with_inference_counting'('$module_call'(Y1,X1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :- - '$prepare_call_clause'(X1,Y1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1), - ( '$call_inline'(X1) - ; expand_call_goal(X1,Y1,Z1), - strip_subst_module(Z1,Y1,A2,B2), - '$call_with_inference_counting'('$module_call'(A2,B2)) - ). + '$strip_module'(A,X1,Y1), + '$prepare_call_clause'(Z1,Y1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1), + expand_call_goal(Z1,X1,A2), + strip_subst_module(A2,X1,B2,C2), + '$call_with_inference_counting'('$module_call'(B2,C2)). :-non_counted_backtracking call/50. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :- var(A), instantiation_error(call/50). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :- - '$strip_module'(A,Y1,Z1), - '$call_inline'(Z1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :- - !, - '$prepare_call_clause'(Y1,Z1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1), - '$call_with_inference_counting'('$module_call'(Z1,Y1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :- - '$prepare_call_clause'(Y1,Z1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1), - ( '$call_inline'(Y1) - ; expand_call_goal(Y1,Z1,A2), - strip_subst_module(A2,Z1,B2,C2), - '$call_with_inference_counting'('$module_call'(B2,C2)) - ). + '$strip_module'(A,Y1,Z1), + '$prepare_call_clause'(A2,Z1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1), + expand_call_goal(A2,Y1,B2), + strip_subst_module(B2,Y1,C2,D2), + '$call_with_inference_counting'('$module_call'(C2,D2)). :-non_counted_backtracking call/51. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :- var(A), instantiation_error(call/51). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :- - '$strip_module'(A,Z1,A2), - '$call_inline'(A2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :- - !, - '$prepare_call_clause'(Z1,A2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1), - '$call_with_inference_counting'('$module_call'(A2,Z1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :- - '$prepare_call_clause'(Z1,A2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1), - ( '$call_inline'(Z1) - ; expand_call_goal(Z1,A2,B2), - strip_subst_module(B2,A2,C2,D2), - '$call_with_inference_counting'('$module_call'(C2,D2)) - ). + '$strip_module'(A,Z1,A2), + '$prepare_call_clause'(B2,A2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1), + expand_call_goal(B2,Z1,C2), + strip_subst_module(C2,Z1,D2,E2), + '$call_with_inference_counting'('$module_call'(D2,E2)). :-non_counted_backtracking call/52. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :- var(A), instantiation_error(call/52). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :- - '$strip_module'(A,A2,B2), - '$call_inline'(B2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :- - !, - '$prepare_call_clause'(A2,B2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1), - '$call_with_inference_counting'('$module_call'(B2,A2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :- - '$prepare_call_clause'(A2,B2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1), - ( '$call_inline'(A2) - ; expand_call_goal(A2,B2,C2), - strip_subst_module(C2,B2,D2,E2), - '$call_with_inference_counting'('$module_call'(D2,E2)) - ). + '$strip_module'(A,A2,B2), + '$prepare_call_clause'(C2,B2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1), + expand_call_goal(C2,A2,D2), + strip_subst_module(D2,A2,E2,F2), + '$call_with_inference_counting'('$module_call'(E2,F2)). :-non_counted_backtracking call/53. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :- var(A), instantiation_error(call/53). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :- - '$strip_module'(A,B2,C2), - '$call_inline'(C2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :- - !, - '$prepare_call_clause'(B2,C2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2), - '$call_with_inference_counting'('$module_call'(C2,B2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :- - '$prepare_call_clause'(B2,C2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2), - ( '$call_inline'(B2) - ; expand_call_goal(B2,C2,D2), - strip_subst_module(D2,C2,E2,F2), - '$call_with_inference_counting'('$module_call'(E2,F2)) - ). + '$strip_module'(A,B2,C2), + '$prepare_call_clause'(D2,C2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2), + expand_call_goal(D2,B2,E2), + strip_subst_module(E2,B2,F2,G2), + '$call_with_inference_counting'('$module_call'(F2,G2)). :-non_counted_backtracking call/54. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2) :- var(A), instantiation_error(call/54). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2) :- - '$strip_module'(A,C2,D2), - '$call_inline'(D2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2) :- - !, - '$prepare_call_clause'(C2,D2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2), - '$call_with_inference_counting'('$module_call'(D2,C2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2) :- - '$prepare_call_clause'(C2,D2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2), - ( '$call_inline'(C2) - ; expand_call_goal(C2,D2,E2), - strip_subst_module(E2,D2,F2,G2), - '$call_with_inference_counting'('$module_call'(F2,G2)) - ). + '$strip_module'(A,C2,D2), + '$prepare_call_clause'(E2,D2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2), + expand_call_goal(E2,C2,F2), + strip_subst_module(F2,C2,G2,H2), + '$call_with_inference_counting'('$module_call'(G2,H2)). :-non_counted_backtracking call/55. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2) :- var(A), instantiation_error(call/55). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2) :- - '$strip_module'(A,D2,E2), - '$call_inline'(E2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2) :- - !, - '$prepare_call_clause'(D2,E2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2), - '$call_with_inference_counting'('$module_call'(E2,D2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2) :- - '$prepare_call_clause'(D2,E2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2), - ( '$call_inline'(D2) - ; expand_call_goal(D2,E2,F2), - strip_subst_module(F2,E2,G2,H2), - '$call_with_inference_counting'('$module_call'(G2,H2)) - ). + '$strip_module'(A,D2,E2), + '$prepare_call_clause'(F2,E2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2), + expand_call_goal(F2,D2,G2), + strip_subst_module(G2,D2,H2,I2), + '$call_with_inference_counting'('$module_call'(H2,I2)). :-non_counted_backtracking call/56. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2) :- var(A), instantiation_error(call/56). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2) :- - '$strip_module'(A,E2,F2), - '$call_inline'(F2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2) :- - !, - '$prepare_call_clause'(E2,F2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2), - '$call_with_inference_counting'('$module_call'(F2,E2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2) :- - '$prepare_call_clause'(E2,F2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2), - ( '$call_inline'(E2) - ; expand_call_goal(E2,F2,G2), - strip_subst_module(G2,F2,H2,I2), - '$call_with_inference_counting'('$module_call'(H2,I2)) - ). + '$strip_module'(A,E2,F2), + '$prepare_call_clause'(G2,F2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2), + expand_call_goal(G2,E2,H2), + strip_subst_module(H2,E2,I2,J2), + '$call_with_inference_counting'('$module_call'(I2,J2)). :-non_counted_backtracking call/57. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2) :- var(A), instantiation_error(call/57). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2) :- - '$strip_module'(A,F2,G2), - '$call_inline'(G2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2) :- - !, - '$prepare_call_clause'(F2,G2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2), - '$call_with_inference_counting'('$module_call'(G2,F2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2) :- - '$prepare_call_clause'(F2,G2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2), - ( '$call_inline'(F2) - ; expand_call_goal(F2,G2,H2), - strip_subst_module(H2,G2,I2,J2), - '$call_with_inference_counting'('$module_call'(I2,J2)) - ). + '$strip_module'(A,F2,G2), + '$prepare_call_clause'(H2,G2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2), + expand_call_goal(H2,F2,I2), + strip_subst_module(I2,F2,J2,K2), + '$call_with_inference_counting'('$module_call'(J2,K2)). :-non_counted_backtracking call/58. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2) :- var(A), instantiation_error(call/58). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2) :- - '$strip_module'(A,G2,H2), - '$call_inline'(H2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2) :- - !, - '$prepare_call_clause'(G2,H2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2), - '$call_with_inference_counting'('$module_call'(H2,G2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2) :- - '$prepare_call_clause'(G2,H2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2), - ( '$call_inline'(G2) - ; expand_call_goal(G2,H2,I2), - strip_subst_module(I2,H2,J2,K2), - '$call_with_inference_counting'('$module_call'(J2,K2)) - ). + '$strip_module'(A,G2,H2), + '$prepare_call_clause'(I2,H2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2), + expand_call_goal(I2,G2,J2), + strip_subst_module(J2,G2,K2,L2), + '$call_with_inference_counting'('$module_call'(K2,L2)). :-non_counted_backtracking call/59. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2) :- var(A), instantiation_error(call/59). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2) :- - '$strip_module'(A,H2,I2), - '$call_inline'(I2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2) :- - !, - '$prepare_call_clause'(H2,I2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2), - '$call_with_inference_counting'('$module_call'(I2,H2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2) :- - '$prepare_call_clause'(H2,I2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2), - ( '$call_inline'(H2) - ; expand_call_goal(H2,I2,J2), - strip_subst_module(J2,I2,K2,L2), - '$call_with_inference_counting'('$module_call'(K2,L2)) - ). + '$strip_module'(A,H2,I2), + '$prepare_call_clause'(J2,I2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2), + expand_call_goal(J2,H2,K2), + strip_subst_module(K2,H2,L2,M2), + '$call_with_inference_counting'('$module_call'(L2,M2)). :-non_counted_backtracking call/60. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2) :- var(A), instantiation_error(call/60). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2) :- - '$strip_module'(A,I2,J2), - '$call_inline'(J2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2) :- - !, - '$prepare_call_clause'(I2,J2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2), - '$call_with_inference_counting'('$module_call'(J2,I2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2) :- - '$prepare_call_clause'(I2,J2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2), - ( '$call_inline'(I2) - ; expand_call_goal(I2,J2,K2), - strip_subst_module(K2,J2,L2,M2), - '$call_with_inference_counting'('$module_call'(L2,M2)) - ). + '$strip_module'(A,I2,J2), + '$prepare_call_clause'(K2,J2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2), + expand_call_goal(K2,I2,L2), + strip_subst_module(L2,I2,M2,N2), + '$call_with_inference_counting'('$module_call'(M2,N2)). :-non_counted_backtracking call/61. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2) :- var(A), instantiation_error(call/61). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2) :- - '$strip_module'(A,J2,K2), - '$call_inline'(K2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2) :- - !, - '$prepare_call_clause'(J2,K2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2), - '$call_with_inference_counting'('$module_call'(K2,J2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2) :- - '$prepare_call_clause'(J2,K2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2), - ( '$call_inline'(J2) - ; expand_call_goal(J2,K2,L2), - strip_subst_module(L2,K2,M2,N2), - '$call_with_inference_counting'('$module_call'(M2,N2)) - ). + '$strip_module'(A,J2,K2), + '$prepare_call_clause'(L2,K2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2), + expand_call_goal(L2,J2,M2), + strip_subst_module(M2,J2,N2,O2), + '$call_with_inference_counting'('$module_call'(N2,O2)). :-non_counted_backtracking call/62. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2) :- var(A), instantiation_error(call/62). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2) :- - '$strip_module'(A,K2,L2), - '$call_inline'(L2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2) :- - !, - '$prepare_call_clause'(K2,L2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2), - '$call_with_inference_counting'('$module_call'(L2,K2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2) :- - '$prepare_call_clause'(K2,L2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2), - ( '$call_inline'(K2) - ; expand_call_goal(K2,L2,M2), - strip_subst_module(M2,L2,N2,O2), - '$call_with_inference_counting'('$module_call'(N2,O2)) - ). + '$strip_module'(A,K2,L2), + '$prepare_call_clause'(M2,L2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2), + expand_call_goal(M2,K2,N2), + strip_subst_module(N2,K2,O2,P2), + '$call_with_inference_counting'('$module_call'(O2,P2)). :-non_counted_backtracking call/63. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2) :- var(A), instantiation_error(call/63). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2) :- - '$strip_module'(A,L2,M2), - '$call_inline'(M2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2) :- - !, - '$prepare_call_clause'(L2,M2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2), - '$call_with_inference_counting'('$module_call'(M2,L2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2) :- - '$prepare_call_clause'(L2,M2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2), - ( '$call_inline'(L2) - ; expand_call_goal(L2,M2,N2), - strip_subst_module(N2,M2,O2,P2), - '$call_with_inference_counting'('$module_call'(O2,P2)) - ). + '$strip_module'(A,L2,M2), + '$prepare_call_clause'(N2,M2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2), + expand_call_goal(N2,L2,O2), + strip_subst_module(O2,L2,P2,Q2), + '$call_with_inference_counting'('$module_call'(P2,Q2)). :-non_counted_backtracking call/64. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2) :- var(A), instantiation_error(call/64). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2) :- - '$strip_module'(A,M2,N2), - '$call_inline'(N2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2) :- - !, - '$prepare_call_clause'(M2,N2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2), - '$call_with_inference_counting'('$module_call'(N2,M2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2) :- - '$prepare_call_clause'(M2,N2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2), - ( '$call_inline'(M2) - ; expand_call_goal(M2,N2,O2), - strip_subst_module(O2,N2,P2,Q2), - '$call_with_inference_counting'('$module_call'(P2,Q2)) - ). + '$strip_module'(A,M2,N2), + '$prepare_call_clause'(O2,N2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2), + expand_call_goal(O2,M2,P2), + strip_subst_module(P2,M2,Q2,R2), + '$call_with_inference_counting'('$module_call'(Q2,R2)). :-non_counted_backtracking call/65. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2) :- var(A), instantiation_error(call/65). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2) :- - '$strip_module'(A,N2,O2), - '$call_inline'(O2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2) :- - !, - '$prepare_call_clause'(N2,O2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2), - '$call_with_inference_counting'('$module_call'(O2,N2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2) :- - '$prepare_call_clause'(N2,O2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2), - ( '$call_inline'(N2) - ; expand_call_goal(N2,O2,P2), - strip_subst_module(P2,O2,Q2,R2), - '$call_with_inference_counting'('$module_call'(Q2,R2)) - ). + '$strip_module'(A,N2,O2), + '$prepare_call_clause'(P2,O2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2), + expand_call_goal(P2,N2,Q2), + strip_subst_module(Q2,N2,R2,S2), + '$call_with_inference_counting'('$module_call'(R2,S2)). diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 9599766d..65c82e48 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4996,51 +4996,11 @@ impl Machine { step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallStripModule(_) => { - let (module_loc, qualified_goal) = self.machine_st.strip_module( - self.machine_st.registers[1], - self.machine_st.registers[2], - ); - - let target_module_loc = self.machine_st.registers[2]; - - unify_fn!( - &mut self.machine_st, - module_loc, - target_module_loc - ); - - let target_qualified_goal = self.machine_st.registers[3]; - - unify_fn!( - &mut self.machine_st, - qualified_goal, - target_qualified_goal - ); - + self.strip_module(); step_or_fail!(self, self.machine_st.p += 1); } &Instruction::ExecuteStripModule(_) => { - let (module_loc, qualified_goal) = self.machine_st.strip_module( - self.machine_st.registers[1], - self.machine_st.registers[2], - ); - - let target_module_loc = self.machine_st.registers[2]; - - unify_fn!( - &mut self.machine_st, - module_loc, - target_module_loc - ); - - let target_qualified_goal = self.machine_st.registers[3]; - - unify_fn!( - &mut self.machine_st, - qualified_goal, - target_qualified_goal - ); - + self.strip_module(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallPrepareCallClause(arity, _) => { @@ -5067,12 +5027,12 @@ impl Machine { self.machine_st.fail = !self.is_expanded_or_inlined(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallInlineCallN(arity, _) => { + &Instruction::CallFastCallN(arity, _) => { let call_at_index = |wam: &mut Machine, name, arity, ptr| { wam.try_call(name, arity, ptr) }; - try_or_throw!(self.machine_st, self.call_inline(arity, call_at_index)); + try_or_throw!(self.machine_st, self.fast_call(arity, call_at_index)); if self.machine_st.fail { self.machine_st.backtrack(); @@ -5083,12 +5043,12 @@ impl Machine { ); } } - &Instruction::ExecuteInlineCallN(arity, _) => { + &Instruction::ExecuteFastCallN(arity, _) => { let call_at_index = |wam: &mut Machine, name, arity, ptr| { wam.try_execute(name, arity, ptr) }; - try_or_throw!(self.machine_st, self.call_inline(arity, call_at_index)); + try_or_throw!(self.machine_st, self.fast_call(arity, call_at_index)); if self.machine_st.fail { self.machine_st.backtrack(); diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 989b963b..f280e5fa 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -1692,6 +1692,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, diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index 11a9d6e8..7c5e761a 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -9,7 +9,7 @@ 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::*; @@ -245,12 +245,15 @@ pub(crate) type LocalExtensiblePredicates = pub(crate) type CodeDir = IndexMap; +pub(crate) type GoalExpansionIndices = IndexSet; + #[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 +262,11 @@ pub struct IndexStore { } impl IndexStore { + #[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, diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 84714436..4da3c2f8 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1203,29 +1203,29 @@ impl Machine { #[inline(always)] pub(crate) fn deref_register(&mut self, i: usize) -> HeapCellValue { - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[i])) + self.machine_st.store(self.machine_st.deref(self.machine_st.registers[i])) } #[inline(always)] - pub(crate) fn call_inline( + pub(crate) fn fast_call( &mut self, arity: usize, call_at_index: impl Fn(&mut Machine, Atom, usize, IndexPtr) -> CallResult, ) -> CallResult { let arity = arity - 1; - let goal = self.deref_register(1); + let (mut module_name, mut goal) = self.machine_st.strip_module( + self.machine_st.registers[1], + heap_loc_as_cell!(0), + ); - let load_registers = |machine_st: &mut MachineState, goal: HeapCellValue| -> Option { + let load_registers = |machine_st: &mut MachineState, goal: HeapCellValue, goal_arity: usize| { read_heap_cell!(goal, - (HeapCellValueTag::Str, s) => { - let (name, goal_arity) = cell_as_atom_cell!(machine_st.heap[s]) - .get_name_and_arity(); - - if goal_arity > 0 { + (HeapCellValueTag::Str | HeapCellValueTag::Atom, s) => { + if goal_arity > 1 { for idx in (1 .. arity + 1).rev() { machine_st.registers[idx + goal_arity] = machine_st.registers[idx + 1]; } - } else { + } else if goal_arity == 0 { for idx in 1 .. arity + 1 { machine_st.registers[idx] = machine_st.registers[idx + 1]; } @@ -1234,8 +1234,6 @@ impl Machine { for idx in 1 .. goal_arity + 1 { machine_st.registers[idx] = machine_st.heap[s+idx]; } - - Some((name, goal_arity)) } _ => { unreachable!() @@ -1243,35 +1241,70 @@ impl Machine { ) }; - read_heap_cell!(goal, + let (mut name, mut goal_arity, index_cell_opt) = read_heap_cell!(goal, (HeapCellValueTag::Str, s) => { - let goal_arity = cell_as_atom_cell!(self.machine_st.heap[s]).get_arity(); + let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity(); - if self.machine_st.heap.len() > s + goal_arity + 1 { - let index_cell = self.machine_st.heap[s+goal_arity+1]; - - if let Some(code_index) = get_structure_index(index_cell) { - if code_index.is_undefined() { - self.machine_st.fail = true; - return Ok(()); - } - - match load_registers(&mut self.machine_st, goal) { - Some((name, goal_arity)) => { - let arity = goal_arity + arity; - self.machine_st.neck_cut(); - return call_at_index(self, name, arity, code_index.get()); - } - None => { - } - } - } - } + (name, arity, if self.machine_st.heap.len() > s + arity + 1 { + get_structure_index(self.machine_st.heap[s + arity + 1]) + } else { + None + }) + } + (HeapCellValueTag::Atom, (name, arity)) => { + debug_assert_eq!(arity, 0); + (name, arity, None) } _ => { + self.machine_st.fail = true; + return Ok(()); } ); + let mut arity = arity + goal_arity; + + let index_cell = index_cell_opt.or_else(|| { + let is_internal_call = name == atom!("$call") && goal_arity > 0; + + if !is_internal_call && self.indices.goal_expansion_defined((name, arity)) { + None + } else { + if is_internal_call { + debug_assert_eq!(goal.get_tag(), HeapCellValueTag::Str); + goal = self.machine_st.heap[goal.get_value()+1]; + (module_name, goal) = self.machine_st.strip_module(goal, module_name); + + if let Some((inner_name, inner_arity)) = self.machine_st.name_and_arity_from_heap(goal) { + arity -= goal_arity; + (name, goal_arity) = (inner_name, inner_arity); + arity += goal_arity; + } else { + return None; + } + } + + let module_name = if module_name.get_tag() != HeapCellValueTag::Atom { + if let Some(load_context) = self.load_contexts.last() { + load_context.module + } else { + atom!("user") + } + } else { + cell_as_atom!(module_name) + }; + + self.indices.get_predicate_code_index(name, arity, module_name) + } + }); + + if let Some(code_index) = index_cell { + if !code_index.is_undefined() { + load_registers(&mut self.machine_st, goal, goal_arity); + self.machine_st.neck_cut(); + return call_at_index(self, name, arity, code_index.get()); + } + } + self.machine_st.fail = true; Ok(()) } @@ -1490,35 +1523,12 @@ impl Machine { } #[inline(always)] - pub(crate) fn prepare_call_clause(&mut self, arity: usize) -> CallResult { + pub(crate) fn strip_module(&mut self) { let (module_loc, qualified_goal) = self.machine_st.strip_module( - self.machine_st.registers[3], + self.machine_st.registers[1], self.machine_st.registers[2], ); - // the first three arguments don't belong to the containing call/N. - let arity = arity - 3; - - let (name, narity, s) = self.machine_st.setup_call_n_init_goal_info( - qualified_goal, - arity, - )?; - - let module_loc = self.machine_st.store(self.machine_st.deref(module_loc)); - - if module_loc.is_var() { - self.load_context_module(module_loc); - - if self.machine_st.fail { - self.machine_st.fail = false; - self.machine_st.unify_atom(atom!("user"), module_loc); - - if self.machine_st.fail { - return Ok(()); - } - } - } - let target_module_loc = self.machine_st.registers[2]; unify_fn!( @@ -1527,9 +1537,26 @@ impl Machine { target_module_loc ); - if self.machine_st.fail { - return Ok(()); - } + let target_qualified_goal = self.machine_st.registers[3]; + + unify_fn!( + &mut self.machine_st, + qualified_goal, + target_qualified_goal + ); + } + + #[inline(always)] + pub(crate) fn prepare_call_clause(&mut self, arity: usize) -> CallResult { + let qualified_goal = self.deref_register(2); + + // the first two arguments don't belong to the containing call/N. + let arity = arity - 2; + + let (name, narity, s) = self.machine_st.setup_call_n_init_goal_info( + qualified_goal, + arity, + )?; // assemble goal from pre-loaded (narity) and supplementary // (arity) arguments. @@ -1545,15 +1572,10 @@ impl Machine { } for idx in 1 .. arity + 1 { - self.machine_st.heap.push(self.machine_st.registers[3 + idx]); + self.machine_st.heap.push(self.machine_st.registers[2 + idx]); } - let index_cell = self.machine_st.heap[s + narity + 1]; - - if get_structure_index(index_cell).is_some() { - self.machine_st.heap.push(index_cell); - str_loc_as_cell!(h) - } else if narity + arity > 0 { + if narity + arity > 0 { str_loc_as_cell!(h) } else { heap_loc_as_cell!(h) @@ -1571,6 +1593,65 @@ impl Machine { Ok(()) } + #[inline(always)] + pub(crate) fn dynamic_module_resolution( + &mut self, + narity: usize, + ) -> Result<(Atom, PredicateKey), MachineStub> { + let module_name = self.deref_register(1); + + let module_name = read_heap_cell!(module_name, + (HeapCellValueTag::Atom, (name, _arity)) => { + debug_assert_eq!(_arity, 0); + name + } + (HeapCellValueTag::Str, s) => { + let (module_name, _arity) = cell_as_atom_cell!(self.machine_st.heap[s]) + .get_name_and_arity(); + + debug_assert_eq!(_arity, 0); + module_name + } + _ if module_name.is_var() => { + if let Some(load_context) = self.load_contexts.last() { + load_context.module + } else { + atom!("user") + } + } + _ => { + unreachable!() + } + ); + + let goal = self.deref_register(2); + + let (name, arity, s) = self.machine_st.setup_call_n_init_goal_info(goal, narity)?; + + // TODO: think we just need the 'Greater' branch here. + match arity.cmp(&2) { + Ordering::Less => { + for i in arity + 1..arity + narity + 1 { + self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity]; + } + } + Ordering::Greater => { + for i in (arity + 1..arity + narity + 1).rev() { + self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity]; + } + } + Ordering::Equal => {} + } + + let key = (name, arity + narity); + + for i in 1..arity + 1 { + self.machine_st.registers[i] = self.machine_st.heap[s + i]; + } + + Ok((module_name, key)) + } + #[inline(always)] pub(crate) fn is_reset_cont_marker(&self, p: usize) -> bool { match &self.code[p] { @@ -3607,60 +3688,6 @@ impl Machine { } } - #[inline(always)] - pub(crate) fn dynamic_module_resolution( - &mut self, - narity: usize, - ) -> Result<(Atom, PredicateKey), MachineStub> { - let module_name = self.deref_register(1); - - let module_name = read_heap_cell!(module_name, - (HeapCellValueTag::Atom, (name, _arity)) => { - debug_assert_eq!(_arity, 0); - name - } - (HeapCellValueTag::Str, s) => { - let (module_name, _arity) = cell_as_atom_cell!(self.machine_st.heap[s]) - .get_name_and_arity(); - - debug_assert_eq!(_arity, 0); - module_name - } - _ if module_name.is_var() => { - atom!("user") - } - _ => { - unreachable!() - } - ); - - let goal = self.deref_register(2); - - let (name, arity, s) = self.machine_st.setup_call_n_init_goal_info(goal, narity)?; - - match arity.cmp(&2) { - Ordering::Less => { - for i in arity + 1..arity + narity + 1 { - self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity]; - } - } - Ordering::Greater => { - for i in (arity + 1..arity + narity + 1).rev() { - self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity]; - } - } - Ordering::Equal => {} - } - - let key = (name, arity + narity); - - for i in 1..arity + 1 { - self.machine_st.registers[i] = self.machine_st.heap[s + i]; - } - - Ok((module_name, key)) - } - #[inline(always)] pub(crate) fn lookup_db_ref(&mut self) { let name = cell_as_atom!(self.deref_register(1)); diff --git a/src/macros.rs b/src/macros.rs index 85e2e086..78c0d89c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -590,6 +590,7 @@ macro_rules! index_store { extensible_predicates: ExtensiblePredicates::with_hasher(FxBuildHasher::default()), local_extensible_predicates: LocalExtensiblePredicates::with_hasher(FxBuildHasher::default()), global_variables: GlobalVarDir::with_hasher(FxBuildHasher::default()), + goal_expansion_indices: GoalExpansionIndices::with_hasher(FxBuildHasher::default()), meta_predicates: MetaPredicateDir::with_hasher(FxBuildHasher::default()), modules: $modules, op_dir: $op_dir, diff --git a/src/toplevel.pl b/src/toplevel.pl index 0bab4415..b554e52c 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -181,7 +181,8 @@ submit_query_and_print_results_(Term, VarList) :- '$get_b_value'(B), bb_put('$report_all', false), bb_put('$report_n_more', 0), - atts:call_residue_vars(user:Term, AttrVars), + expand_goal(Term, user, Term0), + atts:call_residue_vars(user:Term0, AttrVars), write_eqs_and_read_input(B, VarList, AttrVars), !. submit_query_and_print_results_(_, _) :- From de665c05d8642ff1436f4b08fc7af27fc4d2e202 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 22:54:36 +0000 Subject: [PATCH 185/361] Bump openssl from 0.10.48 to 0.10.55 Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.48 to 0.10.55. - [Release notes](https://github.com/sfackler/rust-openssl/releases) - [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.48...openssl-v0.10.55) --- updated-dependencies: - dependency-name: openssl dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Cargo.lock | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 63887d6c..8a569c16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1230,9 +1230,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "openssl" -version = "0.10.48" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518915b97df115dd36109bfa429a48b8f737bd05508cf9588977b599648926d2" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ "bitflags", "cfg-if", @@ -1262,11 +1262,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.83" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", From 5244d71570a48866ecf4b73af6089e2e3b4e9df6 Mon Sep 17 00:00:00 2001 From: infogulch Date: Thu, 22 Jun 2023 22:14:44 -0500 Subject: [PATCH 186/361] Add steps to publish binaries when releases are tagged --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73296baf..87a3331b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,8 @@ on: pull_request: schedule: - cron: '0 0 * * 3' # At 12:00 AM, only on Wednesday + label: + types: [created, edited] jobs: build-test: @@ -53,12 +55,12 @@ jobs: if: "!matrix.extra" run: cargo test --all --verbose - # Extra steps + # Extra steps only run once to avoid duplication, when matrix.extra is true - name: Test and report if: matrix.extra run: | cargo install cargo2junit --force - cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml + RUSTC_BOOTSTRAP=1 cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml - name: Publish cargo test results artifact if: matrix.extra uses: actions/upload-artifact@v3 @@ -99,6 +101,7 @@ jobs: runs-on: ubuntu-20.04 needs: [build-test] steps: + # Download prebuilt ubuntu binary from build-test job, setup logtalk - uses: actions/download-artifact@v3 with: name: scryer-prolog_ubuntu-20.04 @@ -139,3 +142,23 @@ jobs: files: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml' fail_on: nothing comment_mode: off + + # Publish binaries when building for a tag + release: + runs-on: ubuntu-20.04 + needs: [build-test] + if: startsWith(github.ref, 'refs/tags/v') + steps: + - uses: actions/download-artifact@v3 + - name: Zip binaries for release + run: | + zip scryer-prolog_macos-11.zip ./scryer-prolog_macos-11/scryer-prolog + zip scryer-prolog_ubuntu-20.04.zip ./scryer-prolog_ubuntu-20.04/scryer-prolog + zip scryer-prolog_windows-latest.zip ./scryer-prolog_windows-latest/scryer-prolog.exe + - name: Release + uses: softprops/action-gh-release@v1 + with: + files: | + scryer-prolog_macos-11.zip + scryer-prolog_ubuntu-20.04.zip + scryer-prolog_windows-latest.zip From 46317c3a39a381af89ae3906637244212d800272 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Thu, 1 Sep 2022 17:03:00 -0600 Subject: [PATCH 187/361] begin adapting the techniques of "Compiling Large Disjunctions" --- src/iterators.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/iterators.rs b/src/iterators.rs index 62054b04..de091d4a 100644 --- a/src/iterators.rs +++ b/src/iterators.rs @@ -530,3 +530,38 @@ impl<'a> Iterator for ChunkedIterator<'a> { self.iter.next().map(|term| self.take_chunk(term)) } } + +/* +================================================================================ + +This is a disjunction compilation experiment attempting to +adapt the paper "Compiling Large Disjunctions" to Scryer Prolog. + +================================================================================ +*/ + +enum VarInfo { + Perm, + Temp, + Void +} + +pub struct ChunkInfo { + chunk_num: usize, + vars: Vec<(Rc, VarInfo)>, +} + +pub struct BranchInfo { + branch_num: usize, // TODO: Rational?? or own type? + delta: usize, // TODO: Rational?? + chunks: Vec, +} + +pub struct ControlIterator<'a> { + current_branch_num: usize, // TODO: same as above + state_stack: Vec>, + branch_map: IndexMap, Vec>, +} + +impl ControlIterator { +} From d565f5901b5744fed571d9f7c33cea6220169743 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 27 Sep 2022 21:04:52 -0600 Subject: [PATCH 188/361] milestone marker for surgery --- build/instructions_template.rs | 2 + src/arithmetic.rs | 4 +- src/iterators.rs | 105 ++++++++++------- src/lib.rs | 1 + src/machine/dispatch.rs | 2 +- src/machine/loader.rs | 210 +++++++++++++++++---------------- src/parser/ast.rs | 27 +++++ 7 files changed, 204 insertions(+), 147 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index bec70e7e..a7ea3d21 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -1644,6 +1644,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallDeleteAllAttributesFromVar(_) | &Instruction::CallUnattributedVar(_) | &Instruction::CallGetDBRefs(_) | + &Instruction::CallEnqueueAttributedVar(_) | &Instruction::CallFetchGlobalVar(_) | &Instruction::CallFirstStream(_) | &Instruction::CallFlushOutput(_) | @@ -1866,6 +1867,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteDeleteAllAttributesFromVar(_) | &Instruction::ExecuteUnattributedVar(_) | &Instruction::ExecuteGetDBRefs(_) | + &Instruction::ExecuteEnqueueAttributedVar(_) | &Instruction::ExecuteFetchGlobalVar(_) | &Instruction::ExecuteFirstStream(_) | &Instruction::ExecuteFlushOutput(_) | diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 0cbb1eab..fcfd9599 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -74,7 +74,7 @@ impl<'a> ArithInstructionIterator<'a> { 2, )) } - Term::Var(cell, var) => TermIterState::Var(Level::Shallow, cell, var.clone()), + Term::Var(cell, var) => TermIterState::Var(Level::Shallow, cell, RcMutPtr::new(var)), }; Ok(ArithInstructionIterator { @@ -116,7 +116,7 @@ impl<'a> Iterator for ArithInstructionIterator<'a> { } TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(c))), TermIterState::Var(lvl, cell, var) => { - return Some(Ok(ArithTermRef::Var(lvl, cell, var.clone()))); + return Some(Ok(ArithTermRef::Var(lvl, cell, var.owned()))); } _ => { return Some(Err(ArithmeticError::NonEvaluableFunctor( diff --git a/src/iterators.rs b/src/iterators.rs index de091d4a..6bf92a5f 100644 --- a/src/iterators.rs +++ b/src/iterators.rs @@ -6,6 +6,8 @@ use crate::parser::ast::*; use std::cell::Cell; use std::collections::VecDeque; use std::fmt; +use std::fmt::Debug; +use std::hash::{Hash, Hasher}; use std::iter::*; use std::rc::Rc; use std::vec::Vec; @@ -35,6 +37,58 @@ impl<'a> TermRef<'a> { } } +#[derive(Clone, Debug)] +pub(crate) struct RcMutPtr { + owned: Rc, + ptr: *mut Rc, +} + +impl RcMutPtr { + #[inline] + pub(crate) fn new(rc: &Rc) -> Self { + Self { owned: rc.clone(), ptr: rc as *const _ as *mut _ } + } + + #[inline] + pub(crate) fn owned(&self) -> Rc { + self.owned.clone() + } + + #[inline] + pub(crate) fn set(&mut self, var_b_marker: &Rc) { + self.owned = var_b_marker.clone(); + + unsafe { + if !self.ptr.is_null() { + *self.ptr = self.owned.clone(); + } + } + } +} + +impl From for RcMutPtr { + #[inline] + fn from(value: T) -> RcMutPtr { + let owned = Rc::new(value); + RcMutPtr { owned, ptr: std::ptr::null_mut() } + } +} + +impl PartialEq for RcMutPtr { + fn eq(&self, rhs: &Self) -> bool { + &self.owned == &rhs.owned + } +} + +impl Eq for RcMutPtr {} + +impl Hash for RcMutPtr { + #[inline(always)] + fn hash(&self, hasher: &mut H) { + self.owned.hash(hasher) + } +} + #[derive(Debug)] pub(crate) enum TermIterState<'a> { AnonVar(Level), @@ -45,7 +99,7 @@ pub(crate) enum TermIterState<'a> { InitialPartialString(Level, &'a Cell, &'a String, &'a Box), FinalPartialString(Level, &'a Cell, &'a String, &'a Box), CompleteString(Level, &'a Cell, Atom), - Var(Level, &'a Cell, Rc), + Var(Level, &'a Cell, RcMutPtr), } impl<'a> TermIterState<'a> { @@ -65,7 +119,7 @@ impl<'a> TermIterState<'a> { Term::CompleteString(cell, atom) => { TermIterState::CompleteString(lvl, cell, *atom) } - Term::Var(cell, var) => TermIterState::Var(lvl, cell, var.clone()), + Term::Var(cell, var) => TermIterState::Var(lvl, cell, RcMutPtr::new(var)), } } } @@ -106,7 +160,7 @@ impl<'a> QueryIterator<'a> { *name, terms, ), - Term::Var(cell, var) => TermIterState::Var(Level::Root, cell, var.clone()), + Term::Var(cell, var) => TermIterState::Var(Level::Root, cell, RcMutPtr::new(var)), }; QueryIterator { @@ -129,13 +183,13 @@ impl<'a> QueryIterator<'a> { } } &QueryTerm::UnblockedCut(ref cell) => { - let state = TermIterState::Var(Level::Root, cell, Rc::new("!".to_string())); + let state = TermIterState::Var(Level::Root, cell, RcMutPtr::from("!".to_string())); QueryIterator { state_stack: vec![state], } } &QueryTerm::GetLevelAndUnify(ref cell, ref var) => { - let state = TermIterState::Var(Level::Root, cell, var.clone()); + let state = TermIterState::Var(Level::Root, cell, RcMutPtr::new(var)); QueryIterator { state_stack: vec![state], } @@ -213,7 +267,7 @@ impl<'a> Iterator for QueryIterator<'a> { return Some(TermRef::Literal(lvl, cell, constant)); } TermIterState::Var(lvl, cell, var) => { - return Some(TermRef::Var(lvl, cell, var)); + return Some(TermRef::Var(lvl, cell, var.owned())); } }; } @@ -279,7 +333,7 @@ impl<'a> FactIterator<'a> { vec![TermIterState::Literal(Level::Root, cell, constant)] } Term::Var(cell, var) => { - vec![TermIterState::Var(Level::Root, cell, var.clone())] + vec![TermIterState::Var(Level::Root, cell, RcMutPtr::new(var))] } }; @@ -326,7 +380,7 @@ impl<'a> Iterator for FactIterator<'a> { return Some(TermRef::Literal(lvl, cell, constant)) } TermIterState::Var(lvl, cell, var) => { - return Some(TermRef::Var(lvl, cell, var)); + return Some(TermRef::Var(lvl, cell, var.owned())); } _ => {} } @@ -530,38 +584,3 @@ impl<'a> Iterator for ChunkedIterator<'a> { self.iter.next().map(|term| self.take_chunk(term)) } } - -/* -================================================================================ - -This is a disjunction compilation experiment attempting to -adapt the paper "Compiling Large Disjunctions" to Scryer Prolog. - -================================================================================ -*/ - -enum VarInfo { - Perm, - Temp, - Void -} - -pub struct ChunkInfo { - chunk_num: usize, - vars: Vec<(Rc, VarInfo)>, -} - -pub struct BranchInfo { - branch_num: usize, // TODO: Rational?? or own type? - delta: usize, // TODO: Rational?? - chunks: Vec, -} - -pub struct ControlIterator<'a> { - current_branch_num: usize, // TODO: same as above - state_stack: Vec>, - branch_map: IndexMap, Vec>, -} - -impl ControlIterator { -} diff --git a/src/lib.rs b/src/lib.rs index 45dc2385..b117ab16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,7 @@ pub mod instructions { include!(concat!(env!("OUT_DIR"), "/instructions.rs")); } mod iterators; +mod disjuncts; pub mod machine; mod raw_block; pub mod read; diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 3067ecb9..04f39428 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -5253,7 +5253,7 @@ impl Machine { } &Instruction::ExecuteUnattributedVar(_) => { self.machine_st.unattributed_var(); - step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + self.machine_st.p = self.machine_st.cp; } &Instruction::CallGetDBRefs(_) => { self.get_db_refs(); diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 616fe7ee..f268c0f7 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -465,6 +465,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { } } + pub(crate) fn read_term_from_heap(&mut self, r: RegType) -> Result { + let machine_st = LS::machine_st(&mut self.payload); + machine_st.read_term_from_heap(r) + } + pub(crate) fn load(mut self) -> Result { while let Some(decl) = self.dequeue_terms()? { self.load_decl(decl)?; @@ -531,106 +536,6 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { Ok(()) } - pub(super) fn read_term_from_heap(&mut self, heap_term_loc: RegType) -> Result { - 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 +1048,9 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { &mut self, r: RegType, ) -> Result, SessionError> { - let export_list = self.read_term_from_heap(r)?; + let machine_st = LS::machine_st(&mut self.payload); + + let export_list = machine_st.read_term_from_heap(r)?; 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 +1400,107 @@ impl<'a> MachinePreludeView<'a> { } } +impl MachineState { + pub(super) fn read_term_from_heap(&mut self, r: RegType) -> Result { + let term_addr = self[r]; + + let mut term_stack = vec![]; + let mut iter = stackful_post_order_iter(&mut self.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 = 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) => { + 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()) + } +} + impl Machine { pub(crate) fn use_module(&mut self) -> CallResult { let subevacuable_addr = self diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 0933b11c..cf7bf946 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -667,3 +667,30 @@ pub fn unfold_by_str(mut term: Term, s: Atom) -> Vec { terms.push(term); terms } + +fn unfold_by_str_ref_once(term: &Term, s: Atom) -> Option<(&Term, &Term)> { + if let Term::Clause(_, ref name, ref subterms) = term { + if name == &s && subterms.len() == 2 { + let fst = &subterms[0]; + let snd = &subterms[1]; + + return Some((fst, snd)); + } + } + + None +} + +pub fn unfold_by_str_ref(mut term: &Term, s: Atom) -> Vec<&Term> { + let mut terms = vec![]; + + while let Some((fst, snd)) = unfold_by_str_ref_once(&term, s) { + terms.push(fst); + term = snd; + } + + terms.push(term); + terms +} + + From b9c9de522256f7ec2b56fd8c5e0348153fbb53b0 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 4 Oct 2022 09:24:37 -0600 Subject: [PATCH 189/361] add classifications and occurrence counting --- src/allocator.rs | 11 ++-- src/arithmetic.rs | 11 ++-- src/codegen.rs | 19 +++--- src/debray_allocator.rs | 25 ++++---- src/fixtures.rs | 21 +++---- src/forms.rs | 5 +- src/heap_print.rs | 24 ++++--- src/iterators.rs | 110 ++++++++++++++------------------- src/machine/loader.rs | 4 +- src/machine/machine_indices.rs | 5 +- src/machine/machine_state.rs | 13 ++-- src/machine/preprocessor.rs | 7 +-- src/machine/system_calls.rs | 3 +- src/parser/ast.rs | 40 +++++++++++- src/parser/parser.rs | 3 +- 15 files changed, 153 insertions(+), 148 deletions(-) diff --git a/src/allocator.rs b/src/allocator.rs index 5be3aae1..76bdfb53 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -8,7 +8,6 @@ use crate::machine::machine_indices::*; use crate::targets::*; use std::cell::Cell; -use std::rc::Rc; pub(crate) trait Allocator { fn new() -> Self; @@ -30,7 +29,7 @@ pub(crate) trait Allocator { fn mark_reserved_var<'a, Target: CompilationTarget<'a>>( &mut self, - var_name: Rc, + var_name: Var, lvl: Level, cell: &'a Cell, term_loc: GenContext, @@ -41,7 +40,7 @@ pub(crate) trait Allocator { fn mark_var<'a, Target: CompilationTarget<'a>>( &mut self, - var_name: Rc, + var_name: Var, lvl: Level, cell: &'a Cell, context: GenContext, @@ -88,17 +87,17 @@ pub(crate) trait Allocator { perm_vs } - fn get(&self, var: Rc) -> RegType { + fn get(&self, var: Var) -> RegType { self.bindings() .get(&var) .map_or(temp_v!(0), |v| v.as_reg_type()) } - fn is_unbound(&self, var: Rc) -> bool { + fn is_unbound(&self, var: Var) -> bool { self.get(var).reg_num() == 0 } - fn record_register(&mut self, var: Rc, r: RegType) { + fn record_register(&mut self, var: Var, r: RegType) { match self.bindings_mut().get_mut(&var).unwrap() { &mut VarData::Temp(_, ref mut s, _) => *s = r.reg_num(), &mut VarData::Perm(ref mut s) => *s = r.reg_num(), diff --git a/src/arithmetic.rs b/src/arithmetic.rs index fcfd9599..94974a51 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -22,7 +22,6 @@ use std::convert::TryFrom; use std::f64; use std::num::FpCategory; use std::ops::Div; -use std::rc::Rc; use std::vec::Vec; #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -74,7 +73,7 @@ impl<'a> ArithInstructionIterator<'a> { 2, )) } - Term::Var(cell, var) => TermIterState::Var(Level::Shallow, cell, RcMutPtr::new(var)), + Term::Var(cell, var) => TermIterState::Var(Level::Shallow, cell, VarPtr::from(var)), }; Ok(ArithInstructionIterator { @@ -87,7 +86,7 @@ impl<'a> ArithInstructionIterator<'a> { pub(crate) enum ArithTermRef<'a> { Literal(&'a Literal), Op(Atom, usize), // name, arity. - Var(Level, &'a Cell, Rc), + Var(Level, &'a Cell, Var), } impl<'a> Iterator for ArithInstructionIterator<'a> { @@ -115,8 +114,8 @@ impl<'a> Iterator for ArithInstructionIterator<'a> { } } TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(c))), - TermIterState::Var(lvl, cell, var) => { - return Some(Ok(ArithTermRef::Var(lvl, cell, var.owned()))); + TermIterState::Var(lvl, cell, var_ref) => { + return Some(Ok(ArithTermRef::Var(lvl, cell, Var::from(var_ref)))); } _ => { return Some(Err(ArithmeticError::NonEvaluableFunctor( @@ -317,7 +316,7 @@ impl<'a> ArithmeticEvaluator<'a> { ArithTermRef::Var(lvl, cell, name) => { let r = if lvl == Level::Shallow { self.marker.mark_non_callable( - name.clone(), + name, arg, term_loc, cell, diff --git a/src/codegen.rs b/src/codegen.rs index 1aea58d2..a3fdc99b 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -20,7 +20,6 @@ use indexmap::{IndexMap, IndexSet}; use std::cell::Cell; use std::collections::VecDeque; -use std::rc::Rc; #[derive(Debug)] pub(crate) struct ConjunctInfo<'a> { @@ -170,7 +169,7 @@ impl CodeGenSettings { pub(crate) struct CodeGenerator<'a> { pub(crate) atom_tbl: &'a mut AtomTable, marker: DebrayAllocator, - pub(crate) var_count: IndexMap, usize>, + pub(crate) var_count: IndexMap, settings: CodeGenSettings, pub(crate) skeleton: PredicateSkeleton, pub(crate) jmp_by_locs: Vec, @@ -180,7 +179,7 @@ pub(crate) struct CodeGenerator<'a> { impl DebrayAllocator { fn mark_var_in_non_callable( &mut self, - name: Rc, + name: Var, term_loc: GenContext, vr: &Cell, code: &mut Code, @@ -190,7 +189,7 @@ impl DebrayAllocator { } #[inline(always)] - pub(crate) fn get_binding(&self, name: &String) -> Option { + pub(crate) fn get_binding(&self, name: &Var) -> Option { match self.bindings().get(name) { Some(&VarData::Temp(_, t, _)) if t != 0 => Some(RegType::Temp(t)), Some(&VarData::Perm(p)) if p != 0 => Some(RegType::Perm(p)), @@ -200,7 +199,7 @@ impl DebrayAllocator { pub(crate) fn mark_non_callable( &mut self, - name: Rc, + name: Var, arg: usize, term_loc: GenContext, vr: &Cell, @@ -299,7 +298,7 @@ impl<'b> CodeGenerator<'b> { } } - fn get_var_count(&self, var: &String) -> usize { + fn get_var_count(&self, var: &Var) -> usize { *self.var_count.get(var).unwrap() } @@ -320,7 +319,7 @@ impl<'b> CodeGenerator<'b> { fn deep_var_instr<'a, Target: crate::targets::CompilationTarget<'a>>( &mut self, cell: &'a Cell, - var: &Rc, + var: &Var, term_loc: GenContext, target: &mut Code, ) { @@ -429,7 +428,7 @@ impl<'b> CodeGenerator<'b> { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); target.push(Target::to_pstr(lvl, atom, cell.get(), false)); } - TermRef::Var(lvl @ Level::Shallow, cell, ref var) if var.as_str() == "!" => { + TermRef::Var(lvl @ Level::Shallow, cell, var) if var.as_str() == Some("!") => { if self.marker.is_unbound(var.clone()) { if term_loc != GenContext::Head { self.marker.mark_reserved_var::( @@ -835,7 +834,7 @@ impl<'b> CodeGenerator<'b> { #[inline] fn compile_unblocked_cut(&mut self, code: &mut Code, cell: &Cell) { - let r = self.marker.get(Rc::new(String::from("!"))); + let r = self.marker.get(Var::from("!")); cell.set(VarReg::Norm(r)); code.push(instr!("$set_cp", cell.get().norm(), 0)); } @@ -844,7 +843,7 @@ impl<'b> CodeGenerator<'b> { &mut self, code: &mut Code, cell: &Cell, - var: Rc, + var: Var, term_loc: GenContext, ) { let mut target = Code::new(); diff --git a/src/debray_allocator.rs b/src/debray_allocator.rs index e9cc73c7..73645929 100644 --- a/src/debray_allocator.rs +++ b/src/debray_allocator.rs @@ -14,28 +14,27 @@ use fxhash::FxBuildHasher; use std::cell::Cell; use std::collections::BTreeSet; -use std::rc::Rc; #[derive(Debug)] pub(crate) struct DebrayAllocator { - bindings: IndexMap, VarData, FxBuildHasher>, + bindings: IndexMap, arg_c: usize, temp_lb: usize, arity: usize, // 0 if not at head. - contents: IndexMap, FxBuildHasher>, + contents: IndexMap, in_use: BTreeSet, free_list: Vec, } impl DebrayAllocator { - fn is_curr_arg_distinct_from(&self, var: &String) -> bool { + fn is_curr_arg_distinct_from(&self, var: &Var) -> bool { match self.contents.get(&self.arg_c) { - Some(t_var) if **t_var != *var => true, + Some(t_var) if *t_var != *var => true, _ => false, } } - fn occurs_shallowly_in_head(&self, var: &String, r: usize) -> bool { + fn occurs_shallowly_in_head(&self, var: &Var, r: usize) -> bool { match self.bindings.get(var).unwrap() { &VarData::Temp(_, _, ref tvd) => tvd.use_set.contains(&(GenContext::Head, r)), _ => false, @@ -48,7 +47,7 @@ impl DebrayAllocator { in_use_range || self.in_use.contains(&r) } - fn alloc_with_cr(&self, var: &String) -> usize { + fn alloc_with_cr(&self, var: &Var) -> usize { match self.bindings.get(var) { Some(&VarData::Temp(_, _, ref tvd)) => { for &(_, reg) in tvd.use_set.iter() { @@ -74,7 +73,7 @@ impl DebrayAllocator { } } - fn alloc_with_ca(&self, var: &String) -> usize { + fn alloc_with_ca(&self, var: &Var) -> usize { match self.bindings.get(var) { Some(&VarData::Temp(_, _, ref tvd)) => { for &(_, reg) in tvd.use_set.iter() { @@ -102,7 +101,7 @@ impl DebrayAllocator { } } - fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(Rc, usize)> { + fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(Var, usize)> { // we want to allocate a register to the k^{th} parameter, par_k. // par_k may not be a temporary variable. let k = self.arg_c; @@ -154,7 +153,7 @@ impl DebrayAllocator { fn alloc_reg_to_var<'a, Target: CompilationTarget<'a>>( &mut self, - var: &String, + var: &Var, lvl: Level, term_loc: GenContext, target: &mut Vec, @@ -202,7 +201,7 @@ impl DebrayAllocator { final_index } - fn in_place(&self, var: &String, term_loc: GenContext, r: RegType, k: usize) -> bool { + fn in_place(&self, var: &Var, term_loc: GenContext, r: RegType, k: usize) -> bool { match term_loc { GenContext::Head if !r.is_perm() => r.reg_num() == k, _ => match self.bindings().get(var).unwrap() { @@ -293,7 +292,7 @@ impl Allocator for DebrayAllocator { fn mark_var<'a, Target: CompilationTarget<'a>>( &mut self, - var: Rc, + var: Var, lvl: Level, cell: &'a Cell, term_loc: GenContext, @@ -321,7 +320,7 @@ impl Allocator for DebrayAllocator { fn mark_reserved_var<'a, Target: CompilationTarget<'a>>( &mut self, - var: Rc, + var: Var, lvl: Level, cell: &'a Cell, term_loc: GenContext, diff --git a/src/fixtures.rs b/src/fixtures.rs index 65340da0..1433b092 100644 --- a/src/fixtures.rs +++ b/src/fixtures.rs @@ -9,7 +9,6 @@ use indexmap::{IndexMap, IndexSet}; use std::cell::Cell; use std::collections::BTreeSet; use std::mem::swap; -use std::rc::Rc; use std::vec::Vec; // labeled with chunk numbers. @@ -84,8 +83,8 @@ type VariableFixture<'a> = (VarStatus, Vec<&'a Cell>); #[derive(Debug)] pub(crate) struct VariableFixtures<'a> { - perm_vars: IndexMap, VariableFixture<'a>>, - last_chunk_temp_vars: IndexSet>, + perm_vars: IndexMap>, + last_chunk_temp_vars: IndexSet, } impl<'a> VariableFixtures<'a> { @@ -96,11 +95,11 @@ impl<'a> VariableFixtures<'a> { } } - pub(crate) fn insert(&mut self, var: Rc, vs: VariableFixture<'a>) { + pub(crate) fn insert(&mut self, var: Var, vs: VariableFixture<'a>) { self.perm_vars.insert(var, vs); } - pub(crate) fn insert_last_chunk_temp_var(&mut self, var: Rc) { + pub(crate) fn insert_last_chunk_temp_var(&mut self, var: Var) { self.last_chunk_temp_vars.insert(var); } @@ -115,7 +114,7 @@ impl<'a> VariableFixtures<'a> { // Compute the conflict set of u. // 1. - let mut use_sets: IndexMap, OccurrenceSet> = IndexMap::new(); + let mut use_sets: IndexMap = IndexMap::new(); for (var, &mut (ref mut var_status, _)) in self.iter_mut() { if let &mut VarStatus::Temp(_, ref mut var_data) = var_status { @@ -132,7 +131,7 @@ impl<'a> VariableFixtures<'a> { if let GenContext::Last(cn_u) = term_loc { for (ref t, &mut (ref mut var_status, _)) in self.iter_mut() { if let &mut VarStatus::Temp(cn_t, ref mut t_data) = var_status { - if cn_u == cn_t && *u != ***t { + if cn_u == cn_t && u != **t { if !t_data.uses_reg(reg) { t_data.no_use_set.insert(reg); } @@ -153,11 +152,11 @@ impl<'a> VariableFixtures<'a> { } } - fn get_mut(&mut self, u: Rc) -> Option<&mut VariableFixture<'a>> { + fn get_mut(&mut self, u: Var) -> Option<&mut VariableFixture<'a>> { self.perm_vars.get_mut(&u) } - fn iter_mut(&mut self) -> indexmap::map::IterMut, VariableFixture<'a>> { + fn iter_mut(&mut self) -> indexmap::map::IterMut> { self.perm_vars.iter_mut() } @@ -218,11 +217,11 @@ impl<'a> VariableFixtures<'a> { } } - pub(crate) fn into_iter(self) -> indexmap::map::IntoIter, VariableFixture<'a>> { + pub(crate) fn into_iter(self) -> indexmap::map::IntoIter> { self.perm_vars.into_iter() } - fn values(&self) -> indexmap::map::Values, VariableFixture<'a>> { + fn values(&self) -> indexmap::map::Values> { self.perm_vars.values() } diff --git a/src/forms.rs b/src/forms.rs index 1c014587..9db69581 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -21,7 +21,6 @@ use std::convert::TryFrom; use std::fmt; use std::ops::AddAssign; use std::path::PathBuf; -use std::rc::Rc; use crate::{is_infix, is_postfix}; @@ -85,8 +84,8 @@ pub enum QueryTerm { Clause(Cell, ClauseType, Vec, CallPolicy), BlockedCut, // a cut which is 'blocked by letters', like the P term in P -> Q. UnblockedCut(Cell), - GetLevelAndUnify(Cell, Rc), - Jump(JumpStub), + GetLevelAndUnify(Cell, Var), + Jump(JumpStub), // SOON: Branch(Vec), } impl QueryTerm { diff --git a/src/heap_print.rs b/src/heap_print.rs index 54f52cd6..d09211d1 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -472,7 +472,7 @@ pub struct HCPrinter<'a, Outputter> { state_stack: Vec, toplevel_spec: Option, last_item_idx: usize, - pub var_names: IndexMap>, + pub var_names: IndexMap, pub numbervars_offset: Integer, pub numbervars: bool, pub quoted: bool, @@ -803,7 +803,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { if let Some(var) = self.var_names.get(&addr) { read_heap_cell!(addr, (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { - return Some(format!("{}", var.as_str())); + return Some(var.to_string()); } _ => { self.iter.push_stack(h); @@ -847,10 +847,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { // short-circuits handle_heap_term. // self.iter.pop_stack(); - let var_str = var.as_str(); + let var_str = var.to_string(); - push_space_if_amb!(self, var_str, { - append_str!(self, var_str); + push_space_if_amb!(self, &var_str, { + append_str!(self, &var_str); }); None @@ -862,8 +862,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { Some(var) => { // If the term is bound to a named variable, // print the variable's name to output. - push_space_if_amb!(self, &var, { - append_str!(self, &var); + let var_str = var.to_string(); + + push_space_if_amb!(self, &var_str, { + append_str!(self, &var_str); }); } None => { @@ -1715,9 +1717,7 @@ mod tests { heap_loc_as_cell!(0) ); - printer - .var_names - .insert(list_loc_as_cell!(1), Rc::new("L".to_string())); + printer.var_names.insert(list_loc_as_cell!(1), Var::from("L")); let output = printer.print(); @@ -1778,9 +1778,7 @@ mod tests { heap_loc_as_cell!(0) ); - printer - .var_names - .insert(list_loc_as_cell!(1), Rc::new("L".to_string())); + printer.var_names.insert(list_loc_as_cell!(1), Var::from("L")); let output = printer.print(); diff --git a/src/iterators.rs b/src/iterators.rs index 6bf92a5f..ac87a451 100644 --- a/src/iterators.rs +++ b/src/iterators.rs @@ -7,11 +7,40 @@ use std::cell::Cell; use std::collections::VecDeque; use std::fmt; use std::fmt::Debug; -use std::hash::{Hash, Hasher}; +use std::hash::{Hash}; use std::iter::*; -use std::rc::Rc; use std::vec::Vec; +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) struct VarPtr { + ptr: std::ptr::NonNull, +} + +impl From<&Var> for VarPtr { + #[inline] + fn from(value: &Var) -> VarPtr { + unsafe { + VarPtr { ptr: std::ptr::NonNull::new_unchecked(value as *const _ as *mut _) } + } + } +} + +impl From for Var { + #[inline] + fn from(value: VarPtr) -> Var { + unsafe { + (*value.ptr.as_ptr()).clone() + } + } +} + +impl VarPtr { + pub(crate) fn set(&mut self, value: Var) { + unsafe { *self.ptr.as_mut() = value; } + } +} + + #[derive(Debug, Clone)] pub(crate) enum TermRef<'a> { AnonVar(Level), @@ -20,7 +49,7 @@ pub(crate) enum TermRef<'a> { Clause(Level, &'a Cell, Atom, &'a Vec), PartialString(Level, &'a Cell, &'a String, &'a Box), CompleteString(Level, &'a Cell, Atom), - Var(Level, &'a Cell, Rc), + Var(Level, &'a Cell, Var), } impl<'a> TermRef<'a> { @@ -37,58 +66,6 @@ impl<'a> TermRef<'a> { } } -#[derive(Clone, Debug)] -pub(crate) struct RcMutPtr { - owned: Rc, - ptr: *mut Rc, -} - -impl RcMutPtr { - #[inline] - pub(crate) fn new(rc: &Rc) -> Self { - Self { owned: rc.clone(), ptr: rc as *const _ as *mut _ } - } - - #[inline] - pub(crate) fn owned(&self) -> Rc { - self.owned.clone() - } - - #[inline] - pub(crate) fn set(&mut self, var_b_marker: &Rc) { - self.owned = var_b_marker.clone(); - - unsafe { - if !self.ptr.is_null() { - *self.ptr = self.owned.clone(); - } - } - } -} - -impl From for RcMutPtr { - #[inline] - fn from(value: T) -> RcMutPtr { - let owned = Rc::new(value); - RcMutPtr { owned, ptr: std::ptr::null_mut() } - } -} - -impl PartialEq for RcMutPtr { - fn eq(&self, rhs: &Self) -> bool { - &self.owned == &rhs.owned - } -} - -impl Eq for RcMutPtr {} - -impl Hash for RcMutPtr { - #[inline(always)] - fn hash(&self, hasher: &mut H) { - self.owned.hash(hasher) - } -} - #[derive(Debug)] pub(crate) enum TermIterState<'a> { AnonVar(Level), @@ -99,7 +76,8 @@ pub(crate) enum TermIterState<'a> { InitialPartialString(Level, &'a Cell, &'a String, &'a Box), FinalPartialString(Level, &'a Cell, &'a String, &'a Box), CompleteString(Level, &'a Cell, Atom), - Var(Level, &'a Cell, RcMutPtr), + UnblockedCut(Level, &'a Cell), + Var(Level, &'a Cell, VarPtr), } impl<'a> TermIterState<'a> { @@ -119,7 +97,7 @@ impl<'a> TermIterState<'a> { Term::CompleteString(cell, atom) => { TermIterState::CompleteString(lvl, cell, *atom) } - Term::Var(cell, var) => TermIterState::Var(lvl, cell, RcMutPtr::new(var)), + Term::Var(cell, var) => TermIterState::Var(lvl, cell, VarPtr::from(var)), } } } @@ -160,7 +138,7 @@ impl<'a> QueryIterator<'a> { *name, terms, ), - Term::Var(cell, var) => TermIterState::Var(Level::Root, cell, RcMutPtr::new(var)), + Term::Var(cell, var) => TermIterState::Var(Level::Root, cell, VarPtr::from(var)), }; QueryIterator { @@ -183,13 +161,14 @@ impl<'a> QueryIterator<'a> { } } &QueryTerm::UnblockedCut(ref cell) => { - let state = TermIterState::Var(Level::Root, cell, RcMutPtr::from("!".to_string())); + let state = TermIterState::UnblockedCut(Level::Root, cell); + QueryIterator { state_stack: vec![state], } } &QueryTerm::GetLevelAndUnify(ref cell, ref var) => { - let state = TermIterState::Var(Level::Root, cell, RcMutPtr::new(var)); + let state = TermIterState::Var(Level::Root, cell, VarPtr::from(var)); QueryIterator { state_stack: vec![state], } @@ -267,7 +246,10 @@ impl<'a> Iterator for QueryIterator<'a> { return Some(TermRef::Literal(lvl, cell, constant)); } TermIterState::Var(lvl, cell, var) => { - return Some(TermRef::Var(lvl, cell, var.owned())); + return Some(TermRef::Var(lvl, cell, Var::from(var))); + } + TermIterState::UnblockedCut(lvl, cell) => { + return Some(TermRef::Var(lvl, cell, Var::from("!"))); } }; } @@ -333,7 +315,7 @@ impl<'a> FactIterator<'a> { vec![TermIterState::Literal(Level::Root, cell, constant)] } Term::Var(cell, var) => { - vec![TermIterState::Var(Level::Root, cell, RcMutPtr::new(var))] + vec![TermIterState::Var(Level::Root, cell, VarPtr::from(var))] } }; @@ -380,7 +362,7 @@ impl<'a> Iterator for FactIterator<'a> { return Some(TermRef::Literal(lvl, cell, constant)) } TermIterState::Var(lvl, cell, var) => { - return Some(TermRef::Var(lvl, cell, var.owned())); + return Some(TermRef::Var(lvl, cell, Var::from(var))); } _ => {} } @@ -420,7 +402,7 @@ impl<'a> ChunkedTerm<'a> { fn contains_cut_var<'a, Iter: Iterator>(terms: Iter) -> bool { for term in terms { if let &Term::Var(_, ref var) = term { - if var.as_str() == "!" { + if var.as_str() == Some("!") { return true; } } diff --git a/src/machine/loader.rs b/src/machine/loader.rs index f268c0f7..bb093a0e 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -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, @@ -1429,8 +1428,7 @@ impl MachineState { } } (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar, h) => { - let offset_string = format!("_{}", h); - term_stack.push(Term::Var(Cell::default(), Rc::new(offset_string))); + term_stack.push(Term::Var(Cell::default(), Var::Generated(h))); } (HeapCellValueTag::Cons | HeapCellValueTag::CStr | HeapCellValueTag::Fixnum | HeapCellValueTag::Char | HeapCellValueTag::F64) => { diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index 11a9d6e8..3f49e1ce 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -16,7 +16,6 @@ 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,8 @@ impl CodeIndex { } } -pub(crate) type HeapVarDict = IndexMap, HeapCellValue, FxBuildHasher>; -pub(crate) type AllocVarDict = IndexMap, VarData, FxBuildHasher>; +pub(crate) type HeapVarDict = IndexMap; +pub(crate) type AllocVarDict = IndexMap; pub(crate) type GlobalVarDir = IndexMap), FxBuildHasher>; diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index bdaf048c..6d0de7d9 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -21,7 +21,6 @@ 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]; @@ -501,13 +500,13 @@ impl MachineState { pub fn read_term(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult { fn push_var_eq_functors<'a>( heap: &mut Heap, - iter: impl Iterator, &'a HeapCellValue)>, + iter: impl Iterator, atom_tbl: &mut AtomTable, ) -> Vec { let mut list_of_var_eqs = vec![]; for (var, binding) in iter { - let var_atom = atom_tbl.build_with(&var); + let var_atom = atom_tbl.build_with(&var.to_string()); let h = heap.len(); heap.push(atom_as_cell!(atom!("="), 2)); @@ -673,7 +672,7 @@ impl MachineState { let printer = match self.try_from_list(self.registers[6], stub_gen) { Ok(addrs) => { - let mut var_names: IndexMap> = IndexMap::new(); + let mut var_names: IndexMap = IndexMap::new(); for addr in addrs { read_heap_cell!(addr, @@ -691,18 +690,18 @@ impl MachineState { read_heap_cell!(atom, (HeapCellValueTag::Char, c) => { - var_names.insert(var, Rc::new(c.to_string())); + var_names.insert(var, Var::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, Var::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, Var::from(name.as_str())); } _ => { unreachable!(); diff --git a/src/machine/preprocessor.rs b/src/machine/preprocessor.rs index 7f0cc264..d2c33b06 100644 --- a/src/machine/preprocessor.rs +++ b/src/machine/preprocessor.rs @@ -12,7 +12,6 @@ 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 ( .. -> ... ; ...) @@ -373,7 +372,7 @@ fn mark_cut_variable(term: &mut Term) -> bool { }; if cut_var_found { - *term = Term::Var(Cell::default(), Rc::new(String::from("!"))); + *term = Term::Var(Cell::default(), Var::from("!")); true } else { false @@ -656,7 +655,7 @@ fn compute_head(term: &Term) -> Vec { } } - vars.insert(Rc::new(String::from("!"))); + vars.insert(Var::from("!")); vars.into_iter() .map(|v| Term::Var(Cell::default(), v)) .collect() @@ -767,7 +766,7 @@ impl Preprocessor { } } Term::Literal(_, Literal::Char('!')) => Ok(QueryTerm::BlockedCut), - Term::Var(_, ref v) if v.as_str() == "!" => { + Term::Var(_, ref v) if v.as_str() == Some("!") => { Ok(QueryTerm::UnblockedCut(Cell::default())) } Term::Clause(r, name, mut terms) => match (name, source_arity(&terms)) { diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index e457a611..d26468da 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -51,7 +51,6 @@ use std::net::{TcpListener, TcpStream, SocketAddr, ToSocketAddrs}; use std::num::NonZeroU32; use std::ops::Sub; use std::process; -use std::rc::Rc; use std::str::FromStr; use std::sync::Arc; @@ -1410,7 +1409,7 @@ impl Machine { let vars: Vec<_> = vars .union(&result.supp_vars) // difference + union does not cancel. - .map(|v| Term::Var(Cell::default(), Rc::new(format!("_{}", v.get_value())))) + .map(|v| Term::Var(Cell::default(), Var::Generated(v.get_value()))) .collect(); let helper_clause_loc = self.code.len(); diff --git a/src/parser/ast.rs b/src/parser/ast.rs index cf7bf946..78bd55b4 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -572,6 +572,44 @@ impl Literal { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Var { + Generated(usize), + Named(Rc), +} + +impl From for Var { + #[inline(always)] + fn from(value: String) -> Var { + Var::Named(Rc::new(value)) + } +} + +impl From<&str> for Var { + #[inline(always)] + fn from(value: &str) -> Var { + Var::Named(Rc::new(value.to_owned())) + } +} + +impl Var { + #[inline(always)] + pub fn as_str(&self) -> Option<&str> { + match self { + Var::Generated(_) => None, + Var::Named(value) => Some(&value), + } + } + + #[inline(always)] + pub fn to_string(&self) -> String { + match self { + Var::Generated(n) => format!("_{}", n), + Var::Named(value) => value.to_string(), + } + } +} + #[derive(Debug, Clone)] pub enum Term { AnonVar, @@ -582,7 +620,7 @@ pub enum Term { // other PartialString variants in as_partial_string. PartialString(Cell, String, Box), CompleteString(Cell, Atom), - Var(Cell, Rc), + Var(Cell, Var), } impl Term { diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 74f1b930..ce633b94 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -8,7 +8,6 @@ use crate::parser::rug::ops::NegAssign; use std::cell::Cell; use std::mem; -use std::rc::Rc; #[derive(Debug, Clone, Copy, PartialEq)] enum TokenType { @@ -427,7 +426,7 @@ impl<'a, R: CharRead> Parser<'a, R> { if v.trim() == "_" { self.terms.push(Term::AnonVar); } else { - self.terms.push(Term::Var(Cell::default(), Rc::new(v))); + self.terms.push(Term::Var(Cell::default(), Var::from(v))); } TokenType::Term From e41d1b319bb9d55f3b0a8467fc112406cba9854b Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Mon, 17 Oct 2022 22:56:08 -0600 Subject: [PATCH 190/361] adapt code generation --- src/allocator.rs | 3 + src/fixtures.rs | 2 +- src/forms.rs | 23 +- src/lib.rs | 1 - src/machine/disjuncts.rs | 640 ++++++++++++++++++++++++++++++++++++ src/machine/load_state.rs | 2 +- src/machine/mod.rs | 1 + src/machine/preprocessor.rs | 455 +++---------------------- 8 files changed, 700 insertions(+), 427 deletions(-) create mode 100644 src/machine/disjuncts.rs diff --git a/src/allocator.rs b/src/allocator.rs index 76bdfb53..bc0d2f44 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -60,6 +60,9 @@ pub(crate) trait Allocator { fn take_bindings(self) -> AllocVarDict; fn max_reg_allocated(&self) -> usize; + // TODO: wha.. why?? grrr. it drains the VarStatus data from vs (which it owns!) + // into self.bindings and perm_vs after all is computed (i.e. vs.populate_restricting_sets() + // and vs.set_perm_vals(has_deep_cut) have both been called). fn drain_var_data<'a>( &mut self, vs: VariableFixtures<'a>, diff --git a/src/fixtures.rs b/src/fixtures.rs index 1433b092..67740989 100644 --- a/src/fixtures.rs +++ b/src/fixtures.rs @@ -84,7 +84,7 @@ type VariableFixture<'a> = (VarStatus, Vec<&'a Cell>); #[derive(Debug)] pub(crate) struct VariableFixtures<'a> { perm_vars: IndexMap>, - last_chunk_temp_vars: IndexSet, + last_chunk_temp_vars: IndexSet, // TODO: has no use at all! } impl<'a> VariableFixtures<'a> { diff --git a/src/forms.rs b/src/forms.rs index 9db69581..d571e2d9 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -1,6 +1,7 @@ use crate::arena::*; use crate::atom_table::*; use crate::instructions::*; +use crate::machine::disjuncts::VarRecord; use crate::machine::heap::*; use crate::machine::loader::PredicateQueue; use crate::machine::machine_errors::*; @@ -34,7 +35,7 @@ pub type JumpStub = Vec; #[derive(Debug, Clone)] pub enum TopLevel { - Fact(Term), // Term, line_num, col_num + Fact(Fact), // Term, line_num, col_num Predicate(Predicate), Query(Vec), Rule(Rule), // Rule, line_num, col_num @@ -82,10 +83,11 @@ pub enum CallPolicy { pub enum QueryTerm { // register, clause type, subterms, clause call policy. Clause(Cell, ClauseType, Vec, CallPolicy), - BlockedCut, // a cut which is 'blocked by letters', like the P term in P -> Q. - UnblockedCut(Cell), + Cut, + Not(Vec), + IfThen(Vec, Vec), + Branch(Vec>), GetLevelAndUnify(Cell, Var), - Jump(JumpStub), // SOON: Branch(Vec), } impl QueryTerm { @@ -99,17 +101,24 @@ impl QueryTerm { pub(crate) fn arity(&self) -> usize { match self { &QueryTerm::Clause(_, _, ref subterms, ..) => subterms.len(), - &QueryTerm::BlockedCut | &QueryTerm::UnblockedCut(..) => 0, - &QueryTerm::Jump(ref vars) => vars.len(), - &QueryTerm::GetLevelAndUnify(..) => 1, + &QueryTerm::Cut | &QueryTerm::Branch(_) => 0, + &QueryTerm::IfThen(..) => 2, + &QueryTerm::Not(_) | &QueryTerm::GetLevelAndUnify(..) => 1, } } } +#[derive(Debug, Clone)] +pub struct Fact { + pub(crate) head: Term, + pub(crate) var_records: Vec, +} + #[derive(Debug, Clone)] pub struct Rule { pub(crate) head: (Atom, Vec, QueryTerm), pub(crate) clauses: Vec, + pub(crate) var_records: Vec, } #[derive(Clone, Debug, Hash)] diff --git a/src/lib.rs b/src/lib.rs index b117ab16..45dc2385 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,6 @@ pub mod instructions { include!(concat!(env!("OUT_DIR"), "/instructions.rs")); } mod iterators; -mod disjuncts; pub mod machine; mod raw_block; pub mod read; diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs new file mode 100644 index 00000000..a97f08c5 --- /dev/null +++ b/src/machine/disjuncts.rs @@ -0,0 +1,640 @@ + +/* +================================================================================ + +This is a disjunction compilation experiment attempting to adapt the +paper "Compiling Large Disjunctions" to Scryer Prolog. + +================================================================================ + */ + +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::rug::Rational; + +use indexmap::{IndexMap, IndexSet}; + +use std::cell::Cell; +use std::cmp::Ordering; +use std::hash::{Hash, Hasher}; +use std::ops::{Deref, DerefMut}; + +#[derive(Debug, Clone)] +struct BranchNumber { + branch_num: Rational, + delta: Rational, +} + +impl Default for BranchNumber { + fn default() -> Self { + Self { + branch_num: Rational::from(1 << 10), + delta: Rational::from(1), + } + } +} + +impl PartialEq 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(&self, hasher: &mut H) { + self.branch_num.hash(hasher) + } +} + +impl PartialOrd for BranchNumber { + #[inline] + fn partial_cmp(&self, rhs: &BranchNumber) -> Option { + 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 ChunkInfo { + chunk_num: usize, + vars: Vec, // pointer to incidence +} + +impl ChunkInfo { + fn new(chunk_num: usize) -> Self { + ChunkInfo { chunk_num, vars: vec![] } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BranchInfo { + branch_num: BranchNumber, + chunks: Vec, +} + +impl BranchInfo { + fn new(branch_num: BranchNumber) -> Self { + Self { branch_num, chunks: vec![] } + } +} + +type BranchMapInt = IndexMap>; + +#[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; + +enum TraversalState { + BuildDisjunct(usize), // construct a QueryTerm::Branch with number of disjuncts. + BuildIf(usize, Term), // build the P term of P -> Q + BuildThen(usize, Vec), // build the Q term of P -> Q + BuildNot(usize), // build the P term of \+ P + ResetCallPolicy(CallPolicy), + Term(Term), + AddBranchNum(BranchNumber), // set current_branch_number, add it to the root set + RemoveBranchNum, // remove latest branch number from the root set + RepBranchNum(BranchNumber), // replace current_branch_number and the latest in the root set + IncrChunkNum, // increment self.current_chunk_number +} + +impl Term { + #[inline] + fn is_var(&self) -> bool { + if let Term::Var(..) = self { + true + } else { + false + } + } + + #[inline] + fn is_compound(&self) -> bool { + match self { + Term::Clause(..) | Term::Cons(..) => true, + _ => false, + } + } +} + +pub struct VariableClassifier { + call_policy: CallPolicy, + current_branch_num: BranchNumber, + current_chunk_num: usize, + branch_map: BranchMap, + root_set: RootSet, +} + +#[derive(Debug)] +pub enum VarClassification { + Void, + Temp, + Perm, +} + +pub struct VarRecord { + pub classification: VarClassification, + pub chunk_occurrences: Vec, + pub num_occurrences: usize, +} + +pub type ClassifyFactResult = (Term, Vec); +pub type ClassifyRuleResult = (Term, Vec, Vec); + +fn merge_branch_seq>(branches: Iter) -> BranchInfo { + let mut branch_info = BranchInfo::new(BranchNumber::default()); + + for mut branch in branches { + branch_info.branch_num = branch.branch_num; + + if let Some(last_chunk) = branch_info.chunks.last_mut() { + if let Some(first_moved_chunk) = branch.chunks.first_mut() { + if last_chunk.chunk_num == first_moved_chunk.chunk_num { + last_chunk.vars.extend(first_moved_chunk.vars.drain(..)); + branch_info.chunks.extend(branch.chunks.drain(1 ..)); + + continue; + } + } + } + + branch_info.chunks.extend(branch.chunks.drain(..)); + } + + branch_info.branch_num.delta *= 2; + branch_info.branch_num.branch_num -= &branch_info.branch_num.delta; + + branch_info +} + +impl VariableClassifier { + pub fn new(call_policy: CallPolicy) -> Self { + Self { + call_policy, + current_branch_num: BranchNumber::default(), + current_chunk_num: 0, + branch_map: BranchMap(BranchMapInt::new()), + root_set: RootSet::new(), + } + } + + pub fn classify_fact(mut self, term: Term) -> Result { + self.classify_head_variables(&term)?; + Ok((term, self.branch_map.separate_and_classify_variables())) + } + + pub fn classify_rule<'a, LS: LoadState<'a>>( + mut self, + loader: &mut Loader<'a, LS>, + head: Term, + body: Term, + ) -> Result { + self.classify_head_variables(&head)?; + let query_terms = self.classify_body_variables(loader, body)?; + + Ok((head, query_terms, self.branch_map.separate_and_classify_variables())) + } + + /* + pub fn to_branch_map(mut self, term: Term) -> Result { + self.root_set.insert(BranchNumber::default()); + + let (head_term, query_terms) = match term { + Term::Clause(_, atom!(":-"), terms) if terms.len() == 2 => { + let head_term = terms[0]; + + self.classify_head_variables(&head_term)?; + (head_term, self.classify_body_variables(terms[1])?) + } + _ => { + self.classify_head_variables(&term)?; + (term, vec![]) + } + }; + + self.merge_branches(); + Ok((head_term, query_terms, self.branch_map)) + } + */ + + 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 probe_body_term(&mut self, term: &Term) { + // true to iterate the root, which may be a variable! + for term_ref in breadth_first_iter(term, true) { + if let TermRef::Var(_, _, var_name) = term_ref { + self.probe_body_var(Var::from(var_name)); + } + } + } + + fn probe_body_var(&mut self, var_name: Var) { + let branch_info_v = self.branch_map.entry(var_name) + .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::new(self.current_chunk_num)); + } + + let chunk_info = branch_info.chunks.last_mut().unwrap(); + chunk_info.vars.push(VarPtr::from(&var_name)); + } + + fn classify_head_variables(&mut self, term: &Term) -> Result<(), CompilationError> { + match term { + Term::Clause(..) | Term::Literal(_, Literal::Atom(_)) => { + } + _ => return Err(CompilationError::InvalidRuleHead), + } + + // false argument to breadth_first_iter because the root is not iterable. + for term_ref in breadth_first_iter(term, false) { + if let TermRef::Var(_, _, var_name) = term_ref { + // 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::from(var_name)) + .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::new(self.current_chunk_num)); + } + + let chunk_info = branch_info.chunks.last_mut().unwrap(); + chunk_info.vars.push(VarPtr::from(&var_name)); + } + } + + Ok(()) + } + + fn classify_body_variables<'a, LS: LoadState<'a>>( + &mut self, + loader: &mut Loader<'a, LS>, + term: Term, + ) -> Result, CompilationError> { + let mut state_stack = vec![TraversalState::Term(term)]; + let mut build_stack = vec![]; + + 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::IncrChunkNum => { + self.current_chunk_num += 1; + } + TraversalState::BuildDisjunct(preceding_len) => { + let iter = build_stack.drain(preceding_len ..); + + if let QueryTerm::Branch(ref mut disjuncts) = &mut build_stack[preceding_len] { + disjuncts.push(iter.collect()); + } + } + TraversalState::BuildIf(preceding_len, then_term) => { + let iter = build_stack.drain(preceding_len ..); + let build_stack_len = build_stack.len(); + + state_stack.push(TraversalState::BuildThen(build_stack_len, iter.collect())); + } + TraversalState::BuildThen(preceding_len, if_terms) => { + let iter = build_stack.drain(preceding_len ..); + build_stack.push(QueryTerm::IfThen(if_terms, iter.collect())); + } + TraversalState::BuildNot(preceding_len) => { + let iter = build_stack.drain(preceding_len ..); + build_stack.push(QueryTerm::Not(iter.collect())); + } + TraversalState::ResetCallPolicy(call_policy) => { + self.call_policy = call_policy; + } + TraversalState::Term(term) => { + match term { + Term::Clause(_, atom!(","), terms) if terms.len() == 2 => { + state_stack.extend( + unfold_by_str(terms[1], atom!(",")) + .into_iter() + .rev() + .map(TraversalState::Term), + ); + + state_stack.push(TraversalState::Term(terms[0])); + } + Term::Clause(_, atom!(";"), terms) if terms.len() == 2 => { + let first_branch_num = self.current_branch_num.split(); + let branches: Vec<_> = std::iter::once(terms[0]) + .chain(unfold_by_str(terms[1], 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.push(QueryTerm::Branch(vec![])); + state_stack.push(TraversalState::BuildDisjunct(build_stack_len)); + + state_stack.push(TraversalState::RepBranchNum( + self.current_branch_num.halve_delta(), + )); + + let iter = branches.into_iter().zip(branch_numbers.into_iter()); + + 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)); + } + } + Term::Clause(_, atom!("->"), mut terms) if terms.len() == 2 => { + let then_term = terms.pop().unwrap(); + let if_term = terms.pop().unwrap(); + let build_stack_len = build_stack.len(); + + state_stack.push(TraversalState::BuildIf(build_stack_len, then_term)); + state_stack.push(TraversalState::Term(if_term)); + } + Term::Clause(_, atom!("\\+"), terms) if terms.len() == 1 => { + let build_stack_len = build_stack.len(); + + state_stack.push(TraversalState::BuildNot(build_stack_len)); + state_stack.push(TraversalState::Term(terms[0])); + } + Term::Clause(_, atom!("$get_level"), terms) if terms.len() == 1 => { + state_stack.push(TraversalState::IncrChunkNum); + + if let Term::Var(_, ref var) = &terms[0] { + build_stack.push(QueryTerm::GetLevelAndUnify(Cell::default(), var.clone())); + } else { + return Err(CompilationError::InadmissibleQueryTerm); + } + } + 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 !ClauseType::is_inbuilt(name, 0) { + state_stack.push(TraversalState::IncrChunkNum); + } + + build_stack.push( + 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 !ClauseType::is_inbuilt(name, terms.len()) { + state_stack.push(TraversalState::IncrChunkNum); + } + + build_stack.push( + qualified_clause_to_query_term( + loader, + module_name, + name, + terms, + self.call_policy, + ), + ); + } + (module_name, predicate_name) => { + state_stack.push(TraversalState::IncrChunkNum); + + terms.push(module_name); + terms.push(predicate_name); + + build_stack.push( + clause_to_query_term( + loader, + atom!("call"), + vec![Term::Clause(Cell::default(), atom!(":"), terms)], + self.call_policy, + ), + ); + } + } + } + Term::Clause(cell, atom!("$call_with_inference_counting"), terms) if terms.len() == 2 => { + state_stack.push(TraversalState::ResetCallPolicy(self.call_policy)); + state_stack.push(TraversalState::Term(terms[0])); + + self.call_policy = CallPolicy::Counted; + } + Term::Clause(cell, name, terms) => { + if !ClauseType::is_inbuilt(name, terms.len()) { + state_stack.push(TraversalState::IncrChunkNum); + } + + for term in terms.iter() { + self.probe_body_term(term); + } + + build_stack.push( + clause_to_query_term( + loader, + name, + terms, + self.call_policy, + ), + ); + } + Term::Literal(_, Literal::Atom(atom!("!"))) | + Term::Literal(_, Literal::Char('!')) => { + build_stack.push(QueryTerm::Cut); + } + Term::Literal(cell, Literal::Atom(name)) => { + if !ClauseType::is_inbuilt(name, 0) { + state_stack.push(TraversalState::IncrChunkNum); + } + + build_stack.push( + 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) -> Vec { + let mut var_num = 0usize; + let mut records = vec![]; + + for branches in self.values_mut() { + for branch in branches.iter_mut() { + let mut num_occurrences = 0; + let mut chunk_occurrences = vec![]; + + for chunk in branch.chunks.iter_mut() { + num_occurrences += chunk.vars.len(); + + for var in chunk.vars.iter_mut() { + var.set(Var::Generated(var_num)); + } + + chunk_occurrences.push(chunk.chunk_num); + } + + let classification = if branch.chunks.len() > 1 { + VarClassification::Perm + } else { + branch.chunks + .first() + .map(|chunk| if chunk.vars.len() > 1 { + VarClassification::Temp + } else { + VarClassification::Void + }) + .unwrap_or(VarClassification::Void) + }; + + records.push(VarRecord { classification, chunk_occurrences, num_occurrences }); + var_num += 1; + } + } + + debug_assert_eq!(records.len(), var_num); + + records + } +} diff --git a/src/machine/load_state.rs b/src/machine/load_state.rs index 802d51eb..3d0a638a 100644 --- a/src/machine/load_state.rs +++ b/src/machine/load_state.rs @@ -441,7 +441,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { term: Term, preprocessor: &mut Preprocessor, ) -> Result { - 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), diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 5193eb66..dab4c54c 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -16,6 +16,7 @@ pub mod machine_state; pub mod machine_state_impl; pub mod mock_wam; pub mod partial_string; +pub mod disjuncts; pub mod preprocessor; pub mod stack; pub mod streams; diff --git a/src/machine/preprocessor.rs b/src/machine/preprocessor.rs index d2c33b06..0564af8c 100644 --- a/src/machine/preprocessor.rs +++ b/src/machine/preprocessor.rs @@ -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::*; @@ -13,21 +13,6 @@ use std::cell::Cell; use std::collections::VecDeque; use std::convert::TryFrom; -/* - * 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(terms: I, mut term: Term, sym: Atom) -> Term where I: DoubleEndedIterator, @@ -131,6 +116,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, @@ -324,110 +316,6 @@ fn setup_meta_predicate<'a, LS: LoadState<'a>>( } } -fn merge_clauses(tls: &mut VecDeque) -> Result { - 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, 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(), Var::from("!")); - true - } else { - false - } -} - -fn mark_cut_variables(terms: &mut Vec) -> 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) { - if terms.len() != 1 { - return; - } - - if let Some(Term::Clause(_, name, ref subterms)) = terms.last() { - if *name != atom!("->") || source_arity(subterms) != 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, @@ -569,7 +457,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, @@ -608,7 +496,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, @@ -646,308 +534,65 @@ fn qualified_clause_to_query_term<'a, LS: LoadState<'a>>( QueryTerm::Clause(Cell::default(), ct, terms, call_policy) } -fn compute_head(term: &Term) -> Vec { - let mut vars = IndexSet::new(); - - for term in post_order_iter(term) { - if let TermRef::Var(_, _, v) = term { - vars.insert(v.clone()); - } - } - - vars.insert(Var::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) { - // 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) { - 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) { - 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>, settings: CodeGenSettings, } impl Preprocessor { pub(super) fn new(settings: CodeGenSettings) -> Self { Preprocessor { - queue: VecDeque::new(), settings, } } - fn setup_fact(&mut self, term: Term) -> Result { + fn setup_fact(&mut self, term: Term) -> Result { match term { - Term::Clause(..) | Term::Literal(_, Literal::Atom(..)) => Ok(term), + Term::Clause(..) | Term::Literal(_, Literal::Atom(..)) => { + let mut classifier = VariableClassifier::new( + self.settings.default_call_policy(), + ); + + let (head, var_records) = classifier.classify_fact(term)?; + + Ok(Fact { head, var_records }) + } _ => Err(CompilationError::InadmissibleFact), } } - fn to_query_term<'a, LS: LoadState<'a>>( - &mut self, - loader: &mut Loader<'a, LS>, - term: Term, - ) -> Result { - 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() == Some("!") => { - Ok(QueryTerm::UnblockedCut(Cell::default())) - } - Term::Clause(r, name, mut terms) => match (name, source_arity(&terms)) { - (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 { - 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, - cut_context: CutContext, - ) -> Result, 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!(",") && source_arity(&terms) == 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, - cut_context: CutContext, + head: Term, + body: Term, ) -> Result { - let post_head_terms: Vec<_> = terms.drain(1..).collect(); - let mut query_terms = self.setup_query(loader, post_head_terms, cut_context)?; + let mut classifier = VariableClassifier::new( + self.settings.default_call_policy(), + ); + + let (head, mut query_terms, var_records) = + classifier.classify_rule(loader, head, body)?; let clauses = query_terms.drain(1..).collect(); let qt = query_terms.pop().unwrap(); - match terms.pop().unwrap() { + match head { Term::Clause(_, name, terms) => Ok(Rule { head: (name, terms, qt), clauses, + var_records, }), Term::Literal(_, Literal::Atom(name)) => Ok(Rule { head: (name, vec![], qt), clauses, + var_records, }), _ => Err(CompilationError::InvalidRuleHead), } } + /* fn try_term_to_query<'a, LS: LoadState<'a>>( &mut self, loader: &mut Loader<'a, LS>, @@ -960,23 +605,19 @@ 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 { 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, - )?)) + let is_rule = name == atom!(":-") && terms.len() == 2; + + if is_rule { + Ok(TopLevel::Rule(self.setup_rule(loader, terms[0], terms[1])?)) } else { let term = Term::Clause(r, name, terms); Ok(TopLevel::Fact(self.setup_fact(term)?)) @@ -990,33 +631,13 @@ impl Preprocessor { &mut self, loader: &mut Loader<'a, LS>, terms: I, - cut_context: CutContext, ) -> Result, 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, 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) - } } From 170818759deeafbe841e45ca14a79c5e1d92f18e Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 1 Nov 2022 21:10:15 -0600 Subject: [PATCH 191/361] add more variable probing, chunk type labeling --- src/machine/disjuncts.rs | 173 ++++++++++++++++++++++++++++++++------- 1 file changed, 142 insertions(+), 31 deletions(-) diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index a97f08c5..adbf341c 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -133,8 +133,20 @@ impl DerefMut for BranchMap { type RootSet = IndexSet; +#[derive(Debug, Clone, Copy)] +enum ChunkType { + Head, + Mid, + Last, +} + enum TraversalState { - BuildDisjunct(usize), // construct a QueryTerm::Branch with number of disjuncts. + // construct a QueryTerm::Branch with number of disjuncts, reset + // the chunk type to that of the chunk preceding the disjunct. + BuildDisjunct(ChunkType, usize), + // add the last disjunct to a QueryTerm::Branch, continuing from + // where it leaves off. + BuildFinalDisjunct(usize), BuildIf(usize, Term), // build the P term of P -> Q BuildThen(usize, Vec), // build the Q term of P -> Q BuildNot(usize), // build the P term of \+ P @@ -144,6 +156,7 @@ enum TraversalState { RemoveBranchNum, // remove latest branch number from the root set RepBranchNum(BranchNumber), // replace current_branch_number and the latest in the root set IncrChunkNum, // increment self.current_chunk_number + SetLastChunkType, // consider remaining terms as belonging to a last chunk } impl Term { @@ -215,6 +228,62 @@ fn merge_branch_seq>(branches: Iter) -> Branch branch_info } +fn flatten_into_disjunct(build_stack: &mut Vec, preceding_len: usize) { + let iter = build_stack.drain(preceding_len ..); + + if let QueryTerm::Branch(ref mut disjuncts) = &mut build_stack[preceding_len] { + disjuncts.push(iter.collect()); + } +} + +fn term_in_other_chunk(term: &Term) -> Option { + match term { + Term::Clause(_, name, terms) => Some(!ClauseType::is_inbuilt(name, terms.len())), + Term::Literal(_, Literal::Atom(atom!("!"))) | + Term::Literal(_, Literal::Char('!')) => Some(false), + Term::Literal(_, Literal::Atom(name)) => Some(!ClauseType::is_inbuilt(name, 0)), + Term::Var(..) => Some(true), + _ => None, + } +} + +// returns true if the insertion of SetLastChunkType was the final push. +fn insert_set_last_chunk_type( + state_stack: &mut Vec, + iter: impl Iterator, +) -> bool { + let beg = state_stack.len(); + let mut idx = beg; + + while let Some(traversal_st) = iter.next() { + match traversal_st { + TraversalState::Term(term) | TraversalState::BuildIf(_, term) => { + let mut will_break = false; + + match term_in_other_chunk(&term) { + Some(true) if idx > beg => will_break = true, + Some(_) => idx += 1, + None => will_break = true, + } + + if will_break { + state_stack.push(TraversalState::SetLastChunkType); + state_stack.push(traversal_st); + break; + } else { + state_stack.push(traversal_st); + } + } + _ => { + unreachable!(); + } + } + } + + state_stack.extend(iter); + idx == state_stack.len() +} + impl VariableClassifier { pub fn new(call_policy: CallPolicy) -> Self { Self { @@ -286,16 +355,16 @@ impl VariableClassifier { } } - fn probe_body_term(&mut self, term: &Term) { - // true to iterate the root, which may be a variable! + fn probe_body_term(&mut self, term: &Term, term_loc: GenContext) { + // second arg is true to iterate the root, which may be a variable for term_ref in breadth_first_iter(term, true) { if let TermRef::Var(_, _, var_name) = term_ref { - self.probe_body_var(Var::from(var_name)); + self.probe_body_var(Var::from(var_name), term_loc); } } } - fn probe_body_var(&mut self, var_name: Var) { + fn probe_body_var(&mut self, var_name: Var, chunk_type: ChunkType) { let branch_info_v = self.branch_map.entry(var_name) .or_insert_with(|| vec![]); @@ -369,6 +438,7 @@ impl VariableClassifier { ) -> Result, CompilationError> { let mut state_stack = vec![TraversalState::Term(term)]; let mut build_stack = vec![]; + let mut chunk_type = ChunkType::Head; while let Some(traversal_st) = state_stack.pop() { match traversal_st { @@ -386,19 +456,26 @@ impl VariableClassifier { } TraversalState::IncrChunkNum => { self.current_chunk_num += 1; + chunk_type = ChunkType::Mid; } - TraversalState::BuildDisjunct(preceding_len) => { - let iter = build_stack.drain(preceding_len ..); - - if let QueryTerm::Branch(ref mut disjuncts) = &mut build_stack[preceding_len] { - disjuncts.push(iter.collect()); - } + TraversalState::ResetCallPolicy(call_policy) => { + self.call_policy = call_policy; + } + TraversalState::SetLastChunkType => { + chunk_type = ChunkType::Last; + } + TraversalState::BuildDisjunct(reset_chunk_type, preceding_len) => { + chunk_type = reset_chunk_type; + flatten_into_disjunct(&mut build_stack, preceding_len); + } + TraversalState::BuildFinalDisjunct(preceding_len) => { + flatten_into_disjunct(&mut build_stack, preceding_len); } TraversalState::BuildIf(preceding_len, then_term) => { let iter = build_stack.drain(preceding_len ..); - let build_stack_len = build_stack.len(); - state_stack.push(TraversalState::BuildThen(build_stack_len, iter.collect())); + state_stack.push(TraversalState::BuildThen(preceding_len, iter.collect())); + state_stack.push(TraversalState::Term(then_term)); } TraversalState::BuildThen(preceding_len, if_terms) => { let iter = build_stack.drain(preceding_len ..); @@ -408,20 +485,22 @@ impl VariableClassifier { let iter = build_stack.drain(preceding_len ..); build_stack.push(QueryTerm::Not(iter.collect())); } - TraversalState::ResetCallPolicy(call_policy) => { - self.call_policy = call_policy; - } TraversalState::Term(term) => { match term { Term::Clause(_, atom!(","), terms) if terms.len() == 2 => { - state_stack.extend( - unfold_by_str(terms[1], atom!(",")) - .into_iter() - .rev() - .map(TraversalState::Term), - ); + let iter = unfold_by_str(terms[1], atom!(",")) + .into_iter() + .rev() + .chain(std::iter::once(terms[0])) + .map(TraversalState::Term); - state_stack.push(TraversalState::Term(terms[0])); + if let ChunkType::Last = chunk_type { + if !insert_set_last_chunk_type(&mut state_stack, iter) { + chunk_type = ChunkType::Mid; + } + } else { + state_stack.extend(iter); + } } Term::Clause(_, atom!(";"), terms) if terms.len() == 2 => { let first_branch_num = self.current_branch_num.split(); @@ -442,31 +521,46 @@ impl VariableClassifier { } let build_stack_len = build_stack.len(); - build_stack.push(QueryTerm::Branch(vec![])); - state_stack.push(TraversalState::BuildDisjunct(build_stack_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::BuildDisjunct(chunk_type, build_stack_len)); state_stack.push(TraversalState::RemoveBranchNum); state_stack.push(TraversalState::Term(term)); state_stack.push(TraversalState::AddBranchNum(branch_num)); } + + state_stack[final_disjunct_loc] = + TraversalState::BuildFinalDisjunct(build_stack_len); } Term::Clause(_, atom!("->"), mut terms) if terms.len() == 2 => { let then_term = terms.pop().unwrap(); let if_term = terms.pop().unwrap(); + let build_stack_len = build_stack.len(); - state_stack.push(TraversalState::BuildIf(build_stack_len, then_term)); - state_stack.push(TraversalState::Term(if_term)); + // TODO: insert GetLevelAndUnify between + // the two traversal states and detect + // that as a chunk boundary in + // insert_set_last_chunk_type ?? + + let iter = vec![TraversalState::BuildIf(build_stack_len, then_term), + TraversalState::Term(if_term)] + .into_iter(); + + if let ChunkType::Last = chunk_type { + if !insert_set_last_chunk_type(&mut state_stack, iter) { + chunk_type = ChunkType::Mid; + } + } } Term::Clause(_, atom!("\\+"), terms) if terms.len() == 1 => { let build_stack_len = build_stack.len(); @@ -477,8 +571,14 @@ impl VariableClassifier { Term::Clause(_, atom!("$get_level"), terms) if terms.len() == 1 => { state_stack.push(TraversalState::IncrChunkNum); + // TODO: need to classify this variable? if let Term::Var(_, ref var) = &terms[0] { - build_stack.push(QueryTerm::GetLevelAndUnify(Cell::default(), var.clone())); + build_stack.push( + QueryTerm::GetLevelAndUnify( + Cell::default(), + var.clone(), + ), + ); } else { return Err(CompilationError::InadmissibleQueryTerm); } @@ -514,6 +614,10 @@ impl VariableClassifier { state_stack.push(TraversalState::IncrChunkNum); } + for term in terms.iter() { + self.probe_body_term(term, term_loc); + } + build_stack.push( qualified_clause_to_query_term( loader, @@ -527,6 +631,9 @@ impl VariableClassifier { (module_name, predicate_name) => { state_stack.push(TraversalState::IncrChunkNum); + self.probe_body_term(&module_name, term_loc); + self.probe_body_term(&predicate_name, term_loc); + terms.push(module_name); terms.push(predicate_name); @@ -541,7 +648,11 @@ impl VariableClassifier { } } } - Term::Clause(cell, atom!("$call_with_inference_counting"), terms) if terms.len() == 2 => { + Term::Clause(cell, atom!("$call_with_inference_counting"), terms) if terms.len() == 1 => { + for term in terms.iter() { + self.probe_body_term(term, term_loc); + } + state_stack.push(TraversalState::ResetCallPolicy(self.call_policy)); state_stack.push(TraversalState::Term(terms[0])); @@ -553,7 +664,7 @@ impl VariableClassifier { } for term in terms.iter() { - self.probe_body_term(term); + self.probe_body_term(term, term_loc); } build_stack.push( From a66d666beda87024691dda2ce9d17baeb149114e Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 13 Nov 2022 10:13:37 -0700 Subject: [PATCH 192/361] variable classification al a carte --- Cargo.lock | 1 + Cargo.toml | 1 + src/allocator.rs | 22 +++-- src/codegen.rs | 23 +++-- src/fixtures.rs | 180 ++++++++++++--------------------------- src/iterators.rs | 1 - src/machine/disjuncts.rs | 156 ++++++++++++++++++++++++--------- src/parser/ast.rs | 2 +- 8 files changed, 198 insertions(+), 188 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46c5a14f..7cc11f21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1855,6 +1855,7 @@ version = "0.9.1" dependencies = [ "assert_cmd", "base64", + "bit-set", "blake2 0.8.1", "chrono", "cpu-time", diff --git a/Cargo.toml b/Cargo.toml index 975ba64f..f358126a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ to-syn-value_derive = "0.1.0" walkdir = "2" [dependencies] +bit-set = "0.5.3" cpu-time = "1.0.0" crossterm = "0.20.0" dirs-next = "2.0.0" diff --git a/src/allocator.rs b/src/allocator.rs index bc0d2f44..50e9c7c3 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -63,32 +63,30 @@ pub(crate) trait Allocator { // TODO: wha.. why?? grrr. it drains the VarStatus data from vs (which it owns!) // into self.bindings and perm_vs after all is computed (i.e. vs.populate_restricting_sets() // and vs.set_perm_vals(has_deep_cut) have both been called). + /* fn drain_var_data<'a>( &mut self, - vs: VariableFixtures<'a>, + vs: VariableFixtures, num_of_chunks: usize, - ) -> VariableFixtures<'a> { + ) -> VariableFixtures { let mut perm_vs = VariableFixtures::new(); - for (var, (var_status, cells)) in vs.into_iter() { + for (var, var_status) in vs.into_iter() { match var_status { VarStatus::Temp(chunk_num, tvd) => { self.bindings_mut() - .insert(var.clone(), VarData::Temp(chunk_num, 0, tvd)); - - if chunk_num + 1 == num_of_chunks { - perm_vs.insert_last_chunk_temp_var(var); - } + .insert(var.clone(), VarAlloc::Temp(chunk_num, 0, tvd)); } VarStatus::Perm(_) => { - self.bindings_mut().insert(var.clone(), VarData::Perm(0)); - perm_vs.insert(var, (var_status, cells)); + self.bindings_mut().insert(var.clone(), VarAlloc::Perm(0)); + perm_vs.insert(var, var_status); } }; } perm_vs } + */ fn get(&self, var: Var) -> RegType { self.bindings() @@ -102,8 +100,8 @@ pub(crate) trait Allocator { fn record_register(&mut self, var: Var, r: RegType) { match self.bindings_mut().get_mut(&var).unwrap() { - &mut VarData::Temp(_, ref mut s, _) => *s = r.reg_num(), - &mut VarData::Perm(ref mut s) => *s = r.reg_num(), + &mut VarAlloc::Temp(_, ref mut s, _) => *s = r.reg_num(), + &mut VarAlloc::Perm(ref mut s) => *s = r.reg_num(), } } } diff --git a/src/codegen.rs b/src/codegen.rs index a3fdc99b..65f49971 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,7 +1,6 @@ use crate::atom_table::*; use crate::parser::ast::*; use crate::{perm_v, temp_v}; - use crate::allocator::*; use crate::arithmetic::*; use crate::debray_allocator::*; @@ -22,14 +21,14 @@ use std::cell::Cell; use std::collections::VecDeque; #[derive(Debug)] -pub(crate) struct ConjunctInfo<'a> { - pub(crate) perm_vs: VariableFixtures<'a>, +pub(crate) struct ConjunctInfo { + pub(crate) perm_vs: VariableFixtures, pub(crate) num_of_chunks: usize, pub(crate) has_deep_cut: bool, } -impl<'a> ConjunctInfo<'a> { - fn new(perm_vs: VariableFixtures<'a>, num_of_chunks: usize, has_deep_cut: bool) -> Self { +impl ConjunctInfo { + fn new(perm_vs: VariableFixtures, num_of_chunks: usize, has_deep_cut: bool) -> Self { ConjunctInfo { perm_vs, num_of_chunks, @@ -191,8 +190,8 @@ impl DebrayAllocator { #[inline(always)] pub(crate) fn get_binding(&self, name: &Var) -> Option { match self.bindings().get(name) { - Some(&VarData::Temp(_, t, _)) if t != 0 => Some(RegType::Temp(t)), - Some(&VarData::Perm(p)) if p != 0 => Some(RegType::Perm(p)), + Some(&VarAlloc::Temp(_, t, _)) if t != 0 => Some(RegType::Temp(t)), + Some(&VarAlloc::Perm(p)) if p != 0 => Some(RegType::Perm(p)), _ => None, } } @@ -861,7 +860,7 @@ impl<'b> CodeGenerator<'b> { fn compile_seq<'a>( &mut self, iter: ChunkedIterator<'a>, - conjunct_info: &ConjunctInfo<'a>, + conjunct_info: &ConjunctInfo, code: &mut Code, ) -> Result<(), CompilationError> { for (chunk_num, _, terms) in iter.rule_body_iter() { @@ -925,11 +924,11 @@ impl<'b> CodeGenerator<'b> { } } - fn compile_cleanup<'a>( + fn compile_cleanup( &mut self, code: &mut Code, - conjunct_info: &ConjunctInfo<'a>, - toc: &'a QueryTerm, + conjunct_info: &ConjunctInfo, + toc: &QueryTerm, ) { // add a proceed to bookend any trailing cuts. match toc { @@ -937,7 +936,7 @@ impl<'b> CodeGenerator<'b> { code.push(instr!("proceed")); } _ => {} - }; + } // perform lco. let dealloc_index = Self::lco(code); diff --git a/src/fixtures.rs b/src/fixtures.rs index 67740989..f75a8042 100644 --- a/src/fixtures.rs +++ b/src/fixtures.rs @@ -4,6 +4,7 @@ use crate::forms::*; use crate::instructions::*; use crate::iterators::*; +use bit_set::*; use indexmap::{IndexMap, IndexSet}; use std::cell::Cell; @@ -11,15 +12,23 @@ use std::collections::BTreeSet; use std::mem::swap; use std::vec::Vec; -// labeled with chunk numbers. +pub(crate) type OccurrenceSet = IndexSet<(GenContext, usize)>; + #[derive(Debug)] -pub(crate) enum VarStatus { - Perm(usize), - Temp(usize, TempVarData), // Perm(chunk_num) | Temp(chunk_num, _) +pub(crate) struct TempVarData { + pub(crate) last_term_arity: usize, + pub(crate) use_set: OccurrenceSet, + pub(crate) no_use_set: BitSet, + pub(crate) conflict_set: BitSet, } -pub(crate) type OccurrenceSet = BTreeSet<(GenContext, usize)>; +#[derive(Debug)] +pub(crate) struct TempVarStatus { + chunk_num: usize, + temp_var_data: TempVarData, +} +// TODO: get ridda this! I think. // Perm: 0 initially, a stack register once processed. // Temp: labeled with chunk_num and temp offset (unassigned if 0). #[derive(Debug)] @@ -37,21 +46,13 @@ impl VarData { } } -#[derive(Debug)] -pub(crate) struct TempVarData { - pub(crate) last_term_arity: usize, - pub(crate) use_set: OccurrenceSet, - pub(crate) no_use_set: BTreeSet, - pub(crate) conflict_set: BTreeSet, -} - impl TempVarData { pub(crate) fn new(last_term_arity: usize) -> Self { TempVarData { last_term_arity: last_term_arity, - use_set: BTreeSet::new(), - no_use_set: BTreeSet::new(), - conflict_set: BTreeSet::new(), + use_set: BitSet::new(), + no_use_set: BitSet::new(), + conflict_set: BitSet::new(), } } @@ -68,7 +69,7 @@ impl TempVarData { pub(crate) fn populate_conflict_set(&mut self) { if self.last_term_arity > 0 { let arity = self.last_term_arity; - let mut conflict_set: BTreeSet = (1..arity).collect(); + let mut conflict_set: BitSet = (1..arity).collect(); for &(_, reg) in self.use_set.iter() { conflict_set.remove(®); @@ -79,26 +80,26 @@ impl TempVarData { } } -type VariableFixture<'a> = (VarStatus, Vec<&'a Cell>); - #[derive(Debug)] -pub(crate) struct VariableFixtures<'a> { - perm_vars: IndexMap>, - last_chunk_temp_vars: IndexSet, // TODO: has no use at all! +pub(crate) struct VariableFixtures { + temp_vars: IndexMap, + last_chunk_temp_vars: IndexSet, // TODO: has no use at all! remove it. } impl<'a> VariableFixtures<'a> { pub(crate) fn new() -> Self { VariableFixtures { - perm_vars: IndexMap::new(), + temp_vars: IndexMap::new(), last_chunk_temp_vars: IndexSet::new(), } } + // TODO: get rid of this also. pub(crate) fn insert(&mut self, var: Var, vs: VariableFixture<'a>) { - self.perm_vars.insert(var, vs); + self.temp_vars.insert(var, vs); } + // TODO: used? pub(crate) fn insert_last_chunk_temp_var(&mut self, var: Var) { self.last_chunk_temp_vars.insert(var); } @@ -114,27 +115,26 @@ impl<'a> VariableFixtures<'a> { // Compute the conflict set of u. // 1. - let mut use_sets: IndexMap = IndexMap::new(); + let mut use_sets: IndexMap = IndexMap::new(); - for (var, &mut (ref mut var_status, _)) in self.iter_mut() { - if let &mut VarStatus::Temp(_, ref mut var_data) = var_status { - let mut use_set = OccurrenceSet::new(); + for (var_gen_index, ref mut var_status) in self.temp_vars.iter_mut() { + let TempVarStatus { ref mut temp_var_data, .. } = var_status; + let mut use_set = OccurrenceSet::new(); - swap(&mut var_data.use_set, &mut use_set); - use_sets.insert((*var).clone(), use_set); - } + mem::swap(&mut temp_var_data.use_set, &mut use_set); + use_sets.insert(var_gen_index, use_set); } for (u, use_set) in use_sets.drain(..) { // 2. for &(term_loc, reg) in use_set.iter() { if let GenContext::Last(cn_u) = term_loc { - for (ref t, &mut (ref mut var_status, _)) in self.iter_mut() { - if let &mut VarStatus::Temp(cn_t, ref mut t_data) = var_status { - if cn_u == cn_t && u != **t { - if !t_data.uses_reg(reg) { - t_data.no_use_set.insert(reg); - } + for (var_gen_index, ref mut var_status) in self.terms_vars.iter_mut() { + let TempVarStatus { chunk_num, ref mut temp_var_data } = var_status; + + if cn_u == chunk_num && u != var_gen_index { + if !temp_var_data.uses_reg(reg) { + temp_var_data.no_use_set.insert(reg); } } } @@ -142,24 +142,13 @@ impl<'a> VariableFixtures<'a> { } // 3. - match self.get_mut(u).unwrap() { - &mut (VarStatus::Temp(_, ref mut u_data), _) => { - u_data.use_set = use_set; - u_data.populate_conflict_set(); - } - _ => {} - }; + let TempVarStatus { ref mut temp_var_data, ..} = self.temp_vars.get_mut(u).unwrap(); + + temp_var_data.use_set = use_set; + temp_var_data.populate_conflict_set(); } } - fn get_mut(&mut self, u: Var) -> Option<&mut VariableFixture<'a>> { - self.perm_vars.get_mut(&u) - } - - fn iter_mut(&mut self) -> indexmap::map::IterMut> { - self.perm_vars.iter_mut() - } - fn record_temp_info(&mut self, tvd: &mut TempVarData, arg_c: usize, term_loc: GenContext) { match term_loc { GenContext::Head | GenContext::Last(_) => { @@ -169,84 +158,27 @@ impl<'a> VariableFixtures<'a> { }; } - pub(crate) fn vars_above_threshold(&self, index: usize) -> usize { - let mut var_count = 0; - - for &(ref var_status, _) in self.values() { - if let &VarStatus::Perm(i) = var_status { - if i > index { - var_count += 1; - } - } - } - - var_count - } - - pub(crate) fn mark_vars_in_chunk(&mut self, iter: I, lt_arity: usize, term_loc: GenContext) - where - I: Iterator>, - { + pub(crate) fn mark_temp_var( + &mut self, + generated_var_index: usize, + lvl: Level, + classify_info: &ClassifyInfo, + term_loc: GenContext, + ) { let chunk_num = term_loc.chunk_num(); - let mut arg_c = 1; - for term_ref in iter { - if let &TermRef::Var(lvl, cell, ref var) = &term_ref { - let mut status = self.perm_vars.swap_remove(var).unwrap_or(( - VarStatus::Temp(chunk_num, TempVarData::new(lt_arity)), - Vec::new(), - )); - - status.1.push(cell); - - match status.0 { - VarStatus::Temp(cn, ref mut tvd) if cn == chunk_num => { - if let Level::Shallow = lvl { - self.record_temp_info(tvd, arg_c, term_loc); - } - } - _ => status.0 = VarStatus::Perm(chunk_num), - }; - - self.perm_vars.insert(var.clone(), status); + let mut status = self.temp_vars.swap_remove(generated_var_index).unwrap_or_else(|| { + TempVarStatus { + chunk_num, + temp_var_data: TempVarData::new(classify_info.arity), } + }); - if let Level::Shallow = term_ref.level() { - arg_c += 1; - } + if let Level::Shallow = lvl { + self.record_temp_info(&mut status, classify_info.arg_c, term_loc); } - } - pub(crate) fn into_iter(self) -> indexmap::map::IntoIter> { - self.perm_vars.into_iter() - } - - fn values(&self) -> indexmap::map::Values> { - self.perm_vars.values() - } - - pub(crate) fn size(&self) -> usize { - self.perm_vars.len() - } - - pub(crate) fn set_perm_vals(&self, has_deep_cuts: bool) { - let mut values_vec: Vec<_> = self - .values() - .filter_map(|ref v| match &v.0 { - &VarStatus::Perm(i) => Some((i, &v.1)), - _ => None, - }) - .collect(); - - values_vec.sort_by_key(|ref v| v.0); - - let offset = has_deep_cuts as usize; - - for (i, (_, cells)) in values_vec.into_iter().rev().enumerate() { - for cell in cells { - cell.set(VarReg::Norm(RegType::Perm(i + 1 + offset))); - } - } + self.temp_vars.insert(Var::Generated(generated_var_index), status); } } diff --git a/src/iterators.rs b/src/iterators.rs index ac87a451..bbd9fb70 100644 --- a/src/iterators.rs +++ b/src/iterators.rs @@ -40,7 +40,6 @@ impl VarPtr { } } - #[derive(Debug, Clone)] pub(crate) enum TermRef<'a> { AnonVar(Level), diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index adbf341c..19e460c3 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -9,6 +9,7 @@ paper "Compiling Large Disjunctions" to Scryer Prolog. */ use crate::atom_table::*; +use crate::fixtures::VariableFixtures; use crate::forms::*; use crate::instructions::*; use crate::iterators::*; @@ -34,7 +35,7 @@ struct BranchNumber { impl Default for BranchNumber { fn default() -> Self { Self { - branch_num: Rational::from(1 << 10), + branch_num: Rational::from(1 << 63), delta: Rational::from(1), } } @@ -86,16 +87,19 @@ impl BranchNumber { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct VarInfo { + var_ptr: VarPtr, + classify_info: ClassifyInfo, + lvl: Level, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ChunkInfo { chunk_num: usize, - vars: Vec, // pointer to incidence -} - -impl ChunkInfo { - fn new(chunk_num: usize) -> Self { - ChunkInfo { chunk_num, vars: vec![] } - } + term_loc: GenContext, + // pointer to incidence, term occurrence arity. + vars: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -140,6 +144,23 @@ enum ChunkType { Last, } +impl ChunkType { + #[inline(always)] + fn to_gen_context(self, chunk_num: usize) -> GenContext { + match self { + ChunkType::Head => GenContext::Head, + ChunkType::Mid => GenContext::Mid(chunk_num), + ChunkType::Last => GenContext::Last(chunk_num), + } + } +} + +#[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. @@ -199,8 +220,15 @@ pub struct VarRecord { pub num_occurrences: usize, } -pub type ClassifyFactResult = (Term, Vec); -pub type ClassifyRuleResult = (Term, Vec, Vec); +// TODO: already exists a VarData! although it may no longer exist?? +// Also, the name is too similar to VarInfo. Think of better names! +pub struct VarData { + pub records: Vec, + pub fixtures: VariableFixtures, +} + +pub type ClassifyFactResult = (Term, VarData); +pub type ClassifyRuleResult = (Term, Vec, VarData); fn merge_branch_seq>(branches: Iter) -> BranchInfo { let mut branch_info = BranchInfo::new(BranchNumber::default()); @@ -238,19 +266,20 @@ fn flatten_into_disjunct(build_stack: &mut Vec, preceding_len: usize) fn term_in_other_chunk(term: &Term) -> Option { match term { - Term::Clause(_, name, terms) => Some(!ClauseType::is_inbuilt(name, terms.len())), + Term::Clause(_, name, terms) => Some(!ClauseType::is_inbuilt(*name, terms.len())), Term::Literal(_, Literal::Atom(atom!("!"))) | Term::Literal(_, Literal::Char('!')) => Some(false), - Term::Literal(_, Literal::Atom(name)) => Some(!ClauseType::is_inbuilt(name, 0)), + Term::Literal(_, Literal::Atom(name)) => Some(!ClauseType::is_inbuilt(*name, 0)), Term::Var(..) => Some(true), _ => None, } } // returns true if the insertion of SetLastChunkType was the final push. +// expects that iter iterates over a conjunct of Terms in reverse order. fn insert_set_last_chunk_type( state_stack: &mut Vec, - iter: impl Iterator, + mut iter: impl Iterator, ) -> bool { let beg = state_stack.len(); let mut idx = beg; @@ -269,6 +298,7 @@ fn insert_set_last_chunk_type( if will_break { state_stack.push(TraversalState::SetLastChunkType); state_stack.push(traversal_st); + break; } else { state_stack.push(traversal_st); @@ -356,15 +386,22 @@ impl VariableClassifier { } fn probe_body_term(&mut self, term: &Term, term_loc: GenContext) { + let mut classify_info = ClassifyInfo { arg_c: 0, arity: term.arity() }; + // second arg is true to iterate the root, which may be a variable for term_ref in breadth_first_iter(term, true) { - if let TermRef::Var(_, _, var_name) = term_ref { - self.probe_body_var(Var::from(var_name), term_loc); + if let TermRef::Var(lvl, _, var_name) = term_ref { + let var_info = VarInfo { var_ptr: VarPtr::from(&var_name), lvl, classify_info }; + self.probe_body_var(var_name, term_loc, var_info); + } + + if let Level::Shallow = term_ref.level() { + classify_info.arg_c += 1; } } } - fn probe_body_var(&mut self, var_name: Var, chunk_type: ChunkType) { + fn probe_body_var(&mut self, var_name: Var, term_loc: GenContext, var_info: VarInfo) { let branch_info_v = self.branch_map.entry(var_name) .or_insert_with(|| vec![]); @@ -387,11 +424,15 @@ impl VariableClassifier { }; if needs_new_chunk { - branch_info.chunks.push(ChunkInfo::new(self.current_chunk_num)); + 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(VarPtr::from(&var_name)); + chunk_info.vars.push(var_info); } fn classify_head_variables(&mut self, term: &Term) -> Result<(), CompilationError> { @@ -401,9 +442,14 @@ impl VariableClassifier { _ => return Err(CompilationError::InvalidRuleHead), } + let mut classify_info = ClassifyInfo { + arg_c: 0, + arity: term.arity(), + }; + // false argument to breadth_first_iter because the root is not iterable. for term_ref in breadth_first_iter(term, false) { - if let TermRef::Var(_, _, var_name) = term_ref { + if let TermRef::Var(lvl, _, var_name) = term_ref { // the body of the if let here is an inlined // "probe_head_var". note the difference between it // and "probe_body_var". @@ -420,11 +466,21 @@ impl VariableClassifier { let needs_new_chunk = branch_info.chunks.is_empty(); if needs_new_chunk { - branch_info.chunks.push(ChunkInfo::new(self.current_chunk_num)); + 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(); - chunk_info.vars.push(VarPtr::from(&var_name)); + let var_info = VarInfo { var_ptr: VarPtr::from(&var_name), classify_info, lvl }; + + chunk_info.vars.push(var_info); + } + + if let Level::Shallow = term_ref.level() { + classify_info.arg_c += 1; } } @@ -584,6 +640,8 @@ impl VariableClassifier { } } Term::Clause(_, atom!(":"), mut terms) if terms.len() == 2 => { + let term_loc = chunk_type.to_gen_context(self.current_chunk_num); + let predicate_name = terms.pop().unwrap(); let module_name = terms.pop().unwrap(); @@ -592,7 +650,7 @@ impl VariableClassifier { Term::Literal(_, Literal::Atom(module_name)), Term::Literal(_, Literal::Atom(predicate_name)), ) => { - if !ClauseType::is_inbuilt(name, 0) { + if !ClauseType::is_inbuilt(predicate_name, 0) { state_stack.push(TraversalState::IncrChunkNum); } @@ -649,6 +707,8 @@ impl VariableClassifier { } } Term::Clause(cell, atom!("$call_with_inference_counting"), terms) if terms.len() == 1 => { + let term_loc = chunk_type.to_gen_context(self.current_chunk_num); + for term in terms.iter() { self.probe_body_term(term, term_loc); } @@ -663,6 +723,8 @@ impl VariableClassifier { state_stack.push(TraversalState::IncrChunkNum); } + let term_loc = chunk_type.to_gen_context(self.current_chunk_num); + for term in terms.iter() { self.probe_body_term(term, term_loc); } @@ -694,6 +756,7 @@ impl VariableClassifier { ), ); } + _ => { return Err(CompilationError::InadmissibleQueryTerm); } @@ -707,25 +770,18 @@ impl VariableClassifier { } impl BranchMap { - pub fn separate_and_classify_variables(&mut self) -> Vec { - let mut var_num = 0usize; - let mut records = vec![]; + pub fn separate_and_classify_variables(&mut self) -> VarData { + let mut var_num = 0usize; + let mut var_data = VarData { + records: vec![], + fixtures: VariableFixtures::new(), + }; for branches in self.values_mut() { for branch in branches.iter_mut() { let mut num_occurrences = 0; let mut chunk_occurrences = vec![]; - for chunk in branch.chunks.iter_mut() { - num_occurrences += chunk.vars.len(); - - for var in chunk.vars.iter_mut() { - var.set(Var::Generated(var_num)); - } - - chunk_occurrences.push(chunk.chunk_num); - } - let classification = if branch.chunks.len() > 1 { VarClassification::Perm } else { @@ -739,13 +795,37 @@ impl BranchMap { .unwrap_or(VarClassification::Void) }; - records.push(VarRecord { classification, chunk_occurrences, num_occurrences }); + for chunk in branch.chunks.iter_mut() { + num_occurrences += chunk.vars.len(); + + if let VarClassification::Temp = classification { + for var_info in chunk.vars.iter_mut() { + var_info.var_ptr.set(Var::Generated(var_num)); + var_data.fixtures.mark_temp_var( + var_num, + var_info.lvl, + &var_info.classify_info, + chunk.term_loc, + ); + } + } else { + for var_info in chunk.vars.iter_mut() { + var_info.var_ptr.set(Var::Generated(var_num)); + } + } + + chunk_occurrences.push(chunk.chunk_num); + } + + let record = VarRecord { classification, chunk_occurrences, num_occurrences }; + var_data.records.push(record); + var_num += 1; } } - debug_assert_eq!(records.len(), var_num); + debug_assert_eq!(var_data.records.len(), var_num); - records + var_data } } diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 78bd55b4..73c91c6a 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -227,7 +227,7 @@ macro_rules! perm_v { }; } -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum GenContext { Head, Mid(usize), From 063cf0c60869d54310182eb2f3b48ca2f1fefdc8 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Mon, 5 Dec 2022 20:49:23 -0700 Subject: [PATCH 193/361] new TermIterState variants --- src/codegen.rs | 23 +++-- src/debray_allocator.rs | 12 +-- src/fixtures.rs | 39 ++------ src/forms.rs | 12 +-- src/iterators.rs | 178 ++++++++++++++++----------------- src/machine/compile.rs | 10 +- src/machine/disjuncts.rs | 3 + src/machine/machine_indices.rs | 2 +- src/machine/preprocessor.rs | 10 +- 9 files changed, 141 insertions(+), 148 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 65f49971..ec631564 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -456,6 +456,7 @@ impl<'b> CodeGenerator<'b> { target } + /* fn collect_var_data<'a>(&mut self, mut iter: ChunkedIterator<'a>) -> ConjunctInfo<'a> { let mut vs = VariableFixtures::new(); @@ -486,6 +487,7 @@ impl<'b> CodeGenerator<'b> { let vs = self.marker.drain_var_data(vs, num_of_chunks); ConjunctInfo::new(vs, num_of_chunks, has_deep_cut) } + */ fn add_conditional_call(&mut self, code: &mut Code, qt: &QueryTerm, pvs: usize) { match qt { @@ -912,7 +914,8 @@ impl<'b> CodeGenerator<'b> { Ok(()) } - fn compile_seq_prelude(&mut self, conjunct_info: &ConjunctInfo, body: &mut Code) { + fn compile_seq_prelude(&mut self, var_data: &VarData, body: &mut Code) { + /* if conjunct_info.allocates() { let perm_vars = conjunct_info.perm_vars(); @@ -922,6 +925,7 @@ impl<'b> CodeGenerator<'b> { body.push(Instruction::GetLevel(perm_v!(1))); } } + */ } fn compile_cleanup( @@ -955,18 +959,19 @@ impl<'b> CodeGenerator<'b> { } pub(crate) fn compile_rule(&mut self, rule: &Rule) -> Result { - let iter = ChunkedIterator::from_rule(rule); - let conjunct_info = self.collect_var_data(iter); + // let iter = ChunkedIterator::from_rule(rule); + // let conjunct_info = self.collect_var_data(iter); let &Rule { head: (_, ref args, ref p1), ref clauses, + ref var_data, } = rule; let mut code = Code::new(); self.marker.reset_at_head(args); - self.compile_seq_prelude(&conjunct_info, &mut code); + self.compile_seq_prelude(&var_data, &mut code); let iter = FactIterator::from_rule_head_clause(args); let mut fact = self.compile_target::(iter, GenContext::Head); @@ -1015,15 +1020,15 @@ impl<'b> CodeGenerator<'b> { UnsafeVarMarker::from_fact_vars(safe_vars) } - pub(crate) fn compile_fact(&mut self, term: &Term) -> Result { + pub(crate) fn compile_fact(&mut self, fact: &Fact) -> Result { self.update_var_count(post_order_iter(term)); - let mut vs = VariableFixtures::new(); + // let mut vs = VariableFixtures::new(); - vs.mark_vars_in_chunk(post_order_iter(term), term.arity(), GenContext::Head); + // vs.mark_vars_in_chunk(post_order_iter(term), term.arity(), GenContext::Head); - vs.populate_restricting_sets(); - self.marker.drain_var_data(vs, 1); + // vs.populate_restricting_sets(); + // self.marker.drain_var_data(vs, 1); let mut code = Vec::new(); diff --git a/src/debray_allocator.rs b/src/debray_allocator.rs index 73645929..2ad19cab 100644 --- a/src/debray_allocator.rs +++ b/src/debray_allocator.rs @@ -17,7 +17,7 @@ use std::collections::BTreeSet; #[derive(Debug)] pub(crate) struct DebrayAllocator { - bindings: IndexMap, + bindings: IndexMap, arg_c: usize, temp_lb: usize, arity: usize, // 0 if not at head. @@ -36,7 +36,7 @@ impl DebrayAllocator { fn occurs_shallowly_in_head(&self, var: &Var, r: usize) -> bool { match self.bindings.get(var).unwrap() { - &VarData::Temp(_, _, ref tvd) => tvd.use_set.contains(&(GenContext::Head, r)), + &VarAlloc::Temp(_, _, ref tvd) => tvd.use_set.contains(&(GenContext::Head, r)), _ => false, } } @@ -49,7 +49,7 @@ impl DebrayAllocator { fn alloc_with_cr(&self, var: &Var) -> usize { match self.bindings.get(var) { - Some(&VarData::Temp(_, _, ref tvd)) => { + Some(&VarAlloc::Temp(_, _, ref tvd)) => { for &(_, reg) in tvd.use_set.iter() { if !self.is_in_use(reg) { return reg; @@ -75,7 +75,7 @@ impl DebrayAllocator { fn alloc_with_ca(&self, var: &Var) -> usize { match self.bindings.get(var) { - Some(&VarData::Temp(_, _, ref tvd)) => { + Some(&VarAlloc::Temp(_, _, ref tvd)) => { for &(_, reg) in tvd.use_set.iter() { if !self.is_in_use(reg) { return reg; @@ -114,7 +114,7 @@ impl DebrayAllocator { // (GenContext::Last(_), k) is in t_var.use_set. let tvd = self.bindings.get(t_var).unwrap(); - if let &VarData::Temp(_, _, ref tvd) = tvd { + if let &VarAlloc::Temp(_, _, ref tvd) = tvd { if !tvd.use_set.contains(&(GenContext::Last(chunk_num), k)) { return Some((t_var.clone(), self.alloc_with_ca(t_var))); } @@ -205,7 +205,7 @@ impl DebrayAllocator { match term_loc { GenContext::Head if !r.is_perm() => r.reg_num() == k, _ => match self.bindings().get(var).unwrap() { - &VarData::Temp(_, o, _) if r.reg_num() == k => o == k, + &VarAlloc::Temp(_, o, _) if r.reg_num() == k => o == k, _ => false, }, } diff --git a/src/fixtures.rs b/src/fixtures.rs index f75a8042..5f73c716 100644 --- a/src/fixtures.rs +++ b/src/fixtures.rs @@ -1,17 +1,11 @@ -use crate::parser::ast::*; - use crate::forms::*; use crate::instructions::*; -use crate::iterators::*; +use crate::machine::disjuncts::ClassifyInfo; +use crate::parser::ast::*; use bit_set::*; use indexmap::{IndexMap, IndexSet}; -use std::cell::Cell; -use std::collections::BTreeSet; -use std::mem::swap; -use std::vec::Vec; - pub(crate) type OccurrenceSet = IndexSet<(GenContext, usize)>; #[derive(Debug)] @@ -28,20 +22,19 @@ pub(crate) struct TempVarStatus { temp_var_data: TempVarData, } -// TODO: get ridda this! I think. // Perm: 0 initially, a stack register once processed. // Temp: labeled with chunk_num and temp offset (unassigned if 0). #[derive(Debug)] -pub(crate) enum VarData { +pub(crate) enum VarAlloc { Perm(usize), Temp(usize, usize, TempVarData), } -impl VarData { +impl VarAlloc { pub(crate) fn as_reg_type(&self) -> RegType { match self { - &VarData::Temp(_, r, _) => RegType::Temp(r), - &VarData::Perm(r) => RegType::Perm(r), + &VarAlloc::Temp(_, r, _) => RegType::Temp(r), + &VarAlloc::Perm(r) => RegType::Perm(r), } } } @@ -50,7 +43,7 @@ impl TempVarData { pub(crate) fn new(last_term_arity: usize) -> Self { TempVarData { last_term_arity: last_term_arity, - use_set: BitSet::new(), + use_set: BitSet::::new(), no_use_set: BitSet::new(), conflict_set: BitSet::new(), } @@ -72,7 +65,7 @@ impl TempVarData { let mut conflict_set: BitSet = (1..arity).collect(); for &(_, reg) in self.use_set.iter() { - conflict_set.remove(®); + conflict_set.remove(reg); } self.conflict_set = conflict_set; @@ -83,27 +76,15 @@ impl TempVarData { #[derive(Debug)] pub(crate) struct VariableFixtures { temp_vars: IndexMap, - last_chunk_temp_vars: IndexSet, // TODO: has no use at all! remove it. } -impl<'a> VariableFixtures<'a> { +impl VariableFixtures { pub(crate) fn new() -> Self { VariableFixtures { temp_vars: IndexMap::new(), - last_chunk_temp_vars: IndexSet::new(), } } - // TODO: get rid of this also. - pub(crate) fn insert(&mut self, var: Var, vs: VariableFixture<'a>) { - self.temp_vars.insert(var, vs); - } - - // TODO: used? - pub(crate) fn insert_last_chunk_temp_var(&mut self, var: Var) { - self.last_chunk_temp_vars.insert(var); - } - // computes no_use and conflict sets for all temp vars. pub(crate) fn populate_restricting_sets(&mut self) { // three stages: @@ -121,7 +102,7 @@ impl<'a> VariableFixtures<'a> { let TempVarStatus { ref mut temp_var_data, .. } = var_status; let mut use_set = OccurrenceSet::new(); - mem::swap(&mut temp_var_data.use_set, &mut use_set); + std::mem::swap(&mut temp_var_data.use_set, &mut use_set); use_sets.insert(var_gen_index, use_set); } diff --git a/src/forms.rs b/src/forms.rs index d571e2d9..2b46da2c 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -1,7 +1,7 @@ use crate::arena::*; use crate::atom_table::*; use crate::instructions::*; -use crate::machine::disjuncts::VarRecord; +use crate::machine::disjuncts::VarData; use crate::machine::heap::*; use crate::machine::loader::PredicateQueue; use crate::machine::machine_errors::*; @@ -57,7 +57,7 @@ impl AppendOrPrepend { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Level { Deep, Root, @@ -79,7 +79,7 @@ pub enum CallPolicy { Counted, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum QueryTerm { // register, clause type, subterms, clause call policy. Clause(Cell, ClauseType, Vec, CallPolicy), @@ -111,14 +111,14 @@ impl QueryTerm { #[derive(Debug, Clone)] pub struct Fact { pub(crate) head: Term, - pub(crate) var_records: Vec, + pub(crate) var_data: VarData, } #[derive(Debug, Clone)] pub struct Rule { pub(crate) head: (Atom, Vec, QueryTerm), pub(crate) clauses: Vec, - pub(crate) var_records: Vec, + pub(crate) var_data: VarData, } #[derive(Clone, Debug, Hash)] @@ -224,7 +224,7 @@ impl ClauseInfo for PredicateClause { #[derive(Debug, Clone)] pub enum PredicateClause { - Fact(Term), + Fact(Fact), Rule(Rule), } diff --git a/src/iterators.rs b/src/iterators.rs index bbd9fb70..8750fe87 100644 --- a/src/iterators.rs +++ b/src/iterators.rs @@ -43,24 +43,36 @@ impl VarPtr { #[derive(Debug, Clone)] pub(crate) enum TermRef<'a> { AnonVar(Level), + Cut(Level), + GetLevel(Level), Cons(Level, &'a Cell, &'a Term, &'a Term), + Fail(Level), Literal(Level, &'a Cell, &'a Literal), Clause(Level, &'a Cell, Atom, &'a Vec), PartialString(Level, &'a Cell, &'a String, &'a Box), CompleteString(Level, &'a Cell, Atom), Var(Level, &'a Cell, Var), + InitialBranch(Level), + MiddleBranch(Level), + FinalBranch(Level), } impl<'a> TermRef<'a> { pub(crate) fn level(self) -> Level { match self { - TermRef::AnonVar(lvl) - | TermRef::Cons(lvl, ..) - | TermRef::Literal(lvl, ..) - | TermRef::Var(lvl, ..) - | TermRef::Clause(lvl, ..) - | TermRef::CompleteString(lvl, ..) - | TermRef::PartialString(lvl, ..) => lvl, + TermRef::AnonVar(lvl) | + TermRef::Cons(lvl, ..) | + TermRef::Cut(lvl) | + TermRef::GetLevel(lvl) | + TermRef::Literal(lvl, ..) | + TermRef::Var(lvl, ..) | + TermRef::Clause(lvl, ..) | + TermRef::CompleteString(lvl, ..) | + TermRef::PartialString(lvl, ..) | + TermRef::InitialBranch(lvl) | + TermRef::MiddleBranch(lvl) | + TermRef::FinalBranch(lvl) | + TermRef::Fail(lvl) => lvl, } } } @@ -68,14 +80,20 @@ impl<'a> TermRef<'a> { #[derive(Debug)] pub(crate) enum TermIterState<'a> { AnonVar(Level), - Literal(Level, &'a Cell, &'a Literal), Clause(Level, usize, &'a Cell, Atom, &'a Vec), + Cut(Level), + Fail(Level), + GetLevel(Level), + InitialBranch(Level, &'a Vec), + MiddleBranch(Level, &'a Vec), + FinalBranch(Level, &'a Vec), + Sequence(Level, &'a Vec), + Literal(Level, &'a Cell, &'a Literal), InitialCons(Level, &'a Cell, &'a Term, &'a Term), FinalCons(Level, &'a Cell, &'a Term, &'a Term), InitialPartialString(Level, &'a Cell, &'a String, &'a Box), FinalPartialString(Level, &'a Cell, &'a String, &'a Box), CompleteString(Level, &'a Cell, Atom), - UnblockedCut(Level, &'a Cell), Var(Level, &'a Cell, VarPtr), } @@ -108,8 +126,7 @@ pub(crate) struct QueryIterator<'a> { impl<'a> QueryIterator<'a> { fn push_subterm(&mut self, lvl: Level, term: &'a Term) { - self.state_stack - .push(TermIterState::subterm_to_state(lvl, term)); + self.state_stack.push(TermIterState::subterm_to_state(lvl, term)); } fn from_rule_head_clause(terms: &'a Vec) -> Self { @@ -145,47 +162,52 @@ impl<'a> QueryIterator<'a> { } } - fn new(term: &'a QueryTerm) -> Self { + fn extend_state(&mut self, lvl: Level, term: &'a QueryTerm) { match term { &QueryTerm::Clause(ref cell, ClauseType::CallN(_), ref terms, _) => { - let state = TermIterState::Clause(Level::Root, 1, cell, atom!("$call"), terms); - QueryIterator { - state_stack: vec![state], - } + self.state_stack.push(TermIterState::Clause(lvl, 1, cell, atom!("$call"), terms)); } &QueryTerm::Clause(ref cell, ref ct, ref terms, _) => { - let state = TermIterState::Clause(Level::Root, 0, cell, ct.name(), terms); - QueryIterator { - state_stack: vec![state], - } + self.state_stack.push(TermIterState::Clause(lvl, 0, cell, ct.name(), terms)); } - &QueryTerm::UnblockedCut(ref cell) => { - let state = TermIterState::UnblockedCut(Level::Root, cell); - - QueryIterator { - state_stack: vec![state], - } + &QueryTerm::Cut => { + self.state_stack.push(TermIterState::Cut(lvl)); } &QueryTerm::GetLevelAndUnify(ref cell, ref var) => { - let state = TermIterState::Var(Level::Root, cell, VarPtr::from(var)); - QueryIterator { - state_stack: vec![state], - } + // TODO: get rid of it if possible. or! specialized TermIterState variant. + self.state_stack.push(TermIterState::Var(lvl, cell, VarPtr::from(var))); } - &QueryTerm::Jump(ref vars) => { - let state_stack = vars + &QueryTerm::Not(ref terms) => { + self.state_stack.push(TermIterState::Fail(lvl)); + self.state_stack.push(TermIterState::Cut(lvl)); + self.state_stack.push(TermIterState::Sequence(lvl, terms)); + } + &QueryTerm::IfThen(ref if_terms, ref then_terms) => { + self.state_stack.push(TermIterState::Sequence(lvl, then_terms)); + self.state_stack.push(TermIterState::Cut(lvl)); + self.state_stack.push(TermIterState::Sequence(lvl, if_terms)); + self.state_stack.push(TermIterState::GetLevel(lvl)); + } + &QueryTerm::Branch(ref branches) => { + let len = branches.len(); + self.state_stack.push(TermIterState::FinalBranch(lvl, &branches[len - 1])); + + self.state_stack.extend(branches[1 .. len - 1] .iter() .rev() - .map(|t| TermIterState::subterm_to_state(Level::Shallow, t)) - .collect(); + .map(|t| TermIterState::MiddleBranch(lvl, t)), + ); - QueryIterator { state_stack } + self.state_stack.push(TermIterState::InitialBranch(lvl, &branches[0])); } - &QueryTerm::BlockedCut => QueryIterator { - state_stack: vec![], - }, } } + + fn new(term: &'a QueryTerm) -> Self { + let mut iter = QueryIterator { state_stack: vec![] }; + iter.extend_state(Level::Root, term); + iter + } } impl<'a> Iterator for QueryIterator<'a> { @@ -247,8 +269,31 @@ impl<'a> Iterator for QueryIterator<'a> { TermIterState::Var(lvl, cell, var) => { return Some(TermRef::Var(lvl, cell, Var::from(var))); } - TermIterState::UnblockedCut(lvl, cell) => { - return Some(TermRef::Var(lvl, cell, Var::from("!"))); + TermIterState::Cut(lvl) => { + return Some(TermRef::Cut(lvl)); + } + TermIterState::GetLevel(lvl) => { + return Some(TermRef::GetLevel(lvl)); + } + TermIterState::InitialBranch(lvl, ref branch) => { + self.state_stack.push(TermIterState::Sequence(lvl, branch)); + return Some(TermRef::InitialBranch(lvl)); + } + TermIterState::MiddleBranch(lvl, ref branch) => { + self.state_stack.push(TermIterState::Sequence(lvl, branch)); + return Some(TermRef::MiddleBranch(lvl)); + } + TermIterState::FinalBranch(lvl, ref branch) => { + self.state_stack.push(TermIterState::Sequence(lvl, branch)); + return Some(TermRef::FinalBranch(lvl)); + } + TermIterState::Sequence(lvl, ref terms) => { + for term in branch.iter().rev() { + self.extend_state(lvl, term); + } + } + TermIterState::Fail(lvl) => { + return Some(TermRef::Fail(lvl)); } }; } @@ -398,23 +443,9 @@ impl<'a> ChunkedTerm<'a> { } } -fn contains_cut_var<'a, Iter: Iterator>(terms: Iter) -> bool { - for term in terms { - if let &Term::Var(_, ref var) = term { - if var.as_str() == Some("!") { - return true; - } - } - } - - false -} - pub(crate) struct ChunkedIterator<'a> { pub(crate) chunk_num: usize, iter: Box> + 'a>, - deep_cut_encountered: bool, - cut_var_in_head: bool, } impl<'a> fmt::Debug for ChunkedIterator<'a> { @@ -423,8 +454,6 @@ impl<'a> fmt::Debug for ChunkedIterator<'a> { .field("chunk_num", &self.chunk_num) // Hacky solution. .field("iter", &"Box> + 'a>") - .field("deep_cut_encountered", &self.deep_cut_encountered) - .field("cut_var_in_head", &self.cut_var_in_head) .finish() } } @@ -458,8 +487,6 @@ impl<'a> ChunkedIterator<'a> { ChunkedIterator { chunk_num: 0, iter: Box::new(iter), - deep_cut_encountered: false, - cut_var_in_head: false, } } @@ -467,6 +494,7 @@ impl<'a> ChunkedIterator<'a> { let &Rule { head: (ref name, ref args, ref p1), ref clauses, + .. } = rule; let iter = once(ChunkedTerm::HeadClause(name.clone(), args)); @@ -476,15 +504,9 @@ impl<'a> ChunkedIterator<'a> { ChunkedIterator { chunk_num: 0, iter: Box::new(iter), - deep_cut_encountered: false, - cut_var_in_head: false, } } - pub(crate) fn encountered_deep_cut(&self) -> bool { - self.deep_cut_encountered - } - fn take_chunk(&mut self, term: ChunkedTerm<'a>) -> (usize, usize, Vec>) { let mut arity = 0; let mut item = Some(term); @@ -493,42 +515,18 @@ impl<'a> ChunkedIterator<'a> { while let Some(term) = item { match term { ChunkedTerm::HeadClause(_, terms) => { - if contains_cut_var(terms.iter()) { - self.cut_var_in_head = true; - } - result.push(term); } - ChunkedTerm::BodyTerm(&QueryTerm::Jump(ref vars)) => { + ChunkedTerm::BodyTerm(&QueryTerm::Cut) => { result.push(term); - arity = vars.len(); - - if contains_cut_var(vars.iter()) && !self.cut_var_in_head { - self.deep_cut_encountered = true; - } - - break; - } - ChunkedTerm::BodyTerm(&QueryTerm::BlockedCut) => { - result.push(term); - - if self.chunk_num > 0 { - self.deep_cut_encountered = true; - } } ChunkedTerm::BodyTerm(&QueryTerm::GetLevelAndUnify(..)) => { - self.deep_cut_encountered = true; - result.push(term); arity = 1; break; } - ChunkedTerm::BodyTerm(&QueryTerm::UnblockedCut(..)) => { - self.deep_cut_encountered = true; - result.push(term); - } ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::Inlined(_), ..)) => { - result.push(term) + result.push(term); } ChunkedTerm::BodyTerm(&QueryTerm::Clause( _, diff --git a/src/machine/compile.rs b/src/machine/compile.rs index 5f3d8e28..428e2952 100644 --- a/src/machine/compile.rs +++ b/src/machine/compile.rs @@ -57,6 +57,7 @@ pub(super) fn compile_relation( } } +/* pub(super) fn compile_appendix( code: &mut Code, mut queue: VecDeque, @@ -97,6 +98,7 @@ pub(super) fn compile_appendix( Ok(()) } +*/ fn lower_bound_of_target_clause(skeleton: &PredicateSkeleton, target_pos: usize) -> usize { if target_pos == 0 { @@ -1342,7 +1344,7 @@ 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, @@ -1351,6 +1353,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { let mut clause_code = cg.compile_predicate(&vec![clause])?; + /* compile_appendix( &mut clause_code, queue, @@ -1358,6 +1361,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { settings.non_counted_bt, cg.atom_tbl, )?; + */ Ok(StandaloneCompileResult { clause_code, @@ -1385,7 +1389,7 @@ 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 queue = preprocessor.parse_queue(self)?; let mut cg = CodeGenerator::new( &mut LS::machine_st(&mut self.payload).atom_tbl, @@ -1394,6 +1398,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { let mut code = cg.compile_predicate(&clauses)?; + /* compile_appendix( &mut code, queue, @@ -1401,6 +1406,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { settings.non_counted_bt, cg.atom_tbl, )?; + */ if settings.is_extensible { let mut clause_clause_locs = VecDeque::new(); diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index 19e460c3..3231c652 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -487,6 +487,8 @@ impl VariableClassifier { Ok(()) } + // TODO: maybe replace Vec with an iterator that has, in the stream, + // with a 'QueryTerm' that toggles the chunk num and type, like we do here. fn classify_body_variables<'a, LS: LoadState<'a>>( &mut self, loader: &mut Loader<'a, LS>, @@ -826,6 +828,7 @@ impl BranchMap { debug_assert_eq!(var_data.records.len(), var_num); + var_data.fixtures.populate_restricting_sets(); var_data } } diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index 3f49e1ce..afa2bea2 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -228,7 +228,7 @@ impl CodeIndex { } pub(crate) type HeapVarDict = IndexMap; -pub(crate) type AllocVarDict = IndexMap; +pub(crate) type AllocVarDict = IndexMap; pub(crate) type GlobalVarDir = IndexMap), FxBuildHasher>; diff --git a/src/machine/preprocessor.rs b/src/machine/preprocessor.rs index 0564af8c..02e0e29f 100644 --- a/src/machine/preprocessor.rs +++ b/src/machine/preprocessor.rs @@ -553,9 +553,9 @@ impl Preprocessor { self.settings.default_call_policy(), ); - let (head, var_records) = classifier.classify_fact(term)?; + let (head, var_data) = classifier.classify_fact(term)?; - Ok(Fact { head, var_records }) + Ok(Fact { head, var_data }) } _ => Err(CompilationError::InadmissibleFact), } @@ -571,7 +571,7 @@ impl Preprocessor { self.settings.default_call_policy(), ); - let (head, mut query_terms, var_records) = + let (head, mut query_terms, var_data) = classifier.classify_rule(loader, head, body)?; let clauses = query_terms.drain(1..).collect(); @@ -581,12 +581,12 @@ impl Preprocessor { Term::Clause(_, name, terms) => Ok(Rule { head: (name, terms, qt), clauses, - var_records, + var_data, }), Term::Literal(_, Literal::Atom(name)) => Ok(Rule { head: (name, vec![], qt), clauses, - var_records, + var_data, }), _ => Err(CompilationError::InvalidRuleHead), } From c4783062ff14bb8f402b09bbaca96aacb612a98c Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 27 Dec 2022 21:29:29 -0700 Subject: [PATCH 194/361] delete ChunkedTerm, chunked iteration --- src/iterators.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/iterators.rs b/src/iterators.rs index 8750fe87..eac1b185 100644 --- a/src/iterators.rs +++ b/src/iterators.rs @@ -424,6 +424,7 @@ pub(crate) fn breadth_first_iter<'a>(term: &'a Term, iterable_root: bool) -> Fac FactIterator::new(term, iterable_root) } +/* #[derive(Debug)] pub(crate) enum ChunkedTerm<'a> { HeadClause(Atom, &'a Vec), @@ -563,3 +564,4 @@ impl<'a> Iterator for ChunkedIterator<'a> { self.iter.next().map(|term| self.take_chunk(term)) } } +*/ From 097849385ee50ba49a1b7a06125ece28bab597b8 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 27 Dec 2022 23:19:18 -0700 Subject: [PATCH 195/361] add QueryTerm::ChunkTypeBoundary --- src/forms.rs | 19 +++++++++++++++ src/machine/disjuncts.rs | 50 +++++----------------------------------- 2 files changed, 25 insertions(+), 44 deletions(-) diff --git a/src/forms.rs b/src/forms.rs index 2b46da2c..4d7b7f47 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -79,6 +79,24 @@ pub enum CallPolicy { Counted, } +#[derive(Debug, Clone, Copy)] +enum ChunkType { + Head, + Mid, + Last, +} + +impl ChunkType { + #[inline(always)] + pub fn to_gen_context(self, chunk_num: usize) -> GenContext { + match self { + ChunkType::Head => GenContext::Head, + ChunkType::Mid => GenContext::Mid(chunk_num), + ChunkType::Last => GenContext::Last(chunk_num), + } + } +} + #[derive(Debug)] pub enum QueryTerm { // register, clause type, subterms, clause call policy. @@ -88,6 +106,7 @@ pub enum QueryTerm { IfThen(Vec, Vec), Branch(Vec>), GetLevelAndUnify(Cell, Var), + ChunkTypeBoundary(ChunkType), } impl QueryTerm { diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index 3231c652..501aaddd 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -137,24 +137,6 @@ impl DerefMut for BranchMap { type RootSet = IndexSet; -#[derive(Debug, Clone, Copy)] -enum ChunkType { - Head, - Mid, - Last, -} - -impl ChunkType { - #[inline(always)] - fn to_gen_context(self, chunk_num: usize) -> GenContext { - match self { - ChunkType::Head => GenContext::Head, - ChunkType::Mid => GenContext::Mid(chunk_num), - ChunkType::Last => GenContext::Last(chunk_num), - } - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ClassifyInfo { arg_c: usize, @@ -257,7 +239,7 @@ fn merge_branch_seq>(branches: Iter) -> Branch } fn flatten_into_disjunct(build_stack: &mut Vec, preceding_len: usize) { - let iter = build_stack.drain(preceding_len ..); + let iter = build_stack.drain(preceding_len + 1 ..); if let QueryTerm::Branch(ref mut disjuncts) = &mut build_stack[preceding_len] { disjuncts.push(iter.collect()); @@ -342,28 +324,6 @@ impl VariableClassifier { Ok((head, query_terms, self.branch_map.separate_and_classify_variables())) } - /* - pub fn to_branch_map(mut self, term: Term) -> Result { - self.root_set.insert(BranchNumber::default()); - - let (head_term, query_terms) = match term { - Term::Clause(_, atom!(":-"), terms) if terms.len() == 2 => { - let head_term = terms[0]; - - self.classify_head_variables(&head_term)?; - (head_term, self.classify_body_variables(terms[1])?) - } - _ => { - self.classify_head_variables(&term)?; - (term, vec![]) - } - }; - - self.merge_branches(); - Ok((head_term, query_terms, self.branch_map)) - } - */ - fn merge_branches(&mut self) { for branches in self.branch_map.values_mut() { let mut old_branches = std::mem::replace(branches, vec![]); @@ -487,8 +447,6 @@ impl VariableClassifier { Ok(()) } - // TODO: maybe replace Vec with an iterator that has, in the stream, - // with a 'QueryTerm' that toggles the chunk num and type, like we do here. fn classify_body_variables<'a, LS: LoadState<'a>>( &mut self, loader: &mut Loader<'a, LS>, @@ -515,15 +473,18 @@ impl VariableClassifier { TraversalState::IncrChunkNum => { self.current_chunk_num += 1; chunk_type = ChunkType::Mid; + build_stack.push(QueryTerm::ChunkTypeBoundary(chunk_type)); } TraversalState::ResetCallPolicy(call_policy) => { self.call_policy = call_policy; } TraversalState::SetLastChunkType => { chunk_type = ChunkType::Last; + build_stack.push(QueryTerm::ChunkTypeBoundary(chunk_type)); } TraversalState::BuildDisjunct(reset_chunk_type, preceding_len) => { chunk_type = reset_chunk_type; + build_stack.push(QueryTerm::ChunkTypeBoundary(chunk_type)); flatten_into_disjunct(&mut build_stack, preceding_len); } TraversalState::BuildFinalDisjunct(preceding_len) => { @@ -579,7 +540,7 @@ impl VariableClassifier { } let build_stack_len = build_stack.len(); - build_stack.push(QueryTerm::Branch(vec![])); + build_stack.push(QueryTerm::Branch(Vec::with_capacity(branches.len()))); state_stack.push(TraversalState::RepBranchNum( self.current_branch_num.halve_delta(), @@ -630,6 +591,7 @@ impl VariableClassifier { state_stack.push(TraversalState::IncrChunkNum); // TODO: need to classify this variable? + // what is the difference between $get_cp and this exactly? if let Term::Var(_, ref var) = &terms[0] { build_stack.push( QueryTerm::GetLevelAndUnify( From 942095baa773706d136de550db13476d8a19c617 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Tue, 27 Dec 2022 23:42:35 -0700 Subject: [PATCH 196/361] remove GetLevelAndUnify and replace it with GetCutPoint --- build/instructions_template.rs | 6 ------ src/codegen.rs | 22 ---------------------- src/forms.rs | 3 +-- src/iterators.rs | 9 --------- src/lib/iso_ext.pl | 6 +++--- src/machine/disjuncts.rs | 18 +----------------- src/machine/dispatch.rs | 11 ----------- 7 files changed, 5 insertions(+), 70 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index a7ea3d21..d0a8c4d0 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -639,8 +639,6 @@ enum InstructionTemplate { Cut(RegType), #[strum_discriminants(strum(props(Arity = "1", Name = "get_level")))] GetLevel(RegType), - #[strum_discriminants(strum(props(Arity = "1", Name = "get_level_and_unify")))] - GetLevelAndUnify(RegType), #[strum_discriminants(strum(props(Arity = "0", Name = "neck_cut")))] NeckCut, // choice instruction @@ -1298,10 +1296,6 @@ fn generate_instruction_preface() -> TokenStream { let rt_stub = reg_type_into_functor(r); functor!(atom!("get_level"), [str(h, 0)], [rt_stub]) } - &Instruction::GetLevelAndUnify(r) => { - let rt_stub = reg_type_into_functor(r); - functor!(atom!("get_level_and_unify"), [str(h, 0)], [rt_stub]) - } &Instruction::NeckCut => { functor!(atom!("neck_cut")) } diff --git a/src/codegen.rs b/src/codegen.rs index ec631564..e7c6e2ac 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -840,25 +840,6 @@ impl<'b> CodeGenerator<'b> { code.push(instr!("$set_cp", cell.get().norm(), 0)); } - fn compile_get_level_and_unify( - &mut self, - code: &mut Code, - cell: &Cell, - var: Var, - term_loc: GenContext, - ) { - let mut target = Code::new(); - - self.marker.reset_arg(1); - self.marker.mark_var::(var, Level::Shallow, cell, term_loc, &mut target); - - if !target.is_empty() { - code.extend(target.into_iter()); - } - - code.push(instr!("get_level_and_unify", cell.get().norm())); - } - fn compile_seq<'a>( &mut self, iter: ChunkedIterator<'a>, @@ -874,9 +855,6 @@ impl<'b> CodeGenerator<'b> { }; match *term { - &QueryTerm::GetLevelAndUnify(ref cell, ref var) => { - self.compile_get_level_and_unify(code, cell, var.clone(), term_loc) - } &QueryTerm::UnblockedCut(ref cell) => self.compile_unblocked_cut(code, cell), &QueryTerm::BlockedCut => code.push(if chunk_num == 0 { Instruction::NeckCut diff --git a/src/forms.rs b/src/forms.rs index 4d7b7f47..ac7649d5 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -105,7 +105,6 @@ pub enum QueryTerm { Not(Vec), IfThen(Vec, Vec), Branch(Vec>), - GetLevelAndUnify(Cell, Var), ChunkTypeBoundary(ChunkType), } @@ -122,7 +121,7 @@ impl QueryTerm { &QueryTerm::Clause(_, _, ref subterms, ..) => subterms.len(), &QueryTerm::Cut | &QueryTerm::Branch(_) => 0, &QueryTerm::IfThen(..) => 2, - &QueryTerm::Not(_) | &QueryTerm::GetLevelAndUnify(..) => 1, + &QueryTerm::Not(_) => 1, } } } diff --git a/src/iterators.rs b/src/iterators.rs index eac1b185..e1834113 100644 --- a/src/iterators.rs +++ b/src/iterators.rs @@ -173,10 +173,6 @@ impl<'a> QueryIterator<'a> { &QueryTerm::Cut => { self.state_stack.push(TermIterState::Cut(lvl)); } - &QueryTerm::GetLevelAndUnify(ref cell, ref var) => { - // TODO: get rid of it if possible. or! specialized TermIterState variant. - self.state_stack.push(TermIterState::Var(lvl, cell, VarPtr::from(var))); - } &QueryTerm::Not(ref terms) => { self.state_stack.push(TermIterState::Fail(lvl)); self.state_stack.push(TermIterState::Cut(lvl)); @@ -521,11 +517,6 @@ impl<'a> ChunkedIterator<'a> { ChunkedTerm::BodyTerm(&QueryTerm::Cut) => { result.push(term); } - ChunkedTerm::BodyTerm(&QueryTerm::GetLevelAndUnify(..)) => { - result.push(term); - arity = 1; - break; - } ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::Inlined(_), ..)) => { result.push(term); } diff --git a/src/lib/iso_ext.pl b/src/lib/iso_ext.pl index f22420af..d6f13df0 100644 --- a/src/lib/iso_ext.pl +++ b/src/lib/iso_ext.pl @@ -175,7 +175,7 @@ scc_helper(_, _, _) :- run_cleaners_with_handling :- '$get_scc_cleaner'(C), - '$get_level'(B), + '$get_cp'(B), catch(C, _, true), '$set_cp_by_default'(B), run_cleaners_with_handling. @@ -186,7 +186,7 @@ run_cleaners_with_handling :- run_cleaners_without_handling(Cp) :- '$get_scc_cleaner'(C), - '$get_level'(B), + '$get_cp'(B), call(C), '$set_cp_by_default'(B), run_cleaners_without_handling(Cp). @@ -258,7 +258,7 @@ call_with_inference_limit(_, _, R, Bb, B) :- '$remove_inference_counter'(B, _), ( '$get_ball'(Ball), '$push_ball_stack', - '$get_level'(Cp), + '$get_cp'(Cp), '$set_cp_by_default'(Cp) ; '$remove_call_policy_check'(B), '$fail' diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index 501aaddd..ee1711e6 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -566,7 +566,7 @@ impl VariableClassifier { let build_stack_len = build_stack.len(); - // TODO: insert GetLevelAndUnify between + // TODO: insert GetCutPoint between // the two traversal states and detect // that as a chunk boundary in // insert_set_last_chunk_type ?? @@ -587,22 +587,6 @@ impl VariableClassifier { state_stack.push(TraversalState::BuildNot(build_stack_len)); state_stack.push(TraversalState::Term(terms[0])); } - Term::Clause(_, atom!("$get_level"), terms) if terms.len() == 1 => { - state_stack.push(TraversalState::IncrChunkNum); - - // TODO: need to classify this variable? - // what is the difference between $get_cp and this exactly? - if let Term::Var(_, ref var) = &terms[0] { - build_stack.push( - QueryTerm::GetLevelAndUnify( - Cell::default(), - var.clone(), - ), - ); - } else { - return Err(CompilationError::InadmissibleQueryTerm); - } - } Term::Clause(_, atom!(":"), mut terms) if terms.len() == 2 => { let term_loc = chunk_type.to_gen_context(self.current_chunk_num); diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 04f39428..92eca80f 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -1152,17 +1152,6 @@ impl Machine { self.machine_st[r] = fixnum_as_cell!(Fixnum::build_with(b0 as i64)); self.machine_st.p += 1; } - &Instruction::GetLevelAndUnify(r) => { - // let b0 = self.machine_st[perm_v!(1)]; - let b0 = cell_as_fixnum!( - self.machine_st.stack[stack_loc!(AndFrame, self.machine_st.e, 1)] - ); - let a = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); - - // unify_fn!(&mut self.machine_st, a, b0); - self.machine_st.unify_fixnum(b0, a); - step_or_fail!(self, self.machine_st.p += 1); - } &Instruction::Cut(r) => { let value = self.machine_st[r]; self.machine_st.cut_body(value); From cb59c3003af10c872a1b13a36cae511ff2bfca94 Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Sun, 1 Jan 2023 11:04:46 -0700 Subject: [PATCH 197/361] correct chunk type labeling --- Cargo.lock | 59 +++++++++++++++++++++++++++++++++++++++- src/fixtures.rs | 2 +- src/forms.rs | 10 +++++-- src/machine/disjuncts.rs | 44 ++++++++++++++++-------------- 4 files changed, 91 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7cc11f21..542afd09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2608,9 +2608,30 @@ dependencies = [ ] [[package]] -name = "windows_aarch64_gnullvm" +name = "windows-sys" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" [[package]] @@ -2625,6 +2646,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + [[package]] name = "windows_i686_gnu" version = "0.36.1" @@ -2637,6 +2664,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + [[package]] name = "windows_i686_msvc" version = "0.36.1" @@ -2649,6 +2682,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" @@ -2667,6 +2706,18 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -2679,6 +2730,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + [[package]] name = "xmlparser" version = "0.13.5" diff --git a/src/fixtures.rs b/src/fixtures.rs index 5f73c716..9e1f28fe 100644 --- a/src/fixtures.rs +++ b/src/fixtures.rs @@ -148,7 +148,7 @@ impl VariableFixtures { ) { let chunk_num = term_loc.chunk_num(); - let mut status = self.temp_vars.swap_remove(generated_var_index).unwrap_or_else(|| { + let mut status = self.temp_vars.swap_remove(&generated_var_index).unwrap_or_else(|| { TempVarStatus { chunk_num, temp_var_data: TempVarData::new(classify_info.arity), diff --git a/src/forms.rs b/src/forms.rs index ac7649d5..9cc6f1ba 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -79,8 +79,8 @@ pub enum CallPolicy { Counted, } -#[derive(Debug, Clone, Copy)] -enum ChunkType { +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ChunkType { Head, Mid, Last, @@ -95,6 +95,11 @@ impl ChunkType { ChunkType::Last => GenContext::Last(chunk_num), } } + + #[inline(always)] + pub fn is_last(self) -> bool { + self == ChunkType::Last + } } #[derive(Debug)] @@ -104,6 +109,7 @@ pub enum QueryTerm { Cut, Not(Vec), IfThen(Vec, Vec), + LocalCut(Cell), // for IfThen. Branch(Vec>), ChunkTypeBoundary(ChunkType), } diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index ee1711e6..24969ff3 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -202,8 +202,6 @@ pub struct VarRecord { pub num_occurrences: usize, } -// TODO: already exists a VarData! although it may no longer exist?? -// Also, the name is too similar to VarInfo. Think of better names! pub struct VarData { pub records: Vec, pub fixtures: VariableFixtures, @@ -243,47 +241,50 @@ fn flatten_into_disjunct(build_stack: &mut Vec, preceding_len: usize) if let QueryTerm::Branch(ref mut disjuncts) = &mut build_stack[preceding_len] { disjuncts.push(iter.collect()); + } else { + unreachable!(); } } fn term_in_other_chunk(term: &Term) -> Option { match term { Term::Clause(_, name, terms) => Some(!ClauseType::is_inbuilt(*name, terms.len())), - Term::Literal(_, Literal::Atom(atom!("!"))) | - Term::Literal(_, Literal::Char('!')) => Some(false), + Term::Literal(_, Literal::Atom(atom!("!")) | Literal::Char('!')) => Some(false), Term::Literal(_, Literal::Atom(name)) => Some(!ClauseType::is_inbuilt(*name, 0)), Term::Var(..) => Some(true), _ => None, } } -// returns true if the insertion of SetLastChunkType was the final push. +// returns true if SetLastChunkType was pushed. // expects that iter iterates over a conjunct of Terms in reverse order. fn insert_set_last_chunk_type( state_stack: &mut Vec, mut iter: impl Iterator, ) -> bool { let beg = state_stack.len(); - let mut idx = beg; + + let mut will_break = false; + let mut last_chunk_delim = beg; while let Some(traversal_st) = iter.next() { match traversal_st { TraversalState::Term(term) | TraversalState::BuildIf(_, term) => { - let mut will_break = false; + will_break = false; match term_in_other_chunk(&term) { - Some(true) if idx > beg => will_break = true, - Some(_) => idx += 1, + Some(true) if last_chunk_delim > beg => will_break = true, + Some(_) => last_chunk_delim += 1, None => will_break = true, } if will_break { + // recall that iter iterates in reverse order. + // therefore this is the correct push order. state_stack.push(TraversalState::SetLastChunkType); state_stack.push(traversal_st); break; - } else { - state_stack.push(traversal_st); } } _ => { @@ -293,7 +294,7 @@ fn insert_set_last_chunk_type( } state_stack.extend(iter); - idx == state_stack.len() + will_break } impl VariableClassifier { @@ -513,9 +514,11 @@ impl VariableClassifier { .chain(std::iter::once(terms[0])) .map(TraversalState::Term); - if let ChunkType::Last = chunk_type { - if !insert_set_last_chunk_type(&mut state_stack, iter) { - chunk_type = ChunkType::Mid; + if ChunkType::Mid != chunk_type { + if insert_set_last_chunk_type(&mut state_stack, iter) { + if chunk_type.is_last() { + chunk_type = ChunkType::Mid; + } } } else { state_stack.extend(iter); @@ -575,9 +578,11 @@ impl VariableClassifier { TraversalState::Term(if_term)] .into_iter(); - if let ChunkType::Last = chunk_type { - if !insert_set_last_chunk_type(&mut state_stack, iter) { - chunk_type = ChunkType::Mid; + if ChunkType::Mid != chunk_type { + if insert_set_last_chunk_type(&mut state_stack, iter) { + if chunk_type.is_last() { + chunk_type = ChunkType::Mid; + } } } } @@ -686,8 +691,7 @@ impl VariableClassifier { ), ); } - Term::Literal(_, Literal::Atom(atom!("!"))) | - Term::Literal(_, Literal::Char('!')) => { + Term::Literal(_, Literal::Atom(atom!("!")) | Literal::Char('!')) => { build_stack.push(QueryTerm::Cut); } Term::Literal(cell, Literal::Atom(name)) => { From b205abe949c8234e9c2b343a0dae16b854c3d32e Mon Sep 17 00:00:00 2001 From: Mark Thom Date: Mon, 30 Jan 2023 23:26:50 -0700 Subject: [PATCH 198/361] remove BuildIf, BuildNot, BuildThen TermIterState variants --- src/fixtures.rs | 19 ++--- src/forms.rs | 8 +-- src/iterators.rs | 23 ++++-- src/machine/disjuncts.rs | 146 ++++++++++++++++++++++----------------- 4 files changed, 112 insertions(+), 84 deletions(-) diff --git a/src/fixtures.rs b/src/fixtures.rs index 9e1f28fe..66734320 100644 --- a/src/fixtures.rs +++ b/src/fixtures.rs @@ -139,27 +139,22 @@ impl VariableFixtures { }; } - pub(crate) fn mark_temp_var( - &mut self, - generated_var_index: usize, - lvl: Level, - classify_info: &ClassifyInfo, - term_loc: GenContext, - ) { + pub(crate) fn mark_temp_var(&mut self, var_info: &VarInfo) { let chunk_num = term_loc.chunk_num(); + let var = Var::from(var_info.var_ptr); - let mut status = self.temp_vars.swap_remove(&generated_var_index).unwrap_or_else(|| { + let mut status = self.temp_vars.swap_remove(&var).unwrap_or_else(|| { TempVarStatus { chunk_num, - temp_var_data: TempVarData::new(classify_info.arity), + temp_var_data: TempVarData::new(var_info.classify_info.arity), } }); - if let Level::Shallow = lvl { - self.record_temp_info(&mut status, classify_info.arg_c, term_loc); + if let Level::Shallow = var_info.lvl { + self.record_temp_info(&mut status, var_info.classify_info.arg_c, term_loc); } - self.temp_vars.insert(Var::Generated(generated_var_index), status); + self.temp_vars.insert(var, status); } } diff --git a/src/forms.rs b/src/forms.rs index 9cc6f1ba..3ed83866 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -106,10 +106,10 @@ impl ChunkType { pub enum QueryTerm { // register, clause type, subterms, clause call policy. Clause(Cell, ClauseType, Vec, CallPolicy), - Cut, - Not(Vec), - IfThen(Vec, Vec), - LocalCut(Cell), // for IfThen. + Fail, + GlobalCut, + GetCutPoint(usize), + LocalCut(usize), Branch(Vec>), ChunkTypeBoundary(ChunkType), } diff --git a/src/iterators.rs b/src/iterators.rs index e1834113..529c453c 100644 --- a/src/iterators.rs +++ b/src/iterators.rs @@ -12,8 +12,9 @@ use std::iter::*; use std::vec::Vec; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) struct VarPtr { - ptr: std::ptr::NonNull, +pub(crate) enum VarPtr { + ToVar(std::ptr::NonNull), + InSitu(usize), } impl From<&Var> for VarPtr { @@ -26,17 +27,27 @@ impl From<&Var> for VarPtr { } impl From for Var { - #[inline] + #[inline(always)] fn from(value: VarPtr) -> Var { - unsafe { - (*value.ptr.as_ptr()).clone() + match value { + VarPtr::ToPtr(ptr) => unsafe { + (*ptr.ptr.as_ptr()).clone() + }, + VarPtr::InSitu(var_num) => { + Var::Generated(var_num) + } } } } impl VarPtr { pub(crate) fn set(&mut self, value: Var) { - unsafe { *self.ptr.as_mut() = value; } + match self { + VarPtr::ToVar(ref mut ptr) => + unsafe { *ptr.as_mut() = value }, + VarPtr::InSitu(_) => { + } + } } } diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index 24969ff3..98875bc8 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -150,9 +150,9 @@ enum TraversalState { // add the last disjunct to a QueryTerm::Branch, continuing from // where it leaves off. BuildFinalDisjunct(usize), - BuildIf(usize, Term), // build the P term of P -> Q - BuildThen(usize, Vec), // build the Q term of P -> Q - BuildNot(usize), // build the P term of \+ P + Fail, + GetCutPoint(usize), + LocalCut(usize), ResetCallPolicy(CallPolicy), Term(Term), AddBranchNum(BranchNumber), // set current_branch_number, add it to the root set @@ -186,6 +186,7 @@ pub struct VariableClassifier { current_branch_num: BranchNumber, current_chunk_num: usize, branch_map: BranchMap, + var_num: usize, root_set: RootSet, } @@ -196,12 +197,23 @@ pub enum VarClassification { Perm, } +#[derive(Clone, Debug)] pub struct VarRecord { pub classification: VarClassification, pub chunk_occurrences: Vec, pub num_occurrences: usize, } +impl Default for VarRecord { + fn default() -> Self { + VarRecord { + classification: VarClassification::Void, + chunk_occurrences: vec![], + num_occurrences: 0, + } + } +} + pub struct VarData { pub records: Vec, pub fixtures: VariableFixtures, @@ -269,7 +281,7 @@ fn insert_set_last_chunk_type( while let Some(traversal_st) = iter.next() { match traversal_st { - TraversalState::Term(term) | TraversalState::BuildIf(_, term) => { + TraversalState::Term(term) => { will_break = false; match term_in_other_chunk(&term) { @@ -288,7 +300,7 @@ fn insert_set_last_chunk_type( } } _ => { - unreachable!(); + state_stack.push(traversal_st); } } } @@ -305,12 +317,13 @@ impl VariableClassifier { current_chunk_num: 0, branch_map: BranchMap(BranchMapInt::new()), root_set: RootSet::new(), + var_num: 0, } } pub fn classify_fact(mut self, term: Term) -> Result { self.classify_head_variables(&term)?; - Ok((term, self.branch_map.separate_and_classify_variables())) + Ok((term, self.branch_map.separate_and_classify_variables(self.var_num))) } pub fn classify_rule<'a, LS: LoadState<'a>>( @@ -322,7 +335,7 @@ impl VariableClassifier { self.classify_head_variables(&head)?; let query_terms = self.classify_body_variables(loader, body)?; - Ok((head, query_terms, self.branch_map.separate_and_classify_variables())) + Ok((head, query_terms, self.branch_map.separate_and_classify_variables(self.var_num))) } fn merge_branches(&mut self) { @@ -396,6 +409,20 @@ impl VariableClassifier { chunk_info.vars.push(var_info); } + fn probe_in_situ_var(&mut self, chunk_type: ChunkType, var_num: usize) { + let classify_info = ClassifyInfo { arg_c: 0, arity: 0 }; + + let var_info = VarInfo { + var_ptr: VarPtr::InSitu(var_num), + classify_info, + lvl: Level::Shallow, + }; + + let term_loc = chunk_type.to_gen_context(self.current_chunk_num); + + self.probe_body_var(Var::Generated(var_num), term_loc, var_info); + } + fn classify_head_variables(&mut self, term: &Term) -> Result<(), CompilationError> { match term { Term::Clause(..) | Term::Literal(_, Literal::Atom(_)) => { @@ -403,10 +430,7 @@ impl VariableClassifier { _ => return Err(CompilationError::InvalidRuleHead), } - let mut classify_info = ClassifyInfo { - arg_c: 0, - arity: term.arity(), - }; + let mut classify_info = ClassifyInfo { arg_c: 0, arity: term.arity() }; // false argument to breadth_first_iter because the root is not iterable. for term_ref in breadth_first_iter(term, false) { @@ -491,19 +515,20 @@ impl VariableClassifier { TraversalState::BuildFinalDisjunct(preceding_len) => { flatten_into_disjunct(&mut build_stack, preceding_len); } - TraversalState::BuildIf(preceding_len, then_term) => { - let iter = build_stack.drain(preceding_len ..); + TraversalState::GetCutPoint(var_num) => { + let term_loc = chunk_type.to_gen_context(self.current_chunk_num); - state_stack.push(TraversalState::BuildThen(preceding_len, iter.collect())); - state_stack.push(TraversalState::Term(then_term)); + self.probe_in_situ_var(term_loc, var_num); + build_stack.push(QueryTerm::GetCutPoint(var_num)); } - TraversalState::BuildThen(preceding_len, if_terms) => { - let iter = build_stack.drain(preceding_len ..); - build_stack.push(QueryTerm::IfThen(if_terms, iter.collect())); + TraversalState::LocalCut(var_num) => { + let term_loc = chunk_type.to_gen_context(self.current_chunk_num); + + self.probe_in_situ_var(term_loc, var_num); + build_stack.push(QueryTerm::LocalCut(var_num)); } - TraversalState::BuildNot(preceding_len) => { - let iter = build_stack.drain(preceding_len ..); - build_stack.push(QueryTerm::Not(iter.collect())); + TraversalState::Fail => { + build_stack.push(QueryTerm::Fail); } TraversalState::Term(term) => { match term { @@ -567,17 +592,14 @@ impl VariableClassifier { let then_term = terms.pop().unwrap(); let if_term = terms.pop().unwrap(); - let build_stack_len = build_stack.len(); - - // TODO: insert GetCutPoint between - // the two traversal states and detect - // that as a chunk boundary in - // insert_set_last_chunk_type ?? - - let iter = vec![TraversalState::BuildIf(build_stack_len, then_term), - TraversalState::Term(if_term)] + let iter = vec![TraversalState::Term(then_term), + TraversalState::LocalCut(self.var_num), + TraversalState::Term(if_term), + TraversalState::GetCutPoint(self.var_num)] .into_iter(); + self.var_num += 1; + if ChunkType::Mid != chunk_type { if insert_set_last_chunk_type(&mut state_stack, iter) { if chunk_type.is_last() { @@ -587,10 +609,12 @@ impl VariableClassifier { } } Term::Clause(_, atom!("\\+"), terms) if terms.len() == 1 => { - let build_stack_len = build_stack.len(); - - state_stack.push(TraversalState::BuildNot(build_stack_len)); + state_stack.push(TraversalState::Fail); + state_stack.push(TraversalState::LocalCut(self.var_num)); state_stack.push(TraversalState::Term(terms[0])); + state_stack.push(TraversalState::GetCutPoint(self.var_num)); + + self.var_num += 1; } Term::Clause(_, atom!(":"), mut terms) if terms.len() == 2 => { let term_loc = chunk_type.to_gen_context(self.current_chunk_num); @@ -692,7 +716,7 @@ impl VariableClassifier { ); } Term::Literal(_, Literal::Atom(atom!("!")) | Literal::Char('!')) => { - build_stack.push(QueryTerm::Cut); + build_stack.push(QueryTerm::GlobalCut); } Term::Literal(cell, Literal::Atom(name)) => { if !ClauseType::is_inbuilt(name, 0) { @@ -722,43 +746,46 @@ impl VariableClassifier { } impl BranchMap { - pub fn separate_and_classify_variables(&mut self) -> VarData { - let mut var_num = 0usize; + pub fn separate_and_classify_variables(&mut self, mut var_num: usize) -> VarData { let mut var_data = VarData { - records: vec![], + records: vec![VarRecord::default(); self.len()], fixtures: VariableFixtures::new(), }; - for branches in self.values_mut() { + for (var, branches) in self.iter_mut() { for branch in branches.iter_mut() { let mut num_occurrences = 0; - let mut chunk_occurrences = vec![]; - let classification = if branch.chunks.len() > 1 { - VarClassification::Perm + let idx = if let Var::Generated(var_num) = var { + *var_num } else { - branch.chunks - .first() - .map(|chunk| if chunk.vars.len() > 1 { - VarClassification::Temp - } else { - VarClassification::Void - }) - .unwrap_or(VarClassification::Void) + var_num += 1; + var_num - 1 }; + var_data.records[idx].classification = + if branch.chunks.len() > 1 { + VarClassification::Perm + } else { + branch.chunks + .first() + .map(|chunk| if chunk.vars.len() > 1 { + VarClassification::Temp + } else { + VarClassification::Void + }) + .unwrap_or(VarClassification::Void) + }; + + var_data.records[idx].chunk_occurrences.reserve(branch.chunks.len()); + for chunk in branch.chunks.iter_mut() { - num_occurrences += chunk.vars.len(); + var_data.records[idx].num_occurrences += chunk.vars.len(); if let VarClassification::Temp = classification { for var_info in chunk.vars.iter_mut() { var_info.var_ptr.set(Var::Generated(var_num)); - var_data.fixtures.mark_temp_var( - var_num, - var_info.lvl, - &var_info.classify_info, - chunk.term_loc, - ); + var_data.fixtures.mark_temp_var(&var_info); } } else { for var_info in chunk.vars.iter_mut() { @@ -766,13 +793,8 @@ impl BranchMap { } } - chunk_occurrences.push(chunk.chunk_num); + var_data.records[idx].chunk_occurrences.push(chunk.chunk_num); } - - let record = VarRecord { classification, chunk_occurrences, num_occurrences }; - var_data.records.push(record); - - var_num += 1; } } From 0e583d620ab4ad95e55371905482c65dc5431bc9 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 17 Jun 2023 16:28:56 -0600 Subject: [PATCH 199/361] implement new disjunction compilation --- Cargo.lock | 101 ++- Cargo.toml | 1 + build/instructions_template.rs | 1264 ++++++++++++++++---------------- src/allocator.rs | 40 +- src/arithmetic.rs | 33 +- src/codegen.rs | 823 ++++++++++----------- src/debray_allocator.rs | 552 +++++++++++--- src/fixtures.rs | 342 --------- src/forms.rs | 144 +++- src/heap_iter.rs | 1 - src/heap_print.rs | 8 +- src/iterators.rs | 359 +++------ src/lib.rs | 2 +- src/lib/builtins.pl | 4 +- src/lib/format.pl | 2 + src/loader.pl | 4 +- src/machine/code_walker.rs | 6 +- src/machine/compile.rs | 82 +-- src/machine/disjuncts.rs | 648 ++++++++-------- src/machine/dispatch.rs | 1124 ++++++++++++++-------------- src/machine/load_state.rs | 6 +- src/machine/loader.rs | 2 +- src/machine/machine_indices.rs | 5 +- src/machine/machine_state.rs | 12 +- src/machine/mod.rs | 86 +-- src/machine/preprocessor.rs | 61 +- src/machine/system_calls.rs | 10 +- src/macros.rs | 18 +- src/parser/ast.rs | 86 ++- src/parser/parser.rs | 2 +- src/read.rs | 6 +- src/targets.rs | 23 +- 32 files changed, 2877 insertions(+), 2980 deletions(-) delete mode 100644 src/fixtures.rs diff --git a/Cargo.lock b/Cargo.lock index 542afd09..8b4fc8a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,6 +79,18 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake2" version = "0.8.1" @@ -536,6 +548,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futf" version = "0.1.5" @@ -1526,6 +1544,12 @@ dependencies = [ "proc-macro2 1.0.47", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "radix_trie" version = "0.2.1" @@ -1856,6 +1880,7 @@ dependencies = [ "assert_cmd", "base64", "bit-set", + "bitvec", "blake2 0.8.1", "chrono", "cpu-time", @@ -2203,6 +2228,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.3.0" @@ -2592,21 +2623,6 @@ dependencies = [ "windows_x86_64_msvc 0.36.1", ] -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", -] - [[package]] name = "windows-sys" version = "0.42.0" @@ -2628,24 +2644,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" -[[package]] -name = "windows_aarch64_msvc" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" - [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" - [[package]] name = "windows_aarch64_msvc" version = "0.42.1" @@ -2658,12 +2662,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" -[[package]] -name = "windows_i686_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" - [[package]] name = "windows_i686_gnu" version = "0.42.1" @@ -2676,12 +2674,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" -[[package]] -name = "windows_i686_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" - [[package]] name = "windows_i686_msvc" version = "0.42.1" @@ -2694,18 +2686,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" - [[package]] name = "windows_x86_64_gnu" version = "0.42.1" @@ -2714,9 +2694,9 @@ checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" [[package]] name = "windows_x86_64_msvc" @@ -2724,18 +2704,21 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" - [[package]] name = "windows_x86_64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "xmlparser" version = "0.13.5" diff --git a/Cargo.toml b/Cargo.toml index f358126a..1f4fa7aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ walkdir = "2" [dependencies] bit-set = "0.5.3" +bitvec = "1" cpu-time = "1.0.0" crossterm = "0.20.0" dirs-next = "2.0.0" diff --git a/build/instructions_template.rs b/build/instructions_template.rs index d0a8c4d0..25036607 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -639,6 +639,10 @@ enum InstructionTemplate { Cut(RegType), #[strum_discriminants(strum(props(Arity = "1", Name = "get_level")))] GetLevel(RegType), + #[strum_discriminants(strum(props(Arity = "1", Name = "get_prev_level")))] + GetPrevLevel(RegType), + #[strum_discriminants(strum(props(Arity = "1", Name = "get_cut_point")))] + GetCutPoint(RegType), #[strum_discriminants(strum(props(Arity = "0", Name = "neck_cut")))] NeckCut, // choice instruction @@ -740,10 +744,8 @@ enum InstructionTemplate { Allocate(usize), // num_frames. #[strum_discriminants(strum(props(Arity = "0", Name = "deallocate")))] Deallocate, - #[strum_discriminants(strum(props(Arity = "arity", Name = "jmp_by_call")))] - JmpByCall(usize, usize), // arity, relative offset. - #[strum_discriminants(strum(props(Arity = "arity", Name = "jmp_by_execute")))] - JmpByExecute(usize, usize), // arity, relative offset. + #[strum_discriminants(strum(props(Arity = "1", Name = "jmp_by_call")))] + JmpByCall(usize), // relative offset. #[strum_discriminants(strum(props(Arity = "1", Name = "rev_jmp_by")))] RevJmpBy(usize), #[strum_discriminants(strum(props(Arity = "0", Name = "proceed")))] @@ -1114,6 +1116,7 @@ fn generate_instruction_preface() -> TokenStream { } pub type Code = Vec; + pub type CodeDeque = VecDeque; impl Instruction { #[inline] @@ -1296,6 +1299,14 @@ fn generate_instruction_preface() -> TokenStream { let rt_stub = reg_type_into_functor(r); functor!(atom!("get_level"), [str(h, 0)], [rt_stub]) } + &Instruction::GetPrevLevel(r) => { + let rt_stub = reg_type_into_functor(r); + functor!(atom!("get_prev_level"), [str(h, 0)], [rt_stub]) + } + &Instruction::GetCutPoint(r) => { + let rt_stub = reg_type_into_functor(r); + functor!(atom!("get_cut_point"), [str(h, 0)], [rt_stub]) + } &Instruction::NeckCut => { functor!(atom!("neck_cut")) } @@ -1449,30 +1460,30 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::DefaultExecuteNamed(arity, name, ..) => { functor!(atom!("execute_default"), [atom(name), fixnum(arity)]) } - &Instruction::CallN(arity, _) => { + &Instruction::CallN(arity) => { functor!(atom!("call_n"), [fixnum(arity)]) } - &Instruction::ExecuteN(arity, _) => { + &Instruction::ExecuteN(arity) => { functor!(atom!("execute_n"), [fixnum(arity)]) } - &Instruction::DefaultCallN(arity, _) => { + &Instruction::DefaultCallN(arity) => { functor!(atom!("call_default_n"), [fixnum(arity)]) } - &Instruction::DefaultExecuteN(arity, _) => { + &Instruction::DefaultExecuteN(arity) => { functor!(atom!("execute_default_n"), [fixnum(arity)]) } - &Instruction::CallInlineCallN(arity, _) => { + &Instruction::CallInlineCallN(arity) => { functor!(atom!("call_n_inline"), [fixnum(arity)]) } - &Instruction::ExecuteInlineCallN(arity, _) => { + &Instruction::ExecuteInlineCallN(arity) => { functor!(atom!("call_n_inline"), [fixnum(arity)]) } - &Instruction::CallTermGreaterThan(_) | - &Instruction::CallTermLessThan(_) | - &Instruction::CallTermGreaterThanOrEqual(_) | - &Instruction::CallTermLessThanOrEqual(_) | - &Instruction::CallTermEqual(_) | - &Instruction::CallTermNotEqual(_) | + &Instruction::CallTermGreaterThan | + &Instruction::CallTermLessThan | + &Instruction::CallTermGreaterThanOrEqual | + &Instruction::CallTermLessThanOrEqual | + &Instruction::CallTermEqual | + &Instruction::CallTermNotEqual | &Instruction::CallNumberGreaterThan(..) | &Instruction::CallNumberLessThan(..) | &Instruction::CallNumberGreaterThanOrEqual(..) | @@ -1480,563 +1491,561 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallNumberEqual(..) | &Instruction::CallNumberNotEqual(..) | &Instruction::CallIs(..) | - &Instruction::CallAcyclicTerm(_) | - &Instruction::CallArg(_) | - &Instruction::CallCompare(_) | - &Instruction::CallCopyTerm(_) | - &Instruction::CallFunctor(_) | - &Instruction::CallGround(_) | - &Instruction::CallKeySort(_) | - &Instruction::CallRead(_) | - &Instruction::CallSort(_) => { + &Instruction::CallAcyclicTerm | + &Instruction::CallArg | + &Instruction::CallCompare | + &Instruction::CallCopyTerm | + &Instruction::CallFunctor | + &Instruction::CallGround | + &Instruction::CallKeySort | + &Instruction::CallRead | + &Instruction::CallSort => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("call"), [atom(name), fixnum(arity)]) } // - &Instruction::ExecuteTermGreaterThan(_) | - &Instruction::ExecuteTermLessThan(_) | - &Instruction::ExecuteTermGreaterThanOrEqual(_) | - &Instruction::ExecuteTermLessThanOrEqual(_) | - &Instruction::ExecuteTermEqual(_) | - &Instruction::ExecuteTermNotEqual(_) | + &Instruction::ExecuteTermGreaterThan | + &Instruction::ExecuteTermLessThan | + &Instruction::ExecuteTermGreaterThanOrEqual | + &Instruction::ExecuteTermLessThanOrEqual | + &Instruction::ExecuteTermEqual | + &Instruction::ExecuteTermNotEqual | &Instruction::ExecuteNumberGreaterThan(..) | &Instruction::ExecuteNumberLessThan(..) | &Instruction::ExecuteNumberGreaterThanOrEqual(..) | &Instruction::ExecuteNumberLessThanOrEqual(..) | &Instruction::ExecuteNumberEqual(..) | &Instruction::ExecuteNumberNotEqual(..) | - &Instruction::ExecuteAcyclicTerm(_) | - &Instruction::ExecuteArg(_) | - &Instruction::ExecuteCompare(_) | - &Instruction::ExecuteCopyTerm(_) | - &Instruction::ExecuteFunctor(_) | - &Instruction::ExecuteGround(_) | + &Instruction::ExecuteAcyclicTerm | + &Instruction::ExecuteArg | + &Instruction::ExecuteCompare | + &Instruction::ExecuteCopyTerm | + &Instruction::ExecuteFunctor | + &Instruction::ExecuteGround | &Instruction::ExecuteIs(..) | - &Instruction::ExecuteKeySort(_) | - &Instruction::ExecuteRead(_) | - &Instruction::ExecuteSort(_) => { + &Instruction::ExecuteKeySort | + &Instruction::ExecuteRead | + &Instruction::ExecuteSort => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("execute"), [atom(name), fixnum(arity)]) } // - &Instruction::DefaultCallTermGreaterThan(_) | - &Instruction::DefaultCallTermLessThan(_) | - &Instruction::DefaultCallTermGreaterThanOrEqual(_) | - &Instruction::DefaultCallTermLessThanOrEqual(_) | - &Instruction::DefaultCallTermEqual(_) | - &Instruction::DefaultCallTermNotEqual(_) | + &Instruction::DefaultCallTermGreaterThan | + &Instruction::DefaultCallTermLessThan | + &Instruction::DefaultCallTermGreaterThanOrEqual | + &Instruction::DefaultCallTermLessThanOrEqual | + &Instruction::DefaultCallTermEqual | + &Instruction::DefaultCallTermNotEqual | &Instruction::DefaultCallNumberGreaterThan(..) | &Instruction::DefaultCallNumberLessThan(..) | &Instruction::DefaultCallNumberGreaterThanOrEqual(..) | &Instruction::DefaultCallNumberLessThanOrEqual(..) | &Instruction::DefaultCallNumberEqual(..) | &Instruction::DefaultCallNumberNotEqual(..) | - &Instruction::DefaultCallAcyclicTerm(_) | - &Instruction::DefaultCallArg(_) | - &Instruction::DefaultCallCompare(_) | - &Instruction::DefaultCallCopyTerm(_) | - &Instruction::DefaultCallFunctor(_) | - &Instruction::DefaultCallGround(_) | + &Instruction::DefaultCallAcyclicTerm | + &Instruction::DefaultCallArg | + &Instruction::DefaultCallCompare | + &Instruction::DefaultCallCopyTerm | + &Instruction::DefaultCallFunctor | + &Instruction::DefaultCallGround | &Instruction::DefaultCallIs(..) | - &Instruction::DefaultCallKeySort(_) | - &Instruction::DefaultCallRead(_) | - &Instruction::DefaultCallSort(_) => { + &Instruction::DefaultCallKeySort | + &Instruction::DefaultCallRead | + &Instruction::DefaultCallSort => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("call_default"), [atom(name), fixnum(arity)]) } // - &Instruction::DefaultExecuteTermGreaterThan(_) | - &Instruction::DefaultExecuteTermLessThan(_) | - &Instruction::DefaultExecuteTermGreaterThanOrEqual(_) | - &Instruction::DefaultExecuteTermLessThanOrEqual(_) | - &Instruction::DefaultExecuteTermEqual(_) | - &Instruction::DefaultExecuteTermNotEqual(_) | + &Instruction::DefaultExecuteTermGreaterThan | + &Instruction::DefaultExecuteTermLessThan | + &Instruction::DefaultExecuteTermGreaterThanOrEqual | + &Instruction::DefaultExecuteTermLessThanOrEqual | + &Instruction::DefaultExecuteTermEqual | + &Instruction::DefaultExecuteTermNotEqual | &Instruction::DefaultExecuteNumberGreaterThan(..) | &Instruction::DefaultExecuteNumberLessThan(..) | &Instruction::DefaultExecuteNumberGreaterThanOrEqual(..) | &Instruction::DefaultExecuteNumberLessThanOrEqual(..) | &Instruction::DefaultExecuteNumberEqual(..) | &Instruction::DefaultExecuteNumberNotEqual(..) | - &Instruction::DefaultExecuteAcyclicTerm(_) | - &Instruction::DefaultExecuteArg(_) | - &Instruction::DefaultExecuteCompare(_) | - &Instruction::DefaultExecuteCopyTerm(_) | - &Instruction::DefaultExecuteFunctor(_) | - &Instruction::DefaultExecuteGround(_) | + &Instruction::DefaultExecuteAcyclicTerm | + &Instruction::DefaultExecuteArg | + &Instruction::DefaultExecuteCompare | + &Instruction::DefaultExecuteCopyTerm | + &Instruction::DefaultExecuteFunctor | + &Instruction::DefaultExecuteGround | &Instruction::DefaultExecuteIs(..) | - &Instruction::DefaultExecuteKeySort(_) | - &Instruction::DefaultExecuteRead(_) | - &Instruction::DefaultExecuteSort(_) => { + &Instruction::DefaultExecuteKeySort | + &Instruction::DefaultExecuteRead | + &Instruction::DefaultExecuteSort => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("execute_default"), [atom(name), fixnum(arity)]) } - &Instruction::CallIsAtom(_, _) | - &Instruction::CallIsAtomic(_, _) | - &Instruction::CallIsCompound(_, _) | - &Instruction::CallIsInteger(_, _) | - &Instruction::CallIsNumber(_, _) | - &Instruction::CallIsRational(_, _) | - &Instruction::CallIsFloat(_, _) | - &Instruction::CallIsNonVar(_, _) | - &Instruction::CallIsVar(_, _) => { + &Instruction::CallIsAtom(_) | + &Instruction::CallIsAtomic(_) | + &Instruction::CallIsCompound(_) | + &Instruction::CallIsInteger(_) | + &Instruction::CallIsNumber(_) | + &Instruction::CallIsRational(_) | + &Instruction::CallIsFloat(_) | + &Instruction::CallIsNonVar(_) | + &Instruction::CallIsVar(_) => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("call"), [atom(name), fixnum(arity)]) } - &Instruction::ExecuteIsAtom(_, _) | - &Instruction::ExecuteIsAtomic(_, _) | - &Instruction::ExecuteIsCompound(_, _) | - &Instruction::ExecuteIsInteger(_, _) | - &Instruction::ExecuteIsNumber(_, _) | - &Instruction::ExecuteIsRational(_, _) | - &Instruction::ExecuteIsFloat(_, _) | - &Instruction::ExecuteIsNonVar(_, _) | - &Instruction::ExecuteIsVar(_, _) => { + &Instruction::ExecuteIsAtom(_) | + &Instruction::ExecuteIsAtomic(_) | + &Instruction::ExecuteIsCompound(_) | + &Instruction::ExecuteIsInteger(_) | + &Instruction::ExecuteIsNumber(_) | + &Instruction::ExecuteIsRational(_) | + &Instruction::ExecuteIsFloat(_) | + &Instruction::ExecuteIsNonVar(_) | + &Instruction::ExecuteIsVar(_) => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("execute"), [atom(name), fixnum(arity)]) } // - &Instruction::CallAtomChars(_) | - &Instruction::CallAtomCodes(_) | - &Instruction::CallAtomLength(_) | - &Instruction::CallBindFromRegister(_) | - &Instruction::CallContinuation(_) | - &Instruction::CallCharCode(_) | - &Instruction::CallCharType(_) | - &Instruction::CallCharsToNumber(_) | - &Instruction::CallCodesToNumber(_) | - &Instruction::CallCopyTermWithoutAttrVars(_) | - &Instruction::CallCheckCutPoint(_) | - &Instruction::CallClose(_) | - &Instruction::CallCopyToLiftedHeap(_) | - &Instruction::CallCreatePartialString(_) | - &Instruction::CallCurrentHostname(_) | - &Instruction::CallCurrentInput(_) | - &Instruction::CallCurrentOutput(_) | - &Instruction::CallDirectoryFiles(_) | - &Instruction::CallFileSize(_) | - &Instruction::CallFileExists(_) | - &Instruction::CallDirectoryExists(_) | - &Instruction::CallDirectorySeparator(_) | - &Instruction::CallMakeDirectory(_) | - &Instruction::CallMakeDirectoryPath(_) | - &Instruction::CallDeleteFile(_) | - &Instruction::CallRenameFile(_) | - &Instruction::CallFileCopy(_) | - &Instruction::CallWorkingDirectory(_) | - &Instruction::CallDeleteDirectory(_) | - &Instruction::CallPathCanonical(_) | - &Instruction::CallFileTime(_) | + &Instruction::CallAtomChars | + &Instruction::CallAtomCodes | + &Instruction::CallAtomLength | + &Instruction::CallBindFromRegister | + &Instruction::CallContinuation | + &Instruction::CallCharCode | + &Instruction::CallCharType | + &Instruction::CallCharsToNumber | + &Instruction::CallCodesToNumber | + &Instruction::CallCopyTermWithoutAttrVars | + &Instruction::CallCheckCutPoint | + &Instruction::CallClose | + &Instruction::CallCopyToLiftedHeap | + &Instruction::CallCreatePartialString | + &Instruction::CallCurrentHostname | + &Instruction::CallCurrentInput | + &Instruction::CallCurrentOutput | + &Instruction::CallDirectoryFiles | + &Instruction::CallFileSize | + &Instruction::CallFileExists | + &Instruction::CallDirectoryExists | + &Instruction::CallDirectorySeparator | + &Instruction::CallMakeDirectory | + &Instruction::CallMakeDirectoryPath | + &Instruction::CallDeleteFile | + &Instruction::CallRenameFile | + &Instruction::CallFileCopy | + &Instruction::CallWorkingDirectory | + &Instruction::CallDeleteDirectory | + &Instruction::CallPathCanonical | + &Instruction::CallFileTime | &Instruction::CallDynamicModuleResolution(..) | &Instruction::CallPrepareCallClause(..) | - &Instruction::CallCompileInlineOrExpandedGoal(..) | - &Instruction::CallIsExpandedOrInlined(_) | - &Instruction::CallGetClauseP(_) | - &Instruction::CallInvokeClauseAtP(_) | - &Instruction::CallGetFromAttributedVarList(_) | - &Instruction::CallPutToAttributedVarList(_) | - &Instruction::CallDeleteFromAttributedVarList(_) | - &Instruction::CallDeleteAllAttributesFromVar(_) | - &Instruction::CallUnattributedVar(_) | - &Instruction::CallGetDBRefs(_) | - &Instruction::CallEnqueueAttributedVar(_) | - &Instruction::CallFetchGlobalVar(_) | - &Instruction::CallFirstStream(_) | - &Instruction::CallFlushOutput(_) | - &Instruction::CallGetByte(_) | - &Instruction::CallGetChar(_) | - &Instruction::CallGetNChars(_) | - &Instruction::CallGetCode(_) | - &Instruction::CallGetSingleChar(_) | - &Instruction::CallTruncateIfNoLiftedHeapGrowthDiff(_) | - &Instruction::CallTruncateIfNoLiftedHeapGrowth(_) | - &Instruction::CallGetAttributedVariableList(_) | - &Instruction::CallGetAttrVarQueueDelimiter(_) | - &Instruction::CallGetAttrVarQueueBeyond(_) | - &Instruction::CallGetBValue(_) | - &Instruction::CallGetContinuationChunk(_) | - &Instruction::CallGetNextOpDBRef(_) | - &Instruction::CallLookupDBRef(_) | - &Instruction::CallIsPartialString(_) | - &Instruction::CallHalt(_) | - &Instruction::CallGetLiftedHeapFromOffset(_) | - &Instruction::CallGetLiftedHeapFromOffsetDiff(_) | - &Instruction::CallGetSCCCleaner(_) | - &Instruction::CallHeadIsDynamic(_) | - &Instruction::CallInstallSCCCleaner(_) | - &Instruction::CallInstallInferenceCounter(_) | - &Instruction::CallLiftedHeapLength(_) | - &Instruction::CallLoadLibraryAsStream(_) | - &Instruction::CallModuleExists(_) | - &Instruction::CallNextEP(_) | - &Instruction::CallNoSuchPredicate(_) | - &Instruction::CallNumberToChars(_) | - &Instruction::CallNumberToCodes(_) | - &Instruction::CallOpDeclaration(_) | - &Instruction::CallOpen(_) | - &Instruction::CallSetStreamOptions(_) | - &Instruction::CallNextStream(_) | - &Instruction::CallPartialStringTail(_) | - &Instruction::CallPeekByte(_) | - &Instruction::CallPeekChar(_) | - &Instruction::CallPeekCode(_) | - &Instruction::CallPointsToContinuationResetMarker(_) | - &Instruction::CallPutByte(_) | - &Instruction::CallPutChar(_) | - &Instruction::CallPutChars(_) | - &Instruction::CallPutCode(_) | - &Instruction::CallReadQueryTerm(_) | - &Instruction::CallReadTerm(_) | - &Instruction::CallRedoAttrVarBinding(_) | - &Instruction::CallRemoveCallPolicyCheck(_) | - &Instruction::CallRemoveInferenceCounter(_) | - &Instruction::CallResetContinuationMarker(_) | - &Instruction::CallRestoreCutPolicy(_) | + &Instruction::CallCompileInlineOrExpandedGoal | + &Instruction::CallIsExpandedOrInlined | + &Instruction::CallGetClauseP | + &Instruction::CallInvokeClauseAtP | + &Instruction::CallGetFromAttributedVarList | + &Instruction::CallPutToAttributedVarList | + &Instruction::CallDeleteFromAttributedVarList | + &Instruction::CallDeleteAllAttributesFromVar | + &Instruction::CallUnattributedVar | + &Instruction::CallGetDBRefs | + &Instruction::CallFetchGlobalVar | + &Instruction::CallFirstStream | + &Instruction::CallFlushOutput | + &Instruction::CallGetByte | + &Instruction::CallGetChar | + &Instruction::CallGetNChars | + &Instruction::CallGetCode | + &Instruction::CallGetSingleChar | + &Instruction::CallTruncateIfNoLiftedHeapGrowthDiff | + &Instruction::CallTruncateIfNoLiftedHeapGrowth | + &Instruction::CallGetAttributedVariableList | + &Instruction::CallGetAttrVarQueueDelimiter | + &Instruction::CallGetAttrVarQueueBeyond | + &Instruction::CallGetBValue | + &Instruction::CallGetContinuationChunk | + &Instruction::CallGetNextOpDBRef | + &Instruction::CallLookupDBRef | + &Instruction::CallIsPartialString | + &Instruction::CallHalt | + &Instruction::CallGetLiftedHeapFromOffset | + &Instruction::CallGetLiftedHeapFromOffsetDiff | + &Instruction::CallGetSCCCleaner | + &Instruction::CallHeadIsDynamic | + &Instruction::CallInstallSCCCleaner | + &Instruction::CallInstallInferenceCounter | + &Instruction::CallLiftedHeapLength | + &Instruction::CallLoadLibraryAsStream | + &Instruction::CallModuleExists | + &Instruction::CallNextEP | + &Instruction::CallNoSuchPredicate | + &Instruction::CallNumberToChars | + &Instruction::CallNumberToCodes | + &Instruction::CallOpDeclaration | + &Instruction::CallOpen | + &Instruction::CallSetStreamOptions | + &Instruction::CallNextStream | + &Instruction::CallPartialStringTail | + &Instruction::CallPeekByte | + &Instruction::CallPeekChar | + &Instruction::CallPeekCode | + &Instruction::CallPointsToContinuationResetMarker | + &Instruction::CallPutByte | + &Instruction::CallPutChar | + &Instruction::CallPutChars | + &Instruction::CallPutCode | + &Instruction::CallReadQueryTerm | + &Instruction::CallReadTerm | + &Instruction::CallRedoAttrVarBinding | + &Instruction::CallRemoveCallPolicyCheck | + &Instruction::CallRemoveInferenceCounter | + &Instruction::CallResetContinuationMarker | + &Instruction::CallRestoreCutPolicy | &Instruction::CallSetCutPoint(..) | - &Instruction::CallSetInput(_) | - &Instruction::CallSetOutput(_) | - &Instruction::CallStoreBacktrackableGlobalVar(_) | - &Instruction::CallStoreGlobalVar(_) | - &Instruction::CallStreamProperty(_) | - &Instruction::CallSetStreamPosition(_) | - &Instruction::CallInferenceLevel(_) | - &Instruction::CallCleanUpBlock(_) | - &Instruction::CallFail(_) | - &Instruction::CallGetBall(_) | - &Instruction::CallGetCurrentBlock(_) | - &Instruction::CallGetCutPoint(_) | - &Instruction::CallGetDoubleQuotes(_) | - &Instruction::CallInstallNewBlock(_) | - &Instruction::CallMaybe(_) | - &Instruction::CallCpuNow(_) | - &Instruction::CallDeterministicLengthRundown(_) | - &Instruction::CallHttpOpen(_) | - &Instruction::CallHttpListen(_) | - &Instruction::CallHttpAccept(_) | - &Instruction::CallHttpAnswer(_) | - &Instruction::CallLoadForeignLib(_) | - &Instruction::CallForeignCall(_) | - &Instruction::CallDefineForeignStruct(_) | - &Instruction::CallPredicateDefined(_) | - &Instruction::CallStripModule(_) | - &Instruction::CallCurrentTime(_) | - &Instruction::CallQuotedToken(_) | - &Instruction::CallReadTermFromChars(_) | - &Instruction::CallResetBlock(_) | - &Instruction::CallReturnFromVerifyAttr(_) | - &Instruction::CallSetBall(_) | - &Instruction::CallPushBallStack(_) | - &Instruction::CallPopBallStack(_) | - &Instruction::CallPopFromBallStack(_) | + &Instruction::CallSetInput | + &Instruction::CallSetOutput | + &Instruction::CallStoreBacktrackableGlobalVar | + &Instruction::CallStoreGlobalVar | + &Instruction::CallStreamProperty | + &Instruction::CallSetStreamPosition | + &Instruction::CallInferenceLevel | + &Instruction::CallCleanUpBlock | + &Instruction::CallFail | + &Instruction::CallGetBall | + &Instruction::CallGetCurrentBlock | + &Instruction::CallGetCutPoint | + &Instruction::CallGetDoubleQuotes | + &Instruction::CallInstallNewBlock | + &Instruction::CallMaybe | + &Instruction::CallCpuNow | + &Instruction::CallDeterministicLengthRundown | + &Instruction::CallHttpOpen | + &Instruction::CallHttpListen | + &Instruction::CallHttpAccept | + &Instruction::CallHttpAnswer | + &Instruction::CallLoadForeignLib | + &Instruction::CallForeignCall | + &Instruction::CallDefineForeignStruct | + &Instruction::CallPredicateDefined | + &Instruction::CallStripModule | + &Instruction::CallCurrentTime | + &Instruction::CallQuotedToken | + &Instruction::CallReadTermFromChars | + &Instruction::CallResetBlock | + &Instruction::CallReturnFromVerifyAttr | + &Instruction::CallSetBall | + &Instruction::CallPushBallStack | + &Instruction::CallPopBallStack | + &Instruction::CallPopFromBallStack | &Instruction::CallSetCutPointByDefault(..) | - &Instruction::CallSetDoubleQuotes(_) | - &Instruction::CallSetSeed(_) | - &Instruction::CallSkipMaxList(_) | - &Instruction::CallSleep(_) | - &Instruction::CallSocketClientOpen(_) | - &Instruction::CallSocketServerOpen(_) | - &Instruction::CallSocketServerAccept(_) | - &Instruction::CallSocketServerClose(_) | - &Instruction::CallTLSAcceptClient(_) | - &Instruction::CallTLSClientConnect(_) | - &Instruction::CallSucceed(_) | - &Instruction::CallTermAttributedVariables(_) | - &Instruction::CallTermVariables(_) | - &Instruction::CallTermVariablesUnderMaxDepth(_) | - &Instruction::CallTruncateLiftedHeapTo(_) | - &Instruction::CallUnifyWithOccursCheck(_) | - &Instruction::CallUnwindEnvironments(_) | - &Instruction::CallUnwindStack(_) | - &Instruction::CallWAMInstructions(_) | - &Instruction::CallInlinedInstructions(_) | - &Instruction::CallWriteTerm(_) | - &Instruction::CallWriteTermToChars(_) | - &Instruction::CallScryerPrologVersion(_) | - &Instruction::CallCryptoRandomByte(_) | - &Instruction::CallCryptoDataHash(_) | - &Instruction::CallCryptoDataHKDF(_) | - &Instruction::CallCryptoPasswordHash(_) | - &Instruction::CallCryptoDataEncrypt(_) | - &Instruction::CallCryptoDataDecrypt(_) | - &Instruction::CallCryptoCurveScalarMult(_) | - &Instruction::CallEd25519Sign(_) | - &Instruction::CallEd25519Verify(_) | - &Instruction::CallEd25519NewKeyPair(_) | - &Instruction::CallEd25519KeyPairPublicKey(_) | - &Instruction::CallCurve25519ScalarMult(_) | - &Instruction::CallFirstNonOctet(_) | - &Instruction::CallLoadHTML(_) | - &Instruction::CallLoadXML(_) | - &Instruction::CallGetEnv(_) | - &Instruction::CallSetEnv(_) | - &Instruction::CallUnsetEnv(_) | - &Instruction::CallShell(_) | - &Instruction::CallPID(_) | - &Instruction::CallCharsBase64(_) | - &Instruction::CallDevourWhitespace(_) | - &Instruction::CallIsSTOEnabled(_) | - &Instruction::CallSetSTOAsUnify(_) | - &Instruction::CallSetNSTOAsUnify(_) | - &Instruction::CallSetSTOWithErrorAsUnify(_) | - &Instruction::CallHomeDirectory(_) | - &Instruction::CallDebugHook(_) | - &Instruction::CallAddDiscontiguousPredicate(_) | - &Instruction::CallAddDynamicPredicate(_) | - &Instruction::CallAddMultifilePredicate(_) | - &Instruction::CallAddGoalExpansionClause(_) | - &Instruction::CallAddTermExpansionClause(_) | - &Instruction::CallAddInSituFilenameModule(_) | - &Instruction::CallClauseToEvacuable(_) | - &Instruction::CallScopedClauseToEvacuable(_) | - &Instruction::CallConcludeLoad(_) | - &Instruction::CallDeclareModule(_) | - &Instruction::CallLoadCompiledLibrary(_) | - &Instruction::CallLoadContextSource(_) | - &Instruction::CallLoadContextFile(_) | - &Instruction::CallLoadContextDirectory(_) | - &Instruction::CallLoadContextModule(_) | - &Instruction::CallLoadContextStream(_) | - &Instruction::CallPopLoadContext(_) | - &Instruction::CallPopLoadStatePayload(_) | - &Instruction::CallPushLoadContext(_) | - &Instruction::CallPushLoadStatePayload(_) | - &Instruction::CallUseModule(_) | - &Instruction::CallBuiltInProperty(_) | - &Instruction::CallMetaPredicateProperty(_) | - &Instruction::CallMultifileProperty(_) | - &Instruction::CallDiscontiguousProperty(_) | - &Instruction::CallDynamicProperty(_) | - &Instruction::CallAbolishClause(_) | - &Instruction::CallAsserta(_) | - &Instruction::CallAssertz(_) | - &Instruction::CallRetract(_) | - &Instruction::CallIsConsistentWithTermQueue(_) | - &Instruction::CallFlushTermQueue(_) | - &Instruction::CallRemoveModuleExports(_) | - &Instruction::CallAddNonCountedBacktracking(_) | - &Instruction::CallPopCount(_) => { + &Instruction::CallSetDoubleQuotes | + &Instruction::CallSetSeed | + &Instruction::CallSkipMaxList | + &Instruction::CallSleep | + &Instruction::CallSocketClientOpen | + &Instruction::CallSocketServerOpen | + &Instruction::CallSocketServerAccept | + &Instruction::CallSocketServerClose | + &Instruction::CallTLSAcceptClient | + &Instruction::CallTLSClientConnect | + &Instruction::CallSucceed | + &Instruction::CallTermAttributedVariables | + &Instruction::CallTermVariables | + &Instruction::CallTermVariablesUnderMaxDepth | + &Instruction::CallTruncateLiftedHeapTo | + &Instruction::CallUnifyWithOccursCheck | + &Instruction::CallUnwindEnvironments | + &Instruction::CallUnwindStack | + &Instruction::CallWAMInstructions | + &Instruction::CallInlinedInstructions | + &Instruction::CallWriteTerm | + &Instruction::CallWriteTermToChars | + &Instruction::CallScryerPrologVersion | + &Instruction::CallCryptoRandomByte | + &Instruction::CallCryptoDataHash | + &Instruction::CallCryptoDataHKDF | + &Instruction::CallCryptoPasswordHash | + &Instruction::CallCryptoDataEncrypt | + &Instruction::CallCryptoDataDecrypt | + &Instruction::CallCryptoCurveScalarMult | + &Instruction::CallEd25519Sign | + &Instruction::CallEd25519Verify | + &Instruction::CallEd25519NewKeyPair | + &Instruction::CallEd25519KeyPairPublicKey | + &Instruction::CallCurve25519ScalarMult | + &Instruction::CallFirstNonOctet | + &Instruction::CallLoadHTML | + &Instruction::CallLoadXML | + &Instruction::CallGetEnv | + &Instruction::CallSetEnv | + &Instruction::CallUnsetEnv | + &Instruction::CallShell | + &Instruction::CallPID | + &Instruction::CallCharsBase64 | + &Instruction::CallDevourWhitespace | + &Instruction::CallIsSTOEnabled | + &Instruction::CallSetSTOAsUnify | + &Instruction::CallSetNSTOAsUnify | + &Instruction::CallSetSTOWithErrorAsUnify | + &Instruction::CallHomeDirectory | + &Instruction::CallDebugHook | + &Instruction::CallAddDiscontiguousPredicate | + &Instruction::CallAddDynamicPredicate | + &Instruction::CallAddMultifilePredicate | + &Instruction::CallAddGoalExpansionClause | + &Instruction::CallAddTermExpansionClause | + &Instruction::CallAddInSituFilenameModule | + &Instruction::CallClauseToEvacuable | + &Instruction::CallScopedClauseToEvacuable | + &Instruction::CallConcludeLoad | + &Instruction::CallDeclareModule | + &Instruction::CallLoadCompiledLibrary | + &Instruction::CallLoadContextSource | + &Instruction::CallLoadContextFile | + &Instruction::CallLoadContextDirectory | + &Instruction::CallLoadContextModule | + &Instruction::CallLoadContextStream | + &Instruction::CallPopLoadContext | + &Instruction::CallPopLoadStatePayload | + &Instruction::CallPushLoadContext | + &Instruction::CallPushLoadStatePayload | + &Instruction::CallUseModule | + &Instruction::CallBuiltInProperty | + &Instruction::CallMetaPredicateProperty | + &Instruction::CallMultifileProperty | + &Instruction::CallDiscontiguousProperty | + &Instruction::CallDynamicProperty | + &Instruction::CallAbolishClause | + &Instruction::CallAsserta | + &Instruction::CallAssertz | + &Instruction::CallRetract | + &Instruction::CallIsConsistentWithTermQueue | + &Instruction::CallFlushTermQueue | + &Instruction::CallRemoveModuleExports | + &Instruction::CallAddNonCountedBacktracking | + &Instruction::CallPopCount => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("call"), [atom(name), fixnum(arity)]) } // - &Instruction::ExecuteAtomChars(_) | - &Instruction::ExecuteAtomCodes(_) | - &Instruction::ExecuteAtomLength(_) | - &Instruction::ExecuteBindFromRegister(_) | - &Instruction::ExecuteContinuation(_) | - &Instruction::ExecuteCharCode(_) | - &Instruction::ExecuteCharType(_) | - &Instruction::ExecuteCharsToNumber(_) | - &Instruction::ExecuteCodesToNumber(_) | - &Instruction::ExecuteCopyTermWithoutAttrVars(_) | - &Instruction::ExecuteCheckCutPoint(_) | - &Instruction::ExecuteClose(_) | - &Instruction::ExecuteCopyToLiftedHeap(_) | - &Instruction::ExecuteCreatePartialString(_) | - &Instruction::ExecuteCurrentHostname(_) | - &Instruction::ExecuteCurrentInput(_) | - &Instruction::ExecuteCurrentOutput(_) | - &Instruction::ExecuteDirectoryFiles(_) | - &Instruction::ExecuteFileSize(_) | - &Instruction::ExecuteFileExists(_) | - &Instruction::ExecuteDirectoryExists(_) | - &Instruction::ExecuteDirectorySeparator(_) | - &Instruction::ExecuteMakeDirectory(_) | - &Instruction::ExecuteMakeDirectoryPath(_) | - &Instruction::ExecuteDeleteFile(_) | - &Instruction::ExecuteRenameFile(_) | - &Instruction::ExecuteFileCopy(_) | - &Instruction::ExecuteWorkingDirectory(_) | - &Instruction::ExecuteDeleteDirectory(_) | - &Instruction::ExecutePathCanonical(_) | - &Instruction::ExecuteFileTime(_) | + &Instruction::ExecuteAtomChars | + &Instruction::ExecuteAtomCodes | + &Instruction::ExecuteAtomLength | + &Instruction::ExecuteBindFromRegister | + &Instruction::ExecuteContinuation | + &Instruction::ExecuteCharCode | + &Instruction::ExecuteCharType | + &Instruction::ExecuteCharsToNumber | + &Instruction::ExecuteCodesToNumber | + &Instruction::ExecuteCopyTermWithoutAttrVars | + &Instruction::ExecuteCheckCutPoint | + &Instruction::ExecuteClose | + &Instruction::ExecuteCopyToLiftedHeap | + &Instruction::ExecuteCreatePartialString | + &Instruction::ExecuteCurrentHostname | + &Instruction::ExecuteCurrentInput | + &Instruction::ExecuteCurrentOutput | + &Instruction::ExecuteDirectoryFiles | + &Instruction::ExecuteFileSize | + &Instruction::ExecuteFileExists | + &Instruction::ExecuteDirectoryExists | + &Instruction::ExecuteDirectorySeparator | + &Instruction::ExecuteMakeDirectory | + &Instruction::ExecuteMakeDirectoryPath | + &Instruction::ExecuteDeleteFile | + &Instruction::ExecuteRenameFile | + &Instruction::ExecuteFileCopy | + &Instruction::ExecuteWorkingDirectory | + &Instruction::ExecuteDeleteDirectory | + &Instruction::ExecutePathCanonical | + &Instruction::ExecuteFileTime | &Instruction::ExecuteDynamicModuleResolution(..) | &Instruction::ExecutePrepareCallClause(..) | - &Instruction::ExecuteCompileInlineOrExpandedGoal(..) | - &Instruction::ExecuteIsExpandedOrInlined(_) | - &Instruction::ExecuteGetClauseP(_) | - &Instruction::ExecuteInvokeClauseAtP(_) | - &Instruction::ExecuteGetFromAttributedVarList(_) | - &Instruction::ExecutePutToAttributedVarList(_) | - &Instruction::ExecuteDeleteFromAttributedVarList(_) | - &Instruction::ExecuteDeleteAllAttributesFromVar(_) | - &Instruction::ExecuteUnattributedVar(_) | - &Instruction::ExecuteGetDBRefs(_) | - &Instruction::ExecuteEnqueueAttributedVar(_) | - &Instruction::ExecuteFetchGlobalVar(_) | - &Instruction::ExecuteFirstStream(_) | - &Instruction::ExecuteFlushOutput(_) | - &Instruction::ExecuteGetByte(_) | - &Instruction::ExecuteGetChar(_) | - &Instruction::ExecuteGetNChars(_) | - &Instruction::ExecuteGetCode(_) | - &Instruction::ExecuteGetSingleChar(_) | - &Instruction::ExecuteTruncateIfNoLiftedHeapGrowthDiff(_) | - &Instruction::ExecuteTruncateIfNoLiftedHeapGrowth(_) | - &Instruction::ExecuteGetAttributedVariableList(_) | - &Instruction::ExecuteGetAttrVarQueueDelimiter(_) | - &Instruction::ExecuteGetAttrVarQueueBeyond(_) | - &Instruction::ExecuteGetBValue(_) | - &Instruction::ExecuteGetContinuationChunk(_) | - &Instruction::ExecuteGetNextOpDBRef(_) | - &Instruction::ExecuteLookupDBRef(_) | - &Instruction::ExecuteIsPartialString(_) | - &Instruction::ExecuteHalt(_) | - &Instruction::ExecuteGetLiftedHeapFromOffset(_) | - &Instruction::ExecuteGetLiftedHeapFromOffsetDiff(_) | - &Instruction::ExecuteGetSCCCleaner(_) | - &Instruction::ExecuteHeadIsDynamic(_) | - &Instruction::ExecuteInstallSCCCleaner(_) | - &Instruction::ExecuteInstallInferenceCounter(_) | - &Instruction::ExecuteLiftedHeapLength(_) | - &Instruction::ExecuteLoadLibraryAsStream(_) | - &Instruction::ExecuteModuleExists(_) | - &Instruction::ExecuteNextEP(_) | - &Instruction::ExecuteNoSuchPredicate(_) | - &Instruction::ExecuteNumberToChars(_) | - &Instruction::ExecuteNumberToCodes(_) | - &Instruction::ExecuteOpDeclaration(_) | - &Instruction::ExecuteOpen(_) | - &Instruction::ExecuteSetStreamOptions(_) | - &Instruction::ExecuteNextStream(_) | - &Instruction::ExecutePartialStringTail(_) | - &Instruction::ExecutePeekByte(_) | - &Instruction::ExecutePeekChar(_) | - &Instruction::ExecutePeekCode(_) | - &Instruction::ExecutePointsToContinuationResetMarker(_) | - &Instruction::ExecutePutByte(_) | - &Instruction::ExecutePutChar(_) | - &Instruction::ExecutePutChars(_) | - &Instruction::ExecutePutCode(_) | - &Instruction::ExecuteReadQueryTerm(_) | - &Instruction::ExecuteReadTerm(_) | - &Instruction::ExecuteRedoAttrVarBinding(_) | - &Instruction::ExecuteRemoveCallPolicyCheck(_) | - &Instruction::ExecuteRemoveInferenceCounter(_) | - &Instruction::ExecuteResetContinuationMarker(_) | - &Instruction::ExecuteRestoreCutPolicy(_) | - &Instruction::ExecuteSetCutPoint(_, _) | - &Instruction::ExecuteSetInput(_) | - &Instruction::ExecuteSetOutput(_) | - &Instruction::ExecuteStoreBacktrackableGlobalVar(_) | - &Instruction::ExecuteStoreGlobalVar(_) | - &Instruction::ExecuteStreamProperty(_) | - &Instruction::ExecuteSetStreamPosition(_) | - &Instruction::ExecuteInferenceLevel(_) | - &Instruction::ExecuteCleanUpBlock(_) | - &Instruction::ExecuteFail(_) | - &Instruction::ExecuteGetBall(_) | - &Instruction::ExecuteGetCurrentBlock(_) | - &Instruction::ExecuteGetCutPoint(_) | - &Instruction::ExecuteGetDoubleQuotes(_) | - &Instruction::ExecuteInstallNewBlock(_) | - &Instruction::ExecuteMaybe(_) | - &Instruction::ExecuteCpuNow(_) | - &Instruction::ExecuteDeterministicLengthRundown(_) | - &Instruction::ExecuteHttpOpen(_) | - &Instruction::ExecuteHttpListen(_) | - &Instruction::ExecuteHttpAccept(_) | - &Instruction::ExecuteHttpAnswer(_) | - &Instruction::ExecuteLoadForeignLib(_) | - &Instruction::ExecuteForeignCall(_) | - &Instruction::ExecuteDefineForeignStruct(_) | - &Instruction::ExecutePredicateDefined(_) | - &Instruction::ExecuteStripModule(_) | - &Instruction::ExecuteCurrentTime(_) | - &Instruction::ExecuteQuotedToken(_) | - &Instruction::ExecuteReadTermFromChars(_) | - &Instruction::ExecuteResetBlock(_) | - &Instruction::ExecuteReturnFromVerifyAttr(_) | - &Instruction::ExecuteSetBall(_) | - &Instruction::ExecutePushBallStack(_) | - &Instruction::ExecutePopBallStack(_) | - &Instruction::ExecutePopFromBallStack(_) | - &Instruction::ExecuteSetCutPointByDefault(_, _) | - &Instruction::ExecuteSetDoubleQuotes(_) | - &Instruction::ExecuteSetSeed(_) | - &Instruction::ExecuteSkipMaxList(_) | - &Instruction::ExecuteSleep(_) | - &Instruction::ExecuteSocketClientOpen(_) | - &Instruction::ExecuteSocketServerOpen(_) | - &Instruction::ExecuteSocketServerAccept(_) | - &Instruction::ExecuteSocketServerClose(_) | - &Instruction::ExecuteTLSAcceptClient(_) | - &Instruction::ExecuteTLSClientConnect(_) | - &Instruction::ExecuteSucceed(_) | - &Instruction::ExecuteTermAttributedVariables(_) | - &Instruction::ExecuteTermVariables(_) | - &Instruction::ExecuteTermVariablesUnderMaxDepth(_) | - &Instruction::ExecuteTruncateLiftedHeapTo(_) | - &Instruction::ExecuteUnifyWithOccursCheck(_) | - &Instruction::ExecuteUnwindEnvironments(_) | - &Instruction::ExecuteUnwindStack(_) | - &Instruction::ExecuteWAMInstructions(_) | - &Instruction::ExecuteInlinedInstructions(_) | - &Instruction::ExecuteWriteTerm(_) | - &Instruction::ExecuteWriteTermToChars(_) | - &Instruction::ExecuteScryerPrologVersion(_) | - &Instruction::ExecuteCryptoRandomByte(_) | - &Instruction::ExecuteCryptoDataHash(_) | - &Instruction::ExecuteCryptoDataHKDF(_) | - &Instruction::ExecuteCryptoPasswordHash(_) | - &Instruction::ExecuteCryptoDataEncrypt(_) | - &Instruction::ExecuteCryptoDataDecrypt(_) | - &Instruction::ExecuteCryptoCurveScalarMult(_) | - &Instruction::ExecuteEd25519Sign(_) | - &Instruction::ExecuteEd25519Verify(_) | - &Instruction::ExecuteEd25519NewKeyPair(_) | - &Instruction::ExecuteEd25519KeyPairPublicKey(_) | - &Instruction::ExecuteCurve25519ScalarMult(_) | - &Instruction::ExecuteFirstNonOctet(_) | - &Instruction::ExecuteLoadHTML(_) | - &Instruction::ExecuteLoadXML(_) | - &Instruction::ExecuteGetEnv(_) | - &Instruction::ExecuteSetEnv(_) | - &Instruction::ExecuteUnsetEnv(_) | - &Instruction::ExecuteShell(_) | - &Instruction::ExecutePID(_) | - &Instruction::ExecuteCharsBase64(_) | - &Instruction::ExecuteDevourWhitespace(_) | - &Instruction::ExecuteIsSTOEnabled(_) | - &Instruction::ExecuteSetSTOAsUnify(_) | - &Instruction::ExecuteSetNSTOAsUnify(_) | - &Instruction::ExecuteSetSTOWithErrorAsUnify(_) | - &Instruction::ExecuteHomeDirectory(_) | - &Instruction::ExecuteDebugHook(_) | - &Instruction::ExecuteAddDiscontiguousPredicate(_) | - &Instruction::ExecuteAddDynamicPredicate(_) | - &Instruction::ExecuteAddMultifilePredicate(_) | - &Instruction::ExecuteAddGoalExpansionClause(_) | - &Instruction::ExecuteAddTermExpansionClause(_) | - &Instruction::ExecuteAddInSituFilenameModule(_) | - &Instruction::ExecuteClauseToEvacuable(_) | - &Instruction::ExecuteScopedClauseToEvacuable(_) | - &Instruction::ExecuteConcludeLoad(_) | - &Instruction::ExecuteDeclareModule(_) | - &Instruction::ExecuteLoadCompiledLibrary(_) | - &Instruction::ExecuteLoadContextSource(_) | - &Instruction::ExecuteLoadContextFile(_) | - &Instruction::ExecuteLoadContextDirectory(_) | - &Instruction::ExecuteLoadContextModule(_) | - &Instruction::ExecuteLoadContextStream(_) | - &Instruction::ExecutePopLoadContext(_) | - &Instruction::ExecutePopLoadStatePayload(_) | - &Instruction::ExecutePushLoadContext(_) | - &Instruction::ExecutePushLoadStatePayload(_) | - &Instruction::ExecuteUseModule(_) | - &Instruction::ExecuteBuiltInProperty(_) | - &Instruction::ExecuteMetaPredicateProperty(_) | - &Instruction::ExecuteMultifileProperty(_) | - &Instruction::ExecuteDiscontiguousProperty(_) | - &Instruction::ExecuteDynamicProperty(_) | - &Instruction::ExecuteAbolishClause(_) | - &Instruction::ExecuteAsserta(_) | - &Instruction::ExecuteAssertz(_) | - &Instruction::ExecuteRetract(_) | - &Instruction::ExecuteIsConsistentWithTermQueue(_) | - &Instruction::ExecuteFlushTermQueue(_) | - &Instruction::ExecuteRemoveModuleExports(_) | - &Instruction::ExecuteAddNonCountedBacktracking(_) | - &Instruction::ExecutePopCount(_) => { + &Instruction::ExecuteCompileInlineOrExpandedGoal | + &Instruction::ExecuteIsExpandedOrInlined | + &Instruction::ExecuteGetClauseP | + &Instruction::ExecuteInvokeClauseAtP | + &Instruction::ExecuteGetFromAttributedVarList | + &Instruction::ExecutePutToAttributedVarList | + &Instruction::ExecuteDeleteFromAttributedVarList | + &Instruction::ExecuteDeleteAllAttributesFromVar | + &Instruction::ExecuteUnattributedVar | + &Instruction::ExecuteGetDBRefs | + &Instruction::ExecuteFetchGlobalVar | + &Instruction::ExecuteFirstStream | + &Instruction::ExecuteFlushOutput | + &Instruction::ExecuteGetByte | + &Instruction::ExecuteGetChar | + &Instruction::ExecuteGetNChars | + &Instruction::ExecuteGetCode | + &Instruction::ExecuteGetSingleChar | + &Instruction::ExecuteTruncateIfNoLiftedHeapGrowthDiff | + &Instruction::ExecuteTruncateIfNoLiftedHeapGrowth | + &Instruction::ExecuteGetAttributedVariableList | + &Instruction::ExecuteGetAttrVarQueueDelimiter | + &Instruction::ExecuteGetAttrVarQueueBeyond | + &Instruction::ExecuteGetBValue | + &Instruction::ExecuteGetContinuationChunk | + &Instruction::ExecuteGetNextOpDBRef | + &Instruction::ExecuteLookupDBRef | + &Instruction::ExecuteIsPartialString | + &Instruction::ExecuteHalt | + &Instruction::ExecuteGetLiftedHeapFromOffset | + &Instruction::ExecuteGetLiftedHeapFromOffsetDiff | + &Instruction::ExecuteGetSCCCleaner | + &Instruction::ExecuteHeadIsDynamic | + &Instruction::ExecuteInstallSCCCleaner | + &Instruction::ExecuteInstallInferenceCounter | + &Instruction::ExecuteLiftedHeapLength | + &Instruction::ExecuteLoadLibraryAsStream | + &Instruction::ExecuteModuleExists | + &Instruction::ExecuteNextEP | + &Instruction::ExecuteNoSuchPredicate | + &Instruction::ExecuteNumberToChars | + &Instruction::ExecuteNumberToCodes | + &Instruction::ExecuteOpDeclaration | + &Instruction::ExecuteOpen | + &Instruction::ExecuteSetStreamOptions | + &Instruction::ExecuteNextStream | + &Instruction::ExecutePartialStringTail | + &Instruction::ExecutePeekByte | + &Instruction::ExecutePeekChar | + &Instruction::ExecutePeekCode | + &Instruction::ExecutePointsToContinuationResetMarker | + &Instruction::ExecutePutByte | + &Instruction::ExecutePutChar | + &Instruction::ExecutePutChars | + &Instruction::ExecutePutCode | + &Instruction::ExecuteReadQueryTerm | + &Instruction::ExecuteReadTerm | + &Instruction::ExecuteRedoAttrVarBinding | + &Instruction::ExecuteRemoveCallPolicyCheck | + &Instruction::ExecuteRemoveInferenceCounter | + &Instruction::ExecuteResetContinuationMarker | + &Instruction::ExecuteRestoreCutPolicy | + &Instruction::ExecuteSetCutPoint(_) | + &Instruction::ExecuteSetInput | + &Instruction::ExecuteSetOutput | + &Instruction::ExecuteStoreBacktrackableGlobalVar | + &Instruction::ExecuteStoreGlobalVar | + &Instruction::ExecuteStreamProperty | + &Instruction::ExecuteSetStreamPosition | + &Instruction::ExecuteInferenceLevel | + &Instruction::ExecuteCleanUpBlock | + &Instruction::ExecuteFail | + &Instruction::ExecuteGetBall | + &Instruction::ExecuteGetCurrentBlock | + &Instruction::ExecuteGetCutPoint | + &Instruction::ExecuteGetDoubleQuotes | + &Instruction::ExecuteInstallNewBlock | + &Instruction::ExecuteMaybe | + &Instruction::ExecuteCpuNow | + &Instruction::ExecuteDeterministicLengthRundown | + &Instruction::ExecuteHttpOpen | + &Instruction::ExecuteHttpListen | + &Instruction::ExecuteHttpAccept | + &Instruction::ExecuteHttpAnswer | + &Instruction::ExecuteLoadForeignLib | + &Instruction::ExecuteForeignCall | + &Instruction::ExecuteDefineForeignStruct | + &Instruction::ExecutePredicateDefined | + &Instruction::ExecuteStripModule | + &Instruction::ExecuteCurrentTime | + &Instruction::ExecuteQuotedToken | + &Instruction::ExecuteReadTermFromChars | + &Instruction::ExecuteResetBlock | + &Instruction::ExecuteReturnFromVerifyAttr | + &Instruction::ExecuteSetBall | + &Instruction::ExecutePushBallStack | + &Instruction::ExecutePopBallStack | + &Instruction::ExecutePopFromBallStack | + &Instruction::ExecuteSetCutPointByDefault(_) | + &Instruction::ExecuteSetDoubleQuotes | + &Instruction::ExecuteSetSeed | + &Instruction::ExecuteSkipMaxList | + &Instruction::ExecuteSleep | + &Instruction::ExecuteSocketClientOpen | + &Instruction::ExecuteSocketServerOpen | + &Instruction::ExecuteSocketServerAccept | + &Instruction::ExecuteSocketServerClose | + &Instruction::ExecuteTLSAcceptClient | + &Instruction::ExecuteTLSClientConnect | + &Instruction::ExecuteSucceed | + &Instruction::ExecuteTermAttributedVariables | + &Instruction::ExecuteTermVariables | + &Instruction::ExecuteTermVariablesUnderMaxDepth | + &Instruction::ExecuteTruncateLiftedHeapTo | + &Instruction::ExecuteUnifyWithOccursCheck | + &Instruction::ExecuteUnwindEnvironments | + &Instruction::ExecuteUnwindStack | + &Instruction::ExecuteWAMInstructions | + &Instruction::ExecuteInlinedInstructions | + &Instruction::ExecuteWriteTerm | + &Instruction::ExecuteWriteTermToChars | + &Instruction::ExecuteScryerPrologVersion | + &Instruction::ExecuteCryptoRandomByte | + &Instruction::ExecuteCryptoDataHash | + &Instruction::ExecuteCryptoDataHKDF | + &Instruction::ExecuteCryptoPasswordHash | + &Instruction::ExecuteCryptoDataEncrypt | + &Instruction::ExecuteCryptoDataDecrypt | + &Instruction::ExecuteCryptoCurveScalarMult | + &Instruction::ExecuteEd25519Sign | + &Instruction::ExecuteEd25519Verify | + &Instruction::ExecuteEd25519NewKeyPair | + &Instruction::ExecuteEd25519KeyPairPublicKey | + &Instruction::ExecuteCurve25519ScalarMult | + &Instruction::ExecuteFirstNonOctet | + &Instruction::ExecuteLoadHTML | + &Instruction::ExecuteLoadXML | + &Instruction::ExecuteGetEnv | + &Instruction::ExecuteSetEnv | + &Instruction::ExecuteUnsetEnv | + &Instruction::ExecuteShell | + &Instruction::ExecutePID | + &Instruction::ExecuteCharsBase64 | + &Instruction::ExecuteDevourWhitespace | + &Instruction::ExecuteIsSTOEnabled | + &Instruction::ExecuteSetSTOAsUnify | + &Instruction::ExecuteSetNSTOAsUnify | + &Instruction::ExecuteSetSTOWithErrorAsUnify | + &Instruction::ExecuteHomeDirectory | + &Instruction::ExecuteDebugHook | + &Instruction::ExecuteAddDiscontiguousPredicate | + &Instruction::ExecuteAddDynamicPredicate | + &Instruction::ExecuteAddMultifilePredicate | + &Instruction::ExecuteAddGoalExpansionClause | + &Instruction::ExecuteAddTermExpansionClause | + &Instruction::ExecuteAddInSituFilenameModule | + &Instruction::ExecuteClauseToEvacuable | + &Instruction::ExecuteScopedClauseToEvacuable | + &Instruction::ExecuteConcludeLoad | + &Instruction::ExecuteDeclareModule | + &Instruction::ExecuteLoadCompiledLibrary | + &Instruction::ExecuteLoadContextSource | + &Instruction::ExecuteLoadContextFile | + &Instruction::ExecuteLoadContextDirectory | + &Instruction::ExecuteLoadContextModule | + &Instruction::ExecuteLoadContextStream | + &Instruction::ExecutePopLoadContext | + &Instruction::ExecutePopLoadStatePayload | + &Instruction::ExecutePushLoadContext | + &Instruction::ExecutePushLoadStatePayload | + &Instruction::ExecuteUseModule | + &Instruction::ExecuteBuiltInProperty | + &Instruction::ExecuteMetaPredicateProperty | + &Instruction::ExecuteMultifileProperty | + &Instruction::ExecuteDiscontiguousProperty | + &Instruction::ExecuteDynamicProperty | + &Instruction::ExecuteAbolishClause | + &Instruction::ExecuteAsserta | + &Instruction::ExecuteAssertz | + &Instruction::ExecuteRetract | + &Instruction::ExecuteIsConsistentWithTermQueue | + &Instruction::ExecuteFlushTermQueue | + &Instruction::ExecuteRemoveModuleExports | + &Instruction::ExecuteAddNonCountedBacktracking | + &Instruction::ExecutePopCount => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("execute"), [atom(name), fixnum(arity)]) } @@ -2044,12 +2053,9 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::Deallocate => { functor!(atom!("deallocate")) } - &Instruction::JmpByCall(_, offset, ..) => { + &Instruction::JmpByCall(offset) => { functor!(atom!("jmp_by_call"), [fixnum(offset)]) } - &Instruction::JmpByExecute(_, offset, ..) => { - functor!(atom!("jmp_by_execute"), [fixnum(offset)]) - } &Instruction::RevJmpBy(offset) => { functor!(atom!("rev_jmp_by"), [fixnum(offset)]) } @@ -2244,6 +2250,7 @@ pub fn generate_instructions_rs() -> TokenStream { let mut clause_type_to_instr_arms = vec![]; let mut clause_type_name_arms = vec![]; let mut is_inbuilt_arms = vec![]; + let mut is_inlined_arms = vec![]; for (name, arity, variant) in instr_data.compare_number_variants { let ident = variant.ident.clone(); @@ -2295,7 +2302,7 @@ pub fn generate_instructions_rs() -> TokenStream { quote! { ClauseType::Inlined( InlinedClauseType::CompareNumber(CompareNumber::#ident(#(#placeholder_ids),*)) - ) => Instruction::#instr_ident(#(#placeholder_ids),*, 0) + ) => Instruction::#instr_ident(#(*#placeholder_ids),*) } ); @@ -2330,7 +2337,7 @@ pub fn generate_instructions_rs() -> TokenStream { quote! { ClauseType::BuiltIn( BuiltInClauseType::CompareTerm(CompareTerm::#ident) - ) => Instruction::#instr_ident(0) + ) => Instruction::#instr_ident } ); @@ -2391,13 +2398,13 @@ pub fn generate_instructions_rs() -> TokenStream { quote! { ClauseType::BuiltIn( BuiltInClauseType::#ident(#(#placeholder_ids),*) - ) => Instruction::#instr_ident(#(#placeholder_ids),*,0) + ) => Instruction::#instr_ident(#(*#placeholder_ids),*) } } else { quote! { ClauseType::BuiltIn( BuiltInClauseType::#ident - ) => Instruction::#instr_ident(0) + ) => Instruction::#instr_ident } }); @@ -2462,7 +2469,7 @@ pub fn generate_instructions_rs() -> TokenStream { quote! { ClauseType::Inlined( InlinedClauseType::#ident(#(#placeholder_ids),*) - ) => Instruction::#instr_ident(#(#placeholder_ids),*,0) + ) => Instruction::#instr_ident(*#(#placeholder_ids),*) } ); @@ -2471,6 +2478,12 @@ pub fn generate_instructions_rs() -> TokenStream { (atom!(#name), #arity) => true } ); + + is_inlined_arms.push( + quote! { + (atom!(#name), #arity) => true + } + ); } for (name, arity, variant) in instr_data.system_clause_type_variants { @@ -2552,13 +2565,13 @@ pub fn generate_instructions_rs() -> TokenStream { quote! { ClauseType::System( SystemClauseType::#ident(#(#placeholder_ids),*) - ) => Instruction::#instr_ident(#(#placeholder_ids),*,0) + ) => Instruction::#instr_ident(#(*#placeholder_ids),*) } } else { quote! { ClauseType::System( SystemClauseType::#ident - ) => Instruction::#instr_ident(0) + ) => Instruction::#instr_ident } }); @@ -2629,13 +2642,13 @@ pub fn generate_instructions_rs() -> TokenStream { quote! { ClauseType::System(SystemClauseType::REPL( REPLCodePtr::#ident(#(#placeholder_ids),*) - )) => Instruction::#instr_ident(#(#placeholder_ids),*,0) + )) => Instruction::#instr_ident(#(*#placeholder_ids),*) } } else { quote! { ClauseType::System(SystemClauseType::REPL( REPLCodePtr::#ident - )) => Instruction::#instr_ident(0) + )) => Instruction::#instr_ident } }); @@ -2655,7 +2668,7 @@ pub fn generate_instructions_rs() -> TokenStream { }); clause_type_to_instr_arms.push(quote! { - ClauseType::Named(arity, name, idx) => Instruction::CallNamed(arity, name, idx, 0) + ClauseType::Named(arity, name, idx) => Instruction::CallNamed(*arity, *name, *idx) }); clause_type_name_arms.push(quote! { @@ -2706,11 +2719,11 @@ pub fn generate_instructions_rs() -> TokenStream { clause_type_to_instr_arms.push(if !variant_fields.is_empty() { quote! { ClauseType::#ident(#(#placeholder_ids),*) => - Instruction::#ident(#(#placeholder_ids),*,0) + Instruction::#ident(#(*#placeholder_ids),*) } } else { quote! { - ClauseType::#ident => Instruction::#ident(0) + ClauseType::#ident => Instruction::#ident } }); @@ -2767,11 +2780,6 @@ pub fn generate_instructions_rs() -> TokenStream { Instruction::#execute_ident(#(#placeholder_ids),*) } }) - } else if variant_string == "JmpByCall" { - Some(quote! { - Instruction::JmpByCall(#(#placeholder_ids),*) => - Instruction::JmpByExecute(#(#placeholder_ids),*) - }) } else { None } @@ -2835,16 +2843,23 @@ pub fn generate_instructions_rs() -> TokenStream { let enum_arity = if let Fields::Unnamed(fields) = &variant.fields { fields.unnamed.len() } else { - unreachable!() + 0 }; let placeholder_ids: Vec<_> = (0 .. enum_arity) .map(|n| format_ident!("f_{}", n)) .collect(); - Some(quote! { - Instruction::#variant_ident(#(#placeholder_ids),*) => - Instruction::#def_variant_ident(#(#placeholder_ids),*) + Some(if enum_arity == 0 { + quote! { + Instruction::#variant_ident => + Instruction::#def_variant_ident + } + } else { + quote! { + Instruction::#variant_ident(#(#placeholder_ids),*) => + Instruction::#def_variant_ident(#(#placeholder_ids),*) + } }) } else { None @@ -2852,38 +2867,6 @@ pub fn generate_instructions_rs() -> TokenStream { }) .collect(); - let perm_vars_mut_arms: Vec<_> = instr_data.instr_variants - .iter() - .cloned() - .filter_map(|(_, _, _, variant)| { - if !is_callable(&variant.ident) && !is_jmp(&variant.ident) { - return None; - } - - let variant_ident = variant.ident.clone(); - let enum_arity = if let Fields::Unnamed(fields) = &variant.fields { - fields.unnamed.len() - } else { - 0 - }; - - let placeholder_ids: Vec<_> = (1 .. enum_arity) - .map(|_| format_ident!("_")) - .collect(); - - Some(if enum_arity == 1 { - quote! { - Instruction::#variant_ident(ref mut perm_vars) => Some(perm_vars) - } - } else { - quote! { - Instruction::#variant_ident(#(#placeholder_ids),*, ref mut perm_vars) => - Some(perm_vars) - } - }) - }) - .collect(); - let control_flow_arms: Vec<_> = instr_data.instr_variants .iter() .cloned() @@ -2892,10 +2875,22 @@ pub fn generate_instructions_rs() -> TokenStream { return None; } + let enum_arity = if let Fields::Unnamed(fields) = &variant.fields { + fields.unnamed.len() + } else { + 0 + }; + let variant_ident = variant.ident.clone(); - Some(quote! { - Instruction::#variant_ident(..) => true + Some(if enum_arity == 0 { + quote! { + Instruction::#variant_ident => true + } + } else { + quote! { + Instruction::#variant_ident(..) => true + } }) }) .collect(); @@ -2913,27 +2908,59 @@ pub fn generate_instructions_rs() -> TokenStream { }; Some(if variant_string.starts_with("Execute") { - quote! { - (#name, execute, $($args:expr),*) => { - Instruction::#variant_ident($($args),*) + if arity == 0 { + quote! { + (#name, execute) => { + Instruction::#variant_ident + } + } + } else { + quote! { + (#name, execute, $($args:expr),*) => { + Instruction::#variant_ident($($args),*) + } } } } else if variant_string.starts_with("Call") { - quote! { - (#name, $($args:expr),*) => { - Instruction::#variant_ident($($args),*) + if arity == 0 { + quote! { + (#name) => { + Instruction::#variant_ident + } + } + } else { + quote! { + (#name, $($args:expr),*) => { + Instruction::#variant_ident($($args),*) + } } } } else if variant_string.starts_with("DefaultExecute") { - quote! { - (#name, execute, default, $($args:expr),*) => { - Instruction::#variant_ident($($args),*) + if arity == 0 { + quote! { + (#name, execute, default) => { + Instruction::#variant_ident + } + } + } else { + quote! { + (#name, execute, default, $($args:expr),*) => { + Instruction::#variant_ident($($args),*) + } } } } else if variant_string.starts_with("DefaultCall") { - quote! { - (#name, default, $($args:expr),*) => { - Instruction::#variant_ident($($args),*) + if arity == 0 { + quote! { + (#name, default) => { + Instruction::#variant_ident + } + } + } else { + quote! { + (#name, default, $($args:expr),*) => { + Instruction::#variant_ident($($args),*) + } } } } else { @@ -3061,7 +3088,7 @@ pub fn generate_instructions_rs() -> TokenStream { } } - pub fn to_instr(self) -> Instruction { + pub fn to_instr(&self) -> Instruction { match self { #( #clause_type_to_instr_arms, @@ -3085,6 +3112,15 @@ pub fn generate_instructions_rs() -> TokenStream { )* } } + + pub fn is_inlined(name: Atom, arity: usize) -> bool { + match (name, arity) { + #( + #is_inlined_arms, + )* + _ => false, + } + } } #[derive(Clone, Debug)] @@ -3130,15 +3166,6 @@ pub fn generate_instructions_rs() -> TokenStream { } } - pub fn perm_vars_mut(&mut self) -> Option<&mut usize> { - match self { - #( - #perm_vars_mut_arms, - )* - _ => None, - } - } - pub fn is_ctrl_instr(&self) -> bool { match self { &Instruction::Allocate(_) | @@ -3201,41 +3228,6 @@ fn is_jmp(id: &Ident) -> bool { } fn create_instr_variant(id: Ident, mut variant: Variant) -> Variant { - use proc_macro2::Span; - use syn::punctuated::Punctuated; - use syn::token::Paren; - - // add the perm_vars usize field to the variant. - - if is_callable(&id) || is_jmp(&id) { - let field = Field { - attrs: vec![], - vis: Visibility::Inherited, - ident: None, - colon_token: None, - ty: parse_quote! { usize }, - }; - - match &mut variant.fields { - Fields::Unnamed(ref mut fields) => { - fields.unnamed.push(field); - } - Fields::Unit => { - variant.fields = Fields::Unnamed(FieldsUnnamed { - paren_token: Paren(Span::call_site()), - unnamed: { - let mut fields_seq = Punctuated::new(); - fields_seq.push(field); - fields_seq - } - }); - } - _ => { - unreachable!(); - } - } - } - variant.ident = id; variant.attrs.clear(); diff --git a/src/allocator.rs b/src/allocator.rs index 50e9c7c3..f689a802 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -1,10 +1,7 @@ use crate::parser::ast::*; -use crate::temp_v; -use crate::fixtures::*; use crate::forms::*; use crate::instructions::*; -use crate::machine::machine_indices::*; use crate::targets::*; use std::cell::Cell; @@ -16,7 +13,7 @@ pub(crate) trait Allocator { &mut self, lvl: Level, context: GenContext, - code: &mut Code, + code: &mut CodeDeque, ); fn mark_non_var<'a, Target: CompilationTarget<'a>>( @@ -24,40 +21,44 @@ pub(crate) trait Allocator { lvl: Level, context: GenContext, cell: &'a Cell, - code: &mut Code, + code: &mut CodeDeque, ); fn mark_reserved_var<'a, Target: CompilationTarget<'a>>( &mut self, - var_name: Var, + var_num: usize, lvl: Level, cell: &'a Cell, term_loc: GenContext, - code: &mut Code, + code: &mut CodeDeque, r: RegType, is_new_var: bool, ); + fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType; + fn mark_var<'a, Target: CompilationTarget<'a>>( &mut self, - var_name: Var, + var_num: usize, lvl: Level, cell: &'a Cell, context: GenContext, - code: &mut Code, + code: &mut CodeDeque, ); fn reset(&mut self); - fn reset_contents(&mut self) {} fn reset_arg(&mut self, arg_num: usize); fn reset_at_head(&mut self, args: &Vec); + fn reset_contents(&mut self); fn advance_arg(&mut self); + /* fn bindings(&self) -> &AllocVarDict; fn bindings_mut(&mut self) -> &mut AllocVarDict; - fn take_bindings(self) -> AllocVarDict; + */ + fn max_reg_allocated(&self) -> usize; // TODO: wha.. why?? grrr. it drains the VarStatus data from vs (which it owns!) @@ -87,21 +88,4 @@ pub(crate) trait Allocator { perm_vs } */ - - fn get(&self, var: Var) -> RegType { - self.bindings() - .get(&var) - .map_or(temp_v!(0), |v| v.as_reg_type()) - } - - fn is_unbound(&self, var: Var) -> bool { - self.get(var).reg_num() == 0 - } - - fn record_register(&mut self, var: Var, r: RegType) { - match self.bindings_mut().get_mut(&var).unwrap() { - &mut VarAlloc::Temp(_, ref mut s, _) => *s = r.reg_num(), - &mut VarAlloc::Perm(ref mut s) => *s = r.reg_num(), - } - } } diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 94974a51..0fbd91d5 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -52,7 +52,7 @@ pub(crate) struct ArithInstructionIterator<'a> { state_stack: Vec>, } -pub(crate) type ArithCont = (Code, Option); +pub(crate) type ArithCont = (CodeDeque, Option); impl<'a> ArithInstructionIterator<'a> { fn push_subterm(&mut self, lvl: Level, term: &'a Term) { @@ -73,7 +73,7 @@ impl<'a> ArithInstructionIterator<'a> { 2, )) } - Term::Var(cell, var) => TermIterState::Var(Level::Shallow, cell, VarPtr::from(var)), + Term::Var(cell, var_ptr) => TermIterState::Var(Level::Shallow, cell, var_ptr.clone()), }; Ok(ArithInstructionIterator { @@ -86,7 +86,7 @@ impl<'a> ArithInstructionIterator<'a> { pub(crate) enum ArithTermRef<'a> { Literal(&'a Literal), Op(Atom, usize), // name, arity. - Var(Level, &'a Cell, Var), + Var(Level, &'a Cell, VarPtr), } impl<'a> Iterator for ArithInstructionIterator<'a> { @@ -114,8 +114,8 @@ impl<'a> Iterator for ArithInstructionIterator<'a> { } } TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(c))), - TermIterState::Var(lvl, cell, var_ref) => { - return Some(Ok(ArithTermRef::Var(lvl, cell, Var::from(var_ref)))); + TermIterState::Var(lvl, cell, var_ptr) => { + return Some(Ok(ArithTermRef::Var(lvl, cell, var_ptr))); } _ => { return Some(Err(ArithmeticError::NonEvaluableFunctor( @@ -307,43 +307,48 @@ impl<'a> ArithmeticEvaluator<'a> { term_loc: GenContext, arg: usize, ) -> Result { - let mut code = vec![]; + let mut code = CodeDeque::new(); let mut iter = src.iter()?; while let Some(term_ref) = iter.next() { match term_ref? { ArithTermRef::Literal(c) => push_literal(&mut self.interm, c)?, ArithTermRef::Var(lvl, cell, name) => { + let var_num = name.to_var_num().unwrap(); + let r = if lvl == Level::Shallow { self.marker.mark_non_callable( - name, + var_num, arg, term_loc, cell, &mut code, ) } else if term_loc.is_last() || cell.get().norm().reg_num() == 0 { - if let Some(r) = self.marker.get_binding(&name) { - r - } else { + let r = self.marker.get_binding(var_num); + + if r.reg_num() == 0 { self.marker.mark_var::( - name.clone(), + var_num, lvl, cell, term_loc, &mut code, ); - - self.marker.get_binding(&name).unwrap() + } else { + self.marker.increment_running_count(var_num); } + + r } else { + self.marker.increment_running_count(var_num); cell.get().norm() }; self.interm.push(ArithmeticTerm::Reg(r)); } ArithTermRef::Op(name, arity) => { - code.push(self.instr_from_clause(name, arity)?); + code.push_back(self.instr_from_clause(name, arity)?); } } } diff --git a/src/codegen.rs b/src/codegen.rs index e7c6e2ac..794ec65b 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,10 +1,9 @@ use crate::atom_table::*; use crate::parser::ast::*; -use crate::{perm_v, temp_v}; +use crate::temp_v; use crate::allocator::*; use crate::arithmetic::*; use crate::debray_allocator::*; -use crate::fixtures::*; use crate::forms::*; use crate::indexing::*; use crate::instructions::*; @@ -13,39 +12,139 @@ use crate::targets::*; use crate::types::*; use crate::instr; +use crate::machine::disjuncts::*; use crate::machine::machine_errors::*; -use indexmap::{IndexMap, IndexSet}; +use fxhash::FxBuildHasher; +use indexmap::IndexSet; use std::cell::Cell; use std::collections::VecDeque; #[derive(Debug)] -pub(crate) struct ConjunctInfo { - pub(crate) perm_vs: VariableFixtures, - pub(crate) num_of_chunks: usize, - pub(crate) has_deep_cut: bool, +pub struct BranchCodeStack { + pub stack: Vec>, } -impl ConjunctInfo { - fn new(perm_vs: VariableFixtures, num_of_chunks: usize, has_deep_cut: bool) -> Self { - ConjunctInfo { - perm_vs, - num_of_chunks, - has_deep_cut, +pub type SubsumedBranchHits = IndexSet; + +impl BranchCodeStack { + fn new() -> Self { + Self { stack: vec![] } + } + + fn add_new_branch_stack(&mut self) { + self.stack.push(vec![]); + } + + fn add_new_branch(&mut self) { + if self.stack.is_empty() { + self.add_new_branch_stack(); + } + + if let Some(branches) = self.stack.last_mut() { + branches.push(CodeDeque::new()); } } - fn allocates(&self) -> bool { - self.perm_vs.size() > 0 || self.num_of_chunks > 1 || self.has_deep_cut + fn code<'a>(&'a mut self, default_code: &'a mut CodeDeque) -> &'a mut CodeDeque { + self.stack.last_mut() + .and_then(|stack| stack.last_mut()) + .unwrap_or(default_code) } - fn perm_vars(&self) -> usize { - self.perm_vs.size() + self.perm_var_offset() + fn push_missing_vars(&mut self, depth: usize, marker: &mut DebrayAllocator) -> SubsumedBranchHits { + let mut subsumed_hits = SubsumedBranchHits::with_hasher(FxBuildHasher::default()); + + for idx in (self.stack.len() - depth .. self.stack.len()).rev() { + let branch = &mut marker.branch_stack[idx]; + let branch_hits = &branch.hits; + + for (&var_num, branches) in branch_hits.iter() { + let record = &marker.var_data.records[var_num]; + + if record.running_count < record.num_occurrences { + if !branches.all() { + branch.deep_safety.insert(var_num); + branch.shallow_safety.insert(var_num); + + let r = record.allocation.as_reg_type(); + + // iterate over unset bits. + for branch_idx in branches.iter_zeros() { + if branch_idx + 1 == branches.len() && idx + 1 != self.stack.len() { + break; + } + + self.stack[idx][branch_idx].push_back(instr!("put_variable", r, 0)); + } + } + + subsumed_hits.insert(var_num); + } + } + } + + subsumed_hits } - fn perm_var_offset(&self) -> usize { - self.has_deep_cut as usize + fn push_jump_instrs(&mut self, depth: usize) { + // add 2 in each arm length to compensate for each jump + // instruction and each branch instruction not yet added. + let mut jump_span: usize = self.stack[self.stack.len() - depth ..] + .iter() + .map(|branch| branch.iter().map(|code| code.len() + 2).sum::()) + .sum(); + + jump_span -= depth; + + for idx in self.stack.len() - depth .. self.stack.len() { + let inner_len = self.stack[idx].len(); + + for (inner_idx, code) in self.stack[idx].iter_mut().enumerate() { + if inner_idx + 1 == inner_len { + jump_span -= code.len() + 1; // = jump_span.saturating_sub(code.len() + 1); + } else { + jump_span -= code.len() + 1; + code.push_back(instr!("jmp_by_call", jump_span as usize)); + + // saturate at 0 if underflow happens, which only + // happens when jump_span is no longer needed + // anyway. still, we don't want to panic at + // underflow. + jump_span -= 1; + } + } + } + + // eliminate terminating jump instruction in last arm of last + // branch. + // self.stack.last_mut() + // .and_then(|branch| branch.last_mut()) + // .map(|code| code.pop_back()); + } + + fn pop_branch(&mut self, depth: usize, settings: CodeGenSettings) -> CodeDeque { + let mut combined_code = CodeDeque::new(); + + for mut branch_arm in self.stack.drain(self.stack.len() - depth ..).rev() { + let num_branch_arms = branch_arm.len(); + branch_arm.last_mut().map(|code| code.extend(combined_code.drain(..))); + + for (idx, code) in branch_arm.into_iter().enumerate() { + combined_code.push_back(if idx == 0 { + Instruction::TryMeElse(code.len() + 1) + } else if idx + 1 < num_branch_arms { + settings.retry_me_else(code.len() + 1) + } else { + settings.trust_me() + }); + + combined_code.extend(code.into_iter()); + } + } + + combined_code } } @@ -168,53 +267,49 @@ impl CodeGenSettings { pub(crate) struct CodeGenerator<'a> { pub(crate) atom_tbl: &'a mut AtomTable, marker: DebrayAllocator, - pub(crate) var_count: IndexMap, settings: CodeGenSettings, pub(crate) skeleton: PredicateSkeleton, - pub(crate) jmp_by_locs: Vec, - global_jmp_by_locs_offset: usize, } impl DebrayAllocator { fn mark_var_in_non_callable( &mut self, - name: Var, + var_num: usize, term_loc: GenContext, vr: &Cell, - code: &mut Code, + code: &mut CodeDeque, ) -> RegType { - self.mark_var::(name, Level::Shallow, vr, term_loc, code); - vr.get().norm() - } + self.mark_var::( + var_num, + Level::Shallow, + vr, + term_loc, + code, + ); - #[inline(always)] - pub(crate) fn get_binding(&self, name: &Var) -> Option { - match self.bindings().get(name) { - Some(&VarAlloc::Temp(_, t, _)) if t != 0 => Some(RegType::Temp(t)), - Some(&VarAlloc::Perm(p)) if p != 0 => Some(RegType::Perm(p)), - _ => None, - } + vr.get().norm() } pub(crate) fn mark_non_callable( &mut self, - name: Var, + var_num: usize, arg: usize, term_loc: GenContext, vr: &Cell, - code: &mut Code, + code: &mut CodeDeque, ) -> RegType { - match self.get_binding(&name) { - Some(RegType::Temp(t)) => RegType::Temp(t), - Some(RegType::Perm(p)) => { + match self.get_binding(var_num) { + RegType::Temp(t) if t != 0 => RegType::Temp(t), + RegType::Perm(p) if p != 0 => { if let GenContext::Last(_) = term_loc { - self.mark_var_in_non_callable(name.clone(), term_loc, vr, code); + self.mark_var_in_non_callable(var_num, term_loc, vr, code); temp_v!(arg) } else { + self.increment_running_count(var_num); RegType::Perm(p) } } - None => self.mark_var_in_non_callable(name, term_loc, vr, code), + _ => self.mark_var_in_non_callable(var_num, term_loc, vr, code), } } } @@ -280,50 +375,34 @@ impl<'b> CodeGenerator<'b> { CodeGenerator { atom_tbl, marker: DebrayAllocator::new(), - var_count: IndexMap::new(), settings, skeleton: PredicateSkeleton::new(), - jmp_by_locs: vec![], - global_jmp_by_locs_offset: 0, } } - fn update_var_count<'a, Iter: Iterator>>(&mut self, iter: Iter) { - for term in iter { - if let TermRef::Var(_, _, var) = term { - let entry = self.var_count.entry(var).or_insert(0); - *entry += 1; - } - } - } - - fn get_var_count(&self, var: &Var) -> usize { - *self.var_count.get(var).unwrap() - } - - fn add_or_increment_void_instr<'a, Target>(target: &mut Code) + fn add_or_increment_void_instr<'a, Target>(target: &mut CodeDeque) where Target: crate::targets::CompilationTarget<'a>, { - if let Some(ref mut instr) = target.last_mut() { + if let Some(ref mut instr) = target.back_mut() { if Target::is_void_instr(&*instr) { Target::incr_void_instr(instr); return; } } - target.push(Target::to_void(1)); + target.push_back(Target::to_void(1)); } fn deep_var_instr<'a, Target: crate::targets::CompilationTarget<'a>>( &mut self, cell: &'a Cell, - var: &Var, + var_num: usize, term_loc: GenContext, - target: &mut Code, + target: &mut CodeDeque, ) { - if self.get_var_count(var.as_ref()) > 1 { - self.marker.mark_var::(var.clone(), Level::Deep, cell, term_loc, target); + if self.marker.var_data.records[var_num].num_occurrences > 1 { + self.marker.mark_var::(var_num, Level::Deep, cell, term_loc, target); } else { Self::add_or_increment_void_instr::(target); } @@ -333,7 +412,7 @@ impl<'b> CodeGenerator<'b> { &mut self, subterm: &'a Term, term_loc: GenContext, - target: &mut Code, + target: &mut CodeDeque, ) { match subterm { &Term::AnonVar => { @@ -344,13 +423,13 @@ impl<'b> CodeGenerator<'b> { Term::PartialString(ref cell, ..) | Term::CompleteString(ref cell, ..) => { self.marker.mark_non_var::(Level::Deep, term_loc, cell, target); - target.push(Target::clause_arg_to_instr(cell.get())); + target.push_back(Target::clause_arg_to_instr(cell.get())); } &Term::Literal(_, ref constant) => { - target.push(Target::constant_subterm(constant.clone())); + target.push_back(Target::constant_subterm(constant.clone())); } - &Term::Var(ref cell, ref var) => { - self.deep_var_instr::(cell, var, term_loc, target); + &Term::Var(ref cell, ref var_ptr) => { + self.deep_var_instr::(cell, var_ptr.to_var_num().unwrap(), term_loc, target); } }; } @@ -359,13 +438,13 @@ impl<'b> CodeGenerator<'b> { &mut self, iter: Iter, term_loc: GenContext, - ) -> Code + ) -> CodeDeque where Target: crate::targets::CompilationTarget<'a>, Iter: Iterator>, CodeGenerator<'b>: AddToFreeList<'a, Target> { - let mut target: Code = Vec::new(); + let mut target = CodeDeque::new(); for term in iter { match term { @@ -378,11 +457,11 @@ impl<'b> CodeGenerator<'b> { } TermRef::Clause(lvl, cell, name, terms) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); - target.push(Target::to_structure(name, terms.len(), cell.get())); + target.push_back(Target::to_structure(name, terms.len(), cell.get())); as AddToFreeList<'a, Target>>::add_term_to_free_list(self, cell.get()); - if let Some(instr) = target.last_mut() { + if let Some(instr) = target.back_mut() { if let Some(term) = terms.last() { trim_structure_by_last_arg(instr, term); } @@ -398,7 +477,7 @@ impl<'b> CodeGenerator<'b> { } TermRef::Cons(lvl, cell, head, tail) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); - target.push(Target::to_list(lvl, cell.get())); + target.push_back(Target::to_list(lvl, cell.get())); as AddToFreeList<'a, Target>>::add_term_to_free_list(self, cell.get()); @@ -410,44 +489,31 @@ impl<'b> CodeGenerator<'b> { } TermRef::Literal(lvl @ Level::Shallow, cell, Literal::String(ref string)) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); - target.push(Target::to_pstr(lvl, *string, cell.get(), false)); + target.push_back(Target::to_pstr(lvl, *string, cell.get(), false)); } TermRef::Literal(lvl @ Level::Shallow, cell, constant) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); - target.push(Target::to_constant(lvl, *constant, cell.get())); + target.push_back(Target::to_constant(lvl, *constant, cell.get())); } TermRef::PartialString(lvl, cell, string, tail) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); let atom = self.atom_tbl.build_with(&string); - target.push(Target::to_pstr(lvl, atom, cell.get(), true)); + target.push_back(Target::to_pstr(lvl, atom, cell.get(), true)); self.subterm_to_instr::(tail, term_loc, &mut target); } TermRef::CompleteString(lvl, cell, atom) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); - target.push(Target::to_pstr(lvl, atom, cell.get(), false)); - } - TermRef::Var(lvl @ Level::Shallow, cell, var) if var.as_str() == Some("!") => { - if self.marker.is_unbound(var.clone()) { - if term_loc != GenContext::Head { - self.marker.mark_reserved_var::( - var.clone(), - lvl, - cell, - term_loc, - &mut target, - perm_v!(1), - false, - ); - - continue; - } - } - - self.marker.mark_var::(var.clone(), lvl, cell, term_loc, &mut target); + target.push_back(Target::to_pstr(lvl, atom, cell.get(), false)); } TermRef::Var(lvl @ Level::Shallow, cell, var) => { - self.marker.mark_var::(var.clone(), lvl, cell, term_loc, &mut target); + self.marker.mark_var::( + var.to_var_num().unwrap(), + lvl, + cell, + term_loc, + &mut target, + ); } _ => {} }; @@ -456,82 +522,27 @@ impl<'b> CodeGenerator<'b> { target } - /* - fn collect_var_data<'a>(&mut self, mut iter: ChunkedIterator<'a>) -> ConjunctInfo<'a> { - let mut vs = VariableFixtures::new(); - - while let Some((chunk_num, lt_arity, chunked_terms)) = iter.next() { - for (i, chunked_term) in chunked_terms.iter().enumerate() { - let term_loc = match chunked_term { - &ChunkedTerm::HeadClause(..) => GenContext::Head, - &ChunkedTerm::BodyTerm(_) => { - if i < chunked_terms.len() - 1 { - GenContext::Mid(chunk_num) - } else { - GenContext::Last(chunk_num) - } - } - }; - - self.update_var_count(chunked_term.post_order_iter()); - vs.mark_vars_in_chunk(chunked_term.post_order_iter(), lt_arity, term_loc); - } + fn add_call(&mut self, code: &mut CodeDeque, call_instr: Instruction, call_policy: CallPolicy) { + if self.marker.in_tail_position && self.marker.var_data.allocates { + code.push_back(instr!("deallocate")); } - let num_of_chunks = iter.chunk_num; - let has_deep_cut = iter.encountered_deep_cut(); - - vs.populate_restricting_sets(); - vs.set_perm_vals(has_deep_cut); - - let vs = self.marker.drain_var_data(vs, num_of_chunks); - ConjunctInfo::new(vs, num_of_chunks, has_deep_cut) - } - */ - - fn add_conditional_call(&mut self, code: &mut Code, qt: &QueryTerm, pvs: usize) { - match qt { - &QueryTerm::Jump(ref vars) => { - self.jmp_by_locs.push(code.len()); - code.push(instr!("jmp_by_call", vars.len(), 0, pvs)); - } - &QueryTerm::Clause(_, ref ct, _, CallPolicy::Default) => { - code.push(call_clause_by_default!(ct.clone(), pvs)); - } - &QueryTerm::Clause(_, ref ct, _, CallPolicy::Counted) => { - code.push(call_clause!(ct.clone(), pvs)); - } - _ => {} - } - } - - fn lco(code: &mut Code) -> usize { - let mut dealloc_index = code.len() - 1; - let last_instr = code.pop(); - - match last_instr { - Some(instr @ Instruction::Proceed) => { - code.push(instr); - } - Some(instr @ Instruction::Cut(_)) => { - dealloc_index += 1; - code.push(instr); - } - Some(mut instr) if instr.is_ctrl_instr() => { - code.push(if instr.perm_vars_mut().is_some() { - instr.to_execute() + match call_policy { + CallPolicy::Default => { + if self.marker.in_tail_position { + code.push_back(call_instr.to_execute().to_default()); } else { - dealloc_index += 1; - instr - }); + code.push_back(call_instr.to_default()) + } } - Some(instr) => { - code.push(instr); + CallPolicy::Counted => { + if self.marker.in_tail_position { + code.push_back(call_instr.to_execute()); + } else { + code.push_back(call_instr) + } } - None => {} } - - dealloc_index } fn compile_inlined<'a>( @@ -539,9 +550,9 @@ impl<'b> CodeGenerator<'b> { ct: &InlinedClauseType, terms: &'a Vec, term_loc: GenContext, - code: &mut Code, + code: &mut CodeDeque, ) -> Result<(), CompilationError> { - match ct { + let call_instr = match ct { &InlinedClauseType::CompareNumber(mut cmp) => { self.marker.reset_arg(2); @@ -559,29 +570,29 @@ impl<'b> CodeGenerator<'b> { let at_1 = at_1.unwrap_or(interm!(1)); let at_2 = at_2.unwrap_or(interm!(2)); - code.push(compare_number_instr!(cmp, at_1, at_2)); + compare_number_instr!(cmp, at_1, at_2) } &InlinedClauseType::IsAtom(..) => match &terms[0] { &Term::Literal(_, Literal::Char(_)) | &Term::Literal(_, Literal::Atom(atom!("[]"))) | &Term::Literal(_, Literal::Atom(..)) => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("atom", r, 0)); + instr!("atom", r) } _ => { - code.push(instr!("$fail", 0)); + instr!("$fail") } }, &InlinedClauseType::IsAtomic(..) => match &terms[0] { @@ -590,26 +601,26 @@ impl<'b> CodeGenerator<'b> { &Term::Cons(..) | &Term::PartialString(..) | &Term::CompleteString(..) => { - code.push(instr!("$fail", 0)); + instr!("$fail") } &Term::Literal(_, Literal::String(_)) => { - code.push(instr!("$fail", 0)); + instr!("$fail") } &Term::Literal(..) => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("atomic", r, 0)); + instr!("atomic", r) } }, &InlinedClauseType::IsCompound(..) => match &terms[0] { @@ -618,57 +629,57 @@ impl<'b> CodeGenerator<'b> { &Term::PartialString(..) | &Term::CompleteString(..) | &Term::Literal(_, Literal::String(..)) => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("compound", r, 0)); + instr!("compound", r) } _ => { - code.push(instr!("$fail", 0)); + instr!("$fail") } }, &InlinedClauseType::IsRational(..) => match &terms[0] { &Term::Literal(_, Literal::Rational(_)) => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); - let r = self.marker.mark_non_callable(name.clone(), 1, term_loc, vr, code); - code.push(instr!("rational", r, 0)); + let r = self.marker.mark_non_callable(name.to_var_num().unwrap(), 1, term_loc, vr, code); + instr!("rational", r) } _ => { - code.push(instr!("$fail", 0)); + instr!("$fail") } }, &InlinedClauseType::IsFloat(..) => match &terms[0] { &Term::Literal(_, Literal::Float(_)) => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("float", r, 0)); + instr!("float", r) } _ => { - code.push(instr!("$fail", 0)); + instr!("$fail") } }, &InlinedClauseType::IsNumber(..) => match &terms[0] { @@ -676,66 +687,66 @@ impl<'b> CodeGenerator<'b> { &Term::Literal(_, Literal::Rational(_)) | &Term::Literal(_, Literal::Integer(_)) | &Term::Literal(_, Literal::Fixnum(_)) => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("number", r, 0)); + instr!("number", r) } _ => { - code.push(instr!("$fail", 0)); + instr!("$fail") } }, &InlinedClauseType::IsNonVar(..) => match &terms[0] { &Term::AnonVar => { - code.push(instr!("$fail", 0)); + instr!("$fail") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("nonvar", r, 0)); + instr!("nonvar", r) } _ => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } }, &InlinedClauseType::IsInteger(..) => match &terms[0] { &Term::Literal(_, Literal::Integer(_)) | &Term::Literal(_, Literal::Fixnum(_)) => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("integer", r, 0)); + instr!("integer", r) } _ => { - code.push(instr!("$fail", 0)); + instr!("$fail") } }, &InlinedClauseType::IsVar(..) => match &terms[0] { @@ -744,26 +755,29 @@ impl<'b> CodeGenerator<'b> { &Term::Cons(..) | &Term::PartialString(..) | &Term::CompleteString(..) => { - code.push(instr!("$fail", 0)); + instr!("$fail") } &Term::AnonVar => { - code.push(instr!("$succeed", 0)); + instr!("$succeed") } &Term::Var(ref vr, ref name) => { self.marker.reset_arg(1); let r = self.marker.mark_non_callable( - name.clone(), + name.to_var_num().unwrap(), 1, term_loc, vr, code, ); - code.push(instr!("var", r, 0)); + instr!("var", r) } }, - } + }; + + // inlined predicates are never counted, so this overrides nothing. + self.add_call(code, call_instr, CallPolicy::Counted); Ok(()) } @@ -782,7 +796,7 @@ impl<'b> CodeGenerator<'b> { fn compile_is_call( &mut self, terms: &Vec, - code: &mut Code, + code: &mut CodeDeque, term_loc: GenContext, call_policy: CallPolicy, ) -> Result<(), CompilationError> { @@ -798,8 +812,11 @@ impl<'b> CodeGenerator<'b> { let at = match &terms[0] { &Term::Var(ref vr, ref name) => { + let var_num = name.to_var_num().unwrap(); + self.marker.mark_temp_to_safe_perm(var_num); + self.marker.mark_var::( - name.clone(), + var_num, Level::Shallow, vr, term_loc, @@ -813,208 +830,189 @@ impl<'b> CodeGenerator<'b> { c @ Literal::Rational(_) | c @ Literal::Fixnum(_)) => { let v = HeapCellValue::from(c); - code.push(instr!("put_constant", Level::Shallow, v, temp_v!(1))); + code.push_back(instr!("put_constant", Level::Shallow, v, temp_v!(1))); self.marker.advance_arg(); compile_expr!(self, &terms[1], term_loc, code) } _ => { - code.push(instr!("$fail", 0)); + code.push_back(instr!("$fail")); return Ok(()); } }; let at = at.unwrap_or(interm!(1)); + self.add_call(code, instr!("is", temp_v!(1), at), call_policy); - Ok(if let CallPolicy::Default = call_policy { - code.push(instr!("is", default, temp_v!(1), at, 0)); - } else { - code.push(instr!("is", temp_v!(1), at, 0)); - }) - } - - #[inline] - fn compile_unblocked_cut(&mut self, code: &mut Code, cell: &Cell) { - let r = self.marker.get(Var::from("!")); - cell.set(VarReg::Norm(r)); - code.push(instr!("$set_cp", cell.get().norm(), 0)); + Ok(()) } fn compile_seq<'a>( &mut self, - iter: ChunkedIterator<'a>, - conjunct_info: &ConjunctInfo, - code: &mut Code, + clauses: &ChunkedTermVec, + code: &mut CodeDeque, ) -> Result<(), CompilationError> { - for (chunk_num, _, terms) in iter.rule_body_iter() { - for (i, term) in terms.iter().enumerate() { - let term_loc = if i + 1 < terms.len() { - GenContext::Mid(chunk_num) - } else { - GenContext::Last(chunk_num) - }; + let mut chunk_num = 0; + let mut branch_code_stack = BranchCodeStack::new(); + let mut clause_iter = ClauseIterator::new(clauses); - match *term { - &QueryTerm::UnblockedCut(ref cell) => self.compile_unblocked_cut(code, cell), - &QueryTerm::BlockedCut => code.push(if chunk_num == 0 { - Instruction::NeckCut - } else { - Instruction::Cut(perm_v!(1)) - }), - &QueryTerm::Clause( - _, - ClauseType::BuiltIn(BuiltInClauseType::Is(..)), - ref terms, - call_policy, - ) => self.compile_is_call(terms, code, term_loc, call_policy)?, - &QueryTerm::Clause(_, ClauseType::Inlined(ref ct), ref terms, _) => { - self.compile_inlined(ct, terms, term_loc, code)? - } - _ => { - let num_perm_vars = if chunk_num == 0 { - conjunct_info.perm_vars() + while let Some(clause_item) = clause_iter.next() { + match clause_item { + ClauseItem::Chunk(chunk) => { + for (idx, term) in chunk.iter().enumerate() { + let term_loc = if idx + 1 < chunk.len() { + GenContext::Mid(chunk_num) } else { - conjunct_info.perm_vs.vars_above_threshold(i + 1) + self.marker.in_tail_position = clause_iter.in_tail_position(); + GenContext::Last(chunk_num) }; - self.compile_query_line(term, term_loc, code, num_perm_vars); + match term { + &QueryTerm::GetLevel(var_num) => { + let code = branch_code_stack.code(code); + let r = self.marker.mark_cut_var(var_num, chunk_num); + code.push_back(instr!("get_level", r)); + } + &QueryTerm::GetCutPoint { var_num, prev_b } => { + let code = branch_code_stack.code(code); + let r = self.marker.mark_cut_var(var_num, chunk_num); - if self.marker.max_reg_allocated() > MAX_ARITY { - return Err(CompilationError::ExceededMaxArity); + code.push_back(if prev_b { + instr!("get_prev_level", r) + } else { + instr!("get_cut_point", r) + }); + } + &QueryTerm::GlobalCut(var_num) => { + let code = branch_code_stack.code(code); + + if chunk_num == 0 { + code.push_back(instr!("neck_cut")); + } else { + let r = self.marker.get_binding(var_num); + // let r = self.marker.mark_cut_var(var_num, chunk_num); + code.push_back(instr!("cut", r)); + } + + if self.marker.in_tail_position { + if self.marker.var_data.allocates { + code.push_back(instr!("deallocate")); + } + + code.push_back(instr!("proceed")); + } + } + &QueryTerm::LocalCut(var_num) => { + let code = branch_code_stack.code(code); + let r = self.marker.get_binding(var_num); + // let r = self.marker.mark_cut_var(var_num, chunk_num); + code.push_back(instr!("cut", r)); + + if self.marker.in_tail_position { + if self.marker.var_data.allocates { + code.push_back(instr!("deallocate")); + } + + code.push_back(instr!("proceed")); + } + } + &QueryTerm::Clause( + _, + ClauseType::BuiltIn(BuiltInClauseType::Is(..)), + ref terms, + call_policy, + ) => self.compile_is_call(terms, branch_code_stack.code(code), term_loc, call_policy)?, + &QueryTerm::Clause(_, ClauseType::Inlined(ref ct), ref terms, _) => { + self.compile_inlined(ct, terms, term_loc, branch_code_stack.code(code))? + } + &QueryTerm::Fail => { + branch_code_stack.code(code).push_back(instr!("$fail")); + } + term @ &QueryTerm::Clause(..) => { + self.compile_query_line(term, term_loc, branch_code_stack.code(code)); + + if self.marker.max_reg_allocated() > MAX_ARITY { + return Err(CompilationError::ExceededMaxArity); + } + } } } + + chunk_num += 1; + self.marker.in_tail_position = false; + self.marker.reset_contents(); + } + ClauseItem::FirstBranch(num_branches) => { + branch_code_stack.add_new_branch_stack(); + branch_code_stack.add_new_branch(); + + self.marker.add_branch_stack(num_branches); + self.marker.add_branch(); + } + ClauseItem::NextBranch => { + branch_code_stack.add_new_branch(); + self.marker.add_branch(); + self.marker.incr_current_branch(); + } + ClauseItem::BranchEnd(depth) => { + if !clause_iter.in_tail_position() { + let subsumed_hits = branch_code_stack.push_missing_vars(depth, &mut self.marker); + self.marker.pop_branch(depth, subsumed_hits); + branch_code_stack.push_jump_instrs(depth); + } else { + self.marker.drain_branches(depth); + } + + let settings = CodeGenSettings { + non_counted_bt: self.settings.non_counted_bt, + is_extensible: false, + global_clock_tick: None, + }; + + let branch_code = branch_code_stack.pop_branch(depth, settings); + branch_code_stack.code(code).extend(branch_code); } } + } - self.marker.reset_contents(); + if self.marker.var_data.allocates { + code.push_front(instr!("allocate", self.marker.num_perm_vars())); } Ok(()) } - fn compile_seq_prelude(&mut self, var_data: &VarData, body: &mut Code) { - /* - if conjunct_info.allocates() { - let perm_vars = conjunct_info.perm_vars(); - - body.push(Instruction::Allocate(perm_vars)); - - if conjunct_info.has_deep_cut { - body.push(Instruction::GetLevel(perm_v!(1))); - } - } - */ - } - - fn compile_cleanup( - &mut self, - code: &mut Code, - conjunct_info: &ConjunctInfo, - toc: &QueryTerm, - ) { - // add a proceed to bookend any trailing cuts. - match toc { - &QueryTerm::BlockedCut | &QueryTerm::UnblockedCut(..) => { - code.push(instr!("proceed")); - } - _ => {} - } - - // perform lco. - let dealloc_index = Self::lco(code); - - if conjunct_info.allocates() { - let offset = self.global_jmp_by_locs_offset; - - if let Some(jmp_by_offset) = self.jmp_by_locs[offset..].last_mut() { - if *jmp_by_offset == dealloc_index { - *jmp_by_offset += 1; - } - } - - code.insert(dealloc_index, instr!("deallocate")); - } - } - - pub(crate) fn compile_rule(&mut self, rule: &Rule) -> Result { - // let iter = ChunkedIterator::from_rule(rule); - // let conjunct_info = self.collect_var_data(iter); - - let &Rule { - head: (_, ref args, ref p1), - ref clauses, - ref var_data, - } = rule; - - let mut code = Code::new(); + pub(crate) fn compile_rule(&mut self, rule: &Rule, var_data: VarData) -> Result { + let Rule { head: (_, args), clauses } = rule; + self.marker.var_data = var_data; + let mut code = VecDeque::new(); self.marker.reset_at_head(args); - self.compile_seq_prelude(&var_data, &mut code); - let iter = FactIterator::from_rule_head_clause(args); - let mut fact = self.compile_target::(iter, GenContext::Head); + let iter = FactIterator::from_rule_head_clause(&args); + let fact = self.compile_target::(iter, GenContext::Head); if self.marker.max_reg_allocated() > MAX_ARITY { return Err(CompilationError::ExceededMaxArity); } self.marker.reset_free_list(); + code.extend(fact.into_iter()); - let mut unsafe_var_marker = UnsafeVarMarker::new(); + self.compile_seq(clauses, &mut code)?; - if !fact.is_empty() { - unsafe_var_marker = self.mark_unsafe_fact_vars(&mut fact); - code.extend(fact.into_iter()); - } - - let iter = ChunkedIterator::from_rule_body(p1, clauses); - self.compile_seq(iter, &conjunct_info, &mut code)?; - - unsafe_var_marker.mark_unsafe_instrs(&mut code); - - self.compile_cleanup(&mut code, &conjunct_info, clauses.last().unwrap_or(p1)); - - Ok(code) + Ok(Vec::from(code)) } - fn mark_unsafe_fact_vars(&self, fact: &mut Code) -> UnsafeVarMarker { - let mut safe_vars = IndexSet::new(); - - for fact_instr in fact.iter_mut() { - match fact_instr { - &mut Instruction::UnifyValue(r) => { - if !safe_vars.contains(&r) { - *fact_instr = Instruction::UnifyLocalValue(r); - safe_vars.insert(r); - } - } - &mut Instruction::UnifyVariable(r) => { - safe_vars.insert(r); - } - _ => {} - } - } - - UnsafeVarMarker::from_fact_vars(safe_vars) - } - - pub(crate) fn compile_fact(&mut self, fact: &Fact) -> Result { - self.update_var_count(post_order_iter(term)); - - // let mut vs = VariableFixtures::new(); - - // vs.mark_vars_in_chunk(post_order_iter(term), term.arity(), GenContext::Head); - - // vs.populate_restricting_sets(); - // self.marker.drain_var_data(vs, 1); - + pub(crate) fn compile_fact(&mut self, fact: &Fact, var_data: VarData) -> Result { let mut code = Vec::new(); + self.marker.var_data = var_data; - if let &Term::Clause(_, _, ref args) = term { + if let Term::Clause(_, _, args) = &fact.head { self.marker.reset_at_head(args); - let iter = FactInstruction::iter(term); - let mut compiled_fact = self.compile_target::( + let iter = FactInstruction::iter(&fact.head); + let compiled_fact = self.compile_target::( iter, GenContext::Head, ); @@ -1023,40 +1021,27 @@ impl<'b> CodeGenerator<'b> { return Err(CompilationError::ExceededMaxArity); } - self.mark_unsafe_fact_vars(&mut compiled_fact); - - if !compiled_fact.is_empty() { - code.extend(compiled_fact.into_iter()); - } + code.extend(compiled_fact.into_iter()); } code.push(instr!("proceed")); Ok(code) } - fn compile_query_line( - &mut self, - term: &QueryTerm, - term_loc: GenContext, - code: &mut Code, - num_perm_vars_left: usize, - ) { + fn compile_query_line(&mut self, term: &QueryTerm, term_loc: GenContext, code: &mut CodeDeque) { self.marker.reset_arg(term.arity()); - let iter = query_term_post_order_iter(term); + let iter = QueryIterator::new(term); let query = self.compile_target::(iter, term_loc); code.extend(query.into_iter()); - self.add_conditional_call(code, term, num_perm_vars_left); - } - #[inline] - fn increment_jmp_by_locs_by(&mut self, incr: usize) { - let offset = self.global_jmp_by_locs_offset; - - for loc in &mut self.jmp_by_locs[offset..] { - *loc += incr; - } + match term { + &QueryTerm::Clause(_, ref ct, _, call_policy) => { + self.add_call(code, ct.to_instr(), call_policy); + } + _ => unreachable!() + }; } fn split_predicate(clauses: &[PredicateClause]) -> Vec { @@ -1121,30 +1106,35 @@ impl<'b> CodeGenerator<'b> { fn compile_pred_subseq( &mut self, - clauses: &[PredicateClause], + clauses: &mut [PredicateClause], optimal_index: usize, ) -> Result { let mut code = VecDeque::new(); let mut code_offsets = CodeOffsets::new(I::new(), optimal_index + 1); let mut skip_stub_try_me_else = false; - let jmp_by_locs_len = self.jmp_by_locs.len(); + let clauses_len = clauses.len(); - for (i, clause) in clauses.iter().enumerate() { + for (i, clause) in clauses.iter_mut().enumerate() { self.marker.reset(); let mut clause_index_info = ClauseIndexInfo::new(code.len()); - self.global_jmp_by_locs_offset = self.jmp_by_locs.len(); let clause_code = match clause { - &PredicateClause::Fact(ref fact, ..) => self.compile_fact(fact)?, - &PredicateClause::Rule(ref rule, ..) => self.compile_rule(rule)?, + PredicateClause::Fact(fact, var_data) => { + let var_data = std::mem::replace(var_data, VarData::default()); + self.compile_fact(&fact, var_data)? + } + PredicateClause::Rule(rule, var_data) => { + let var_data = std::mem::replace(var_data, VarData::default()); + self.compile_rule(&rule, var_data)? + } }; - if clauses.len() > 1 { + if clauses_len > 1 { let choice = match i { 0 => self.settings.internal_try_me_else(clause_code.len() + 1), - _ if i == clauses.len() - 1 => self.settings.internal_trust_me(), + _ if i + 1 == clauses_len => self.settings.internal_trust_me(), _ => self.settings.internal_retry_me_else(clause_code.len() + 1), }; @@ -1170,45 +1160,23 @@ impl<'b> CodeGenerator<'b> { if let Some(arg) = arg { let index = code.len(); - if clauses.len() > 1 || self.settings.is_extensible { + if clauses_len > 1 || self.settings.is_extensible { code_offsets.index_term(arg, index, &mut clause_index_info, self.atom_tbl); } } - if !(code_offsets.no_indices() && clauses.len() == 1 && self.settings.is_extensible) { - // the peculiar condition of this block, when false, - // anticipates code.pop_front() being called about a - // dozen lines below. - - if !skip_stub_try_me_else { - // if the condition is false, code_offsets.no_indices() is false, - // so don't repeat the work of the condition on skip_stub_try_me_else - // below. - self.increment_jmp_by_locs_by(code.len()); - } - } - self.skeleton.clauses.push_back(clause_index_info); code.extend(clause_code.into_iter()); } - let index_code = if clauses.len() > 1 || self.settings.is_extensible { + let index_code = if clauses_len > 1 || self.settings.is_extensible { code_offsets.compute_indices(skip_stub_try_me_else) } else { vec![] }; - self.global_jmp_by_locs_offset = jmp_by_locs_len; - if !index_code.is_empty() { code.push_front(Instruction::IndexingCode(index_code)); - - if skip_stub_try_me_else { - // skip the TryMeElse(0) also. - self.increment_jmp_by_locs_by(2); - } else { - self.increment_jmp_by_locs_by(1); - } } else if clauses.len() == 1 && self.settings.is_extensible { // the condition is the value of skip_stub_try_me_else, which is // true if the predicate is not dynamic. This operation must apply @@ -1223,7 +1191,7 @@ impl<'b> CodeGenerator<'b> { pub(crate) fn compile_predicate( &mut self, - clauses: &Vec, + mut clauses: Vec, ) -> Result { let mut code = Code::new(); @@ -1234,12 +1202,12 @@ impl<'b> CodeGenerator<'b> { let skel_lower_bound = self.skeleton.clauses.len(); let code_segment = if self.settings.is_dynamic() { self.compile_pred_subseq::( - &clauses[left..right], + &mut clauses[left..right], instantiated_arg_index, )? } else { self.compile_pred_subseq::( - &clauses[left..right], + &mut clauses[left..right], instantiated_arg_index, )? }; @@ -1271,12 +1239,17 @@ impl<'b> CodeGenerator<'b> { } } - self.increment_jmp_by_locs_by(code.len()); - self.global_jmp_by_locs_offset = self.jmp_by_locs.len(); - code.extend(code_segment.into_iter()); } + /* + for line in &code { + println!("{:?}", line); + } + + println!(""); + */ + Ok(code) } } diff --git a/src/debray_allocator.rs b/src/debray_allocator.rs index 2ad19cab..2f8d442e 100644 --- a/src/debray_allocator.rs +++ b/src/debray_allocator.rs @@ -1,42 +1,179 @@ -use indexmap::IndexMap; - use crate::allocator::*; -use crate::fixtures::*; +use crate::codegen::SubsumedBranchHits; use crate::forms::Level; use crate::instructions::*; -use crate::machine::machine_indices::*; +use crate::machine::disjuncts::VarData; use crate::parser::ast::*; use crate::targets::*; +use crate::variable_records::*; -use crate::temp_v; - +use bit_set::*; +use bitvec::prelude::*; use fxhash::FxBuildHasher; +use indexmap::IndexMap; use std::cell::Cell; -use std::collections::BTreeSet; +use std::collections::VecDeque; + +pub type BranchHits = IndexMap; // key: var_num, value: branch arm occurrences. + +#[derive(Debug, Default)] +pub struct BranchOccurrences { + pub hits: BranchHits, + pub shallow_safety: BitSet, // unset means safe, set means unsafe (after the branch merge) + pub deep_safety: BitSet, + pub num_branches: usize, + pub current_branch: usize, + pub subsumed_hits: SubsumedBranchHits, +} + +impl BranchOccurrences { + fn new(num_branches: usize) -> Self { + Self { + hits: BranchHits::with_hasher(FxBuildHasher::default()), + shallow_safety: BitSet::default(), + deep_safety: BitSet::default(), + num_branches, + current_branch: 0, + subsumed_hits: SubsumedBranchHits::with_hasher(FxBuildHasher::default()), + } + } +} #[derive(Debug)] pub(crate) struct DebrayAllocator { - bindings: IndexMap, + pub(crate) var_data: VarData, // var_data replaces bindings. + pub(crate) branch_stack: Vec, + pub(crate) in_tail_position: bool, + // bindings: IndexMap, // VarNum -> VarWitness arg_c: usize, temp_lb: usize, + perm_lb: usize, arity: usize, // 0 if not at head. - contents: IndexMap, - in_use: BTreeSet, - free_list: Vec, + shallow_temp_mappings: IndexMap, + in_use: BitSet, // deep and non-var allocations + temp_free_list: Vec, + perm_free_list: VecDeque<(usize, usize)>, // chunk_num, var_num } impl DebrayAllocator { - fn is_curr_arg_distinct_from(&self, var: &Var) -> bool { - match self.contents.get(&self.arg_c) { - Some(t_var) if *t_var != *var => true, + pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) { + if let Some(occurrences) = self.branch_stack.last_mut() { + debug_assert!(occurrences.current_branch < occurrences.num_branches); + + let num_branches = occurrences.num_branches; + + let entry = occurrences.hits.entry(var_num) + .or_insert_with(|| BitVec::repeat(false, num_branches)); + + entry.set(occurrences.current_branch, true); + occurrences.subsumed_hits.insert(var_num); + } + } + + pub(crate) fn add_branch_stack(&mut self, num_branches: usize) { + self.branch_stack.push(BranchOccurrences::new(num_branches)); + } + + pub(crate) fn add_branch(&mut self) { + let branch_occurrences = self.branch_stack.last_mut().unwrap(); + + for var_num in branch_occurrences.subsumed_hits.drain(..) { + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, ref mut allocation) => { + match allocation { + PermVarAllocation::Done { shallow_safety, deep_safety, .. } => { + if !shallow_safety.unneeded() { + branch_occurrences.shallow_safety.insert(var_num); + } + + if !deep_safety.unneeded() { + branch_occurrences.deep_safety.insert(var_num); + } + } + _ => { + unreachable!(); + } + } + + *allocation = PermVarAllocation::Pending; + } + _ => unreachable!(), + } + } + } + + #[inline] + pub(crate) fn incr_current_branch(&mut self) { + let branch_occurrences = self.branch_stack.last_mut().unwrap(); + branch_occurrences.current_branch += 1; + } + + #[inline] + pub(crate) fn drain_branches(&mut self, depth: usize) -> std::vec::Drain { + let start_idx = self.branch_stack.len() - depth; + self.branch_stack.drain(start_idx ..) + } + + pub(crate) fn pop_branch(&mut self, depth: usize, subsumed_hits: SubsumedBranchHits) { + let removed_branches = self.drain_branches(depth); + + let (deep_safety, shallow_safety) = removed_branches + .into_iter() + .fold((BitSet::default(), BitSet::default()), + |(mut deep_safety, mut shallow_safety), branch_occurrences| { + deep_safety.union_with(&branch_occurrences.deep_safety); + shallow_safety.union_with(&branch_occurrences.shallow_safety); + + (deep_safety, shallow_safety) + }); + + let (deep_safety, shallow_safety) = match self.branch_stack.last_mut() { + Some(latest_branch) => { + latest_branch.deep_safety.union_with(&deep_safety); + latest_branch.shallow_safety.union_with(&shallow_safety); + + (&latest_branch.deep_safety, &latest_branch.shallow_safety) + } + None => (&deep_safety, &shallow_safety) + }; + + for var_num in subsumed_hits.iter().cloned() { + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, ref mut allocation) => { + let shallow_safety = VarSafetyStatus::needed_if( + shallow_safety.contains(var_num), + ); + + let deep_safety = VarSafetyStatus::needed_if( + deep_safety.contains(var_num), + ); + + *allocation = PermVarAllocation::Done { shallow_safety, deep_safety }; + } + _ => unreachable!() + } + } + + if self.branch_stack.len() > 0 { + for var_num in subsumed_hits { + self.add_branch_occurrence(var_num); + } + } + } + + fn is_curr_arg_distinct_from(&self, var_num: usize) -> bool { + match self.shallow_temp_mappings.get(&self.arg_c).cloned() { + Some(t_var) => t_var != var_num, _ => false, } } - fn occurs_shallowly_in_head(&self, var: &Var, r: usize) -> bool { - match self.bindings.get(var).unwrap() { - &VarAlloc::Temp(_, _, ref tvd) => tvd.use_set.contains(&(GenContext::Head, r)), + fn occurs_shallowly_in_head(&self, var_num: usize, r: usize) -> bool { + match &self.var_data.records[var_num].allocation { + VarAlloc::Temp { temp_var_data, term_loc: GenContext::Head, .. } => { + temp_var_data.use_set.contains(&(GenContext::Head, r)) + } _ => false, } } @@ -44,13 +181,13 @@ impl DebrayAllocator { #[inline] fn is_in_use(&self, r: usize) -> bool { let in_use_range = r <= self.arity && r >= self.arg_c; - in_use_range || self.in_use.contains(&r) + in_use_range || self.in_use.contains(r) } - fn alloc_with_cr(&self, var: &Var) -> usize { - match self.bindings.get(var) { - Some(&VarAlloc::Temp(_, _, ref tvd)) => { - for &(_, reg) in tvd.use_set.iter() { + fn alloc_with_cr(&self, var_num: usize) -> usize { + match &self.var_data.records[var_num].allocation { + VarAlloc::Temp { temp_var_data, .. } => { + for &(_, reg) in temp_var_data.use_set.iter() { if !self.is_in_use(reg) { return reg; } @@ -60,7 +197,7 @@ impl DebrayAllocator { for reg in self.temp_lb.. { if !self.is_in_use(reg) { - if !tvd.no_use_set.contains(®) { + if !temp_var_data.no_use_set.contains(reg) { result = reg; break; } @@ -73,10 +210,10 @@ impl DebrayAllocator { } } - fn alloc_with_ca(&self, var: &Var) -> usize { - match self.bindings.get(var) { - Some(&VarAlloc::Temp(_, _, ref tvd)) => { - for &(_, reg) in tvd.use_set.iter() { + fn alloc_with_ca(&self, var_num: usize) -> usize { + match &self.var_data.records[var_num].allocation { + VarAlloc::Temp { temp_var_data, .. } => { + for &(_, reg) in temp_var_data.use_set.iter() { if !self.is_in_use(reg) { return reg; } @@ -86,8 +223,8 @@ impl DebrayAllocator { for reg in self.temp_lb.. { if !self.is_in_use(reg) { - if !tvd.no_use_set.contains(®) { - if !tvd.conflict_set.contains(®) { + if !temp_var_data.no_use_set.contains(reg) { + if !temp_var_data.conflict_set.contains(reg) { result = reg; break; } @@ -101,22 +238,25 @@ impl DebrayAllocator { } } - fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(Var, usize)> { + fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(usize, usize)> { // we want to allocate a register to the k^{th} parameter, par_k. // par_k may not be a temporary variable. let k = self.arg_c; - match self.contents.get(&k) { + match self.shallow_temp_mappings.get(&k).cloned() { Some(t_var) => { // suppose this branch fires. then t_var is a // temp. var. belonging to the current chunk. // consider its use set. T == par_k iff // (GenContext::Last(_), k) is in t_var.use_set. - let tvd = self.bindings.get(t_var).unwrap(); - if let &VarAlloc::Temp(_, _, ref tvd) = tvd { - if !tvd.use_set.contains(&(GenContext::Last(chunk_num), k)) { - return Some((t_var.clone(), self.alloc_with_ca(t_var))); + match &self.var_data.records[t_var].allocation { + VarAlloc::Temp { temp_var_data, .. } => { + if !temp_var_data.use_set.contains(&(GenContext::Last(chunk_num), k)) { + return Some((t_var, self.alloc_with_ca(t_var))); + } + } + _ => { } } @@ -129,21 +269,21 @@ impl DebrayAllocator { fn evacuate_arg<'a, Target: CompilationTarget<'a>>( &mut self, chunk_num: usize, - code: &mut Code, + code: &mut CodeDeque, ) { match self.alloc_in_last_goal_hint(chunk_num) { - Some((var, r)) => { + Some((var_num, r)) => { let k = self.arg_c; if r != k { let r = RegType::Temp(r); - code.push(Target::move_to_register(r, k)); + code.push_back(Target::move_to_register(r, k)); - self.contents.swap_remove(&k); - self.contents.insert(r.reg_num(), var.clone()); + self.shallow_temp_mappings.swap_remove(&k); + self.shallow_temp_mappings.insert(r.reg_num(), var_num); - self.record_register(var, r); + self.var_data.records[var_num].allocation.set_register(r.reg_num()); self.in_use.insert(r.reg_num()); } } @@ -153,27 +293,27 @@ impl DebrayAllocator { fn alloc_reg_to_var<'a, Target: CompilationTarget<'a>>( &mut self, - var: &Var, + var_num: usize, lvl: Level, term_loc: GenContext, - target: &mut Vec, + target: &mut CodeDeque, ) -> usize { match term_loc { GenContext::Head => { if let Level::Shallow = lvl { self.evacuate_arg::(0, target); - self.alloc_with_cr(var) + self.alloc_with_cr(var_num) } else { - self.alloc_with_ca(var) + self.alloc_with_ca(var_num) } } - GenContext::Mid(_) => self.alloc_with_ca(var), + GenContext::Mid(_) => self.alloc_with_ca(var_num), GenContext::Last(chunk_num) => { if let Level::Shallow = lvl { self.evacuate_arg::(chunk_num, target); - self.alloc_with_cr(var) + self.alloc_with_cr(var_num) } else { - self.alloc_with_ca(var) + self.alloc_with_ca(var_num) } } } @@ -182,15 +322,15 @@ impl DebrayAllocator { fn alloc_reg_to_non_var(&mut self) -> usize { let mut final_index = 0; - while let Some(r) = self.free_list.pop() { - if !self.in_use.contains(&r) { + while let Some(r) = self.temp_free_list.pop() { + if !self.is_in_use(r) { self.in_use.insert(r); return r; } } for index in self.temp_lb.. { - if !self.in_use.contains(&index) { + if !self.in_use.contains(index) { final_index = index; self.in_use.insert(final_index); break; @@ -201,38 +341,194 @@ impl DebrayAllocator { final_index } - fn in_place(&self, var: &Var, term_loc: GenContext, r: RegType, k: usize) -> bool { + fn in_place(&self, var_num: usize, term_loc: GenContext, r: RegType, k: usize) -> bool { match term_loc { GenContext::Head if !r.is_perm() => r.reg_num() == k, - _ => match self.bindings().get(var).unwrap() { - &VarAlloc::Temp(_, o, _) if r.reg_num() == k => o == k, - _ => false, + _ => { + match &self.var_data.records[var_num].allocation { + &VarAlloc::Temp { temp_reg, .. } if r.reg_num() == k => + temp_reg == k, + _ => false, + } }, } } + fn alloc_perm_var(&mut self, var_num: usize, chunk_num: usize) -> usize { + let p = if let Some(p) = self.pop_free_perm(chunk_num) { + p + } else { + let p = self.perm_lb; + self.perm_lb += 1; + + p + }; + + self.var_data.records[var_num].allocation = VarAlloc::Perm(p, PermVarAllocation::done()); + p + } + pub fn add_to_free_list(&mut self, r: RegType) { if let RegType::Temp(r) = r { - self.in_use.remove(&r); - self.free_list.push(r); + self.in_use.remove(r); + self.temp_free_list.push(r); } } pub fn reset_free_list(&mut self) { - self.free_list.clear(); + self.temp_free_list.clear(); + } + + #[inline(always)] + pub fn get_binding(&self, var_num: usize) -> RegType { + self.var_data.records[var_num].allocation.as_reg_type() + } + + pub fn num_perm_vars(&self) -> usize { + self.perm_lb - 1 + } + + pub fn increment_running_count(&mut self, var_num: usize) { + self.var_data.records[var_num].running_count += 1; + } + + fn pop_free_perm(&mut self, chunk_num: usize) -> Option { + if let Some((perm_chunk_num, var_num)) = self.perm_free_list.front().cloned() { + if chunk_num == perm_chunk_num { + None + } else { + self.perm_free_list.pop_front(); + + match &mut self.var_data.records[var_num].allocation { + &mut VarAlloc::Perm(p, ref mut allocation) => { + *allocation = PermVarAllocation::Pending; + Some(p) + } + _ => unreachable!() + } + } + } else { + None + } + } + + pub(crate) fn mark_temp_to_safe_perm(&mut self, var_num: usize) { + match &self.var_data.records[var_num].allocation { + &VarAlloc::Temp { to_perm_var_num: Some(perm_var_num), .. } => { + match &mut self.var_data.records[perm_var_num].allocation { + VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, shallow_safety, .. }) => { + *deep_safety = VarSafetyStatus::Unneeded; + *shallow_safety = VarSafetyStatus::Unneeded; + } + _ => unreachable!() + } + } + _ => { + } + } + } + + fn mark_safe_var(&mut self, var_num: usize, lvl: Level, term_loc: GenContext) { + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, shallow_safety, .. }) => { + // GetVariable in head chunk is considered safe. + if lvl == Level::Deep { + *deep_safety = VarSafetyStatus::Unneeded; + *shallow_safety = VarSafetyStatus::Unneeded; + } else if term_loc == GenContext::Head { + *shallow_safety = VarSafetyStatus::Unneeded; + } else { + if let Some(temp_var_num) = self.shallow_temp_mappings.get(&self.arg_c).cloned() { + match &mut self.var_data.records[temp_var_num].allocation { + VarAlloc::Temp { ref mut to_perm_var_num, .. } => { + *to_perm_var_num = Some(var_num); + } + _ => unreachable!() + } + } + } + } + VarAlloc::Temp { ref mut safety, .. } => { + *safety = VarSafetyStatus::Unneeded; + } + _ => { + unreachable!() + } + } + } + + fn argument_to_value<'a, Target: CompilationTarget<'a>>( + &mut self, + var_num: usize, + r: RegType, + arg_c: usize, + ) -> Instruction { + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, PermVarAllocation::Done { ref mut shallow_safety, .. }) => { + if !self.in_tail_position || shallow_safety.unneeded() { + Target::argument_to_value(r, arg_c) + } else { + *shallow_safety = VarSafetyStatus::Unneeded; + Target::unsafe_argument_to_value(r, arg_c) + } + } + VarAlloc::Temp { ref mut safety, .. } => { + if safety.unneeded() { + Target::argument_to_value(r, arg_c) + } else { + *safety = VarSafetyStatus::Unneeded; + Target::unsafe_argument_to_value(r, arg_c) + } + } + _ => { + unreachable!() + } + } + } + + fn subterm_to_value<'a, Target: CompilationTarget<'a>>( + &mut self, + var_num: usize, + r: RegType, + ) -> Instruction { + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, PermVarAllocation::Done { ref mut deep_safety, .. }) => { + if deep_safety.unneeded() { + Target::subterm_to_value(r) + } else { + *deep_safety = VarSafetyStatus::Unneeded; + Target::unsafe_subterm_to_value(r) + } + } + VarAlloc::Temp { ref mut safety, .. } => { + if safety.unneeded() { + Target::subterm_to_value(r) + } else { + *safety = VarSafetyStatus::Unneeded; + Target::unsafe_subterm_to_value(r) + } + } + _ => { + unreachable!() + } + } } } impl Allocator for DebrayAllocator { fn new() -> DebrayAllocator { - DebrayAllocator { + Self { + var_data: VarData::default(), + in_tail_position: false, arity: 0, arg_c: 1, temp_lb: 1, - bindings: IndexMap::with_hasher(FxBuildHasher::default()), - contents: IndexMap::with_hasher(FxBuildHasher::default()), - in_use: BTreeSet::new(), - free_list: vec![], + perm_lb: 1, + shallow_temp_mappings: IndexMap::with_hasher(FxBuildHasher::default()), + in_use: BitSet::default(), + temp_free_list: vec![], + perm_free_list: VecDeque::new(), + branch_stack: vec![], } } @@ -240,12 +536,12 @@ impl Allocator for DebrayAllocator { &mut self, lvl: Level, term_loc: GenContext, - code: &mut Code, + code: &mut CodeDeque, ) { let r = RegType::Temp(self.alloc_reg_to_non_var()); match lvl { - Level::Deep => code.push(Target::subterm_to_variable(r)), + Level::Deep => code.push_back(Target::subterm_to_variable(r)), Level::Root | Level::Shallow => { let k = self.arg_c; @@ -255,7 +551,7 @@ impl Allocator for DebrayAllocator { self.arg_c += 1; - code.push(Target::argument_to_variable(r, k)); + code.push_back(Target::argument_to_variable(r, k)); } }; } @@ -265,7 +561,7 @@ impl Allocator for DebrayAllocator { lvl: Level, term_loc: GenContext, cell: &'a Cell, - code: &mut Code, + code: &mut CodeDeque, ) { let r = cell.get(); @@ -292,39 +588,49 @@ impl Allocator for DebrayAllocator { fn mark_var<'a, Target: CompilationTarget<'a>>( &mut self, - var: Var, + var_num: usize, lvl: Level, cell: &'a Cell, term_loc: GenContext, - code: &mut Code, + code: &mut CodeDeque, ) { - let (r, is_new_var) = match self.get(var.clone()) { + let (r, is_new_var) = match self.get_binding(var_num) { RegType::Temp(0) => { - // here, r is temporary *and* unassigned. - let o = self.alloc_reg_to_var::(&var, lvl, term_loc, code); + let o = self.alloc_reg_to_var::(var_num, lvl, term_loc, code); cell.set(VarReg::Norm(RegType::Temp(o))); (RegType::Temp(o), true) } RegType::Perm(0) => { - let pr = cell.get().norm(); - self.record_register(var.clone(), pr); + let p = self.alloc_perm_var(var_num, term_loc.chunk_num()); + (RegType::Perm(p), true) + } + r @ RegType::Perm(_) => { + let is_new_var = match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, allocation) => if allocation.pending() { + *allocation = PermVarAllocation::done(); + true + } else { + false + }, + _ => unreachable!(), + }; - (pr, true) + (r, is_new_var) } r => (r, false), }; - self.mark_reserved_var::(var, lvl, cell, term_loc, code, r, is_new_var); + self.mark_reserved_var::(var_num, lvl, cell, term_loc, code, r, is_new_var); } fn mark_reserved_var<'a, Target: CompilationTarget<'a>>( &mut self, - var: Var, + var_num: usize, lvl: Level, cell: &'a Cell, term_loc: GenContext, - code: &mut Code, + code: &mut CodeDeque, r: RegType, is_new_var: bool, ) { @@ -332,86 +638,104 @@ impl Allocator for DebrayAllocator { Level::Root | Level::Shallow => { let k = self.arg_c; - if self.is_curr_arg_distinct_from(&var) { + if self.is_curr_arg_distinct_from(var_num) { self.evacuate_arg::(term_loc.chunk_num(), code); } - self.arg_c += 1; - cell.set(VarReg::ArgAndNorm(r, k)); - if !self.in_place(&var, term_loc, r, k) { + if !self.in_place(var_num, term_loc, r, k) { if is_new_var { - code.push(Target::argument_to_variable(r, k)); + self.mark_safe_var(var_num, lvl, term_loc); + code.push_back(Target::argument_to_variable(r, k)); } else { - code.push(Target::argument_to_value(r, k)); + code.push_back(self.argument_to_value::(var_num, r, k)); } } + + self.arg_c += 1; } Level::Deep if is_new_var => { if let GenContext::Head = term_loc { - if self.occurs_shallowly_in_head(&var, r.reg_num()) { - code.push(Target::subterm_to_value(r)); + if self.occurs_shallowly_in_head(var_num, r.reg_num()) { + code.push_back(self.subterm_to_value::(var_num, r)); } else { - code.push(Target::subterm_to_variable(r)); + self.mark_safe_var(var_num, lvl, term_loc); + code.push_back(Target::subterm_to_variable(r)); } } else { - code.push(Target::subterm_to_variable(r)); + self.mark_safe_var(var_num, lvl, term_loc); + code.push_back(Target::subterm_to_variable(r)); } } - Level::Deep => code.push(Target::subterm_to_value(r)), - }; + Level::Deep => code.push_back(self.subterm_to_value::(var_num, r)), + } + + let o = r.reg_num(); if !r.is_perm() { - let o = r.reg_num(); + self.shallow_temp_mappings.insert(o, var_num); + } else if r.is_perm() && is_new_var { + self.add_branch_occurrence(var_num); + } - self.contents.insert(o, var.clone()); - self.record_register(var.clone(), r); - self.in_use.insert(o); + let record = &mut self.var_data.records[var_num]; + + record.allocation.set_register(o); + + if record.running_count < record.num_occurrences { + record.running_count += 1; + } else if r.is_perm() { + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, allocation) => *allocation = PermVarAllocation::Pending, + _ => unreachable!(), + } + + self.perm_free_list.push_back((term_loc.chunk_num(), var_num)); + } + + self.in_use.insert(o); + } + + fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType { + match self.get_binding(var_num) { + RegType::Perm(0) | RegType::Temp(0) => { + RegType::Perm(self.alloc_perm_var(var_num, chunk_num)) + } + r => r, } } fn reset(&mut self) { - self.bindings.clear(); - self.contents.clear(); + self.perm_lb = 1; + self.shallow_temp_mappings.clear(); self.in_use.clear(); - self.free_list.clear(); + self.temp_free_list.clear(); } fn reset_contents(&mut self) { - self.contents.clear(); self.in_use.clear(); - self.free_list.clear(); + self.shallow_temp_mappings.clear(); + self.temp_free_list.clear(); } fn advance_arg(&mut self) { self.arg_c += 1; } - fn bindings(&self) -> &AllocVarDict { - &self.bindings - } - - fn bindings_mut(&mut self) -> &mut AllocVarDict { - &mut self.bindings - } - - fn take_bindings(self) -> AllocVarDict { - self.bindings - } - fn reset_at_head(&mut self, args: &Vec) { self.reset_arg(args.len()); self.arity = args.len(); for (idx, arg) in args.iter().enumerate() { if let &Term::Var(_, ref var) = arg { - let r = self.get(var.clone()); + let var_num = var.to_var_num().unwrap(); + let r = self.get_binding(var_num); if !r.is_perm() && r.reg_num() == 0 { self.in_use.insert(idx + 1); - self.contents.insert(idx + 1, var.clone()); - self.record_register(var.clone(), temp_v!(idx + 1)); + self.shallow_temp_mappings.insert(idx + 1, var_num); + self.var_data.records[var_num].allocation.set_register(idx + 1); } } } diff --git a/src/fixtures.rs b/src/fixtures.rs deleted file mode 100644 index 66734320..00000000 --- a/src/fixtures.rs +++ /dev/null @@ -1,342 +0,0 @@ -use crate::forms::*; -use crate::instructions::*; -use crate::machine::disjuncts::ClassifyInfo; -use crate::parser::ast::*; - -use bit_set::*; -use indexmap::{IndexMap, IndexSet}; - -pub(crate) type OccurrenceSet = IndexSet<(GenContext, usize)>; - -#[derive(Debug)] -pub(crate) struct TempVarData { - pub(crate) last_term_arity: usize, - pub(crate) use_set: OccurrenceSet, - pub(crate) no_use_set: BitSet, - pub(crate) conflict_set: BitSet, -} - -#[derive(Debug)] -pub(crate) struct TempVarStatus { - chunk_num: usize, - temp_var_data: TempVarData, -} - -// Perm: 0 initially, a stack register once processed. -// Temp: labeled with chunk_num and temp offset (unassigned if 0). -#[derive(Debug)] -pub(crate) enum VarAlloc { - Perm(usize), - Temp(usize, usize, TempVarData), -} - -impl VarAlloc { - pub(crate) fn as_reg_type(&self) -> RegType { - match self { - &VarAlloc::Temp(_, r, _) => RegType::Temp(r), - &VarAlloc::Perm(r) => RegType::Perm(r), - } - } -} - -impl TempVarData { - pub(crate) fn new(last_term_arity: usize) -> Self { - TempVarData { - last_term_arity: last_term_arity, - use_set: BitSet::::new(), - no_use_set: BitSet::new(), - conflict_set: BitSet::new(), - } - } - - pub(crate) fn uses_reg(&self, reg: usize) -> bool { - for &(_, nreg) in self.use_set.iter() { - if reg == nreg { - return true; - } - } - - return false; - } - - pub(crate) fn populate_conflict_set(&mut self) { - if self.last_term_arity > 0 { - let arity = self.last_term_arity; - let mut conflict_set: BitSet = (1..arity).collect(); - - for &(_, reg) in self.use_set.iter() { - conflict_set.remove(reg); - } - - self.conflict_set = conflict_set; - } - } -} - -#[derive(Debug)] -pub(crate) struct VariableFixtures { - temp_vars: IndexMap, -} - -impl VariableFixtures { - pub(crate) fn new() -> Self { - VariableFixtures { - temp_vars: IndexMap::new(), - } - } - - // computes no_use and conflict sets for all temp vars. - pub(crate) fn populate_restricting_sets(&mut self) { - // three stages: - // 1. move the use sets of each variable to a local IndexMap, use_set - // (iterate mutably, swap mutable refs). - // 2. drain use_set. For each use set of U, add into the - // no-use sets of appropriate variables T =/= U. - // 3. Move the use sets back to their original locations in the fixture. - // Compute the conflict set of u. - - // 1. - let mut use_sets: IndexMap = IndexMap::new(); - - for (var_gen_index, ref mut var_status) in self.temp_vars.iter_mut() { - let TempVarStatus { ref mut temp_var_data, .. } = var_status; - let mut use_set = OccurrenceSet::new(); - - std::mem::swap(&mut temp_var_data.use_set, &mut use_set); - use_sets.insert(var_gen_index, use_set); - } - - for (u, use_set) in use_sets.drain(..) { - // 2. - for &(term_loc, reg) in use_set.iter() { - if let GenContext::Last(cn_u) = term_loc { - for (var_gen_index, ref mut var_status) in self.terms_vars.iter_mut() { - let TempVarStatus { chunk_num, ref mut temp_var_data } = var_status; - - if cn_u == chunk_num && u != var_gen_index { - if !temp_var_data.uses_reg(reg) { - temp_var_data.no_use_set.insert(reg); - } - } - } - } - } - - // 3. - let TempVarStatus { ref mut temp_var_data, ..} = self.temp_vars.get_mut(u).unwrap(); - - temp_var_data.use_set = use_set; - temp_var_data.populate_conflict_set(); - } - } - - fn record_temp_info(&mut self, tvd: &mut TempVarData, arg_c: usize, term_loc: GenContext) { - match term_loc { - GenContext::Head | GenContext::Last(_) => { - tvd.use_set.insert((term_loc, arg_c)); - } - _ => {} - }; - } - - pub(crate) fn mark_temp_var(&mut self, var_info: &VarInfo) { - let chunk_num = term_loc.chunk_num(); - let var = Var::from(var_info.var_ptr); - - let mut status = self.temp_vars.swap_remove(&var).unwrap_or_else(|| { - TempVarStatus { - chunk_num, - temp_var_data: TempVarData::new(var_info.classify_info.arity), - } - }); - - if let Level::Shallow = var_info.lvl { - self.record_temp_info(&mut status, var_info.classify_info.arg_c, term_loc); - } - - self.temp_vars.insert(var, status); - } -} - -#[derive(Debug)] -pub(crate) struct UnsafeVarMarker { - pub(crate) unsafe_perm_vars: IndexMap, - pub(crate) unsafe_temp_vars: IndexSet, - pub(crate) safe_perm_vars: IndexSet, - pub(crate) safe_temp_vars: IndexSet, - pub(crate) temp_vars_to_perm_vars: IndexMap, -} - -impl UnsafeVarMarker { - pub(crate) fn new() -> Self { - UnsafeVarMarker { - unsafe_perm_vars: IndexMap::new(), - unsafe_temp_vars: IndexSet::new(), - safe_perm_vars: IndexSet::new(), - safe_temp_vars: IndexSet::new(), - temp_vars_to_perm_vars: IndexMap::new(), - } - } - - pub(crate) fn from_fact_vars(safe_vars: IndexSet) -> Self { - let mut unsafe_var_marker = Self::new(); - - for r in safe_vars { - unsafe_var_marker.mark_var_as_safe(r); - } - - unsafe_var_marker - } - - fn mark_var_as_safe(&mut self, r: RegType) { - match r { - RegType::Temp(t) => { - self.safe_temp_vars.insert(t); - } - RegType::Perm(p) => { - self.safe_perm_vars.insert(p); - } - }; - } - - fn mark_var_as_unsafe(&mut self, r: RegType, phase: usize) { - match r { - RegType::Temp(t) => { - self.unsafe_temp_vars.insert(t); - } - RegType::Perm(p) => { - self.unsafe_perm_vars.insert(p, phase); - } - } - } - - // returns true if the instruction at *query_instr cannot be - // changed by mark_unsafe_vars. - fn mark_safe_vars(&mut self, query_instr: &Instruction) -> bool { - match query_instr { - &Instruction::PutVariable(r @ RegType::Temp(_), _) | - &Instruction::SetVariable(r) => { - self.mark_var_as_safe(r); - true - } - &Instruction::PutVariable(RegType::Perm(p), t) => { - self.temp_vars_to_perm_vars.insert(t, p); - true - } - &Instruction::CallIs(RegType::Temp(t), ..) => { - if let Some(p) = self.temp_vars_to_perm_vars.get(&t) { - self.mark_var_as_safe(RegType::Perm(*p)); - } - - true - } - _ => false, - } - } - - fn mark_phase(&mut self, query_instr: &Instruction, phase: usize) { - match query_instr { - &Instruction::PutValue(r @ RegType::Perm(_), _) | - &Instruction::SetValue(r) => { - self.mark_var_as_unsafe(r, phase); - } - _ => {} - } - } - - fn mark_unsafe_perm_vars(&mut self, query_instr: &mut Instruction, phase: usize) { - match query_instr { - &mut Instruction::PutValue(RegType::Perm(p), arg) - if !self.safe_perm_vars.contains(&p) => { - if let Some(ph) = self.unsafe_perm_vars.swap_remove(&p) { - if ph == phase { - *query_instr = Instruction::PutUnsafeValue(p, arg); - self.safe_perm_vars.insert(p); - } else { - self.unsafe_perm_vars.insert(p, ph); - } - } - } - &mut Instruction::SetValue(r @ RegType::Perm(p)) - if !self.safe_perm_vars.contains(&p) => { - *query_instr = Instruction::SetLocalValue(r); - - self.safe_perm_vars.insert(p); - self.unsafe_perm_vars.remove(&p); - } - _ => {} - } - } - - fn mark_unsafe_temp_vars(&mut self, query_instr: &mut Instruction) { - match query_instr { - &mut Instruction::SetValue(r @ RegType::Temp(t)) - if !self.safe_temp_vars.contains(&t) => { - *query_instr = Instruction::SetLocalValue(r); - - self.safe_temp_vars.insert(t); - self.unsafe_temp_vars.remove(&t); - } - _ => { - } - } - } - - fn clear_temp_vars(&mut self) { - self.safe_temp_vars.clear(); - self.unsafe_temp_vars.clear(); - self.temp_vars_to_perm_vars.clear(); - } - - pub(crate) fn mark_unsafe_instrs(&mut self, code: &mut Code) { - if code.is_empty() { - return; - } - - let mut code_index = 0; - - for phase in 0.. { - while code[code_index].is_query_instr() { - let query_instr = &mut code[code_index]; - - if !self.mark_safe_vars(query_instr) { - self.mark_phase(query_instr, phase); - self.mark_unsafe_temp_vars(query_instr); - } - - code_index += 1; - } - - while code_index < code.len() && !code[code_index].is_query_instr() { - self.mark_safe_vars(&code[code_index]); - code_index += 1; - } - - self.clear_temp_vars(); - - if code_index >= code.len() { - break; - } - } - - code_index = 0; - - for phase in 0.. { - while code[code_index].is_query_instr() { - let query_instr = &mut code[code_index]; - self.mark_unsafe_perm_vars(query_instr, phase); - code_index += 1; - } - - // ensure phase->instruction assignments match those of - // the previous for loop. - while code_index < code.len() && !code[code_index].is_query_instr() { - code_index += 1; - } - - if code_index >= code.len() { - break; - } - } - } -} diff --git a/src/forms.rs b/src/forms.rs index 3ed83866..627fb4b2 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -20,25 +20,23 @@ use std::cell::Cell; use std::collections::VecDeque; use std::convert::TryFrom; use std::fmt; -use std::ops::AddAssign; +use std::ops::{AddAssign, Deref, DerefMut}; use std::path::PathBuf; use crate::{is_infix, is_postfix}; pub type PredicateKey = (Atom, usize); // name, arity. -pub type Predicate = Vec; - +/* // vars of predicate, toplevel offset. Vec is always a vector // of vars (we get their adjoining cells this way). pub type JumpStub = Vec; +*/ -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum TopLevel { - Fact(Fact), // Term, line_num, col_num - Predicate(Predicate), - Query(Vec), - Rule(Rule), // Rule, line_num, col_num + Fact(Fact, VarData), // Term, line_num, col_num + Rule(Rule, VarData), // Rule, line_num, col_num } #[derive(Debug, Clone, Copy)] @@ -79,13 +77,30 @@ pub enum CallPolicy { Counted, } -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ChunkType { Head, Mid, Last, } +#[derive(Debug)] +pub enum RootIterationPolicy { + Iterated, + NotIterated, +} + +impl RootIterationPolicy { + #[inline(always)] + pub fn iterable(&self) -> bool { + if let RootIterationPolicy::Iterated = self { + true + } else { + false + } + } +} + impl ChunkType { #[inline(always)] pub fn to_gen_context(self, chunk_num: usize) -> GenContext { @@ -102,47 +117,104 @@ impl ChunkType { } } +#[derive(Debug)] +pub enum ChunkedTerms { + Branch(Vec>), + Chunk(VecDeque), +} + +#[derive(Debug)] +pub struct ChunkedTermVec { + pub chunk_vec: VecDeque, +} + +impl Deref for ChunkedTermVec { + type Target = VecDeque; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.chunk_vec + } +} + +impl DerefMut for ChunkedTermVec { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.chunk_vec + } +} + +impl ChunkedTermVec { + #[inline] + pub fn new() -> Self { + Self { chunk_vec: VecDeque::new() } + } + + pub fn reserve_branch(&mut self, capacity: usize) { + self.chunk_vec.push_back(ChunkedTerms::Branch(Vec::with_capacity(capacity))); + } + + pub fn push_branch_arm(&mut self, branch: VecDeque) { + match self.chunk_vec.back_mut().unwrap() { + ChunkedTerms::Branch(branches) => { + branches.push(branch); + } + ChunkedTerms::Chunk(_) => { + self.chunk_vec.push_back(ChunkedTerms::Branch(vec![branch])); + } + } + } + + #[inline] + pub fn add_chunk(&mut self) { + self.chunk_vec.push_back(ChunkedTerms::Chunk(VecDeque::from(vec![]))); + } + + pub fn push_chunk_term(&mut self, term: QueryTerm) { + match self.chunk_vec.back_mut() { + Some(ChunkedTerms::Branch(_)) => { + self.chunk_vec.push_back(ChunkedTerms::Chunk(VecDeque::from(vec![term]))); + } + Some(ChunkedTerms::Chunk(chunk)) => { + chunk.push_back(term); + } + None => { + self.chunk_vec.push_back(ChunkedTerms::Chunk(VecDeque::from(vec![term]))); + } + } + } +} + #[derive(Debug)] pub enum QueryTerm { // register, clause type, subterms, clause call policy. Clause(Cell, ClauseType, Vec, CallPolicy), Fail, - GlobalCut, - GetCutPoint(usize), - LocalCut(usize), - Branch(Vec>), - ChunkTypeBoundary(ChunkType), + LocalCut(usize), // var_num + GlobalCut(usize), // var_num + GetCutPoint { var_num: usize, prev_b: bool }, + GetLevel(usize), // var_num } impl QueryTerm { - pub(crate) fn set_call_policy(&mut self, cp: CallPolicy) { - match self { - &mut QueryTerm::Clause(_, _, _, ref mut clause_cp) => *clause_cp = cp, - _ => {} - } - } - pub(crate) fn arity(&self) -> usize { match self { &QueryTerm::Clause(_, _, ref subterms, ..) => subterms.len(), - &QueryTerm::Cut | &QueryTerm::Branch(_) => 0, - &QueryTerm::IfThen(..) => 2, - &QueryTerm::Not(_) => 1, + &QueryTerm::GetLevel(_) | &QueryTerm::GetCutPoint { .. } => 1, + _ => 0, } } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Fact { pub(crate) head: Term, - pub(crate) var_data: VarData, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Rule { - pub(crate) head: (Atom, Vec, QueryTerm), - pub(crate) clauses: Vec, - pub(crate) var_data: VarData, + pub(crate) head: (Atom, Vec), + pub(crate) clauses: ChunkedTermVec, } #[derive(Clone, Debug, Hash)] @@ -233,29 +305,29 @@ impl ClauseInfo for Rule { impl ClauseInfo for PredicateClause { fn name(&self) -> Option { match self { - &PredicateClause::Fact(ref term, ..) => term.name(), + &PredicateClause::Fact(ref term, ..) => term.head.name(), &PredicateClause::Rule(ref rule, ..) => rule.name(), } } fn arity(&self) -> usize { match self { - &PredicateClause::Fact(ref term, ..) => term.arity(), + &PredicateClause::Fact(ref term, ..) => term.head.arity(), &PredicateClause::Rule(ref rule, ..) => rule.arity(), } } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum PredicateClause { - Fact(Fact), - Rule(Rule), + Fact(Fact, VarData), + Rule(Rule, VarData), } impl PredicateClause { pub(crate) fn args(&self) -> Option<&[Term]> { match self { - PredicateClause::Fact(term, ..) => match term { + PredicateClause::Fact(term, ..) => match &term.head { Term::Clause(_, _, args) => Some(&args), _ => None, }, diff --git a/src/heap_iter.rs b/src/heap_iter.rs index 9760be9e..d7f1f2e4 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -424,7 +424,6 @@ mod tests { use super::*; use crate::machine::mock_wam::*; - #[test] fn heap_stackless_iter_tests() { let mut wam = MockWAM::new(); diff --git a/src/heap_print.rs b/src/heap_print.rs index d09211d1..aa93ad29 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -472,7 +472,7 @@ pub struct HCPrinter<'a, Outputter> { state_stack: Vec, toplevel_spec: Option, last_item_idx: usize, - pub var_names: IndexMap, + pub var_names: IndexMap, pub numbervars_offset: Integer, pub numbervars: bool, pub quoted: bool, @@ -803,7 +803,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { if let Some(var) = self.var_names.get(&addr) { read_heap_cell!(addr, (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { - return Some(var.to_string()); + return Some(var.borrow().to_string()); } _ => { self.iter.push_stack(h); @@ -847,7 +847,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { // short-circuits handle_heap_term. // self.iter.pop_stack(); - let var_str = var.to_string(); + let var_str = var.borrow().to_string(); push_space_if_amb!(self, &var_str, { append_str!(self, &var_str); @@ -862,7 +862,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { Some(var) => { // If the term is bound to a named variable, // print the variable's name to output. - let var_str = var.to_string(); + let var_str = var.borrow().to_string(); push_space_if_amb!(self, &var_str, { append_str!(self, &var_str); diff --git a/src/iterators.rs b/src/iterators.rs index 529c453c..adec2e4c 100644 --- a/src/iterators.rs +++ b/src/iterators.rs @@ -5,100 +5,40 @@ use crate::parser::ast::*; use std::cell::Cell; use std::collections::VecDeque; -use std::fmt; -use std::fmt::Debug; -use std::hash::{Hash}; use std::iter::*; use std::vec::Vec; -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) enum VarPtr { - ToVar(std::ptr::NonNull), - InSitu(usize), -} - -impl From<&Var> for VarPtr { - #[inline] - fn from(value: &Var) -> VarPtr { - unsafe { - VarPtr { ptr: std::ptr::NonNull::new_unchecked(value as *const _ as *mut _) } - } - } -} - -impl From for Var { - #[inline(always)] - fn from(value: VarPtr) -> Var { - match value { - VarPtr::ToPtr(ptr) => unsafe { - (*ptr.ptr.as_ptr()).clone() - }, - VarPtr::InSitu(var_num) => { - Var::Generated(var_num) - } - } - } -} - -impl VarPtr { - pub(crate) fn set(&mut self, value: Var) { - match self { - VarPtr::ToVar(ref mut ptr) => - unsafe { *ptr.as_mut() = value }, - VarPtr::InSitu(_) => { - } - } - } -} - #[derive(Debug, Clone)] pub(crate) enum TermRef<'a> { AnonVar(Level), - Cut(Level), - GetLevel(Level), Cons(Level, &'a Cell, &'a Term, &'a Term), - Fail(Level), Literal(Level, &'a Cell, &'a Literal), Clause(Level, &'a Cell, Atom, &'a Vec), PartialString(Level, &'a Cell, &'a String, &'a Box), CompleteString(Level, &'a Cell, Atom), - Var(Level, &'a Cell, Var), - InitialBranch(Level), - MiddleBranch(Level), - FinalBranch(Level), + Var(Level, &'a Cell, VarPtr), } +/* impl<'a> TermRef<'a> { - pub(crate) fn level(self) -> Level { + pub(crate) fn level(&self) -> Level { match self { TermRef::AnonVar(lvl) | TermRef::Cons(lvl, ..) | - TermRef::Cut(lvl) | - TermRef::GetLevel(lvl) | TermRef::Literal(lvl, ..) | TermRef::Var(lvl, ..) | TermRef::Clause(lvl, ..) | TermRef::CompleteString(lvl, ..) | - TermRef::PartialString(lvl, ..) | - TermRef::InitialBranch(lvl) | - TermRef::MiddleBranch(lvl) | - TermRef::FinalBranch(lvl) | - TermRef::Fail(lvl) => lvl, + TermRef::PartialString(lvl, ..) => *lvl, } } } +*/ #[derive(Debug)] pub(crate) enum TermIterState<'a> { AnonVar(Level), Clause(Level, usize, &'a Cell, Atom, &'a Vec), - Cut(Level), - Fail(Level), - GetLevel(Level), - InitialBranch(Level, &'a Vec), - MiddleBranch(Level, &'a Vec), - FinalBranch(Level, &'a Vec), - Sequence(Level, &'a Vec), Literal(Level, &'a Cell, &'a Literal), InitialCons(Level, &'a Cell, &'a Term, &'a Term), FinalCons(Level, &'a Cell, &'a Term, &'a Term), @@ -125,7 +65,7 @@ impl<'a> TermIterState<'a> { Term::CompleteString(cell, atom) => { TermIterState::CompleteString(lvl, cell, *atom) } - Term::Var(cell, var) => TermIterState::Var(lvl, cell, VarPtr::from(var)), + Term::Var(cell, var_ptr) => TermIterState::Var(lvl, cell, var_ptr.clone()), } } } @@ -140,6 +80,7 @@ impl<'a> QueryIterator<'a> { self.state_stack.push(TermIterState::subterm_to_state(lvl, term)); } + /* fn from_rule_head_clause(terms: &'a Vec) -> Self { let state_stack = terms .iter() @@ -149,6 +90,7 @@ impl<'a> QueryIterator<'a> { QueryIterator { state_stack } } + */ fn from_term(term: &'a Term) -> Self { let state = match term { @@ -165,7 +107,7 @@ impl<'a> QueryIterator<'a> { *name, terms, ), - Term::Var(cell, var) => TermIterState::Var(Level::Root, cell, VarPtr::from(var)), + Term::Var(cell, var_ptr) => TermIterState::Var(Level::Root, cell, var_ptr.clone()), }; QueryIterator { @@ -181,36 +123,12 @@ impl<'a> QueryIterator<'a> { &QueryTerm::Clause(ref cell, ref ct, ref terms, _) => { self.state_stack.push(TermIterState::Clause(lvl, 0, cell, ct.name(), terms)); } - &QueryTerm::Cut => { - self.state_stack.push(TermIterState::Cut(lvl)); - } - &QueryTerm::Not(ref terms) => { - self.state_stack.push(TermIterState::Fail(lvl)); - self.state_stack.push(TermIterState::Cut(lvl)); - self.state_stack.push(TermIterState::Sequence(lvl, terms)); - } - &QueryTerm::IfThen(ref if_terms, ref then_terms) => { - self.state_stack.push(TermIterState::Sequence(lvl, then_terms)); - self.state_stack.push(TermIterState::Cut(lvl)); - self.state_stack.push(TermIterState::Sequence(lvl, if_terms)); - self.state_stack.push(TermIterState::GetLevel(lvl)); - } - &QueryTerm::Branch(ref branches) => { - let len = branches.len(); - self.state_stack.push(TermIterState::FinalBranch(lvl, &branches[len - 1])); - - self.state_stack.extend(branches[1 .. len - 1] - .iter() - .rev() - .map(|t| TermIterState::MiddleBranch(lvl, t)), - ); - - self.state_stack.push(TermIterState::InitialBranch(lvl, &branches[0])); + _ => { } } } - fn new(term: &'a QueryTerm) -> Self { + pub fn new(term: &'a QueryTerm) -> Self { let mut iter = QueryIterator { state_stack: vec![] }; iter.extend_state(Level::Root, term); iter @@ -273,34 +191,8 @@ impl<'a> Iterator for QueryIterator<'a> { TermIterState::Literal(lvl, cell, constant) => { return Some(TermRef::Literal(lvl, cell, constant)); } - TermIterState::Var(lvl, cell, var) => { - return Some(TermRef::Var(lvl, cell, Var::from(var))); - } - TermIterState::Cut(lvl) => { - return Some(TermRef::Cut(lvl)); - } - TermIterState::GetLevel(lvl) => { - return Some(TermRef::GetLevel(lvl)); - } - TermIterState::InitialBranch(lvl, ref branch) => { - self.state_stack.push(TermIterState::Sequence(lvl, branch)); - return Some(TermRef::InitialBranch(lvl)); - } - TermIterState::MiddleBranch(lvl, ref branch) => { - self.state_stack.push(TermIterState::Sequence(lvl, branch)); - return Some(TermRef::MiddleBranch(lvl)); - } - TermIterState::FinalBranch(lvl, ref branch) => { - self.state_stack.push(TermIterState::Sequence(lvl, branch)); - return Some(TermRef::FinalBranch(lvl)); - } - TermIterState::Sequence(lvl, ref terms) => { - for term in branch.iter().rev() { - self.extend_state(lvl, term); - } - } - TermIterState::Fail(lvl) => { - return Some(TermRef::Fail(lvl)); + TermIterState::Var(lvl, cell, var_ptr) => { + return Some(TermRef::Var(lvl, cell, var_ptr)); } }; } @@ -312,7 +204,7 @@ impl<'a> Iterator for QueryIterator<'a> { #[derive(Debug)] pub(crate) struct FactIterator<'a> { state_queue: VecDeque>, - iterable_root: bool, + iterable_root: RootIterationPolicy, } impl<'a> FactIterator<'a> { @@ -329,11 +221,11 @@ impl<'a> FactIterator<'a> { FactIterator { state_queue, - iterable_root: false, + iterable_root: RootIterationPolicy::NotIterated, } } - fn new(term: &'a Term, iterable_root: bool) -> Self { + fn new(term: &'a Term, iterable_root: RootIterationPolicy) -> Self { let states = match term { Term::AnonVar => { vec![TermIterState::AnonVar(Level::Root)] @@ -365,8 +257,8 @@ impl<'a> FactIterator<'a> { Term::Literal(cell, constant) => { vec![TermIterState::Literal(Level::Root, cell, constant)] } - Term::Var(cell, var) => { - vec![TermIterState::Var(Level::Root, cell, VarPtr::from(var))] + Term::Var(cell, var_ptr) => { + vec![TermIterState::Var(Level::Root, cell, var_ptr.clone())] } }; @@ -392,7 +284,7 @@ impl<'a> Iterator for FactIterator<'a> { } match lvl { - Level::Root if !self.iterable_root => continue, + Level::Root if !self.iterable_root.iterable() => continue, _ => return Some(TermRef::Clause(lvl, cell, name, child_terms)), }; } @@ -412,8 +304,8 @@ impl<'a> Iterator for FactIterator<'a> { TermIterState::Literal(lvl, cell, constant) => { return Some(TermRef::Literal(lvl, cell, constant)) } - TermIterState::Var(lvl, cell, var) => { - return Some(TermRef::Var(lvl, cell, Var::from(var))); + TermIterState::Var(lvl, cell, var_ptr) => { + return Some(TermRef::Var(lvl, cell, var_ptr)); } _ => {} } @@ -427,143 +319,130 @@ pub(crate) fn post_order_iter<'a>(term: &'a Term) -> QueryIterator<'a> { QueryIterator::from_term(term) } -pub(crate) fn breadth_first_iter<'a>(term: &'a Term, iterable_root: bool) -> FactIterator<'a> { +pub(crate) fn breadth_first_iter<'a>(term: &'a Term, iterable_root: RootIterationPolicy) -> FactIterator<'a> { FactIterator::new(term, iterable_root) } -/* +#[derive(Debug, Copy, Clone)] +enum ClauseIteratorState<'a> { + RemainingChunks(&'a VecDeque, usize), + RemainingBranches(&'a Vec>, usize), +} + +#[derive(Debug, Clone)] +pub(crate) enum ClauseItem<'a> { + FirstBranch(usize), + NextBranch, + BranchEnd(usize), + Chunk(&'a VecDeque), +} + #[derive(Debug)] -pub(crate) enum ChunkedTerm<'a> { - HeadClause(Atom, &'a Vec), - BodyTerm(&'a QueryTerm), +pub(crate) struct ClauseIterator<'a> { + state_stack: Vec>, + remaining_chunks_on_stack: usize, } -pub(crate) fn query_term_post_order_iter<'a>(query_term: &'a QueryTerm) -> QueryIterator<'a> { - QueryIterator::new(query_term) -} - -impl<'a> ChunkedTerm<'a> { - pub(crate) fn post_order_iter(&self) -> QueryIterator<'a> { - match self { - &ChunkedTerm::BodyTerm(qt) => QueryIterator::new(qt), - &ChunkedTerm::HeadClause(_, terms) => QueryIterator::from_rule_head_clause(terms), +fn state_from_chunked_terms<'a>(chunk_vec: &'a VecDeque) -> ClauseIteratorState<'a> { + if chunk_vec.len() == 1 { + if let Some(ChunkedTerms::Branch(ref branches)) = chunk_vec.front() { + return ClauseIteratorState::RemainingBranches(branches, 0); } } + + ClauseIteratorState::RemainingChunks(chunk_vec, 0) } -pub(crate) struct ChunkedIterator<'a> { - pub(crate) chunk_num: usize, - iter: Box> + 'a>, -} - -impl<'a> fmt::Debug for ChunkedIterator<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("ChunkedIterator") - .field("chunk_num", &self.chunk_num) - // Hacky solution. - .field("iter", &"Box> + 'a>") - .finish() - } -} - -type ChunkedIteratorItem<'a> = (usize, usize, Vec>); -type RuleBodyIteratorItem<'a> = (usize, usize, Vec<&'a QueryTerm>); - -impl<'a> ChunkedIterator<'a> { - pub(crate) fn rule_body_iter(self) -> Box> + 'a> { - Box::new(self.filter_map(|(cn, lt_arity, terms)| { - let filtered_terms: Vec<_> = terms - .into_iter() - .filter_map(|ct| match ct { - ChunkedTerm::BodyTerm(qt) => Some(qt), - _ => None, - }) - .collect(); - - if filtered_terms.is_empty() { - None - } else { - Some((cn, lt_arity, filtered_terms)) +impl<'a> ClauseIterator<'a> { + pub fn new(clauses: &'a ChunkedTermVec) -> Self { + match state_from_chunked_terms(&clauses.chunk_vec) { + state @ ClauseIteratorState::RemainingBranches(..) => { + Self { + state_stack: vec![state], + remaining_chunks_on_stack: 0, + } + } + state @ ClauseIteratorState::RemainingChunks(..) => { + Self { + state_stack: vec![state], + remaining_chunks_on_stack: 1, + } } - })) - } - - pub(crate) fn from_rule_body(p1: &'a QueryTerm, clauses: &'a Vec) -> Self { - let inner_iter = Box::new(once(ChunkedTerm::BodyTerm(p1))); - let iter = inner_iter.chain(clauses.iter().map(|t| ChunkedTerm::BodyTerm(t))); - - ChunkedIterator { - chunk_num: 0, - iter: Box::new(iter), } } - pub(crate) fn from_rule(rule: &'a Rule) -> Self { - let &Rule { - head: (ref name, ref args, ref p1), - ref clauses, - .. - } = rule; - - let iter = once(ChunkedTerm::HeadClause(name.clone(), args)); - let inner_iter = Box::new(once(ChunkedTerm::BodyTerm(p1))); - let iter = iter.chain(inner_iter.chain(clauses.iter().map(|t| ChunkedTerm::BodyTerm(t)))); - - ChunkedIterator { - chunk_num: 0, - iter: Box::new(iter), - } + #[inline(always)] + pub fn in_tail_position(&self) -> bool { + self.remaining_chunks_on_stack == 0 } - fn take_chunk(&mut self, term: ChunkedTerm<'a>) -> (usize, usize, Vec>) { - let mut arity = 0; - let mut item = Some(term); - let mut result = Vec::new(); + fn branch_end_depth(&mut self) -> usize { + let mut depth = 1; - while let Some(term) = item { - match term { - ChunkedTerm::HeadClause(_, terms) => { - result.push(term); + while let Some(state) = self.state_stack.pop() { + match state { + ClauseIteratorState::RemainingBranches(terms, focus) if terms.len() == focus => { + depth += 1; } - ChunkedTerm::BodyTerm(&QueryTerm::Cut) => { - result.push(term); - } - ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::Inlined(_), ..)) => { - result.push(term); - } - ChunkedTerm::BodyTerm(&QueryTerm::Clause( - _, - ClauseType::CallN(_), - ref subterms, - _, - )) => { - result.push(term); - arity = subterms.len() + 1; + _ => { + self.state_stack.push(state); break; } - ChunkedTerm::BodyTerm(qt) => { - result.push(term); - arity = qt.arity(); - break; - } - }; - - item = self.iter.next(); + } } - let chunk_num = self.chunk_num; - self.chunk_num += 1; - - (chunk_num, arity, result) + depth } } -impl<'a> Iterator for ChunkedIterator<'a> { - // the chunk number, last term arity, and vector of references. - type Item = ChunkedIteratorItem<'a>; +impl<'a> Iterator for ClauseIterator<'a> { + type Item = ClauseItem<'a>; fn next(&mut self) -> Option { - self.iter.next().map(|term| self.take_chunk(term)) + while let Some(state) = self.state_stack.pop() { + match state { + ClauseIteratorState::RemainingChunks(chunks, focus) if focus < chunks.len() => { + if focus + 1 < chunks.len() { + self.state_stack.push(ClauseIteratorState::RemainingChunks(chunks, focus + 1)); + } else { + self.remaining_chunks_on_stack -= 1; + } + + match &chunks[focus] { + ChunkedTerms::Branch(branches) => { + self.state_stack.push(ClauseIteratorState::RemainingBranches(branches, 0)); + } + ChunkedTerms::Chunk(chunk) => { + return Some(ClauseItem::Chunk(chunk)); + } + } + } + ClauseIteratorState::RemainingChunks(chunks, focus) => { + debug_assert_eq!(chunks.len(), focus); + } + ClauseIteratorState::RemainingBranches(branches, focus) if focus < branches.len() => { + self.state_stack.push(ClauseIteratorState::RemainingBranches(&branches, focus + 1)); + let state = state_from_chunked_terms(&branches[focus]); + + if let ClauseIteratorState::RemainingChunks(..) = &state { + self.remaining_chunks_on_stack += 1; + } + + self.state_stack.push(state); + + return if focus == 0 { + Some(ClauseItem::FirstBranch(branches.len())) + } else { + Some(ClauseItem::NextBranch) + }; + } + ClauseIteratorState::RemainingBranches(branches, focus) => { + debug_assert_eq!(branches.len(), focus); + return Some(ClauseItem::BranchEnd(self.branch_end_depth())); + } + } + } + + None } } -*/ diff --git a/src/lib.rs b/src/lib.rs index 45dc2385..36f7a45b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ mod arithmetic; pub mod codegen; mod debray_allocator; mod ffi; -mod fixtures; +mod variable_records; mod forms; mod heap_iter; pub mod heap_print; diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 0005d395..1c183a0c 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -218,13 +218,13 @@ fail :- '$fail'. %% \+(Goal) % % True iff Goal fails -\+ G :- call(G), !, false. +\+ G :- call(G), !, '$fail'. \+ _. %% \=(?X, ?Y) % % True iff X and Y can't be unified -X \= X :- !, false. +X \= X :- !, '$fail'. _ \= _. diff --git a/src/lib/format.pl b/src/lib/format.pl index 32ad2ff9..be1cd532 100644 --- a/src/lib/format.pl +++ b/src/lib/format.pl @@ -513,10 +513,12 @@ portray_clause(Stream, Term) :- phrase_to_stream(portray_clause_(Term), Stream), flush_output(Stream). +% called once. portray_clause_(Term) --> { unique_variable_names(Term, VNs) }, portray_(Term, VNs), ".\n". +% mysteriously called twice, the second time with the truncated B3. unique_variable_names(Term, VNs) :- term_variables(Term, Vs), foldl(var_name, Vs, VNs, 0, _). diff --git a/src/loader.pl b/src/loader.pl index 896d6bff..5c35822e 100644 --- a/src/loader.pl +++ b/src/loader.pl @@ -541,6 +541,7 @@ open_file(Path, Stream) :- ) ). + use_module(Module, Exports, Evacuable) :- ( var(Module) -> instantiation_error(load/1) @@ -562,12 +563,11 @@ use_module(Module, Exports, Evacuable) :- stream_property(Stream, file_name(PathFileName)), file_load(Stream, PathFileName, Subevacuable), '$use_module'(Evacuable, Subevacuable, Exports) - ; type_error(atom, Library, load/1) + ; type_error(atom, Module, load/1) ) ). - check_predicate_property(meta_predicate, Module, Name, Arity, MetaPredicateTerm) :- '$meta_predicate_property'(Module, Name, Arity, MetaPredicateTerm). check_predicate_property(built_in, _, Name, Arity, built_in) :- diff --git a/src/machine/code_walker.rs b/src/machine/code_walker.rs index fcda8710..1244eb20 100644 --- a/src/machine/code_walker.rs +++ b/src/machine/code_walker.rs @@ -23,13 +23,9 @@ fn capture_offset(line: &Instruction, index: usize, stack: &mut Vec) -> b { stack.push(index + offset); } - &Instruction::JmpByCall(_, offset, _) => { + &Instruction::JmpByCall(offset) => { stack.push(index + offset); } - &Instruction::JmpByExecute(_, offset, _) => { - stack.push(index + offset); - return true; - } &Instruction::Proceed => { return true; } diff --git a/src/machine/compile.rs b/src/machine/compile.rs index 428e2952..0faf34c3 100644 --- a/src/machine/compile.rs +++ b/src/machine/compile.rs @@ -44,62 +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 { - 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, - jmp_by_locs: Vec, - 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; @@ -1351,17 +1295,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { 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, @@ -1389,24 +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(); diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index 98875bc8..1b65ef9e 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -1,4 +1,3 @@ - /* ================================================================================ @@ -9,7 +8,6 @@ paper "Compiling Large Disjunctions" to Scryer Prolog. */ use crate::atom_table::*; -use crate::fixtures::VariableFixtures; use crate::forms::*; use crate::instructions::*; use crate::iterators::*; @@ -18,16 +16,18 @@ use crate::machine::machine_errors::CompilationError; use crate::machine::preprocessor::*; use crate::parser::ast::*; use crate::parser::rug::Rational; +use crate::variable_records::*; 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)] -struct BranchNumber { +#[derive(Debug, Clone)] //, PartialOrd, PartialEq, Eq, Hash)] +pub struct BranchNumber { branch_num: Rational, delta: Rational, } @@ -35,7 +35,7 @@ struct BranchNumber { impl Default for BranchNumber { fn default() -> Self { Self { - branch_num: Rational::from(1 << 63), + branch_num: Rational::from(1usize << 63), delta: Rational::from(1), } } @@ -87,9 +87,10 @@ impl BranchNumber { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct VarInfo { var_ptr: VarPtr, + chunk_type: ChunkType, classify_info: ClassifyInfo, lvl: Level, } @@ -102,6 +103,11 @@ pub struct ChunkInfo { vars: Vec, } +#[derive(Debug)] +pub struct BranchArm { + pub arm_terms: Vec, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct BranchInfo { branch_num: BranchNumber, @@ -114,7 +120,7 @@ impl BranchInfo { } } -type BranchMapInt = IndexMap>; +type BranchMapInt = IndexMap>; #[derive(Debug, Clone)] pub struct BranchMap(BranchMapInt); @@ -145,82 +151,77 @@ pub struct ClassifyInfo { enum TraversalState { // construct a QueryTerm::Branch with number of disjuncts, reset - // the chunk type to that of the chunk preceding the disjunct. - BuildDisjunct(ChunkType, usize), + // 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(usize), - LocalCut(usize), + GetCutPoint{ var_num: usize, prev_b: bool }, + Cut { var_num: usize, is_global: bool }, ResetCallPolicy(CallPolicy), Term(Term), - AddBranchNum(BranchNumber), // set current_branch_number, add it to the root set - RemoveBranchNum, // remove latest branch number from the root set - RepBranchNum(BranchNumber), // replace current_branch_number and the latest in the root set - IncrChunkNum, // increment self.current_chunk_number - SetLastChunkType, // consider remaining terms as belonging to a last chunk -} - -impl Term { - #[inline] - fn is_var(&self) -> bool { - if let Term::Var(..) = self { - true - } else { - false - } - } - - #[inline] - fn is_compound(&self) -> bool { - match self { - Term::Clause(..) | Term::Cons(..) => true, - _ => false, - } - } + 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 + // SetChunkType(ChunkType), // consider remaining terms as belonging to a last chunk } +#[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, } -#[derive(Debug)] -pub enum VarClassification { - Void, - Temp, - Perm, +#[derive(Debug, Default)] +pub struct VarData { + pub records: VariableRecords, + pub global_cut_var_num: Option, + pub allocates: bool, } -#[derive(Clone, Debug)] -pub struct VarRecord { - pub classification: VarClassification, - pub chunk_occurrences: Vec, - pub num_occurrences: usize, -} +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 + }; -impl Default for VarRecord { - fn default() -> Self { - VarRecord { - classification: VarClassification::Void, - chunk_occurrences: vec![], - num_occurrences: 0, + 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 struct VarData { - pub records: Vec, - pub fixtures: VariableFixtures, -} - pub type ClassifyFactResult = (Term, VarData); -pub type ClassifyRuleResult = (Term, Vec, VarData); +pub type ClassifyRuleResult = (Term, ChunkedTermVec, VarData); fn merge_branch_seq>(branches: Iter) -> BranchInfo { let mut branch_info = BranchInfo::new(BranchNumber::default()); @@ -228,6 +229,7 @@ fn merge_branch_seq>(branches: Iter) -> Branch for mut branch in branches { branch_info.branch_num = branch.branch_num; + /* if let Some(last_chunk) = branch_info.chunks.last_mut() { if let Some(first_moved_chunk) = branch.chunks.first_mut() { if last_chunk.chunk_num == first_moved_chunk.chunk_num { @@ -238,6 +240,7 @@ fn merge_branch_seq>(branches: Iter) -> Branch } } } + */ branch_info.chunks.extend(branch.chunks.drain(..)); } @@ -248,82 +251,37 @@ fn merge_branch_seq>(branches: Iter) -> Branch branch_info } -fn flatten_into_disjunct(build_stack: &mut Vec, preceding_len: usize) { - let iter = build_stack.drain(preceding_len + 1 ..); +fn flatten_into_disjunct(build_stack: &mut ChunkedTermVec, preceding_len: usize) { + let branch_vec = build_stack.drain(preceding_len + 1 ..).collect(); - if let QueryTerm::Branch(ref mut disjuncts) = &mut build_stack[preceding_len] { - disjuncts.push(iter.collect()); + if let ChunkedTerms::Branch(ref mut disjuncts) = &mut build_stack[preceding_len] { + disjuncts.push(branch_vec); } else { unreachable!(); } } -fn term_in_other_chunk(term: &Term) -> Option { - match term { - Term::Clause(_, name, terms) => Some(!ClauseType::is_inbuilt(*name, terms.len())), - Term::Literal(_, Literal::Atom(atom!("!")) | Literal::Char('!')) => Some(false), - Term::Literal(_, Literal::Atom(name)) => Some(!ClauseType::is_inbuilt(*name, 0)), - Term::Var(..) => Some(true), - _ => None, - } -} - -// returns true if SetLastChunkType was pushed. -// expects that iter iterates over a conjunct of Terms in reverse order. -fn insert_set_last_chunk_type( - state_stack: &mut Vec, - mut iter: impl Iterator, -) -> bool { - let beg = state_stack.len(); - - let mut will_break = false; - let mut last_chunk_delim = beg; - - while let Some(traversal_st) = iter.next() { - match traversal_st { - TraversalState::Term(term) => { - will_break = false; - - match term_in_other_chunk(&term) { - Some(true) if last_chunk_delim > beg => will_break = true, - Some(_) => last_chunk_delim += 1, - None => will_break = true, - } - - if will_break { - // recall that iter iterates in reverse order. - // therefore this is the correct push order. - state_stack.push(TraversalState::SetLastChunkType); - state_stack.push(traversal_st); - - break; - } - } - _ => { - state_stack.push(traversal_st); - } - } - } - - state_stack.extend(iter); - will_break -} - 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 { self.classify_head_variables(&term)?; - Ok((term, self.branch_map.separate_and_classify_variables(self.var_num))) + 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>>( @@ -333,9 +291,21 @@ impl VariableClassifier { body: Term, ) -> Result { self.classify_head_variables(&head)?; - let query_terms = self.classify_body_variables(loader, body)?; + self.root_set.insert(self.current_branch_num.clone()); - Ok((head, query_terms, self.branch_map.separate_and_classify_variables(self.var_num))) + 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) { @@ -359,24 +329,49 @@ impl VariableClassifier { } } - fn probe_body_term(&mut self, term: &Term, term_loc: GenContext) { - let mut classify_info = ClassifyInfo { arg_c: 0, arity: term.arity() }; + 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, true) { - if let TermRef::Var(lvl, _, var_name) = term_ref { - let var_info = VarInfo { var_ptr: VarPtr::from(&var_name), lvl, classify_info }; - self.probe_body_var(var_name, term_loc, var_info); - } - - if let Level::Shallow = term_ref.level() { - classify_info.arg_c += 1; + 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_name: Var, term_loc: GenContext, var_info: VarInfo) { - let branch_info_v = self.branch_map.entry(var_name) + 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() { @@ -409,18 +404,17 @@ impl VariableClassifier { chunk_info.vars.push(var_info); } - fn probe_in_situ_var(&mut self, chunk_type: ChunkType, var_num: usize) { - let classify_info = ClassifyInfo { arg_c: 0, arity: 0 }; + 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::InSitu(var_num), + var_ptr: VarPtr::from(Var::InSitu(var_num)), classify_info, + chunk_type: self.current_chunk_type, lvl: Level::Shallow, }; - let term_loc = chunk_type.to_gen_context(self.current_chunk_num); - - self.probe_body_var(Var::Generated(var_num), term_loc, var_info); + self.probe_body_var(var_info); } fn classify_head_variables(&mut self, term: &Term) -> Result<(), CompilationError> { @@ -430,43 +424,55 @@ impl VariableClassifier { _ => return Err(CompilationError::InvalidRuleHead), } - let mut classify_info = ClassifyInfo { arg_c: 0, arity: term.arity() }; + let mut classify_info = ClassifyInfo { arg_c: 1, arity: term.arity() }; - // false argument to breadth_first_iter because the root is not iterable. - for term_ref in breadth_first_iter(term, false) { - if let TermRef::Var(lvl, _, var_name) = term_ref { - // 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::from(var_name)) - .or_insert_with(|| vec![]); + 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(); - let needs_new_branch = branch_info_v.is_empty(); + // 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![]); - if needs_new_branch { - branch_info_v.push(BranchInfo::new(self.current_branch_num.clone())); + 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; } - - 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: VarPtr::from(&var_name), classify_info, lvl }; - - chunk_info.vars.push(var_info); - } - - if let Level::Shallow = term_ref.level() { - classify_info.arg_c += 1; } + _ => {} } Ok(()) @@ -476,10 +482,11 @@ impl VariableClassifier { &mut self, loader: &mut Loader<'a, LS>, term: Term, - ) -> Result, CompilationError> { + ) -> Result { let mut state_stack = vec![TraversalState::Term(term)]; - let mut build_stack = vec![]; - let mut chunk_type = ChunkType::Head; + let mut build_stack = ChunkedTermVec::new(); + + self.current_chunk_type = ChunkType::Mid; while let Some(traversal_st) = state_stack.pop() { match traversal_st { @@ -495,64 +502,78 @@ impl VariableClassifier { self.root_set.insert(branch_num.clone()); self.current_branch_num = branch_num; } - TraversalState::IncrChunkNum => { - self.current_chunk_num += 1; - chunk_type = ChunkType::Mid; - build_stack.push(QueryTerm::ChunkTypeBoundary(chunk_type)); - } TraversalState::ResetCallPolicy(call_policy) => { self.call_policy = call_policy; } - TraversalState::SetLastChunkType => { - chunk_type = ChunkType::Last; - build_stack.push(QueryTerm::ChunkTypeBoundary(chunk_type)); - } - TraversalState::BuildDisjunct(reset_chunk_type, preceding_len) => { - chunk_type = reset_chunk_type; - build_stack.push(QueryTerm::ChunkTypeBoundary(chunk_type)); + TraversalState::BuildDisjunct(preceding_len) => { flatten_into_disjunct(&mut build_stack, preceding_len); + + // self.current_chunk_type = ChunkType::Last; + self.current_chunk_type = ChunkType::Mid; + self.current_chunk_num += 1; } TraversalState::BuildFinalDisjunct(preceding_len) => { flatten_into_disjunct(&mut build_stack, preceding_len); - } - TraversalState::GetCutPoint(var_num) => { - let term_loc = chunk_type.to_gen_context(self.current_chunk_num); - self.probe_in_situ_var(term_loc, var_num); - build_stack.push(QueryTerm::GetCutPoint(var_num)); + self.current_chunk_type = ChunkType::Mid; + self.current_chunk_num += 1; } - TraversalState::LocalCut(var_num) => { - let term_loc = chunk_type.to_gen_context(self.current_chunk_num); + TraversalState::GetCutPoint { var_num, prev_b } => { + if self.try_set_chunk_at_inlined_boundary() { + build_stack.add_chunk(); + } - self.probe_in_situ_var(term_loc, var_num); - build_stack.push(QueryTerm::LocalCut(var_num)); + 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(QueryTerm::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!(","), terms) if terms.len() == 2 => { - let iter = unfold_by_str(terms[1], atom!(",")) + 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(terms[0])) + .chain(std::iter::once(head)) .map(TraversalState::Term); - if ChunkType::Mid != chunk_type { - if insert_set_last_chunk_type(&mut state_stack, iter) { - if chunk_type.is_last() { - chunk_type = ChunkType::Mid; - } - } - } else { - state_stack.extend(iter); - } + state_stack.extend(iter); } - Term::Clause(_, atom!(";"), terms) if terms.len() == 2 => { + 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(terms[0]) - .chain(unfold_by_str(terms[1], atom!(";")).into_iter()) + let branches: Vec<_> = std::iter::once(head) + .chain(unfold_by_str(tail, atom!(";")).into_iter()) .collect(); let mut branch_numbers = vec![first_branch_num]; @@ -568,7 +589,7 @@ impl VariableClassifier { } let build_stack_len = build_stack.len(); - build_stack.push(QueryTerm::Branch(Vec::with_capacity(branches.len()))); + build_stack.reserve_branch(branches.len()); state_stack.push(TraversalState::RepBranchNum( self.current_branch_num.halve_delta(), @@ -578,47 +599,52 @@ impl VariableClassifier { let final_disjunct_loc = state_stack.len(); for (term, branch_num) in iter.rev() { - state_stack.push(TraversalState::BuildDisjunct(chunk_type, build_stack_len)); - + 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)); } - state_stack[final_disjunct_loc] = - TraversalState::BuildFinalDisjunct(build_stack_len); + if let TraversalState::BuildDisjunct(build_stack_len) = state_stack[final_disjunct_loc] { + state_stack[final_disjunct_loc] = TraversalState::BuildFinalDisjunct(build_stack_len); + } } Term::Clause(_, atom!("->"), mut terms) if terms.len() == 2 => { let then_term = terms.pop().unwrap(); let if_term = terms.pop().unwrap(); - let iter = vec![TraversalState::Term(then_term), - TraversalState::LocalCut(self.var_num), - TraversalState::Term(if_term), - TraversalState::GetCutPoint(self.var_num)] - .into_iter(); + 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; - - if ChunkType::Mid != chunk_type { - if insert_set_last_chunk_type(&mut state_stack, iter) { - if chunk_type.is_last() { - chunk_type = ChunkType::Mid; - } - } - } } - Term::Clause(_, atom!("\\+"), terms) if terms.len() == 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::LocalCut(self.var_num)); - state_stack.push(TraversalState::Term(terms[0])); - state_stack.push(TraversalState::GetCutPoint(self.var_num)); + 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.var_num += 1; } Term::Clause(_, atom!(":"), mut terms) if terms.len() == 2 => { - let term_loc = chunk_type.to_gen_context(self.current_chunk_num); - let predicate_name = terms.pop().unwrap(); let module_name = terms.pop().unwrap(); @@ -627,11 +653,11 @@ impl VariableClassifier { Term::Literal(_, Literal::Atom(module_name)), Term::Literal(_, Literal::Atom(predicate_name)), ) => { - if !ClauseType::is_inbuilt(predicate_name, 0) { - state_stack.push(TraversalState::IncrChunkNum); + if update_chunk_data(self, predicate_name, 0) { + build_stack.add_chunk(); } - build_stack.push( + build_stack.push_chunk_term( qualified_clause_to_query_term( loader, module_name, @@ -645,15 +671,15 @@ impl VariableClassifier { Term::Literal(_, Literal::Atom(module_name)), Term::Clause(_, name, terms), ) => { - if !ClauseType::is_inbuilt(name, terms.len()) { - state_stack.push(TraversalState::IncrChunkNum); + if update_chunk_data(self, name, terms.len()) { + build_stack.add_chunk(); } - for term in terms.iter() { - self.probe_body_term(term, term_loc); + for (arg_c, term) in terms.iter().enumerate() { + self.probe_body_term(arg_c + 1, terms.len(), term); } - build_stack.push( + build_stack.push_chunk_term( qualified_clause_to_query_term( loader, module_name, @@ -664,15 +690,17 @@ impl VariableClassifier { ); } (module_name, predicate_name) => { - state_stack.push(TraversalState::IncrChunkNum); + if update_chunk_data(self, atom!("call"), 2) { + build_stack.add_chunk(); + } - self.probe_body_term(&module_name, term_loc); - self.probe_body_term(&predicate_name, term_loc); + 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( + build_stack.push_chunk_term( clause_to_query_term( loader, atom!("call"), @@ -683,30 +711,22 @@ impl VariableClassifier { } } } - Term::Clause(cell, atom!("$call_with_inference_counting"), terms) if terms.len() == 1 => { - let term_loc = chunk_type.to_gen_context(self.current_chunk_num); - - for term in terms.iter() { - self.probe_body_term(term, term_loc); - } - + 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[0])); + state_stack.push(TraversalState::Term(terms.pop().unwrap())); self.call_policy = CallPolicy::Counted; } - Term::Clause(cell, name, terms) => { - if !ClauseType::is_inbuilt(name, terms.len()) { - state_stack.push(TraversalState::IncrChunkNum); + Term::Clause(_, name, terms) => { + if update_chunk_data(self, name, terms.len()) { + build_stack.add_chunk(); } - let term_loc = chunk_type.to_gen_context(self.current_chunk_num); - - for term in terms.iter() { - self.probe_body_term(term, term_loc); + for (arg_c, term) in terms.iter().enumerate() { + self.probe_body_term(arg_c + 1, terms.len(), term); } - build_stack.push( + build_stack.push_chunk_term( clause_to_query_term( loader, name, @@ -716,14 +736,24 @@ impl VariableClassifier { ); } Term::Literal(_, Literal::Atom(atom!("!")) | Literal::Char('!')) => { - build_stack.push(QueryTerm::GlobalCut); - } - Term::Literal(cell, Literal::Atom(name)) => { - if !ClauseType::is_inbuilt(name, 0) { - state_stack.push(TraversalState::IncrChunkNum); + if self.global_cut_var_num.is_none() { + self.global_cut_var_num = Some(self.var_num); + self.var_num += 1; } - build_stack.push( + 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, @@ -732,7 +762,6 @@ impl VariableClassifier { ), ); } - _ => { return Err(CompilationError::InadmissibleQueryTerm); } @@ -746,61 +775,76 @@ impl VariableClassifier { } impl BranchMap { - pub fn separate_and_classify_variables(&mut self, mut var_num: usize) -> VarData { + pub fn separate_and_classify_variables( + &mut self, + var_num: usize, + global_cut_var_num: Option, + current_chunk_num: usize, + ) -> VarData { let mut var_data = VarData { - records: vec![VarRecord::default(); self.len()], - fixtures: VariableFixtures::new(), + records: VariableRecords::new(var_num), + global_cut_var_num, + allocates: current_chunk_num > 0, }; for (var, branches) in self.iter_mut() { - for branch in branches.iter_mut() { - let mut num_occurrences = 0; - - let idx = if let Var::Generated(var_num) = var { - *var_num + let (mut var_num, var_num_incr) = + if let Var::InSitu(var_num) = *var.borrow() { + (var_num, false) } else { - var_num += 1; - var_num - 1 + (var_data.records.len(), true) }; - var_data.records[idx].classification = - if branch.chunks.len() > 1 { - VarClassification::Perm - } else { - branch.chunks - .first() - .map(|chunk| if chunk.vars.len() > 1 { - VarClassification::Temp - } else { - VarClassification::Void - }) - .unwrap_or(VarClassification::Void) - }; + for branch in branches.iter_mut() { + if var_num_incr { + var_num = var_data.records.len(); + var_data.records.push(VariableRecord::default()); + } - var_data.records[idx].chunk_occurrences.reserve(branch.chunks.len()); + if branch.chunks.len() <= 1 { // true iff var is a temporary variable. + debug_assert_eq!(branch.chunks.len(), 1); - for chunk in branch.chunks.iter_mut() { - var_data.records[idx].num_occurrences += chunk.vars.len(); + let chunk = &mut branch.chunks[0]; + let mut temp_var_data = TempVarData::new(); - if let VarClassification::Temp = classification { - for var_info in chunk.vars.iter_mut() { - var_info.var_ptr.set(Var::Generated(var_num)); - var_data.fixtures.mark_temp_var(&var_info); - } - } else { - for var_info in chunk.vars.iter_mut() { - var_info.var_ptr.set(Var::Generated(var_num)); + 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[idx].chunk_occurrences.push(chunk.chunk_num); + 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)); + } } } } - debug_assert_eq!(var_data.records.len(), var_num); + // debug_assert_eq!(var_data.records.len(), var_num); - var_data.fixtures.populate_restricting_sets(); + var_data.records.populate_restricting_sets(); var_data } } + +#[cfg(test)] +mod tests { + #[test] + fn disjunct_compilation() { + let mut wam = MachineState::new(); + let mut op_dir = default_op_dir(); + } +} diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 92eca80f..1d310bcd 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -1152,6 +1152,16 @@ impl Machine { self.machine_st[r] = fixnum_as_cell!(Fixnum::build_with(b0 as i64)); self.machine_st.p += 1; } + &Instruction::GetPrevLevel(r) => { + let prev_b = self.machine_st.stack.index_or_frame(self.machine_st.b).prelude.b; + + self.machine_st[r] = fixnum_as_cell!(Fixnum::build_with(prev_b as i64)); + self.machine_st.p += 1; + } + &Instruction::GetCutPoint(r) => { + self.machine_st[r] = fixnum_as_cell!(Fixnum::build_with(self.machine_st.b as i64)); + self.machine_st.p += 1; + } &Instruction::Cut(r) => { let value = self.machine_st[r]; self.machine_st.cut_body(value); @@ -1170,7 +1180,7 @@ impl Machine { &Instruction::Allocate(num_cells) => { self.machine_st.allocate(num_cells); } - &Instruction::DefaultCallAcyclicTerm(_) => { + &Instruction::DefaultCallAcyclicTerm => { let addr = self.machine_st.registers[1]; if self.machine_st.is_cyclic_term(addr) { @@ -1179,7 +1189,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::DefaultExecuteAcyclicTerm(_) => { + &Instruction::DefaultExecuteAcyclicTerm => { let addr = self.machine_st.registers[1]; if self.machine_st.is_cyclic_term(addr) { @@ -1188,23 +1198,23 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallArg(_) => { + &Instruction::DefaultCallArg => { try_or_throw!(self.machine_st, self.machine_st.try_arg()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteArg(_) => { + &Instruction::DefaultExecuteArg => { try_or_throw!(self.machine_st, self.machine_st.try_arg()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::DefaultCallCompare(_) => { + &Instruction::DefaultCallCompare => { try_or_throw!(self.machine_st, self.machine_st.compare()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteCompare(_) => { + &Instruction::DefaultExecuteCompare => { try_or_throw!(self.machine_st, self.machine_st.compare()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::DefaultCallTermGreaterThan(_) => { + &Instruction::DefaultCallTermGreaterThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1214,7 +1224,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::DefaultExecuteTermGreaterThan(_) => { + &Instruction::DefaultExecuteTermGreaterThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1224,7 +1234,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::DefaultCallTermLessThan(_) => { + &Instruction::DefaultCallTermLessThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1234,7 +1244,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::DefaultExecuteTermLessThan(_) => { + &Instruction::DefaultExecuteTermLessThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1244,7 +1254,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::DefaultCallTermGreaterThanOrEqual(_) => { + &Instruction::DefaultCallTermGreaterThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1257,7 +1267,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteTermGreaterThanOrEqual(_) => { + &Instruction::DefaultExecuteTermGreaterThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1270,7 +1280,7 @@ impl Machine { } } } - &Instruction::DefaultCallTermLessThanOrEqual(_) => { + &Instruction::DefaultCallTermLessThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1283,7 +1293,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteTermLessThanOrEqual(_) => { + &Instruction::DefaultExecuteTermLessThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1296,11 +1306,11 @@ impl Machine { } } } - &Instruction::DefaultCallRead(_) => { + &Instruction::DefaultCallRead => { try_or_throw!(self.machine_st, self.read()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteRead(_) => { + &Instruction::DefaultExecuteRead => { try_or_throw!(self.machine_st, self.read()); if self.machine_st.fail { @@ -1309,11 +1319,11 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallCopyTerm(_) => { + &Instruction::DefaultCallCopyTerm => { self.machine_st.copy_term(AttrVarPolicy::DeepCopy); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteCopyTerm(_) => { + &Instruction::DefaultExecuteCopyTerm => { self.machine_st.copy_term(AttrVarPolicy::DeepCopy); if self.machine_st.fail { @@ -1322,7 +1332,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallTermEqual(_) => { + &Instruction::DefaultCallTermEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1332,7 +1342,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::DefaultExecuteTermEqual(_) => { + &Instruction::DefaultExecuteTermEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1342,26 +1352,26 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallGround(_) => { + &Instruction::DefaultCallGround => { if self.machine_st.ground_test() { self.machine_st.backtrack(); } else { self.machine_st.p += 1; } } - &Instruction::DefaultExecuteGround(_) => { + &Instruction::DefaultExecuteGround => { if self.machine_st.ground_test() { self.machine_st.backtrack(); } else { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallFunctor(_) => { + &Instruction::DefaultCallFunctor => { try_or_throw!(self.machine_st, self.machine_st.try_functor()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteFunctor(_) => { + &Instruction::DefaultExecuteFunctor => { try_or_throw!(self.machine_st, self.machine_st.try_functor()); if self.machine_st.fail { @@ -1370,7 +1380,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallTermNotEqual(_) => { + &Instruction::DefaultCallTermNotEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1380,7 +1390,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::DefaultExecuteTermNotEqual(_) => { + &Instruction::DefaultExecuteTermNotEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1390,19 +1400,19 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallSort(_) => { + &Instruction::DefaultCallSort => { try_or_throw!(self.machine_st, self.machine_st.sort()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteSort(_) => { + &Instruction::DefaultExecuteSort => { try_or_throw!(self.machine_st, self.machine_st.sort()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::DefaultCallKeySort(_) => { + &Instruction::DefaultCallKeySort => { try_or_throw!(self.machine_st, self.machine_st.keysort()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteKeySort(_) => { + &Instruction::DefaultExecuteKeySort => { try_or_throw!(self.machine_st, self.machine_st.keysort()); if self.machine_st.fail { @@ -1411,15 +1421,15 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::DefaultCallIs(r, at, _) => { + &Instruction::DefaultCallIs(r, at) => { try_or_throw!(self.machine_st, self.machine_st.is(r, at)); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::DefaultExecuteIs(r, at, _) => { + &Instruction::DefaultExecuteIs(r, at) => { try_or_throw!(self.machine_st, self.machine_st.is(r, at)); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallAcyclicTerm(_) => { + &Instruction::CallAcyclicTerm => { let addr = self.machine_st.registers[1]; if self.machine_st.is_cyclic_term(addr) { @@ -1433,7 +1443,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteAcyclicTerm(_) => { + &Instruction::ExecuteAcyclicTerm => { let addr = self.machine_st.registers[1]; if self.machine_st.is_cyclic_term(addr) { @@ -1447,7 +1457,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallArg(_) => { + &Instruction::CallArg => { try_or_throw!(self.machine_st, self.machine_st.try_arg()); if self.machine_st.fail { @@ -1461,7 +1471,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteArg(_) => { + &Instruction::ExecuteArg => { try_or_throw!(self.machine_st, self.machine_st.try_arg()); if self.machine_st.fail { @@ -1475,7 +1485,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallCompare(_) => { + &Instruction::CallCompare => { try_or_throw!(self.machine_st, self.machine_st.compare()); if self.machine_st.fail { @@ -1489,7 +1499,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteCompare(_) => { + &Instruction::ExecuteCompare => { try_or_throw!(self.machine_st, self.machine_st.compare()); if self.machine_st.fail { @@ -1503,7 +1513,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallTermGreaterThan(_) => { + &Instruction::CallTermGreaterThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1518,7 +1528,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::ExecuteTermGreaterThan(_) => { + &Instruction::ExecuteTermGreaterThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1533,7 +1543,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::CallTermLessThan(_) => { + &Instruction::CallTermLessThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1548,7 +1558,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::ExecuteTermLessThan(_) => { + &Instruction::ExecuteTermLessThan => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1563,7 +1573,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::CallTermGreaterThanOrEqual(_) => { + &Instruction::CallTermGreaterThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1581,7 +1591,7 @@ impl Machine { } } } - &Instruction::ExecuteTermGreaterThanOrEqual(_) => { + &Instruction::ExecuteTermGreaterThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1599,7 +1609,7 @@ impl Machine { } } } - &Instruction::CallTermLessThanOrEqual(_) => { + &Instruction::CallTermLessThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1617,7 +1627,7 @@ impl Machine { } } } - &Instruction::ExecuteTermLessThanOrEqual(_) => { + &Instruction::ExecuteTermLessThanOrEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1635,7 +1645,7 @@ impl Machine { } } } - &Instruction::CallRead(_) => { + &Instruction::CallRead => { try_or_throw!(self.machine_st, self.read()); if self.machine_st.fail { @@ -1649,7 +1659,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteRead(_) => { + &Instruction::ExecuteRead => { try_or_throw!(self.machine_st, self.read()); if self.machine_st.fail { @@ -1663,7 +1673,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallCopyTerm(_) => { + &Instruction::CallCopyTerm => { self.machine_st.copy_term(AttrVarPolicy::DeepCopy); if self.machine_st.fail { @@ -1677,7 +1687,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteCopyTerm(_) => { + &Instruction::ExecuteCopyTerm => { self.machine_st.copy_term(AttrVarPolicy::DeepCopy); if self.machine_st.fail { @@ -1691,7 +1701,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallTermEqual(_) => { + &Instruction::CallTermEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1706,7 +1716,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteTermEqual(_) => { + &Instruction::ExecuteTermEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1721,7 +1731,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallGround(_) => { + &Instruction::CallGround => { if self.machine_st.ground_test() { self.machine_st.backtrack(); } else { @@ -1733,7 +1743,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteGround(_) => { + &Instruction::ExecuteGround => { if self.machine_st.ground_test() { self.machine_st.backtrack(); } else { @@ -1745,7 +1755,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallFunctor(_) => { + &Instruction::CallFunctor => { try_or_throw!(self.machine_st, self.machine_st.try_functor()); if self.machine_st.fail { @@ -1759,7 +1769,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteFunctor(_) => { + &Instruction::ExecuteFunctor => { try_or_throw!(self.machine_st, self.machine_st.try_functor()); if self.machine_st.fail { @@ -1773,7 +1783,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallTermNotEqual(_) => { + &Instruction::CallTermNotEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1788,7 +1798,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteTermNotEqual(_) => { + &Instruction::ExecuteTermNotEqual => { let a1 = self.machine_st.registers[1]; let a2 = self.machine_st.registers[2]; @@ -1803,7 +1813,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallSort(_) => { + &Instruction::CallSort => { try_or_throw!(self.machine_st, self.machine_st.sort()); if self.machine_st.fail { @@ -1817,7 +1827,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteSort(_) => { + &Instruction::ExecuteSort => { try_or_throw!(self.machine_st, self.machine_st.sort()); if self.machine_st.fail { @@ -1831,7 +1841,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallKeySort(_) => { + &Instruction::CallKeySort => { try_or_throw!(self.machine_st, self.machine_st.keysort()); if self.machine_st.fail { @@ -1845,7 +1855,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteKeySort(_) => { + &Instruction::ExecuteKeySort => { try_or_throw!(self.machine_st, self.machine_st.keysort()); if self.machine_st.fail { @@ -1859,7 +1869,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallIs(r, at, _) => { + &Instruction::CallIs(r, at) => { try_or_throw!(self.machine_st, self.machine_st.is(r, at)); if self.machine_st.fail { @@ -1873,7 +1883,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteIs(r, at, _) => { + &Instruction::ExecuteIs(r, at) => { try_or_throw!(self.machine_st, self.machine_st.is(r, at)); if self.machine_st.fail { @@ -1887,7 +1897,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallN(arity, _) => { + &Instruction::CallN(arity) => { let pred = self.machine_st.registers[1]; for i in 2..arity + 1 { @@ -1910,7 +1920,7 @@ impl Machine { ); } } - &Instruction::ExecuteN(arity, _) => { + &Instruction::ExecuteN(arity) => { let pred = self.machine_st.registers[1]; for i in 2..arity + 1 { @@ -1933,7 +1943,7 @@ impl Machine { ); } } - &Instruction::DefaultCallN(arity, _) => { + &Instruction::DefaultCallN(arity) => { let pred = self.machine_st.registers[1]; for i in 2..arity + 1 { @@ -1951,7 +1961,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::DefaultExecuteN(arity, _) => { + &Instruction::DefaultExecuteN(arity) => { let pred = self.machine_st.registers[1]; for i in 2..arity + 1 { @@ -1969,7 +1979,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::CallNumberLessThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::CallNumberLessThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -1987,7 +1997,7 @@ impl Machine { } } } - &Instruction::ExecuteNumberLessThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::ExecuteNumberLessThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2005,7 +2015,7 @@ impl Machine { } } } - &Instruction::CallNumberEqual(ref at_1, ref at_2, _) => { + &Instruction::CallNumberEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2023,7 +2033,7 @@ impl Machine { } } } - &Instruction::ExecuteNumberEqual(ref at_1, ref at_2, _) => { + &Instruction::ExecuteNumberEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2041,7 +2051,7 @@ impl Machine { } } } - &Instruction::CallNumberNotEqual(ref at_1, ref at_2, _) => { + &Instruction::CallNumberNotEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2059,7 +2069,7 @@ impl Machine { } } } - &Instruction::ExecuteNumberNotEqual(ref at_1, ref at_2, _) => { + &Instruction::ExecuteNumberNotEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2077,7 +2087,7 @@ impl Machine { } } } - &Instruction::CallNumberGreaterThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::CallNumberGreaterThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2095,7 +2105,7 @@ impl Machine { } } } - &Instruction::ExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::ExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2113,7 +2123,7 @@ impl Machine { } } } - &Instruction::CallNumberGreaterThan(ref at_1, ref at_2, _) => { + &Instruction::CallNumberGreaterThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2131,7 +2141,7 @@ impl Machine { } } } - &Instruction::ExecuteNumberGreaterThan(ref at_1, ref at_2, _) => { + &Instruction::ExecuteNumberGreaterThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2149,7 +2159,7 @@ impl Machine { } } } - &Instruction::CallNumberLessThan(ref at_1, ref at_2, _) => { + &Instruction::CallNumberLessThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2167,7 +2177,7 @@ impl Machine { } } } - &Instruction::ExecuteNumberLessThan(ref at_1, ref at_2, _) => { + &Instruction::ExecuteNumberLessThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2185,7 +2195,7 @@ impl Machine { } } } - &Instruction::DefaultCallNumberLessThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultCallNumberLessThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2198,7 +2208,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteNumberLessThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultExecuteNumberLessThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2211,7 +2221,7 @@ impl Machine { } } } - &Instruction::DefaultCallNumberNotEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultCallNumberNotEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2224,7 +2234,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteNumberNotEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultExecuteNumberNotEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2237,7 +2247,7 @@ impl Machine { } } } - &Instruction::DefaultCallNumberEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultCallNumberEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2250,7 +2260,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteNumberEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultExecuteNumberEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2263,7 +2273,7 @@ impl Machine { } } } - &Instruction::DefaultCallNumberGreaterThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultCallNumberGreaterThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2276,7 +2286,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2, _) => { + &Instruction::DefaultExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2289,7 +2299,7 @@ impl Machine { } } } - &Instruction::DefaultCallNumberGreaterThan(ref at_1, ref at_2, _) => { + &Instruction::DefaultCallNumberGreaterThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2302,7 +2312,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteNumberGreaterThan(ref at_1, ref at_2, _) => { + &Instruction::DefaultExecuteNumberGreaterThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2315,7 +2325,7 @@ impl Machine { } } } - &Instruction::DefaultCallNumberLessThan(ref at_1, ref at_2, _) => { + &Instruction::DefaultCallNumberLessThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2328,7 +2338,7 @@ impl Machine { } } } - &Instruction::DefaultExecuteNumberLessThan(ref at_1, ref at_2, _) => { + &Instruction::DefaultExecuteNumberLessThan(ref at_1, ref at_2) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); @@ -2342,7 +2352,7 @@ impl Machine { } } // - &Instruction::CallIsAtom(r, _) => { + &Instruction::CallIsAtom(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, @@ -2371,7 +2381,7 @@ impl Machine { } ); } - &Instruction::ExecuteIsAtom(r, _) => { + &Instruction::ExecuteIsAtom(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, @@ -2400,7 +2410,7 @@ impl Machine { } ); } - &Instruction::CallIsAtomic(r, _) => { + &Instruction::CallIsAtomic(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, @@ -2430,7 +2440,7 @@ impl Machine { } ); } - &Instruction::ExecuteIsAtomic(r, _) => { + &Instruction::ExecuteIsAtomic(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, @@ -2460,7 +2470,7 @@ impl Machine { } ); } - &Instruction::CallIsCompound(r, _) => { + &Instruction::CallIsCompound(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, @@ -2491,7 +2501,7 @@ impl Machine { } ); } - &Instruction::ExecuteIsCompound(r, _) => { + &Instruction::ExecuteIsCompound(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, @@ -2522,7 +2532,7 @@ impl Machine { } ); } - &Instruction::CallIsInteger(r, _) => { + &Instruction::CallIsInteger(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match Number::try_from(d) { @@ -2541,7 +2551,7 @@ impl Machine { } } } - &Instruction::ExecuteIsInteger(r, _) => { + &Instruction::ExecuteIsInteger(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match Number::try_from(d) { @@ -2560,7 +2570,7 @@ impl Machine { } } } - &Instruction::CallIsNumber(r, _) => { + &Instruction::CallIsNumber(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match Number::try_from(d) { @@ -2572,7 +2582,7 @@ impl Machine { } } } - &Instruction::ExecuteIsNumber(r, _) => { + &Instruction::ExecuteIsNumber(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match Number::try_from(d) { @@ -2584,7 +2594,7 @@ impl Machine { } } } - &Instruction::CallIsRational(r, _) => { + &Instruction::CallIsRational(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, @@ -2603,7 +2613,7 @@ impl Machine { } ); } - &Instruction::ExecuteIsRational(r, _) => { + &Instruction::ExecuteIsRational(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); read_heap_cell!(d, @@ -2622,7 +2632,7 @@ impl Machine { } ); } - &Instruction::CallIsFloat(r, _) => { + &Instruction::CallIsFloat(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match Number::try_from(d) { @@ -2634,7 +2644,7 @@ impl Machine { } } } - &Instruction::ExecuteIsFloat(r, _) => { + &Instruction::ExecuteIsFloat(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match Number::try_from(d) { @@ -2646,7 +2656,7 @@ impl Machine { } } } - &Instruction::CallIsNonVar(r, _) => { + &Instruction::CallIsNonVar(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match d.get_tag() { @@ -2660,7 +2670,7 @@ impl Machine { } } } - &Instruction::ExecuteIsNonVar(r, _) => { + &Instruction::ExecuteIsNonVar(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match d.get_tag() { @@ -2674,7 +2684,7 @@ impl Machine { } } } - &Instruction::CallIsVar(r, _) => { + &Instruction::CallIsVar(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match d.get_tag() { @@ -2688,7 +2698,7 @@ impl Machine { } } } - &Instruction::ExecuteIsVar(r, _) => { + &Instruction::ExecuteIsVar(r) => { let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r])); match d.get_tag() { @@ -2702,7 +2712,7 @@ impl Machine { } } } - &Instruction::CallNamed(arity, name, ref idx, _) => { + &Instruction::CallNamed(arity, name, ref idx) => { let idx = idx.get(); try_or_throw!( @@ -2719,7 +2729,7 @@ impl Machine { ); } } - &Instruction::ExecuteNamed(arity, name, ref idx, _) => { + &Instruction::ExecuteNamed(arity, name, ref idx) => { let idx = idx.get(); try_or_throw!( @@ -2736,7 +2746,7 @@ impl Machine { ); } } - &Instruction::DefaultCallNamed(arity, name, ref idx, _) => { + &Instruction::DefaultCallNamed(arity, name, ref idx) => { let idx = idx.get(); try_or_throw!( @@ -2748,7 +2758,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::DefaultExecuteNamed(arity, name, ref idx, _) => { + &Instruction::DefaultExecuteNamed(arity, name, ref idx) => { let idx = idx.get(); try_or_throw!( @@ -2763,15 +2773,7 @@ impl Machine { &Instruction::Deallocate => { self.machine_st.deallocate() } - &Instruction::JmpByCall(arity, offset, _) => { - self.machine_st.num_of_args = arity; - self.machine_st.b0 = self.machine_st.b; - self.machine_st.cp = self.machine_st.p + 1; - self.machine_st.p += offset; - } - &Instruction::JmpByExecute(arity, offset, _) => { - self.machine_st.num_of_args = arity; - self.machine_st.b0 = self.machine_st.b; + &Instruction::JmpByCall(offset) => { self.machine_st.p += offset; } &Instruction::RevJmpBy(offset) => { @@ -3219,8 +3221,8 @@ impl Machine { self.machine_st.p += 1; } - &Instruction::PutUnsafeValue(n, arg) => { - let s = stack_loc!(AndFrame, self.machine_st.e, n); + &Instruction::PutUnsafeValue(perm_slot, arg) => { + let s = stack_loc!(AndFrame, self.machine_st.e, perm_slot); let addr = self.machine_st.store(self.machine_st.deref(stack_loc_as_cell!(s))); if addr.is_protected(self.machine_st.e) { @@ -3298,11 +3300,11 @@ impl Machine { self.machine_st.p += 1; } // - &Instruction::CallAtomChars(_) => { + &Instruction::CallAtomChars => { self.atom_chars(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteAtomChars(_) => { + &Instruction::ExecuteAtomChars => { self.atom_chars(); if self.machine_st.fail { @@ -3311,7 +3313,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallAtomCodes(_) => { + &Instruction::CallAtomCodes => { try_or_throw!(self.machine_st, self.atom_codes()); if self.machine_st.fail { @@ -3320,7 +3322,7 @@ impl Machine { self.machine_st.p += 1; } } - &Instruction::ExecuteAtomCodes(_) => { + &Instruction::ExecuteAtomCodes => { try_or_throw!(self.machine_st, self.atom_codes()); if self.machine_st.fail { @@ -3329,237 +3331,237 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallAtomLength(_) => { + &Instruction::CallAtomLength => { self.atom_length(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteAtomLength(_) => { + &Instruction::ExecuteAtomLength => { self.atom_length(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallBindFromRegister(_) => { + &Instruction::CallBindFromRegister => { self.bind_from_register(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteBindFromRegister(_) => { + &Instruction::ExecuteBindFromRegister => { self.bind_from_register(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallContinuation(_) => { + &Instruction::CallContinuation => { try_or_throw!(self.machine_st, self.call_continuation(false)); } - &Instruction::ExecuteContinuation(_) => { + &Instruction::ExecuteContinuation => { try_or_throw!(self.machine_st, self.call_continuation(true)); } - &Instruction::CallCharCode(_) => { + &Instruction::CallCharCode => { try_or_throw!(self.machine_st, self.char_code()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCharCode(_) => { + &Instruction::ExecuteCharCode => { try_or_throw!(self.machine_st, self.char_code()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCharType(_) => { + &Instruction::CallCharType => { self.char_type(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCharType(_) => { + &Instruction::ExecuteCharType => { self.char_type(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCharsToNumber(_) => { + &Instruction::CallCharsToNumber => { try_or_throw!(self.machine_st, self.chars_to_number()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCharsToNumber(_) => { + &Instruction::ExecuteCharsToNumber => { try_or_throw!(self.machine_st, self.chars_to_number()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCodesToNumber(_) => { + &Instruction::CallCodesToNumber => { try_or_throw!(self.machine_st, self.codes_to_number()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCodesToNumber(_) => { + &Instruction::ExecuteCodesToNumber => { try_or_throw!(self.machine_st, self.codes_to_number()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCopyTermWithoutAttrVars(_) => { + &Instruction::CallCopyTermWithoutAttrVars => { self.copy_term_without_attr_vars(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCopyTermWithoutAttrVars(_) => { + &Instruction::ExecuteCopyTermWithoutAttrVars => { self.copy_term_without_attr_vars(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCheckCutPoint(_) => { + &Instruction::CallCheckCutPoint => { self.check_cut_point(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCheckCutPoint(_) => { + &Instruction::ExecuteCheckCutPoint => { self.check_cut_point(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallClose(_) => { + &Instruction::CallClose => { try_or_throw!(self.machine_st, self.close()); self.machine_st.p += 1; } - &Instruction::ExecuteClose(_) => { + &Instruction::ExecuteClose => { try_or_throw!(self.machine_st, self.close()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallCopyToLiftedHeap(_) => { + &Instruction::CallCopyToLiftedHeap => { self.copy_to_lifted_heap(); self.machine_st.p += 1; } - &Instruction::ExecuteCopyToLiftedHeap(_) => { + &Instruction::ExecuteCopyToLiftedHeap => { self.copy_to_lifted_heap(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallCreatePartialString(_) => { + &Instruction::CallCreatePartialString => { self.create_partial_string(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCreatePartialString(_) => { + &Instruction::ExecuteCreatePartialString => { self.create_partial_string(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCurrentHostname(_) => { + &Instruction::CallCurrentHostname => { self.current_hostname(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCurrentHostname(_) => { + &Instruction::ExecuteCurrentHostname => { self.current_hostname(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCurrentInput(_) => { + &Instruction::CallCurrentInput => { try_or_throw!(self.machine_st, self.current_input()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCurrentInput(_) => { + &Instruction::ExecuteCurrentInput => { try_or_throw!(self.machine_st, self.current_input()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCurrentOutput(_) => { + &Instruction::CallCurrentOutput => { try_or_throw!(self.machine_st, self.current_output()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCurrentOutput(_) => { + &Instruction::ExecuteCurrentOutput => { try_or_throw!(self.machine_st, self.current_output()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDirectoryFiles(_) => { + &Instruction::CallDirectoryFiles => { try_or_throw!(self.machine_st, self.directory_files()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDirectoryFiles(_) => { + &Instruction::ExecuteDirectoryFiles => { try_or_throw!(self.machine_st, self.directory_files()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallFileSize(_) => { + &Instruction::CallFileSize => { self.file_size(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteFileSize(_) => { + &Instruction::ExecuteFileSize => { self.file_size(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallFileExists(_) => { + &Instruction::CallFileExists => { self.file_exists(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteFileExists(_) => { + &Instruction::ExecuteFileExists => { self.file_exists(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDirectoryExists(_) => { + &Instruction::CallDirectoryExists => { self.directory_exists(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDirectoryExists(_) => { + &Instruction::ExecuteDirectoryExists => { self.directory_exists(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDirectorySeparator(_) => { + &Instruction::CallDirectorySeparator => { self.directory_separator(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDirectorySeparator(_) => { + &Instruction::ExecuteDirectorySeparator => { self.directory_separator(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallMakeDirectory(_) => { + &Instruction::CallMakeDirectory => { self.make_directory(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteMakeDirectory(_) => { + &Instruction::ExecuteMakeDirectory => { self.make_directory(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallMakeDirectoryPath(_) => { + &Instruction::CallMakeDirectoryPath => { self.make_directory_path(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteMakeDirectoryPath(_) => { + &Instruction::ExecuteMakeDirectoryPath => { self.make_directory_path(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDeleteFile(_) => { + &Instruction::CallDeleteFile => { self.delete_file(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDeleteFile(_) => { + &Instruction::ExecuteDeleteFile => { self.delete_file(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallRenameFile(_) => { + &Instruction::CallRenameFile => { self.rename_file(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteRenameFile(_) => { + &Instruction::ExecuteRenameFile => { self.rename_file(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallFileCopy(_) => { + &Instruction::CallFileCopy => { self.file_copy(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteFileCopy(_) => { + &Instruction::ExecuteFileCopy => { self.file_copy(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallWorkingDirectory(_) => { + &Instruction::CallWorkingDirectory => { try_or_throw!(self.machine_st, self.working_directory()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteWorkingDirectory(_) => { + &Instruction::ExecuteWorkingDirectory => { try_or_throw!(self.machine_st, self.working_directory()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDeleteDirectory(_) => { + &Instruction::CallDeleteDirectory => { self.delete_directory(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDeleteDirectory(_) => { + &Instruction::ExecuteDeleteDirectory => { self.delete_directory(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPathCanonical(_) => { + &Instruction::CallPathCanonical => { try_or_throw!(self.machine_st, self.path_canonical()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePathCanonical(_) => { + &Instruction::ExecutePathCanonical => { try_or_throw!(self.machine_st, self.path_canonical()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallFileTime(_) => { + &Instruction::CallFileTime => { self.file_time(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteFileTime(_) => { + &Instruction::ExecuteFileTime => { self.file_time(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDynamicModuleResolution(arity, _) => { + &Instruction::CallDynamicModuleResolution(arity) => { let (module_name, key) = try_or_throw!( self.machine_st, self.dynamic_module_resolution(arity - 2) @@ -3574,7 +3576,7 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::ExecuteDynamicModuleResolution(arity, _) => { + &Instruction::ExecuteDynamicModuleResolution(arity) => { let (module_name, key) = try_or_throw!( self.machine_st, self.dynamic_module_resolution(arity - 2) @@ -3589,428 +3591,428 @@ impl Machine { self.machine_st.backtrack(); } } - &Instruction::CallFetchGlobalVar(_) => { + &Instruction::CallFetchGlobalVar => { self.fetch_global_var(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteFetchGlobalVar(_) => { + &Instruction::ExecuteFetchGlobalVar => { self.fetch_global_var(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallFirstStream(_) => { + &Instruction::CallFirstStream => { self.first_stream(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteFirstStream(_) => { + &Instruction::ExecuteFirstStream => { self.first_stream(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallFlushOutput(_) => { + &Instruction::CallFlushOutput => { try_or_throw!(self.machine_st, self.flush_output()); self.machine_st.p += 1; } - &Instruction::ExecuteFlushOutput(_) => { + &Instruction::ExecuteFlushOutput => { try_or_throw!(self.machine_st, self.flush_output()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallGetByte(_) => { + &Instruction::CallGetByte => { try_or_throw!(self.machine_st, self.get_byte()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetByte(_) => { + &Instruction::ExecuteGetByte => { try_or_throw!(self.machine_st, self.get_byte()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetChar(_) => { + &Instruction::CallGetChar => { try_or_throw!(self.machine_st, self.get_char()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetChar(_) => { + &Instruction::ExecuteGetChar => { try_or_throw!(self.machine_st, self.get_char()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetNChars(_) => { + &Instruction::CallGetNChars => { try_or_throw!(self.machine_st, self.get_n_chars()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetNChars(_) => { + &Instruction::ExecuteGetNChars => { try_or_throw!(self.machine_st, self.get_n_chars()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetCode(_) => { + &Instruction::CallGetCode => { try_or_throw!(self.machine_st, self.get_code()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetCode(_) => { + &Instruction::ExecuteGetCode => { try_or_throw!(self.machine_st, self.get_code()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetSingleChar(_) => { + &Instruction::CallGetSingleChar => { try_or_throw!(self.machine_st, self.get_single_char()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetSingleChar(_) => { + &Instruction::ExecuteGetSingleChar => { try_or_throw!(self.machine_st, self.get_single_char()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallTruncateIfNoLiftedHeapGrowthDiff(_) => { + &Instruction::CallTruncateIfNoLiftedHeapGrowthDiff => { self.truncate_if_no_lifted_heap_growth_diff(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteTruncateIfNoLiftedHeapGrowthDiff(_) => { + &Instruction::ExecuteTruncateIfNoLiftedHeapGrowthDiff => { self.truncate_if_no_lifted_heap_growth_diff(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallTruncateIfNoLiftedHeapGrowth(_) => { + &Instruction::CallTruncateIfNoLiftedHeapGrowth => { self.truncate_if_no_lifted_heap_growth(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteTruncateIfNoLiftedHeapGrowth(_) => { + &Instruction::ExecuteTruncateIfNoLiftedHeapGrowth => { self.truncate_if_no_lifted_heap_growth(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetAttributedVariableList(_) => { + &Instruction::CallGetAttributedVariableList => { self.get_attributed_variable_list(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetAttributedVariableList(_) => { + &Instruction::ExecuteGetAttributedVariableList => { self.get_attributed_variable_list(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetAttrVarQueueDelimiter(_) => { + &Instruction::CallGetAttrVarQueueDelimiter => { self.get_attr_var_queue_delimiter(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetAttrVarQueueDelimiter(_) => { + &Instruction::ExecuteGetAttrVarQueueDelimiter => { self.get_attr_var_queue_delimiter(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetAttrVarQueueBeyond(_) => { + &Instruction::CallGetAttrVarQueueBeyond => { self.get_attr_var_queue_beyond(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetAttrVarQueueBeyond(_) => { + &Instruction::ExecuteGetAttrVarQueueBeyond => { self.get_attr_var_queue_beyond(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetBValue(_) => { + &Instruction::CallGetBValue => { self.get_b_value(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetBValue(_) => { + &Instruction::ExecuteGetBValue => { self.get_b_value(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetContinuationChunk(_) => { + &Instruction::CallGetContinuationChunk => { self.get_continuation_chunk(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetContinuationChunk(_) => { + &Instruction::ExecuteGetContinuationChunk => { self.get_continuation_chunk(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLookupDBRef(_) => { + &Instruction::CallLookupDBRef => { self.lookup_db_ref(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLookupDBRef(_) => { + &Instruction::ExecuteLookupDBRef => { self.lookup_db_ref(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetNextOpDBRef(_) => { + &Instruction::CallGetNextOpDBRef => { self.get_next_op_db_ref(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetNextOpDBRef(_) => { + &Instruction::ExecuteGetNextOpDBRef => { self.get_next_op_db_ref(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallIsPartialString(_) => { + &Instruction::CallIsPartialString => { self.is_partial_string(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteIsPartialString(_) => { + &Instruction::ExecuteIsPartialString => { self.is_partial_string(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallHalt(_) => { + &Instruction::CallHalt => { self.halt(); self.machine_st.p += 1; } - &Instruction::ExecuteHalt(_) => { + &Instruction::ExecuteHalt => { self.halt(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallGetLiftedHeapFromOffset(_) => { + &Instruction::CallGetLiftedHeapFromOffset => { self.get_lifted_heap_from_offset(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetLiftedHeapFromOffset(_) => { + &Instruction::ExecuteGetLiftedHeapFromOffset => { self.get_lifted_heap_from_offset(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetLiftedHeapFromOffsetDiff(_) => { + &Instruction::CallGetLiftedHeapFromOffsetDiff => { self.get_lifted_heap_from_offset_diff(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetLiftedHeapFromOffsetDiff(_) => { + &Instruction::ExecuteGetLiftedHeapFromOffsetDiff => { self.get_lifted_heap_from_offset_diff(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetSCCCleaner(_) => { + &Instruction::CallGetSCCCleaner => { self.get_scc_cleaner(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetSCCCleaner(_) => { + &Instruction::ExecuteGetSCCCleaner => { self.get_scc_cleaner(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallHeadIsDynamic(_) => { + &Instruction::CallHeadIsDynamic => { self.head_is_dynamic(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteHeadIsDynamic(_) => { + &Instruction::ExecuteHeadIsDynamic => { self.head_is_dynamic(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallInstallSCCCleaner(_) => { + &Instruction::CallInstallSCCCleaner => { self.install_scc_cleaner(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteInstallSCCCleaner(_) => { + &Instruction::ExecuteInstallSCCCleaner => { self.install_scc_cleaner(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallInstallInferenceCounter(_) => { + &Instruction::CallInstallInferenceCounter => { try_or_throw!(self.machine_st, self.install_inference_counter()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteInstallInferenceCounter(_) => { + &Instruction::ExecuteInstallInferenceCounter => { try_or_throw!(self.machine_st, self.install_inference_counter()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLiftedHeapLength(_) => { + &Instruction::CallLiftedHeapLength => { self.lifted_heap_length(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLiftedHeapLength(_) => { + &Instruction::ExecuteLiftedHeapLength => { self.lifted_heap_length(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadLibraryAsStream(_) => { + &Instruction::CallLoadLibraryAsStream => { try_or_throw!(self.machine_st, self.load_library_as_stream()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadLibraryAsStream(_) => { + &Instruction::ExecuteLoadLibraryAsStream => { try_or_throw!(self.machine_st, self.load_library_as_stream()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallModuleExists(_) => { + &Instruction::CallModuleExists => { self.module_exists(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteModuleExists(_) => { + &Instruction::ExecuteModuleExists => { self.module_exists(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallNextEP(_) => { + &Instruction::CallNextEP => { self.next_ep(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteNextEP(_) => { + &Instruction::ExecuteNextEP => { self.next_ep(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallNoSuchPredicate(_) => { + &Instruction::CallNoSuchPredicate => { try_or_throw!(self.machine_st, self.no_such_predicate()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteNoSuchPredicate(_) => { + &Instruction::ExecuteNoSuchPredicate => { try_or_throw!(self.machine_st, self.no_such_predicate()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallNumberToChars(_) => { + &Instruction::CallNumberToChars => { self.number_to_chars(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteNumberToChars(_) => { + &Instruction::ExecuteNumberToChars => { self.number_to_chars(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallNumberToCodes(_) => { + &Instruction::CallNumberToCodes => { self.number_to_codes(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteNumberToCodes(_) => { + &Instruction::ExecuteNumberToCodes => { self.number_to_codes(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallOpDeclaration(_) => { + &Instruction::CallOpDeclaration => { try_or_throw!(self.machine_st, self.op_declaration()); self.machine_st.p += 1; } - &Instruction::ExecuteOpDeclaration(_) => { + &Instruction::ExecuteOpDeclaration => { try_or_throw!(self.machine_st, self.op_declaration()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallOpen(_) => { + &Instruction::CallOpen => { try_or_throw!(self.machine_st, self.open()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteOpen(_) => { + &Instruction::ExecuteOpen => { try_or_throw!(self.machine_st, self.open()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSetStreamOptions(_) => { + &Instruction::CallSetStreamOptions => { try_or_throw!(self.machine_st, self.set_stream_options()); self.machine_st.p += 1; } - &Instruction::ExecuteSetStreamOptions(_) => { + &Instruction::ExecuteSetStreamOptions => { try_or_throw!(self.machine_st, self.set_stream_options()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallNextStream(_) => { + &Instruction::CallNextStream => { self.next_stream(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteNextStream(_) => { + &Instruction::ExecuteNextStream => { self.next_stream(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPartialStringTail(_) => { + &Instruction::CallPartialStringTail => { self.partial_string_tail(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePartialStringTail(_) => { + &Instruction::ExecutePartialStringTail => { self.partial_string_tail(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPeekByte(_) => { + &Instruction::CallPeekByte => { try_or_throw!(self.machine_st, self.peek_byte()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePeekByte(_) => { + &Instruction::ExecutePeekByte => { try_or_throw!(self.machine_st, self.peek_byte()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPeekChar(_) => { + &Instruction::CallPeekChar => { try_or_throw!(self.machine_st, self.peek_char()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePeekChar(_) => { + &Instruction::ExecutePeekChar => { try_or_throw!(self.machine_st, self.peek_char()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPeekCode(_) => { + &Instruction::CallPeekCode => { try_or_throw!(self.machine_st, self.peek_code()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePeekCode(_) => { + &Instruction::ExecutePeekCode => { try_or_throw!(self.machine_st, self.peek_code()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPointsToContinuationResetMarker(_) => { + &Instruction::CallPointsToContinuationResetMarker => { self.points_to_continuation_reset_marker(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePointsToContinuationResetMarker(_) => { + &Instruction::ExecutePointsToContinuationResetMarker => { self.points_to_continuation_reset_marker(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPutByte(_) => { + &Instruction::CallPutByte => { try_or_throw!(self.machine_st, self.put_byte()); self.machine_st.p += 1; } - &Instruction::ExecutePutByte(_) => { + &Instruction::ExecutePutByte => { try_or_throw!(self.machine_st, self.put_byte()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPutChar(_) => { + &Instruction::CallPutChar => { try_or_throw!(self.machine_st, self.put_char()); self.machine_st.p += 1; } - &Instruction::ExecutePutChar(_) => { + &Instruction::ExecutePutChar => { try_or_throw!(self.machine_st, self.put_char()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPutChars(_) => { + &Instruction::CallPutChars => { try_or_throw!(self.machine_st, self.put_chars()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePutChars(_) => { + &Instruction::ExecutePutChars => { try_or_throw!(self.machine_st, self.put_chars()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPutCode(_) => { + &Instruction::CallPutCode => { try_or_throw!(self.machine_st, self.put_code()); self.machine_st.p += 1; } - &Instruction::ExecutePutCode(_) => { + &Instruction::ExecutePutCode => { try_or_throw!(self.machine_st, self.put_code()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallReadQueryTerm(_) => { + &Instruction::CallReadQueryTerm => { try_or_throw!(self.machine_st, self.read_query_term()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteReadQueryTerm(_) => { + &Instruction::ExecuteReadQueryTerm => { try_or_throw!(self.machine_st, self.read_query_term()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallReadTerm(_) => { + &Instruction::CallReadTerm => { try_or_throw!(self.machine_st, self.read_term()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteReadTerm(_) => { + &Instruction::ExecuteReadTerm => { try_or_throw!(self.machine_st, self.read_term()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallRedoAttrVarBinding(_) => { + &Instruction::CallRedoAttrVarBinding => { self.redo_attr_var_binding(); self.machine_st.p += 1; } - &Instruction::ExecuteRedoAttrVarBinding(_) => { + &Instruction::ExecuteRedoAttrVarBinding => { self.redo_attr_var_binding(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallRemoveCallPolicyCheck(_) => { + &Instruction::CallRemoveCallPolicyCheck => { self.remove_call_policy_check(); self.machine_st.p += 1; } - &Instruction::ExecuteRemoveCallPolicyCheck(_) => { + &Instruction::ExecuteRemoveCallPolicyCheck => { self.remove_call_policy_check(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallRemoveInferenceCounter(_) => { + &Instruction::CallRemoveInferenceCounter => { self.remove_inference_counter(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteRemoveInferenceCounter(_) => { + &Instruction::ExecuteRemoveInferenceCounter => { self.remove_inference_counter(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallResetContinuationMarker(_) => { + &Instruction::CallResetContinuationMarker => { self.reset_continuation_marker(); self.machine_st.p += 1; } - &Instruction::ExecuteResetContinuationMarker(_) => { + &Instruction::ExecuteResetContinuationMarker => { self.reset_continuation_marker(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallRestoreCutPolicy(_) => { + &Instruction::CallRestoreCutPolicy => { self.restore_cut_policy(); self.machine_st.p += 1; } - &Instruction::ExecuteRestoreCutPolicy(_) => { + &Instruction::ExecuteRestoreCutPolicy => { self.restore_cut_policy(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallSetCutPoint(r, _) => { + &Instruction::CallSetCutPoint(r) => { if !self.set_cut_point(r) { step_or_fail!(self, self.machine_st.p += 1); } } - &Instruction::ExecuteSetCutPoint(r, _) => { + &Instruction::ExecuteSetCutPoint(r) => { let cp = self.machine_st.cp; if !self.set_cut_point(r) { @@ -4023,962 +4025,962 @@ impl Machine { self.machine_st.cp = cp; } } - &Instruction::CallSetInput(_) => { + &Instruction::CallSetInput => { try_or_throw!(self.machine_st, self.set_input()); self.machine_st.p += 1; } - &Instruction::ExecuteSetInput(_) => { + &Instruction::ExecuteSetInput => { try_or_throw!(self.machine_st, self.set_input()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallSetOutput(_) => { + &Instruction::CallSetOutput => { try_or_throw!(self.machine_st, self.set_output()); self.machine_st.p += 1; } - &Instruction::ExecuteSetOutput(_) => { + &Instruction::ExecuteSetOutput => { try_or_throw!(self.machine_st, self.set_output()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallStoreBacktrackableGlobalVar(_) => { + &Instruction::CallStoreBacktrackableGlobalVar => { self.store_backtrackable_global_var(); self.machine_st.p += 1; } - &Instruction::ExecuteStoreBacktrackableGlobalVar(_) => { + &Instruction::ExecuteStoreBacktrackableGlobalVar => { self.store_backtrackable_global_var(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallStoreGlobalVar(_) => { + &Instruction::CallStoreGlobalVar => { self.store_global_var(); self.machine_st.p += 1; } - &Instruction::ExecuteStoreGlobalVar(_) => { + &Instruction::ExecuteStoreGlobalVar => { self.store_global_var(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallStreamProperty(_) => { + &Instruction::CallStreamProperty => { try_or_throw!(self.machine_st, self.stream_property()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteStreamProperty(_) => { + &Instruction::ExecuteStreamProperty => { try_or_throw!(self.machine_st, self.stream_property()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSetStreamPosition(_) => { + &Instruction::CallSetStreamPosition => { try_or_throw!(self.machine_st, self.set_stream_position()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSetStreamPosition(_) => { + &Instruction::ExecuteSetStreamPosition => { try_or_throw!(self.machine_st, self.set_stream_position()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallInferenceLevel(_) => { + &Instruction::CallInferenceLevel => { self.inference_level(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteInferenceLevel(_) => { + &Instruction::ExecuteInferenceLevel => { self.inference_level(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCleanUpBlock(_) => { + &Instruction::CallCleanUpBlock => { self.clean_up_block(); self.machine_st.p += 1; } - &Instruction::ExecuteCleanUpBlock(_) => { + &Instruction::ExecuteCleanUpBlock => { self.clean_up_block(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallFail(_) | &Instruction::ExecuteFail(_) => { + &Instruction::CallFail | &Instruction::ExecuteFail => { self.machine_st.backtrack(); } - &Instruction::CallGetBall(_) => { + &Instruction::CallGetBall => { self.get_ball(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetBall(_) => { + &Instruction::ExecuteGetBall => { self.get_ball(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetCurrentBlock(_) => { + &Instruction::CallGetCurrentBlock => { self.get_current_block(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetCurrentBlock(_) => { + &Instruction::ExecuteGetCurrentBlock => { self.get_current_block(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetCutPoint(_) => { + &Instruction::CallGetCutPoint => { self.get_cut_point(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetCutPoint(_) => { + &Instruction::ExecuteGetCutPoint => { self.get_cut_point(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetDoubleQuotes(_) => { + &Instruction::CallGetDoubleQuotes => { self.get_double_quotes(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetDoubleQuotes(_) => { + &Instruction::ExecuteGetDoubleQuotes => { self.get_double_quotes(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallInstallNewBlock(_) => { + &Instruction::CallInstallNewBlock => { self.machine_st.install_new_block(self.machine_st.registers[1]); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteInstallNewBlock(_) => { + &Instruction::ExecuteInstallNewBlock => { self.machine_st.install_new_block(self.machine_st.registers[1]); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallMaybe(_) => { + &Instruction::CallMaybe => { self.maybe(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteMaybe(_) => { + &Instruction::ExecuteMaybe => { self.maybe(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCpuNow(_) => { + &Instruction::CallCpuNow => { self.cpu_now(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCpuNow(_) => { + &Instruction::ExecuteCpuNow => { self.cpu_now(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDeterministicLengthRundown(_) => { + &Instruction::CallDeterministicLengthRundown => { try_or_throw!(self.machine_st, self.det_length_rundown()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDeterministicLengthRundown(_) => { + &Instruction::ExecuteDeterministicLengthRundown => { try_or_throw!(self.machine_st, self.det_length_rundown()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallHttpOpen(_) => { + &Instruction::CallHttpOpen => { try_or_throw!(self.machine_st, self.http_open()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteHttpOpen(_) => { + &Instruction::ExecuteHttpOpen => { try_or_throw!(self.machine_st, self.http_open()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallHttpListen(_) => { + &Instruction::CallHttpListen => { try_or_throw!(self.machine_st, self.http_listen()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteHttpListen(_) => { + &Instruction::ExecuteHttpListen => { try_or_throw!(self.machine_st, self.http_listen()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallHttpAccept(_) => { + &Instruction::CallHttpAccept => { try_or_throw!(self.machine_st, self.http_accept()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteHttpAccept(_) => { + &Instruction::ExecuteHttpAccept => { try_or_throw!(self.machine_st, self.http_accept()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallHttpAnswer(_) => { + &Instruction::CallHttpAnswer => { try_or_throw!(self.machine_st, self.http_answer()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteHttpAnswer(_) => { + &Instruction::ExecuteHttpAnswer => { try_or_throw!(self.machine_st, self.http_answer()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadForeignLib(_) => { + &Instruction::CallLoadForeignLib => { try_or_throw!(self.machine_st, self.load_foreign_lib()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadForeignLib(_) => { + &Instruction::ExecuteLoadForeignLib => { try_or_throw!(self.machine_st, self.load_foreign_lib()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallForeignCall(_) => { + &Instruction::CallForeignCall => { try_or_throw!(self.machine_st, self.foreign_call()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteForeignCall(_) => { + &Instruction::ExecuteForeignCall => { try_or_throw!(self.machine_st, self.foreign_call()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDefineForeignStruct(_) => { + &Instruction::CallDefineForeignStruct => { try_or_throw!(self.machine_st, self.define_foreign_struct()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDefineForeignStruct(_) => { + &Instruction::ExecuteDefineForeignStruct => { try_or_throw!(self.machine_st, self.define_foreign_struct()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCurrentTime(_) => { + &Instruction::CallCurrentTime => { self.current_time(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCurrentTime(_) => { + &Instruction::ExecuteCurrentTime => { self.current_time(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallQuotedToken(_) => { + &Instruction::CallQuotedToken => { self.quoted_token(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteQuotedToken(_) => { + &Instruction::ExecuteQuotedToken => { self.quoted_token(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallReadTermFromChars(_) => { + &Instruction::CallReadTermFromChars => { try_or_throw!(self.machine_st, self.read_term_from_chars()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteReadTermFromChars(_) => { + &Instruction::ExecuteReadTermFromChars => { try_or_throw!(self.machine_st, self.read_term_from_chars()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallResetBlock(_) => { + &Instruction::CallResetBlock => { self.reset_block(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteResetBlock(_) => { + &Instruction::ExecuteResetBlock => { self.reset_block(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallReturnFromVerifyAttr(_) | - &Instruction::ExecuteReturnFromVerifyAttr(_) => { + &Instruction::CallReturnFromVerifyAttr | + &Instruction::ExecuteReturnFromVerifyAttr => { self.return_from_verify_attr(); } - &Instruction::CallSetBall(_) => { + &Instruction::CallSetBall => { self.set_ball(); self.machine_st.p += 1; } - &Instruction::ExecuteSetBall(_) => { + &Instruction::ExecuteSetBall => { self.set_ball(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPushBallStack(_) => { + &Instruction::CallPushBallStack => { self.push_ball_stack(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePushBallStack(_) => { + &Instruction::ExecutePushBallStack => { self.push_ball_stack(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPopBallStack(_) => { + &Instruction::CallPopBallStack => { self.pop_ball_stack(); self.machine_st.p += 1; } - &Instruction::ExecutePopBallStack(_) => { + &Instruction::ExecutePopBallStack => { self.pop_ball_stack(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPopFromBallStack(_) => { + &Instruction::CallPopFromBallStack => { self.pop_from_ball_stack(); self.machine_st.p += 1; } - &Instruction::ExecutePopFromBallStack(_) => { + &Instruction::ExecutePopFromBallStack => { self.pop_from_ball_stack(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallSetCutPointByDefault(r, _) => { + &Instruction::CallSetCutPointByDefault(r) => { self.set_cut_point_by_default(r); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSetCutPointByDefault(r, _) => { + &Instruction::ExecuteSetCutPointByDefault(r) => { self.set_cut_point_by_default(r); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSetDoubleQuotes(_) => { + &Instruction::CallSetDoubleQuotes => { self.set_double_quotes(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSetDoubleQuotes(_) => { + &Instruction::ExecuteSetDoubleQuotes => { self.set_double_quotes(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSetSeed(_) => { + &Instruction::CallSetSeed => { self.set_seed(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSetSeed(_) => { + &Instruction::ExecuteSetSeed => { self.set_seed(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSkipMaxList(_) => { + &Instruction::CallSkipMaxList => { try_or_throw!(self.machine_st, self.machine_st.skip_max_list()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSkipMaxList(_) => { + &Instruction::ExecuteSkipMaxList => { try_or_throw!(self.machine_st, self.machine_st.skip_max_list()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSleep(_) => { + &Instruction::CallSleep => { self.sleep(); self.machine_st.p += 1; } - &Instruction::ExecuteSleep(_) => { + &Instruction::ExecuteSleep => { self.sleep(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallSocketClientOpen(_) => { + &Instruction::CallSocketClientOpen => { try_or_throw!(self.machine_st, self.socket_client_open()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSocketClientOpen(_) => { + &Instruction::ExecuteSocketClientOpen => { try_or_throw!(self.machine_st, self.socket_client_open()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSocketServerOpen(_) => { + &Instruction::CallSocketServerOpen => { try_or_throw!(self.machine_st, self.socket_server_open()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSocketServerOpen(_) => { + &Instruction::ExecuteSocketServerOpen => { try_or_throw!(self.machine_st, self.socket_server_open()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSocketServerAccept(_) => { + &Instruction::CallSocketServerAccept => { try_or_throw!(self.machine_st, self.socket_server_accept()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteSocketServerAccept(_) => { + &Instruction::ExecuteSocketServerAccept => { try_or_throw!(self.machine_st, self.socket_server_accept()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSocketServerClose(_) => { + &Instruction::CallSocketServerClose => { try_or_throw!(self.machine_st, self.socket_server_close()); self.machine_st.p += 1; } - &Instruction::ExecuteSocketServerClose(_) => { + &Instruction::ExecuteSocketServerClose => { try_or_throw!(self.machine_st, self.socket_server_close()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallTLSAcceptClient(_) => { + &Instruction::CallTLSAcceptClient => { try_or_throw!(self.machine_st, self.tls_accept_client()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteTLSAcceptClient(_) => { + &Instruction::ExecuteTLSAcceptClient => { try_or_throw!(self.machine_st, self.tls_accept_client()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallTLSClientConnect(_) => { + &Instruction::CallTLSClientConnect => { try_or_throw!(self.machine_st, self.tls_client_connect()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteTLSClientConnect(_) => { + &Instruction::ExecuteTLSClientConnect => { try_or_throw!(self.machine_st, self.tls_client_connect()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSucceed(_) => { + &Instruction::CallSucceed => { self.machine_st.p += 1; } - &Instruction::ExecuteSucceed(_) => { + &Instruction::ExecuteSucceed => { self.machine_st.p = self.machine_st.cp; } - &Instruction::CallTermAttributedVariables(_) => { + &Instruction::CallTermAttributedVariables => { self.term_attributed_variables(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteTermAttributedVariables(_) => { + &Instruction::ExecuteTermAttributedVariables => { self.term_attributed_variables(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallTermVariables(_) => { + &Instruction::CallTermVariables => { self.term_variables(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteTermVariables(_) => { + &Instruction::ExecuteTermVariables => { self.term_variables(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallTermVariablesUnderMaxDepth(_) => { + &Instruction::CallTermVariablesUnderMaxDepth => { self.term_variables_under_max_depth(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteTermVariablesUnderMaxDepth(_) => { + &Instruction::ExecuteTermVariablesUnderMaxDepth => { self.term_variables_under_max_depth(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallTruncateLiftedHeapTo(_) => { + &Instruction::CallTruncateLiftedHeapTo => { self.truncate_lifted_heap_to(); self.machine_st.p += 1; } - &Instruction::ExecuteTruncateLiftedHeapTo(_) => { + &Instruction::ExecuteTruncateLiftedHeapTo => { self.truncate_lifted_heap_to(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallUnifyWithOccursCheck(_) => { + &Instruction::CallUnifyWithOccursCheck => { self.unify_with_occurs_check(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteUnifyWithOccursCheck(_) => { + &Instruction::ExecuteUnifyWithOccursCheck => { self.unify_with_occurs_check(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallUnwindEnvironments(_) => { + &Instruction::CallUnwindEnvironments => { if !self.unwind_environments() { self.machine_st.p += 1; } } - &Instruction::ExecuteUnwindEnvironments(_) => { + &Instruction::ExecuteUnwindEnvironments => { if !self.unwind_environments() { self.machine_st.p = self.machine_st.cp; } } - &Instruction::CallUnwindStack(_) | &Instruction::ExecuteUnwindStack(_) => { + &Instruction::CallUnwindStack | &Instruction::ExecuteUnwindStack => { self.machine_st.unwind_stack(); self.machine_st.backtrack(); } - &Instruction::CallWAMInstructions(_) => { + &Instruction::CallWAMInstructions => { try_or_throw!(self.machine_st, self.wam_instructions()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteWAMInstructions(_) => { + &Instruction::ExecuteWAMInstructions => { try_or_throw!(self.machine_st, self.wam_instructions()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallInlinedInstructions(_) => { + &Instruction::CallInlinedInstructions => { self.inlined_instructions(); self.machine_st.p += 1; } - &Instruction::ExecuteInlinedInstructions(_) => { + &Instruction::ExecuteInlinedInstructions => { self.inlined_instructions(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallWriteTerm(_) => { + &Instruction::CallWriteTerm => { try_or_throw!(self.machine_st, self.write_term()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteWriteTerm(_) => { + &Instruction::ExecuteWriteTerm => { try_or_throw!(self.machine_st, self.write_term()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallWriteTermToChars(_) => { + &Instruction::CallWriteTermToChars => { try_or_throw!(self.machine_st, self.write_term_to_chars()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteWriteTermToChars(_) => { + &Instruction::ExecuteWriteTermToChars => { try_or_throw!(self.machine_st, self.write_term_to_chars()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallScryerPrologVersion(_) => { + &Instruction::CallScryerPrologVersion => { self.scryer_prolog_version(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteScryerPrologVersion(_) => { + &Instruction::ExecuteScryerPrologVersion => { self.scryer_prolog_version(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoRandomByte(_) => { + &Instruction::CallCryptoRandomByte => { self.crypto_random_byte(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCryptoRandomByte(_) => { + &Instruction::ExecuteCryptoRandomByte => { self.crypto_random_byte(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoDataHash(_) => { + &Instruction::CallCryptoDataHash => { self.crypto_data_hash(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCryptoDataHash(_) => { + &Instruction::ExecuteCryptoDataHash => { self.crypto_data_hash(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoDataHKDF(_) => { + &Instruction::CallCryptoDataHKDF => { self.crypto_data_hkdf(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCryptoDataHKDF(_) => { + &Instruction::ExecuteCryptoDataHKDF => { self.crypto_data_hkdf(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoPasswordHash(_) => { + &Instruction::CallCryptoPasswordHash => { self.crypto_password_hash(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCryptoPasswordHash(_) => { + &Instruction::ExecuteCryptoPasswordHash => { self.crypto_password_hash(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoDataEncrypt(_) => { + &Instruction::CallCryptoDataEncrypt => { self.crypto_data_encrypt(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCryptoDataEncrypt(_) => { + &Instruction::ExecuteCryptoDataEncrypt => { self.crypto_data_encrypt(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoDataDecrypt(_) => { + &Instruction::CallCryptoDataDecrypt => { self.crypto_data_decrypt(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCryptoDataDecrypt(_) => { + &Instruction::ExecuteCryptoDataDecrypt => { self.crypto_data_decrypt(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCryptoCurveScalarMult(_) => { + &Instruction::CallCryptoCurveScalarMult => { self.crypto_curve_scalar_mult(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCryptoCurveScalarMult(_) => { + &Instruction::ExecuteCryptoCurveScalarMult => { self.crypto_curve_scalar_mult(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallEd25519Sign(_) => { + &Instruction::CallEd25519Sign => { self.ed25519_sign(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteEd25519Sign(_) => { + &Instruction::ExecuteEd25519Sign => { self.ed25519_sign(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallEd25519Verify(_) => { + &Instruction::CallEd25519Verify => { self.ed25519_verify(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteEd25519Verify(_) => { + &Instruction::ExecuteEd25519Verify => { self.ed25519_verify(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallEd25519NewKeyPair(_) => { + &Instruction::CallEd25519NewKeyPair => { self.ed25519_new_key_pair(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteEd25519NewKeyPair(_) => { + &Instruction::ExecuteEd25519NewKeyPair => { self.ed25519_new_key_pair(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallEd25519KeyPairPublicKey(_) => { + &Instruction::CallEd25519KeyPairPublicKey => { self.ed25519_key_pair_public_key(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteEd25519KeyPairPublicKey(_) => { + &Instruction::ExecuteEd25519KeyPairPublicKey => { self.ed25519_key_pair_public_key(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCurve25519ScalarMult(_) => { + &Instruction::CallCurve25519ScalarMult => { self.curve25519_scalar_mult(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCurve25519ScalarMult(_) => { + &Instruction::ExecuteCurve25519ScalarMult => { self.curve25519_scalar_mult(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallFirstNonOctet(_) => { + &Instruction::CallFirstNonOctet => { self.first_non_octet(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteFirstNonOctet(_) => { + &Instruction::ExecuteFirstNonOctet => { self.first_non_octet(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadHTML(_) => { + &Instruction::CallLoadHTML => { self.load_html(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadHTML(_) => { + &Instruction::ExecuteLoadHTML => { self.load_html(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadXML(_) => { + &Instruction::CallLoadXML => { self.load_xml(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadXML(_) => { + &Instruction::ExecuteLoadXML => { self.load_xml(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallGetEnv(_) => { + &Instruction::CallGetEnv => { self.get_env(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetEnv(_) => { + &Instruction::ExecuteGetEnv => { self.get_env(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSetEnv(_) => { + &Instruction::CallSetEnv => { self.set_env(); self.machine_st.p += 1; } - &Instruction::ExecuteSetEnv(_) => { + &Instruction::ExecuteSetEnv => { self.set_env(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallUnsetEnv(_) => { + &Instruction::CallUnsetEnv => { self.unset_env(); self.machine_st.p += 1; } - &Instruction::ExecuteUnsetEnv(_) => { + &Instruction::ExecuteUnsetEnv => { self.unset_env(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallShell(_) => { + &Instruction::CallShell => { self.shell(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteShell(_) => { + &Instruction::ExecuteShell => { self.shell(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPID(_) => { + &Instruction::CallPID => { self.pid(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePID(_) => { + &Instruction::ExecutePID => { self.pid(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCharsBase64(_) => { + &Instruction::CallCharsBase64 => { try_or_throw!(self.machine_st, self.chars_base64()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCharsBase64(_) => { + &Instruction::ExecuteCharsBase64 => { try_or_throw!(self.machine_st, self.chars_base64()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDevourWhitespace(_) => { + &Instruction::CallDevourWhitespace => { try_or_throw!(self.machine_st, self.devour_whitespace()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDevourWhitespace(_) => { + &Instruction::ExecuteDevourWhitespace => { try_or_throw!(self.machine_st, self.devour_whitespace()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallIsSTOEnabled(_) => { + &Instruction::CallIsSTOEnabled => { self.is_sto_enabled(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteIsSTOEnabled(_) => { + &Instruction::ExecuteIsSTOEnabled => { self.is_sto_enabled(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallSetSTOAsUnify(_) => { + &Instruction::CallSetSTOAsUnify => { self.set_sto_as_unify(); self.machine_st.p += 1; } - &Instruction::ExecuteSetSTOAsUnify(_) => { + &Instruction::ExecuteSetSTOAsUnify => { self.set_sto_as_unify(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallSetNSTOAsUnify(_) => { + &Instruction::CallSetNSTOAsUnify => { self.set_nsto_as_unify(); self.machine_st.p += 1; } - &Instruction::ExecuteSetNSTOAsUnify(_) => { + &Instruction::ExecuteSetNSTOAsUnify => { self.set_nsto_as_unify(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallSetSTOWithErrorAsUnify(_) => { + &Instruction::CallSetSTOWithErrorAsUnify => { self.set_sto_with_error_as_unify(); self.machine_st.p += 1; } - &Instruction::ExecuteSetSTOWithErrorAsUnify(_) => { + &Instruction::ExecuteSetSTOWithErrorAsUnify => { self.set_sto_with_error_as_unify(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallHomeDirectory(_) => { + &Instruction::CallHomeDirectory => { self.home_directory(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteHomeDirectory(_) => { + &Instruction::ExecuteHomeDirectory => { self.home_directory(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDebugHook(_) => { + &Instruction::CallDebugHook => { self.debug_hook(); self.machine_st.p += 1; } - &Instruction::ExecuteDebugHook(_) => { + &Instruction::ExecuteDebugHook => { self.debug_hook(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPopCount(_) => { + &Instruction::CallPopCount => { self.pop_count(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePopCount(_) => { + &Instruction::ExecutePopCount => { self.pop_count(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallAddDiscontiguousPredicate(_) => { + &Instruction::CallAddDiscontiguousPredicate => { try_or_throw!(self.machine_st, self.add_discontiguous_predicate()); self.machine_st.p += 1; } - &Instruction::ExecuteAddDiscontiguousPredicate(_) => { + &Instruction::ExecuteAddDiscontiguousPredicate => { try_or_throw!(self.machine_st, self.add_discontiguous_predicate()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAddDynamicPredicate(_) => { + &Instruction::CallAddDynamicPredicate => { try_or_throw!(self.machine_st, self.add_dynamic_predicate()); self.machine_st.p += 1; } - &Instruction::ExecuteAddDynamicPredicate(_) => { + &Instruction::ExecuteAddDynamicPredicate => { try_or_throw!(self.machine_st, self.add_dynamic_predicate()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAddMultifilePredicate(_) => { + &Instruction::CallAddMultifilePredicate => { try_or_throw!(self.machine_st, self.add_multifile_predicate()); self.machine_st.p += 1; } - &Instruction::ExecuteAddMultifilePredicate(_) => { + &Instruction::ExecuteAddMultifilePredicate => { try_or_throw!(self.machine_st, self.add_multifile_predicate()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAddGoalExpansionClause(_) => { + &Instruction::CallAddGoalExpansionClause => { try_or_throw!(self.machine_st, self.add_goal_expansion_clause()); self.machine_st.p += 1; } - &Instruction::ExecuteAddGoalExpansionClause(_) => { + &Instruction::ExecuteAddGoalExpansionClause => { try_or_throw!(self.machine_st, self.add_goal_expansion_clause()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAddTermExpansionClause(_) => { + &Instruction::CallAddTermExpansionClause => { try_or_throw!(self.machine_st, self.add_term_expansion_clause()); self.machine_st.p += 1; } - &Instruction::ExecuteAddTermExpansionClause(_) => { + &Instruction::ExecuteAddTermExpansionClause => { try_or_throw!(self.machine_st, self.add_term_expansion_clause()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAddInSituFilenameModule(_) => { + &Instruction::CallAddInSituFilenameModule => { try_or_throw!(self.machine_st, self.add_in_situ_filename_module()); self.machine_st.p += 1; } - &Instruction::ExecuteAddInSituFilenameModule(_) => { + &Instruction::ExecuteAddInSituFilenameModule => { try_or_throw!(self.machine_st, self.add_in_situ_filename_module()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallClauseToEvacuable(_) => { + &Instruction::CallClauseToEvacuable => { try_or_throw!(self.machine_st, self.clause_to_evacuable()); self.machine_st.p += 1; } - &Instruction::ExecuteClauseToEvacuable(_) => { + &Instruction::ExecuteClauseToEvacuable => { try_or_throw!(self.machine_st, self.clause_to_evacuable()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallScopedClauseToEvacuable(_) => { + &Instruction::CallScopedClauseToEvacuable => { try_or_throw!(self.machine_st, self.scoped_clause_to_evacuable()); self.machine_st.p += 1; } - &Instruction::ExecuteScopedClauseToEvacuable(_) => { + &Instruction::ExecuteScopedClauseToEvacuable => { try_or_throw!(self.machine_st, self.scoped_clause_to_evacuable()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallConcludeLoad(_) => { + &Instruction::CallConcludeLoad => { try_or_throw!(self.machine_st, self.conclude_load()); self.machine_st.p += 1; } - &Instruction::ExecuteConcludeLoad(_) => { + &Instruction::ExecuteConcludeLoad => { try_or_throw!(self.machine_st, self.conclude_load()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallDeclareModule(_) => { + &Instruction::CallDeclareModule => { try_or_throw!(self.machine_st, self.declare_module()); self.machine_st.p += 1; } - &Instruction::ExecuteDeclareModule(_) => { + &Instruction::ExecuteDeclareModule => { try_or_throw!(self.machine_st, self.declare_module()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallLoadCompiledLibrary(_) => { + &Instruction::CallLoadCompiledLibrary => { try_or_throw!(self.machine_st, self.load_compiled_library()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadCompiledLibrary(_) => { + &Instruction::ExecuteLoadCompiledLibrary => { try_or_throw!(self.machine_st, self.load_compiled_library()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadContextSource(_) => { + &Instruction::CallLoadContextSource => { self.load_context_source(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadContextSource(_) => { + &Instruction::ExecuteLoadContextSource => { self.load_context_source(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadContextFile(_) => { + &Instruction::CallLoadContextFile => { self.load_context_file(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadContextFile(_) => { + &Instruction::ExecuteLoadContextFile => { self.load_context_file(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadContextDirectory(_) => { + &Instruction::CallLoadContextDirectory => { self.load_context_directory(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadContextDirectory(_) => { + &Instruction::ExecuteLoadContextDirectory => { self.load_context_directory(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadContextModule(_) => { + &Instruction::CallLoadContextModule => { self.load_context_module(self.machine_st.registers[1]); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadContextModule(_) => { + &Instruction::ExecuteLoadContextModule => { self.load_context_module(self.machine_st.registers[1]); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallLoadContextStream(_) => { + &Instruction::CallLoadContextStream => { self.load_context_stream(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteLoadContextStream(_) => { + &Instruction::ExecuteLoadContextStream => { self.load_context_stream(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPopLoadContext(_) => { + &Instruction::CallPopLoadContext => { self.pop_load_context(); self.machine_st.p += 1; } - &Instruction::ExecutePopLoadContext(_) => { + &Instruction::ExecutePopLoadContext => { self.pop_load_context(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPopLoadStatePayload(_) => { + &Instruction::CallPopLoadStatePayload => { self.pop_load_state_payload(); self.machine_st.p += 1; } - &Instruction::ExecutePopLoadStatePayload(_) => { + &Instruction::ExecutePopLoadStatePayload => { self.pop_load_state_payload(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPushLoadContext(_) => { + &Instruction::CallPushLoadContext => { try_or_throw!(self.machine_st, self.push_load_context()); self.machine_st.p += 1; } - &Instruction::ExecutePushLoadContext(_) => { + &Instruction::ExecutePushLoadContext => { try_or_throw!(self.machine_st, self.push_load_context()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPushLoadStatePayload(_) => { + &Instruction::CallPushLoadStatePayload => { self.push_load_state_payload(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePushLoadStatePayload(_) => { + &Instruction::ExecutePushLoadStatePayload => { self.push_load_state_payload(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallUseModule(_) => { + &Instruction::CallUseModule => { try_or_throw!(self.machine_st, self.use_module()); self.machine_st.p += 1; } - &Instruction::ExecuteUseModule(_) => { + &Instruction::ExecuteUseModule => { try_or_throw!(self.machine_st, self.use_module()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallBuiltInProperty(_) => { + &Instruction::CallBuiltInProperty => { self.builtin_property(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteBuiltInProperty(_) => { + &Instruction::ExecuteBuiltInProperty => { self.builtin_property(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallMetaPredicateProperty(_) => { + &Instruction::CallMetaPredicateProperty => { self.meta_predicate_property(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteMetaPredicateProperty(_) => { + &Instruction::ExecuteMetaPredicateProperty => { self.meta_predicate_property(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallMultifileProperty(_) => { + &Instruction::CallMultifileProperty => { self.multifile_property(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteMultifileProperty(_) => { + &Instruction::ExecuteMultifileProperty => { self.multifile_property(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDiscontiguousProperty(_) => { + &Instruction::CallDiscontiguousProperty => { self.discontiguous_property(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDiscontiguousProperty(_) => { + &Instruction::ExecuteDiscontiguousProperty => { self.discontiguous_property(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDynamicProperty(_) => { + &Instruction::CallDynamicProperty => { self.dynamic_property(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDynamicProperty(_) => { + &Instruction::ExecuteDynamicProperty => { self.dynamic_property(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallAbolishClause(_) => { + &Instruction::CallAbolishClause => { try_or_throw!(self.machine_st, self.abolish_clause()); self.machine_st.p += 1; } - &Instruction::ExecuteAbolishClause(_) => { + &Instruction::ExecuteAbolishClause => { try_or_throw!(self.machine_st, self.abolish_clause()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAsserta(_) => { + &Instruction::CallAsserta => { try_or_throw!(self.machine_st, self.compile_assert(AppendOrPrepend::Prepend)); self.machine_st.p += 1; } - &Instruction::ExecuteAsserta(_) => { + &Instruction::ExecuteAsserta => { try_or_throw!(self.machine_st, self.compile_assert(AppendOrPrepend::Prepend)); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAssertz(_) => { + &Instruction::CallAssertz => { try_or_throw!(self.machine_st, self.compile_assert(AppendOrPrepend::Append)); self.machine_st.p += 1; } - &Instruction::ExecuteAssertz(_) => { + &Instruction::ExecuteAssertz => { try_or_throw!(self.machine_st, self.compile_assert(AppendOrPrepend::Append)); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallRetract(_) => { + &Instruction::CallRetract => { try_or_throw!(self.machine_st, self.retract_clause()); self.machine_st.p += 1; } - &Instruction::ExecuteRetract(_) => { + &Instruction::ExecuteRetract => { try_or_throw!(self.machine_st, self.retract_clause()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallIsConsistentWithTermQueue(_) => { + &Instruction::CallIsConsistentWithTermQueue => { try_or_throw!(self.machine_st, self.is_consistent_with_term_queue()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteIsConsistentWithTermQueue(_) => { + &Instruction::ExecuteIsConsistentWithTermQueue => { try_or_throw!(self.machine_st, self.is_consistent_with_term_queue()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::CallFlushTermQueue(_) => { + &Instruction::CallFlushTermQueue => { try_or_throw!(self.machine_st, self.flush_term_queue()); self.machine_st.p += 1; } - &Instruction::ExecuteFlushTermQueue(_) => { + &Instruction::ExecuteFlushTermQueue => { try_or_throw!(self.machine_st, self.flush_term_queue()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallRemoveModuleExports(_) => { + &Instruction::CallRemoveModuleExports => { try_or_throw!(self.machine_st, self.remove_module_exports()); self.machine_st.p += 1; } - &Instruction::ExecuteRemoveModuleExports(_) => { + &Instruction::ExecuteRemoveModuleExports => { try_or_throw!(self.machine_st, self.remove_module_exports()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallAddNonCountedBacktracking(_) => { + &Instruction::CallAddNonCountedBacktracking => { try_or_throw!(self.machine_st, self.add_non_counted_backtracking()); self.machine_st.p += 1; } - &Instruction::ExecuteAddNonCountedBacktracking(_) => { + &Instruction::ExecuteAddNonCountedBacktracking => { try_or_throw!(self.machine_st, self.add_non_counted_backtracking()); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallPredicateDefined(_) => { + &Instruction::CallPredicateDefined => { self.machine_st.fail = !self.predicate_defined(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePredicateDefined(_) => { + &Instruction::ExecutePredicateDefined => { self.machine_st.fail = !self.predicate_defined(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallStripModule(_) => { + &Instruction::CallStripModule => { let (module_loc, qualified_goal) = self.machine_st.strip_module( self.machine_st.registers[1], self.machine_st.registers[2], @@ -5002,7 +5004,7 @@ impl Machine { step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteStripModule(_) => { + &Instruction::ExecuteStripModule => { let (module_loc, qualified_goal) = self.machine_st.strip_module( self.machine_st.registers[1], self.machine_st.registers[2], @@ -5026,31 +5028,31 @@ impl Machine { step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPrepareCallClause(arity, _) => { + &Instruction::CallPrepareCallClause(arity) => { try_or_throw!(self.machine_st, self.prepare_call_clause(arity)); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePrepareCallClause(arity, _) => { + &Instruction::ExecutePrepareCallClause(arity) => { try_or_throw!(self.machine_st, self.prepare_call_clause(arity)); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallCompileInlineOrExpandedGoal(_) => { + &Instruction::CallCompileInlineOrExpandedGoal => { try_or_throw!(self.machine_st, self.compile_inline_or_expanded_goal()); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteCompileInlineOrExpandedGoal(_) => { + &Instruction::ExecuteCompileInlineOrExpandedGoal => { try_or_throw!(self.machine_st, self.compile_inline_or_expanded_goal()); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallIsExpandedOrInlined(_) => { + &Instruction::CallIsExpandedOrInlined => { self.machine_st.fail = !self.is_expanded_or_inlined(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteIsExpandedOrInlined(_) => { + &Instruction::ExecuteIsExpandedOrInlined => { self.machine_st.fail = !self.is_expanded_or_inlined(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallInlineCallN(arity, _) => { + &Instruction::CallInlineCallN(arity) => { let call_at_index = |wam: &mut Machine, name, arity, ptr| { wam.try_call(name, arity, ptr) }; @@ -5066,7 +5068,7 @@ impl Machine { ); } } - &Instruction::ExecuteInlineCallN(arity, _) => { + &Instruction::ExecuteInlineCallN(arity) => { let call_at_index = |wam: &mut Machine, name, arity, ptr| { wam.try_execute(name, arity, ptr) }; @@ -5082,7 +5084,7 @@ impl Machine { ); } } - &Instruction::CallGetClauseP(_) => { + &Instruction::CallGetClauseP => { let module_name = cell_as_atom!(self.deref_register(3)); let (n, p) = self.get_clause_p(module_name); @@ -5098,7 +5100,7 @@ impl Machine { step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetClauseP(_) => { + &Instruction::ExecuteGetClauseP => { let module_name = cell_as_atom!(self.deref_register(3)); let (n, p) = self.get_clause_p(module_name); @@ -5114,7 +5116,7 @@ impl Machine { step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallInvokeClauseAtP(_) => { + &Instruction::CallInvokeClauseAtP => { let key_cell = self.machine_st.registers[1]; let key = self.machine_st.name_and_arity_from_heap(key_cell).unwrap(); @@ -5159,7 +5161,7 @@ impl Machine { self.machine_st.call_at_index(2, p); } - &Instruction::ExecuteInvokeClauseAtP(_) => { + &Instruction::ExecuteInvokeClauseAtP => { let key_cell = self.machine_st.registers[1]; let key = self.machine_st.name_and_arity_from_heap(key_cell).unwrap(); @@ -5204,51 +5206,51 @@ impl Machine { self.machine_st.execute_at_index(2, p); } - &Instruction::CallGetFromAttributedVarList(_) => { + &Instruction::CallGetFromAttributedVarList => { self.get_from_attributed_variable_list(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetFromAttributedVarList(_) => { + &Instruction::ExecuteGetFromAttributedVarList => { self.get_from_attributed_variable_list(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallPutToAttributedVarList(_) => { + &Instruction::CallPutToAttributedVarList => { self.put_to_attributed_variable_list(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecutePutToAttributedVarList(_) => { + &Instruction::ExecutePutToAttributedVarList => { self.put_to_attributed_variable_list(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDeleteFromAttributedVarList(_) => { + &Instruction::CallDeleteFromAttributedVarList => { self.delete_from_attributed_variable_list(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteDeleteFromAttributedVarList(_) => { + &Instruction::ExecuteDeleteFromAttributedVarList => { self.delete_from_attributed_variable_list(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallDeleteAllAttributesFromVar(_) => { + &Instruction::CallDeleteAllAttributesFromVar => { self.delete_all_attributes_from_var(); self.machine_st.p += 1; } - &Instruction::ExecuteDeleteAllAttributesFromVar(_) => { + &Instruction::ExecuteDeleteAllAttributesFromVar => { self.delete_all_attributes_from_var(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallUnattributedVar(_) => { + &Instruction::CallUnattributedVar => { self.machine_st.unattributed_var(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteUnattributedVar(_) => { + &Instruction::ExecuteUnattributedVar => { self.machine_st.unattributed_var(); self.machine_st.p = self.machine_st.cp; } - &Instruction::CallGetDBRefs(_) => { + &Instruction::CallGetDBRefs => { self.get_db_refs(); step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::ExecuteGetDBRefs(_) => { + &Instruction::ExecuteGetDBRefs => { self.get_db_refs(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } diff --git a/src/machine/load_state.rs b/src/machine/load_state.rs index 3d0a638a..56aa88eb 100644 --- a/src/machine/load_state.rs +++ b/src/machine/load_state.rs @@ -444,10 +444,8 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { 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), }) } diff --git a/src/machine/loader.rs b/src/machine/loader.rs index bb093a0e..51815c7e 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -1428,7 +1428,7 @@ impl MachineState { } } (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar, h) => { - term_stack.push(Term::Var(Cell::default(), Var::Generated(h))); + term_stack.push(Term::Var(Cell::default(), VarPtr::from(format!("_{}", h)))); } (HeapCellValueTag::Cons | HeapCellValueTag::CStr | HeapCellValueTag::Fixnum | HeapCellValueTag::Char | HeapCellValueTag::F64) => { diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index afa2bea2..fdc60e0b 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -2,7 +2,6 @@ use crate::parser::ast::*; use crate::arena::*; use crate::atom_table::*; -use crate::fixtures::*; use crate::forms::*; use crate::machine::loader::*; use crate::machine::machine_state::*; @@ -227,8 +226,8 @@ impl CodeIndex { } } -pub(crate) type HeapVarDict = IndexMap; -pub(crate) type AllocVarDict = IndexMap; +pub(crate) type HeapVarDict = IndexMap; +// pub(crate) type AllocVarDict = IndexMap; pub(crate) type GlobalVarDir = IndexMap), FxBuildHasher>; diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 6d0de7d9..26d0309b 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -500,13 +500,13 @@ impl MachineState { pub fn read_term(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult { fn push_var_eq_functors<'a>( heap: &mut Heap, - iter: impl Iterator, + iter: impl Iterator, atom_tbl: &mut AtomTable, ) -> Vec { let mut list_of_var_eqs = vec![]; for (var, binding) in iter { - let var_atom = atom_tbl.build_with(&var.to_string()); + let var_atom = atom_tbl.build_with(&var.borrow().to_string()); let h = heap.len(); heap.push(atom_as_cell!(atom!("="), 2)); @@ -672,7 +672,7 @@ impl MachineState { let printer = match self.try_from_list(self.registers[6], stub_gen) { Ok(addrs) => { - let mut var_names: IndexMap = IndexMap::new(); + let mut var_names: IndexMap = IndexMap::new(); for addr in addrs { read_heap_cell!(addr, @@ -690,18 +690,18 @@ impl MachineState { read_heap_cell!(atom, (HeapCellValueTag::Char, c) => { - var_names.insert(var, Var::from(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, Var::from(name.as_str())); + 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, Var::from(name.as_str())); + var_names.insert(var, VarPtr::from(name.as_str())); } _ => { unreachable!(); diff --git a/src/machine/mod.rs b/src/machine/mod.rs index dab4c54c..ddf64d34 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -68,7 +68,7 @@ pub struct Machine { pub(super) user_error: Stream, pub(super) load_contexts: Vec, pub(super) runtime: Runtime, - pub(super) foreign_function_table: ForeignFunctionTable, + pub(super) foreign_function_table: ForeignFunctionTable, } #[derive(Debug)] @@ -365,46 +365,46 @@ 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::ExecuteRead, + 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() { @@ -690,6 +690,8 @@ impl Machine { fn try_call(&mut self, name: Atom, arity: usize, idx: IndexPtr) -> CallResult { let compiled_tl_index = idx.p() as usize; + // println!("calling {}/{}", name.as_str(), arity); + match idx.tag() { IndexPtrTag::DynamicUndefined => { self.machine_st.fail = true; @@ -713,6 +715,8 @@ impl Machine { fn try_execute(&mut self, name: Atom, arity: usize, idx: IndexPtr) -> CallResult { let compiled_tl_index = idx.p() as usize; + // println!("executing {}/{}", name.as_str(), arity); + match idx.tag() { IndexPtrTag::DynamicUndefined => { self.machine_st.fail = true; diff --git a/src/machine/preprocessor.rs b/src/machine/preprocessor.rs index 02e0e29f..a0cab869 100644 --- a/src/machine/preprocessor.rs +++ b/src/machine/preprocessor.rs @@ -10,20 +10,8 @@ use crate::parser::ast::*; use indexmap::IndexSet; use std::cell::Cell; -use std::collections::VecDeque; use std::convert::TryFrom; -pub(crate) fn fold_by_str(terms: I, mut term: Term, sym: Atom) -> Term -where - I: DoubleEndedIterator, -{ - for prec in terms.rev() { - term = Term::Clause(Cell::default(), sym, vec![prec, term]); - } - - term -} - pub(crate) fn to_op_decl( prec: u16, spec: Atom, @@ -546,16 +534,15 @@ impl Preprocessor { } } - fn setup_fact(&mut self, term: Term) -> Result { + fn setup_fact(&mut self, term: Term) -> Result<(Fact, VarData), CompilationError> { match term { Term::Clause(..) | Term::Literal(_, Literal::Atom(..)) => { - let mut classifier = VariableClassifier::new( + let classifier = VariableClassifier::new( self.settings.default_call_policy(), ); let (head, var_data) = classifier.classify_fact(term)?; - - Ok(Fact { head, var_data }) + Ok((Fact { head }, var_data)) } _ => Err(CompilationError::InadmissibleFact), } @@ -566,28 +553,22 @@ impl Preprocessor { loader: &mut Loader<'a, LS>, head: Term, body: Term, - ) -> Result { - let mut classifier = VariableClassifier::new( + ) -> Result<(Rule, VarData), CompilationError> { + let classifier = VariableClassifier::new( self.settings.default_call_policy(), ); - let (head, mut query_terms, var_data) = - classifier.classify_rule(loader, head, body)?; - - 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 head { - Term::Clause(_, name, terms) => Ok(Rule { - head: (name, terms, qt), + Term::Clause(_, name, terms) => Ok((Rule { + head: (name, terms), clauses, - var_data, - }), - 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, - }), + }, var_data)), _ => Err(CompilationError::InvalidRuleHead), } } @@ -613,20 +594,29 @@ impl Preprocessor { term: Term, ) -> Result { match term { - Term::Clause(r, name, terms) => { + Term::Clause(r, name, mut terms) => { let is_rule = name == atom!(":-") && terms.len() == 2; if is_rule { - Ok(TopLevel::Rule(self.setup_rule(loader, terms[0], terms[1])?)) + 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, LS: LoadState<'a>>( &mut self, loader: &mut Loader<'a, LS>, @@ -640,4 +630,5 @@ impl Preprocessor { Ok(results) } + */ } diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index d26468da..7985f5fe 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1409,7 +1409,7 @@ impl Machine { let vars: Vec<_> = vars .union(&result.supp_vars) // difference + union does not cancel. - .map(|v| Term::Var(Cell::default(), Var::Generated(v.get_value()))) + .map(|v| Term::Var(Cell::default(), VarPtr::from(format!("_{}", v.get_value())))) .collect(); let helper_clause_loc = self.code.len(); @@ -1571,8 +1571,8 @@ impl Machine { #[inline(always)] pub(crate) fn is_reset_cont_marker(&self, p: usize) -> bool { match &self.code[p] { - &Instruction::CallResetContinuationMarker(_) | - &Instruction::ExecuteResetContinuationMarker(_) => true, + &Instruction::CallResetContinuationMarker | + &Instruction::ExecuteResetContinuationMarker => true, _ => false } } @@ -4911,9 +4911,7 @@ impl Machine { let p_functor = self.deref_register(2); - let p = to_local_code_ptr(&self.machine_st.heap, p_functor).unwrap(); - - let num_cells = *self.code[p].perm_vars_mut().unwrap(); + let num_cells = self.machine_st.stack.index_and_frame(e).prelude.num_cells; let mut addrs = vec![]; for idx in 1..num_cells + 1 { diff --git a/src/macros.rs b/src/macros.rs index 85e2e086..c1f1552f 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -540,23 +540,7 @@ macro_rules! functor_term { macro_rules! compare_number_instr { ($cmp: expr, $at_1: expr, $at_2: expr) => {{ $cmp.set_terms($at_1, $at_2); - call_clause!(ClauseType::Inlined(InlinedClauseType::CompareNumber($cmp)), 0) - }}; -} - -macro_rules! call_clause { - ($clause_type:expr, $pvs:expr) => {{ - let mut instr = $clause_type.to_instr(); - instr.perm_vars_mut().map(|pvs| *pvs = $pvs); - instr - }}; -} - -macro_rules! call_clause_by_default { - ($clause_type:expr, $pvs:expr) => {{ - let mut instr = $clause_type.to_instr().to_default(); - instr.perm_vars_mut().map(|pvs| *pvs = $pvs); - instr + ClauseType::Inlined(InlinedClauseType::CompareNumber($cmp)).to_instr() }}; } diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 73c91c6a..283a9dc0 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -4,11 +4,11 @@ use crate::machine::machine_indices::*; use crate::parser::char_reader::*; use crate::types::HeapCellValueTag; -use std::cell::Cell; +use std::cell::{Cell, Ref, RefCell, RefMut}; use std::fmt; -use std::hash::Hash; +use std::hash::{Hash, Hasher}; use std::io::{Error as IOError}; -use std::ops::Neg; +use std::ops::{Deref, Neg}; use std::rc::Rc; use std::vec::Vec; @@ -572,23 +572,89 @@ impl Literal { } } + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct VarPtr(Rc>); + +impl Hash for VarPtr { + #[inline(always)] + fn hash(&self, hasher: &mut H) { + self.borrow().hash(hasher) + } +} + +impl Deref for VarPtr { + type Target = RefCell; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +impl VarPtr { + #[inline(always)] + pub(crate) fn borrow(&self) -> Ref<'_, Var> { + self.0.borrow() + } + + #[inline(always)] + pub(crate) fn borrow_mut(&self) -> RefMut<'_, Var> { + self.0.borrow_mut() + } + + pub(crate) fn to_var_num(&self) -> Option { + match *self.borrow() { + Var::Generated(var_num) => Some(var_num), + _ => None, + } + } + + pub(crate) fn set(&self, var: Var) { + let mut var_ref = self.borrow_mut(); + *var_ref = var; + } +} + +impl From for VarPtr { + #[inline(always)] + fn from(value: Var) -> VarPtr { + VarPtr(Rc::new(RefCell::new(value))) + } +} + +impl From for VarPtr { + #[inline(always)] + fn from(value: String) -> VarPtr { + VarPtr::from(Var::from(value)) + } +} + +impl From<&str> for VarPtr { + #[inline(always)] + fn from(value: &str) -> VarPtr { + VarPtr::from(value.to_owned()) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Var { Generated(usize), - Named(Rc), + InSitu(usize), + Named(String), } impl From for Var { #[inline(always)] fn from(value: String) -> Var { - Var::Named(Rc::new(value)) + Var::Named(value) } } impl From<&str> for Var { #[inline(always)] fn from(value: &str) -> Var { - Var::Named(Rc::new(value.to_owned())) + Var::Named(value.to_owned()) } } @@ -596,16 +662,16 @@ impl Var { #[inline(always)] pub fn as_str(&self) -> Option<&str> { match self { - Var::Generated(_) => None, Var::Named(value) => Some(&value), + _ => None, } } #[inline(always)] pub fn to_string(&self) -> String { match self { - Var::Generated(n) => format!("_{}", n), - Var::Named(value) => value.to_string(), + Var::InSitu(n) | Var::Generated(n) => format!("_{}", n), + Var::Named(value) => value.to_owned(), } } } @@ -620,7 +686,7 @@ pub enum Term { // other PartialString variants in as_partial_string. PartialString(Cell, String, Box), CompleteString(Cell, Atom), - Var(Cell, Var), + Var(Cell, VarPtr), } impl Term { diff --git a/src/parser/parser.rs b/src/parser/parser.rs index ce633b94..021147ea 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -426,7 +426,7 @@ impl<'a, R: CharRead> Parser<'a, R> { if v.trim() == "_" { self.terms.push(Term::AnonVar); } else { - self.terms.push(Term::Var(Cell::default(), Var::from(v))); + self.terms.push(Term::Var(Cell::default(), VarPtr::from(v))); } TokenType::Term diff --git a/src/read.rs b/src/read.rs index c8743c2f..8f70eeec 100644 --- a/src/read.rs +++ b/src/read.rs @@ -317,7 +317,7 @@ impl<'a, 'b> TermWriter<'a, 'b> { fn write_term_to_heap(mut self, term: &'a Term) -> Result { let heap_loc = self.heap.len(); - for term in breadth_first_iter(term, true) { + for term in breadth_first_iter(term, RootIterationPolicy::Iterated) { let h = self.heap.len(); match &term { @@ -372,9 +372,9 @@ impl<'a, 'b> TermWriter<'a, 'b> { let addr = self.term_as_addr(&term, h); self.heap.push(addr); } - &TermRef::Var(Level::Root, _, ref var) => { + &TermRef::Var(Level::Root, _, ref var_ptr) => { let addr = self.term_as_addr(&term, h); - self.var_dict.insert(var.clone(), heap_loc_as_cell!(h)); + self.var_dict.insert(var_ptr.clone(), heap_loc_as_cell!(h)); self.heap.push(addr); } &TermRef::AnonVar(_) => { diff --git a/src/targets.rs b/src/targets.rs index cbb469f9..56a4c127 100644 --- a/src/targets.rs +++ b/src/targets.rs @@ -29,11 +29,13 @@ pub(crate) trait CompilationTarget<'a> { fn argument_to_variable(r: RegType, r: usize) -> Instruction; fn argument_to_value(r: RegType, val: usize) -> Instruction; + fn unsafe_argument_to_value(r: RegType, val: usize) -> Instruction; fn move_to_register(r: RegType, val: usize) -> Instruction; fn subterm_to_variable(r: RegType) -> Instruction; fn subterm_to_value(r: RegType) -> Instruction; + fn unsafe_subterm_to_value(r: RegType) -> Instruction; fn clause_arg_to_instr(r: RegType) -> Instruction; } @@ -42,7 +44,7 @@ impl<'a> CompilationTarget<'a> for FactInstruction { type Iterator = FactIterator<'a>; fn iter(term: &'a Term) -> Self::Iterator { - breadth_first_iter(term, false) // do not iterate over the root clause if one exists. + breadth_first_iter(term, RootIterationPolicy::NotIterated) } fn to_constant(lvl: Level, constant: Literal, reg: RegType) -> Instruction { @@ -95,6 +97,10 @@ impl<'a> CompilationTarget<'a> for FactInstruction { Instruction::GetValue(arg, val) } + fn unsafe_argument_to_value(arg: RegType, val: usize) -> Instruction { + Instruction::GetValue(arg, val) + } + fn subterm_to_variable(val: RegType) -> Instruction { Instruction::UnifyVariable(val) } @@ -103,6 +109,10 @@ impl<'a> CompilationTarget<'a> for FactInstruction { Instruction::UnifyValue(val) } + fn unsafe_subterm_to_value(val: RegType) -> Instruction { + Instruction::UnifyLocalValue(val) + } + fn clause_arg_to_instr(val: RegType) -> Instruction { Instruction::UnifyVariable(val) } @@ -165,6 +175,13 @@ impl<'a> CompilationTarget<'a> for QueryInstruction { Instruction::PutValue(arg, val) } + fn unsafe_argument_to_value(arg: RegType, val: usize) -> Instruction { + match arg { + RegType::Perm(p) => Instruction::PutUnsafeValue(p, val), + RegType::Temp(_) => Instruction::PutValue(arg, val), + } + } + fn subterm_to_variable(val: RegType) -> Instruction { Instruction::SetVariable(val) } @@ -173,6 +190,10 @@ impl<'a> CompilationTarget<'a> for QueryInstruction { Instruction::SetValue(val) } + fn unsafe_subterm_to_value(val: RegType) -> Instruction { + Instruction::SetLocalValue(val) + } + fn clause_arg_to_instr(val: RegType) -> Instruction { Instruction::SetValue(val) } From 9ea6cb4cab4409e0f5cee917d37c3ff9ac0b0fc1 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 22 Jun 2023 18:28:10 -0600 Subject: [PATCH 200/361] backtrack on emission of unsafe register instructions on internal branches --- src/codegen.rs | 20 +-------------- src/debray_allocator.rs | 54 ++++++++++++++++++++++++++++------------ src/machine/disjuncts.rs | 9 ------- 3 files changed, 39 insertions(+), 44 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 794ec65b..000c0e65 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -103,25 +103,15 @@ impl BranchCodeStack { for (inner_idx, code) in self.stack[idx].iter_mut().enumerate() { if inner_idx + 1 == inner_len { - jump_span -= code.len() + 1; // = jump_span.saturating_sub(code.len() + 1); + jump_span -= code.len() + 1; } else { jump_span -= code.len() + 1; code.push_back(instr!("jmp_by_call", jump_span as usize)); - // saturate at 0 if underflow happens, which only - // happens when jump_span is no longer needed - // anyway. still, we don't want to panic at - // underflow. jump_span -= 1; } } } - - // eliminate terminating jump instruction in last arm of last - // branch. - // self.stack.last_mut() - // .and_then(|branch| branch.last_mut()) - // .map(|code| code.pop_back()); } fn pop_branch(&mut self, depth: usize, settings: CodeGenSettings) -> CodeDeque { @@ -1242,14 +1232,6 @@ impl<'b> CodeGenerator<'b> { code.extend(code_segment.into_iter()); } - /* - for line in &code { - println!("{:?}", line); - } - - println!(""); - */ - Ok(code) } } diff --git a/src/debray_allocator.rs b/src/debray_allocator.rs index 2f8d442e..2cd853c7 100644 --- a/src/debray_allocator.rs +++ b/src/debray_allocator.rs @@ -75,7 +75,17 @@ impl DebrayAllocator { self.branch_stack.push(BranchOccurrences::new(num_branches)); } + pub(crate) fn current_branch_designator(&self) -> BranchDesignator { + let num_branches = self.branch_stack.len(); + let current_branch = self.branch_stack.last() + .map(|occurrences| occurrences.current_branch) + .unwrap_or(0); + + BranchDesignator((num_branches, current_branch)) + } + pub(crate) fn add_branch(&mut self) { + let branch_designator = self.current_branch_designator(); let branch_occurrences = self.branch_stack.last_mut().unwrap(); for var_num in branch_occurrences.subsumed_hits.drain(..) { @@ -83,11 +93,11 @@ impl DebrayAllocator { VarAlloc::Perm(_, ref mut allocation) => { match allocation { PermVarAllocation::Done { shallow_safety, deep_safety, .. } => { - if !shallow_safety.unneeded() { + if !shallow_safety.is_unneeded(branch_designator) { branch_occurrences.shallow_safety.insert(var_num); } - if !deep_safety.unneeded() { + if !deep_safety.is_unneeded(branch_designator) { branch_occurrences.deep_safety.insert(var_num); } } @@ -128,6 +138,8 @@ impl DebrayAllocator { (deep_safety, shallow_safety) }); + let branch_designator = self.current_branch_designator(); + let (deep_safety, shallow_safety) = match self.branch_stack.last_mut() { Some(latest_branch) => { latest_branch.deep_safety.union_with(&deep_safety); @@ -143,10 +155,12 @@ impl DebrayAllocator { VarAlloc::Perm(_, ref mut allocation) => { let shallow_safety = VarSafetyStatus::needed_if( shallow_safety.contains(var_num), + branch_designator, ); let deep_safety = VarSafetyStatus::needed_if( deep_safety.contains(var_num), + branch_designator, ); *allocation = PermVarAllocation::Done { shallow_safety, deep_safety }; @@ -415,10 +429,12 @@ impl DebrayAllocator { pub(crate) fn mark_temp_to_safe_perm(&mut self, var_num: usize) { match &self.var_data.records[var_num].allocation { &VarAlloc::Temp { to_perm_var_num: Some(perm_var_num), .. } => { + let branch_designator = self.current_branch_designator(); + match &mut self.var_data.records[perm_var_num].allocation { VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, shallow_safety, .. }) => { - *deep_safety = VarSafetyStatus::Unneeded; - *shallow_safety = VarSafetyStatus::Unneeded; + *deep_safety = VarSafetyStatus::unneeded(branch_designator); + *shallow_safety = VarSafetyStatus::unneeded(branch_designator); } _ => unreachable!() } @@ -429,14 +445,16 @@ impl DebrayAllocator { } fn mark_safe_var(&mut self, var_num: usize, lvl: Level, term_loc: GenContext) { + let branch_designator = self.current_branch_designator(); + match &mut self.var_data.records[var_num].allocation { VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, shallow_safety, .. }) => { // GetVariable in head chunk is considered safe. if lvl == Level::Deep { - *deep_safety = VarSafetyStatus::Unneeded; - *shallow_safety = VarSafetyStatus::Unneeded; + *deep_safety = VarSafetyStatus::unneeded(branch_designator); + *shallow_safety = VarSafetyStatus::unneeded(branch_designator); } else if term_loc == GenContext::Head { - *shallow_safety = VarSafetyStatus::Unneeded; + *shallow_safety = VarSafetyStatus::GloballyUnneeded; } else { if let Some(temp_var_num) = self.shallow_temp_mappings.get(&self.arg_c).cloned() { match &mut self.var_data.records[temp_var_num].allocation { @@ -449,7 +467,7 @@ impl DebrayAllocator { } } VarAlloc::Temp { ref mut safety, .. } => { - *safety = VarSafetyStatus::Unneeded; + *safety = VarSafetyStatus::GloballyUnneeded; } _ => { unreachable!() @@ -463,20 +481,22 @@ impl DebrayAllocator { r: RegType, arg_c: usize, ) -> Instruction { + let branch_designator = self.current_branch_designator(); + match &mut self.var_data.records[var_num].allocation { VarAlloc::Perm(_, PermVarAllocation::Done { ref mut shallow_safety, .. }) => { - if !self.in_tail_position || shallow_safety.unneeded() { + if !self.in_tail_position || shallow_safety.is_unneeded(branch_designator) { Target::argument_to_value(r, arg_c) } else { - *shallow_safety = VarSafetyStatus::Unneeded; + *shallow_safety = VarSafetyStatus::unneeded(branch_designator); Target::unsafe_argument_to_value(r, arg_c) } } VarAlloc::Temp { ref mut safety, .. } => { - if safety.unneeded() { + if safety.is_unneeded(branch_designator) { Target::argument_to_value(r, arg_c) } else { - *safety = VarSafetyStatus::Unneeded; + *safety = VarSafetyStatus::GloballyUnneeded; Target::unsafe_argument_to_value(r, arg_c) } } @@ -491,20 +511,22 @@ impl DebrayAllocator { var_num: usize, r: RegType, ) -> Instruction { + let branch_designator = self.current_branch_designator(); + match &mut self.var_data.records[var_num].allocation { VarAlloc::Perm(_, PermVarAllocation::Done { ref mut deep_safety, .. }) => { - if deep_safety.unneeded() { + if deep_safety.is_unneeded(branch_designator) { Target::subterm_to_value(r) } else { - *deep_safety = VarSafetyStatus::Unneeded; + *deep_safety = VarSafetyStatus::unneeded(branch_designator); Target::unsafe_subterm_to_value(r) } } VarAlloc::Temp { ref mut safety, .. } => { - if safety.unneeded() { + if safety.is_unneeded(branch_designator) { Target::subterm_to_value(r) } else { - *safety = VarSafetyStatus::Unneeded; + *safety = VarSafetyStatus::unneeded(branch_designator); Target::unsafe_subterm_to_value(r) } } diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index 1b65ef9e..a701dba3 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -839,12 +839,3 @@ impl BranchMap { var_data } } - -#[cfg(test)] -mod tests { - #[test] - fn disjunct_compilation() { - let mut wam = MachineState::new(); - let mut op_dir = default_op_dir(); - } -} From 33f65210eeb6bd573a694aa4ffa8c252140403be Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 22 Jun 2023 18:50:20 -0600 Subject: [PATCH 201/361] make tests compatible --- src/heap_print.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index aa93ad29..2a2149d2 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -1717,7 +1717,7 @@ mod tests { heap_loc_as_cell!(0) ); - printer.var_names.insert(list_loc_as_cell!(1), Var::from("L")); + printer.var_names.insert(list_loc_as_cell!(1), VarPtr::from("L")); let output = printer.print(); @@ -1778,7 +1778,7 @@ mod tests { heap_loc_as_cell!(0) ); - printer.var_names.insert(list_loc_as_cell!(1), Var::from("L")); + printer.var_names.insert(list_loc_as_cell!(1), VarPtr::from("L")); let output = printer.print(); From 47d4e6d2f99de18df627a6dcbd52fc7d851b845b Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 3 May 2023 23:23:53 +0200 Subject: [PATCH 202/361] FIXED: consistent read/write of further control characters, and non-breaking space Example: ?- X = '\xa0\'. X = '\xa0\'. This addresses #1768. --- src/heap_print.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 2a2149d2..df22113c 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -172,7 +172,9 @@ fn char_to_string(is_quoted: bool, c: char) -> String { '\'' | '\n' | '\r' | '\t' | '\u{0b}' | '\u{0c}' | '\u{08}' | '\u{07}' | '"' | '\\' => { c.to_string() } - '\u{0}'..='\u{1f}' => format!("\\x{:x}\\", c as u32), // print all other control characters in hex. + '\u{0}'..='\u{1f}' | '\u{7f}' ..= '\u{a0}' + // print all other control characters, and also non-breaking space, in hex. + => format!("\\x{:x}\\", c as u32), _ => c.to_string(), } } From 5e124ccf44a5b3ef0cdbbe62c12bc1d7e76cda56 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 3 May 2023 21:56:04 +0200 Subject: [PATCH 203/361] ENHANCED: allow Roman numerals in strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example: ?- X = "ↁ". X = "ↁ". This addresses #1790. --- src/parser/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/macros.rs b/src/parser/macros.rs index 27b106fc..3e6826c9 100644 --- a/src/parser/macros.rs +++ b/src/parser/macros.rs @@ -20,7 +20,7 @@ macro_rules! alpha_char { #[macro_export] macro_rules! alpha_numeric_char { ($c: expr) => { - $crate::alpha_char!($c) || $crate::decimal_digit_char!($c) + $crate::alpha_char!($c) || $c.is_numeric() }; } From 86beb222ae092baf7a0316eecb82810058fac65b Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 4 May 2023 00:50:27 +0200 Subject: [PATCH 204/361] rely on first instantiated argument indexing in the definitions of foldl/N This allows shorter and more natural definitions. --- src/lib/lists.pl | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/lib/lists.pl b/src/lib/lists.pl index 92bc202d..815e2b2a 100644 --- a/src/lib/lists.pl +++ b/src/lib/lists.pl @@ -295,25 +295,19 @@ same_length([_|As], [_|Bs]) :- % sum_list(Ls, S) :- foldl(sum_, Ls, 0, S). % ``` -foldl(Goal_3, Ls, A0, A) :- - foldl_(Ls, Goal_3, A0, A). - -foldl_([], _, A, A). -foldl_([L|Ls], G_3, A0, A) :- +foldl(_, [], A, A). +foldl(G_3, [L|Ls], A0, A) :- call(G_3, L, A0, A1), - foldl_(Ls, G_3, A1, A). + foldl(G_3, Ls, A1, A). %% foldl(+Predicate, ?Ls0, ?Ls1, +A0, ?A). % % Same as `foldl/4` but with an extra list -foldl(Goal_4, Xs, Ys, A0, A) :- - foldl_(Xs, Ys, Goal_4, A0, A). - -foldl_([], [], _, A, A). -foldl_([X|Xs], [Y|Ys], G_4, A0, A) :- +foldl(_, [], [], A, A). +foldl(G_4, [X|Xs], [Y|Ys], A0, A) :- call(G_4, X, Y, A0, A1), - foldl_(Xs, Ys, G_4, A1, A). + foldl(G_4, Xs, Ys, A1, A). %% transpose(?Ls, ?Ts). % From 8e4465315f8ec3e4e91b52b460ac4f1ac994817b Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 10 May 2023 00:04:35 -0600 Subject: [PATCH 205/361] use same logic to print Chars and Atoms (#1804) --- src/heap_print.rs | 123 ++++++++++++++++++++--------------- src/machine/machine_state.rs | 1 + src/machine/mock_wam.rs | 1 + 3 files changed, 73 insertions(+), 52 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index df22113c..c861a26c 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -470,6 +470,7 @@ pub fn fmt_float(mut fl: f64) -> String { pub struct HCPrinter<'a, Outputter> { outputter: Outputter, iter: StackfulPreOrderHeapIter<'a>, + atom_tbl: &'a mut AtomTable, op_dir: &'a OpDir, state_stack: Vec, toplevel_spec: Option, @@ -534,6 +535,7 @@ pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { pub fn new( heap: &'a mut Heap, + atom_tbl: &'a mut AtomTable, op_dir: &'a OpDir, output: Outputter, cell: HeapCellValue, @@ -541,6 +543,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { HCPrinter { outputter: output, iter: stackful_preorder_iter(heap, cell), + atom_tbl, op_dir, state_stack: vec![], toplevel_spec: None, @@ -890,7 +893,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } } - fn print_atom(&mut self, atom: Atom) { + fn print_impromptu_atom(&mut self, atom: Atom) { let result = self.print_op_addendum(atom.as_str()); push_space_if_amb!(self, result.as_str(), { @@ -1406,7 +1409,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { fn print_stream(&mut self, stream: Stream, max_depth: usize) { if let Some(alias) = stream.options().get_alias() { - self.print_atom(alias); + self.print_impromptu_atom(alias); } else { let stream_atom = atom!("$stream"); @@ -1442,53 +1445,62 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { None => return, }; - read_heap_cell!(addr, - (HeapCellValueTag::Atom, (name, arity)) => { - if name == atom!("[]") && arity == 0 { - if !self.at_cdr("") { - append_str!(self, "[]"); - } - } else if arity > 0 { - if let Some(spec) = fetch_op_spec(name, arity, self.op_dir) { - self.handle_op_as_struct( - name, - arity, - &op, - is_functor_redirect, - spec, - negated_operand, - max_depth, - ); - } else { - push_space_if_amb!(self, name.as_str(), { - self.format_clause(max_depth, arity, name, None); - }); - } - } else if fetch_op_spec(name, arity, self.op_dir).is_some() { - let mut result = String::new(); - - if let Some(ref op) = op { - if self.outputter.ends_with(&format!(" {}", op.as_atom().as_str())) { - result.push(' '); - } - - result.push('('); - } - - result += &self.print_op_addendum(name.as_str()); - - if op.is_some() { - result.push(')'); - } - - push_space_if_amb!(self, &result, { - append_str!(self, &result); - }); + let print_atom = |printer: &mut Self, name: Atom, arity: usize| { + if name == atom!("[]") && arity == 0 { + if !printer.at_cdr("") { + append_str!(printer, "[]"); + } + } else if arity > 0 { + if let Some(spec) = fetch_op_spec(name, arity, printer.op_dir) { + printer.handle_op_as_struct( + name, + arity, + &op, + is_functor_redirect, + spec, + negated_operand, + max_depth, + ); } else { - push_space_if_amb!(self, name.as_str(), { - self.print_atom(name); + push_space_if_amb!(printer, name.as_str(), { + printer.format_clause(max_depth, arity, name, None); }); } + } else if fetch_op_spec(name, arity, printer.op_dir).is_some() { + let mut result = String::new(); + + if let Some(ref op) = op { + if printer.outputter.ends_with(&format!(" {}", op.as_atom().as_str())) { + result.push(' '); + } + + result.push('('); + } + + result += &printer.print_op_addendum(name.as_str()); + + if op.is_some() { + result.push(')'); + } + + push_space_if_amb!(printer, &result, { + append_str!(printer, &result); + }); + } else { + push_space_if_amb!(printer, name.as_str(), { + printer.print_impromptu_atom(name); + }); + } + }; + + read_heap_cell!(addr, + (HeapCellValueTag::Atom, (name, arity)) => { + print_atom(self, name, arity); + } + (HeapCellValueTag::Char, c) => { + let name = self.atom_tbl.build_with(&String::from(c)); + print_atom(self, name, 0); + // print_char!(self, self.quoted, c); } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.iter.heap[s]) @@ -1536,9 +1548,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { }) } } - (HeapCellValueTag::Char, c) => { - print_char!(self, self.quoted, c); - } (HeapCellValueTag::Cons, c) => { match_untyped_arena_ptr!(c, (ArenaHeaderTag::Integer, n) => { @@ -1551,10 +1560,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.print_stream(stream, max_depth); } (ArenaHeaderTag::OssifiedOpDir, _op_dir) => { - self.print_atom(atom!("$ossified_op_dir")); + self.print_impromptu_atom(atom!("$ossified_op_dir")); } (ArenaHeaderTag::Dropped, _value) => { - self.print_atom(atom!("$dropped_value")); + self.print_impromptu_atom(atom!("$dropped_value")); } (ArenaHeaderTag::IndexPtr, index_ptr) => { self.print_index_ptr(*index_ptr, max_depth); @@ -1588,7 +1597,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { while let Some(loc_data) = self.state_stack.pop() { match loc_data { - TokenOrRedirect::Atom(atom) => self.print_atom(atom), + TokenOrRedirect::Atom(atom) => self.print_impromptu_atom(atom), TokenOrRedirect::BarAsOp => append_str!(self, " | "), TokenOrRedirect::Char(c) => print_char!(self, self.quoted, c), TokenOrRedirect::Op(atom, _) => self.print_op(atom.as_str()), @@ -1654,6 +1663,7 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1681,6 +1691,7 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1703,6 +1714,7 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1714,6 +1726,7 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1743,6 +1756,7 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), @@ -1760,6 +1774,7 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), @@ -1775,6 +1790,7 @@ mod tests { { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1803,6 +1819,7 @@ mod tests { { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1824,6 +1841,7 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), pstr_loc_as_cell!(0) @@ -1850,6 +1868,7 @@ mod tests { { let printer = HCPrinter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.atom_tbl, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 26d0309b..7d0f6c77 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -764,6 +764,7 @@ impl MachineState { let mut printer = HCPrinter::new( &mut self.heap, + &mut self.atom_tbl, op_dir, PrinterOutputter::new(), term_to_be_printed, diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index 761590fb..2ddde129 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -61,6 +61,7 @@ impl MockWAM { let mut printer = HCPrinter::new( &mut self.machine_st.heap, + &mut self.machine_st.atom_tbl, &self.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(term_write_result.heap_loc), From c2f26234718ed285f7f28d96cdb3782707e76aec Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 14 May 2023 09:14:10 +0200 Subject: [PATCH 206/361] extend logic to all control and whitespace characters This addresses #1802. --- src/heap_print.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index c861a26c..dfb2efde 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -169,13 +169,16 @@ fn char_to_string(is_quoted: bool, c: char) -> String { '\u{08}' if is_quoted => "\\b".to_string(), // UTF-8 backspace '\u{07}' if is_quoted => "\\a".to_string(), // UTF-8 alert '\\' if is_quoted => "\\\\".to_string(), - '\'' | '\n' | '\r' | '\t' | '\u{0b}' | '\u{0c}' | '\u{08}' | '\u{07}' | '"' | '\\' => { + ' ' | '\'' | '\n' | '\r' | '\t' | '\u{0b}' | '\u{0c}' | '\u{08}' | '\u{07}' | '"' | '\\' => { c.to_string() } - '\u{0}'..='\u{1f}' | '\u{7f}' ..= '\u{a0}' - // print all other control characters, and also non-breaking space, in hex. - => format!("\\x{:x}\\", c as u32), - _ => c.to_string(), + _ => + if c.is_whitespace() || c.is_control() { + // print all other control and whitespace characters in hex. + format!("\\x{:x}\\", c as u32) + } else { + c.to_string() + } } } From 43df2e2649ad6d11f4a8e0dca9774f9c338d9d9f Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 16 May 2023 22:41:20 +0200 Subject: [PATCH 207/361] shorten gensym/2 --- src/lib/gensym.pl | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/lib/gensym.pl b/src/lib/gensym.pl index 92cd4d1f..86e7ad5b 100644 --- a/src/lib/gensym.pl +++ b/src/lib/gensym.pl @@ -19,13 +19,12 @@ gensym(Base, Unique) :- must_be(var, Unique), atom_si(Base), gensym_key(Base, BaseKey), - ( bb_get(BaseKey, UniqueID0) -> - UniqueID is UniqueID0 + 1, - bb_put(BaseKey, UniqueID), - append_id(Base, UniqueID, Unique) - ; bb_put(BaseKey, 1), - append_id(Base, 1, Unique) - ). + ( bb_get(BaseKey, UniqueID0) -> true + ; UniqueID0 = 0 + ), + UniqueID is UniqueID0 + 1, + append_id(Base, UniqueID, Unique), + bb_put(BaseKey, UniqueID). reset_gensym(Base) :- atom_si(Base), From 97bd77874571954bda1c93f14ba41a42c50386e1 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 16 May 2023 22:42:10 +0200 Subject: [PATCH 208/361] FIXED: correctly reset counter in reset_gensym/2 (#1807) Many thanks to @infradig for detecting this issue and suggesting this correction! --- src/lib/gensym.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/gensym.pl b/src/lib/gensym.pl index 86e7ad5b..272e68bd 100644 --- a/src/lib/gensym.pl +++ b/src/lib/gensym.pl @@ -28,4 +28,5 @@ gensym(Base, Unique) :- reset_gensym(Base) :- atom_si(Base), - bb_put(Base, 0). + gensym_key(Base, BaseKey), + bb_put(BaseKey, 0). From 5850125d97b1b8e7a013f92091525c49908dc47e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Wed, 17 May 2023 18:19:19 +0200 Subject: [PATCH 209/361] Update select crate to 0.6.0 and remove warning --- Cargo.lock | 477 ++++++++++++++++------------------------------------- Cargo.toml | 2 +- 2 files changed, 142 insertions(+), 337 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b4fc8a9..76c1c10c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,15 +31,6 @@ dependencies = [ "wait-timeout", ] -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -215,15 +206,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - [[package]] name = "codespan-reporting" version = "0.11.1" @@ -301,7 +283,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2db40892a506901e4e8281f00e42687df82d1d3448cb0289ae9183a60cb42ec1" dependencies = [ "blake2 0.10.4", - "rand_core 0.6.4", + "rand_core", "sha2", ] @@ -356,10 +338,10 @@ dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2", + "quote", "scratch", - "syn 1.0.103", + "syn", ] [[package]] @@ -374,9 +356,9 @@ version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -385,9 +367,9 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcdbcee2d9941369faba772587a565f4f534e42cb8d17e5295871de730163b2b" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -542,18 +524,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futf" version = "0.1.5" @@ -618,9 +588,9 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -709,9 +679,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe69f1cbdb6e28af2bac214e943b99ce8a0a06b447d15d3e61161b0423139f3f" dependencies = [ "proc-macro-hack", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -780,16 +750,16 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.23.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce65ac8028cf5a287a7dbf6c4e0a6cf2dcf022ed5b167a81bae66ebf599a8b7" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" dependencies = [ "log", "mac", "markup5ever", - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -893,7 +863,7 @@ version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ - "autocfg 1.1.0", + "autocfg", "hashbrown", ] @@ -1043,7 +1013,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ - "autocfg 1.1.0", + "autocfg", "scopeguard", ] @@ -1064,21 +1034,30 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "markup5ever" -version = "0.8.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1af46a727284117e09780d05038b1ce6fc9c76cc6df183c3dae5a8955a25e21" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" dependencies = [ "log", - "phf 0.7.24", + "phf 0.10.1", "phf_codegen", - "serde", - "serde_derive", - "serde_json", "string_cache", "string_cache_codegen", "tendril", ] +[[package]] +name = "markup5ever_rcdom" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9521dd6750f8e80ee6c53d65e2e4656d7de37064f3a7a5d2d11d05df93839c2" +dependencies = [ + "html5ever", + "markup5ever", + "tendril", + "xml5ever", +] + [[package]] name = "match_cfg" version = "0.1.0" @@ -1097,7 +1076,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -1148,9 +1127,9 @@ name = "modular-bitfield-impl" version = "0.11.2" source = "git+https://github.com/mthom/modular-bitfield#213535c684af277563678179d8496f11b84a283f" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1205,7 +1184,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" dependencies = [ - "autocfg 1.1.0", + "autocfg", "bitflags", "cfg-if", "libc", @@ -1226,7 +1205,7 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-traits", ] @@ -1236,7 +1215,7 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -1282,9 +1261,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1299,7 +1278,7 @@ version = "0.9.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b" dependencies = [ - "autocfg 1.1.0", + "autocfg", "cc", "libc", "pkg-config", @@ -1363,15 +1342,6 @@ dependencies = [ "windows-sys 0.42.0", ] -[[package]] -name = "phf" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" -dependencies = [ - "phf_shared 0.7.24", -] - [[package]] name = "phf" version = "0.9.0" @@ -1384,23 +1354,22 @@ dependencies = [ ] [[package]] -name = "phf_codegen" -version = "0.7.24" +name = "phf" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ - "phf_generator 0.7.24", - "phf_shared 0.7.24", + "phf_shared 0.10.0", ] [[package]] -name = "phf_generator" -version = "0.7.24" +name = "phf_codegen" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" dependencies = [ - "phf_shared 0.7.24", - "rand 0.6.5", + "phf_generator 0.10.0", + "phf_shared 0.10.0", ] [[package]] @@ -1410,7 +1379,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082" dependencies = [ "phf_shared 0.9.0", - "rand 0.8.5", + "rand", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand", ] [[package]] @@ -1422,18 +1401,9 @@ dependencies = [ "phf_generator 0.9.1", "phf_shared 0.9.0", "proc-macro-hack", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", -] - -[[package]] -name = "phf_shared" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" -dependencies = [ - "siphasher 0.2.3", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1442,7 +1412,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9" dependencies = [ - "siphasher 0.3.10", + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", ] [[package]] @@ -1508,15 +1487,6 @@ version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid", -] - [[package]] name = "proc-macro2" version = "1.0.47" @@ -1526,22 +1496,13 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -dependencies = [ - "proc-macro2 0.4.30", -] - [[package]] name = "quote" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ - "proc-macro2 1.0.47", + "proc-macro2", ] [[package]] @@ -1560,25 +1521,6 @@ dependencies = [ "nibble_vec", ] -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift", - "winapi", -] - [[package]] name = "rand" version = "0.8.5" @@ -1586,18 +1528,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -1607,24 +1539,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.6.4" @@ -1634,77 +1551,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "redox_syscall" version = "0.2.16" @@ -1907,8 +1753,8 @@ dependencies = [ "ordered-float", "phf 0.9.0", "predicates-core", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2", + "quote", "ref_thread_local", "ring", "ripemd160", @@ -1924,7 +1770,7 @@ dependencies = [ "static_assertions", "strum", "strum_macros", - "syn 1.0.103", + "syn", "to-syn-value", "to-syn-value_derive", "tokio", @@ -1956,12 +1802,13 @@ dependencies = [ [[package]] name = "select" -version = "0.4.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac645958c62108d11f90f8d34e4dc2799c838fc995ed4c2075867a2a8d5be76b" +checksum = "6f9da09dc3f4dfdb6374cbffff7a2cffcec316874d4429899eefdc97b3b94dcd" dependencies = [ "bit-set", "html5ever", + "markup5ever_rcdom", ] [[package]] @@ -1970,28 +1817,6 @@ version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" -[[package]] -name = "serde_derive" -version = "1.0.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" -dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", -] - -[[package]] -name = "serde_json" -version = "1.0.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "serial_test" version = "0.5.1" @@ -2009,9 +1834,9 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2074,12 +1899,6 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -[[package]] -name = "siphasher" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" - [[package]] name = "siphasher" version = "0.3.10" @@ -2092,7 +1911,7 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -2143,38 +1962,30 @@ checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" [[package]] name = "string_cache" -version = "0.7.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c058a82f9fd69b1becf8c274f412281038877c553182f1d02eb027045a2d67" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ - "lazy_static", "new_debug_unreachable", - "phf_shared 0.7.24", + "once_cell", + "parking_lot 0.12.1", + "phf_shared 0.10.0", "precomputed-hash", "serde", - "string_cache_codegen", - "string_cache_shared", ] [[package]] name = "string_cache_codegen" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f45ed1b65bf9a4bf2f7b7dc59212d1926e9eaf00fa998988e420fd124467c6" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" dependencies = [ - "phf_generator 0.7.24", - "phf_shared 0.7.24", - "proc-macro2 1.0.47", - "quote 1.0.21", - "string_cache_shared", + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", ] -[[package]] -name = "string_cache_shared" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" - [[package]] name = "strum" version = "0.23.0" @@ -2188,10 +1999,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38" dependencies = [ "heck", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2", + "quote", "rustversion", - "syn 1.0.103", + "syn", ] [[package]] @@ -2206,25 +2017,14 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" -[[package]] -name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid", -] - [[package]] name = "syn" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2", + "quote", "unicode-ident", ] @@ -2289,9 +2089,9 @@ version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2311,7 +2111,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45dcb7b4108a4793bdd74aa3714296c6eaf43663edf73fa8625d0d7621e68447" dependencies = [ - "syn 1.0.103", + "syn", "to-syn-value_derive", ] @@ -2321,9 +2121,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd4fdec6de01b568c1d3721c9d46a352623c536cd55a8a5acfefb63d1fccccbc" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2332,7 +2132,7 @@ version = "1.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" dependencies = [ - "autocfg 1.1.0", + "autocfg", "bytes", "libc", "memchr", @@ -2352,9 +2152,9 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2437,12 +2237,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - [[package]] name = "untrusted" version = "0.7.1" @@ -2534,9 +2328,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", "wasm-bindgen-shared", ] @@ -2546,7 +2340,7 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ - "quote 1.0.21", + "quote", "wasm-bindgen-macro-support", ] @@ -2556,9 +2350,9 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", + "proc-macro2", + "quote", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2719,6 +2513,17 @@ dependencies = [ "tap", ] +[[package]] +name = "xml5ever" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4034e1d05af98b51ad7214527730626f019682d797ba38b51689212118d8e650" +dependencies = [ + "log", + "mac", + "markup5ever", +] + [[package]] name = "xmlparser" version = "0.13.5" diff --git a/Cargo.toml b/Cargo.toml index 1f4fa7aa..99218c71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ blake2 = "0.8.1" crrl = "0.2.0" native-tls = "0.2.4" chrono = "0.4.11" -select = "0.4.3" +select = "0.6.0" roxmltree = "0.11.0" base64 = "0.12.3" smallvec = "1.8.0" From dae34b60099cdd000d8dc16513d9d6311c9d2251 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 24 May 2023 13:43:52 -0600 Subject: [PATCH 210/361] affirm integers as rational/1 (#1810) --- src/machine/dispatch.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 1d310bcd..a6e7963a 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -2600,7 +2600,7 @@ impl Machine { read_heap_cell!(d, (HeapCellValueTag::Cons, ptr) => { match_untyped_arena_ptr!(ptr, - (ArenaHeaderTag::Rational, _r) => { + (ArenaHeaderTag::Rational | ArenaHeaderTag::Integer, _r) => { self.machine_st.p += 1; } _ => { @@ -2608,6 +2608,9 @@ impl Machine { } ); } + (HeapCellValueTag::Fixnum) => { + self.machine_st.p += 1; + } _ => { self.machine_st.backtrack(); } @@ -2619,7 +2622,7 @@ impl Machine { read_heap_cell!(d, (HeapCellValueTag::Cons, ptr) => { match_untyped_arena_ptr!(ptr, - (ArenaHeaderTag::Rational, _r) => { + (ArenaHeaderTag::Rational | ArenaHeaderTag::Integer, _r) => { self.machine_st.p = self.machine_st.cp; } _ => { @@ -2627,6 +2630,9 @@ impl Machine { } ); } + (HeapCellValueTag::Fixnum) => { + self.machine_st.p = self.machine_st.cp; + } _ => { self.machine_st.backtrack(); } From e0f49e8f43e30c20fe3c646a44f32407e097d1d0 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 26 May 2023 15:19:07 -0600 Subject: [PATCH 211/361] optionally read from machine stack in stackful pre-order iterator (#1812) --- Cargo.lock | 6 + src/heap_iter.rs | 425 +++++++++++++++++++++++++---------- src/heap_print.rs | 93 ++++---- src/machine/loader.rs | 2 +- src/machine/machine_state.rs | 1 + src/machine/mock_wam.rs | 1 + 6 files changed, 374 insertions(+), 154 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76c1c10c..364b4891 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -524,6 +524,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futf" version = "0.1.5" diff --git a/src/heap_iter.rs b/src/heap_iter.rs index d7f1f2e4..96aef41d 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -1,8 +1,9 @@ #[cfg(test)] pub(crate) use crate::machine::gc::{IteratorUMP, StacklessPreOrderHeapIter}; -use crate::machine::heap::*; use crate::atom_table::*; +use crate::machine::heap::*; +use crate::machine::stack::*; use crate::types::*; use modular_bitfield::prelude::*; @@ -18,28 +19,45 @@ enum IterStackLocTag { PendingMark, } +#[derive(BitfieldSpecifier, Clone, Copy, Debug, PartialEq, Eq)] +#[bits = 1] +pub enum HeapOrStackTag { + Heap, + Stack, +} + #[bitfield] #[repr(u64)] #[derive(Clone, Copy, Debug)] pub struct IterStackLoc { - value: B62, + pub value: B61, tag: IterStackLocTag, + heap_or_stack: HeapOrStackTag, } impl IterStackLoc { #[inline] - pub fn iterable_heap_loc(h: usize) -> Self { - IterStackLoc::new().with_tag(IterStackLocTag::Iterable).with_value(h as u64) + pub fn iterable_loc(h: usize, heap_or_stack: HeapOrStackTag) -> Self { + IterStackLoc::new() + .with_tag(IterStackLocTag::Iterable) + .with_heap_or_stack(heap_or_stack) + .with_value(h as u64) } #[inline] - pub fn mark_heap_loc(h: usize) -> Self { - IterStackLoc::new().with_tag(IterStackLocTag::Marked).with_value(h as u64) + fn mark_loc(h: usize, heap_or_stack: HeapOrStackTag) -> Self { + IterStackLoc::new() + .with_tag(IterStackLocTag::Marked) + .with_heap_or_stack(heap_or_stack) + .with_value(h as u64) } #[inline] - pub fn pending_mark_heap_loc(h: usize) -> Self { - IterStackLoc::new().with_tag(IterStackLocTag::PendingMark).with_value(h as u64) + fn pending_mark_loc(h: usize, heap_or_stack: HeapOrStackTag) -> Self { + IterStackLoc::new() + .with_tag(IterStackLocTag::PendingMark) + .with_heap_or_stack(heap_or_stack) + .with_value(h as u64) } #[inline] @@ -51,38 +69,35 @@ impl IterStackLoc { pub fn is_pending_mark(self) -> bool { self.tag() == IterStackLocTag::PendingMark } -} -#[inline] -fn forward_if_referent_marked(heap: &mut [HeapCellValue], h: usize) { - read_heap_cell!(heap[h], - (HeapCellValueTag::Str - | HeapCellValueTag::Lis - | HeapCellValueTag::AttrVar - | HeapCellValueTag::Var - | HeapCellValueTag::PStrLoc, vh) => { - if heap[vh].get_mark_bit() { - heap[h].set_forwarding_bit(true); + #[inline] + pub fn as_ref(self) -> Ref { + match self.heap_or_stack() { + HeapOrStackTag::Heap => { + Ref::heap_cell(self.value() as usize) + } + HeapOrStackTag::Stack => { + Ref::stack_cell(self.value() as usize) } } - _ => {} - ) + } } #[derive(Debug)] pub struct StackfulPreOrderHeapIter<'a> { pub heap: &'a mut Vec, + pub machine_stack: &'a mut Stack, stack: Vec, - h: usize, + h: IterStackLoc, } impl<'a> Drop for StackfulPreOrderHeapIter<'a> { fn drop(&mut self) { while let Some(h) = self.stack.pop() { - let h = h.value() as usize; + let cell = self.read_cell_mut(h); - self.heap[h].set_forwarding_bit(false); - self.heap[h].set_mark_bit(false); + cell.set_forwarding_bit(false); + cell.set_mark_bit(false); } self.heap.pop(); @@ -90,48 +105,93 @@ impl<'a> Drop for StackfulPreOrderHeapIter<'a> { } pub trait FocusedHeapIter: Iterator { - fn focus(&self) -> usize; + fn focus(&self) -> IterStackLoc; } impl<'a> FocusedHeapIter for StackfulPreOrderHeapIter<'a> { #[inline] - fn focus(&self) -> usize { + fn focus(&self) -> IterStackLoc { self.h } } impl<'a> StackfulPreOrderHeapIter<'a> { #[inline] - fn new(heap: &'a mut Vec, cell: HeapCellValue) -> Self { - let h = heap.len(); + fn new(heap: &'a mut Vec, stack: &'a mut Stack, cell: HeapCellValue) -> Self { + let h = IterStackLoc::iterable_loc(heap.len(), HeapOrStackTag::Heap); heap.push(cell); Self { heap, h, - stack: vec![IterStackLoc::iterable_heap_loc(h)], + machine_stack: stack, + stack: vec![h], } } #[inline] - pub fn push_stack(&mut self, h: usize) { - self.stack.push(IterStackLoc::iterable_heap_loc(h)); + fn forward_if_referent_marked(&mut self, loc: IterStackLoc) { + read_heap_cell!(self.read_cell(loc), + (HeapCellValueTag::Str | + HeapCellValueTag::Lis | + HeapCellValueTag::AttrVar | + HeapCellValueTag::Var | + HeapCellValueTag::PStrLoc, vh) => { + if self.heap[vh].get_mark_bit() { + self.read_cell_mut(loc).set_forwarding_bit(true); + } + } + (HeapCellValueTag::StackVar, vs) => { + if self.machine_stack[vs].get_mark_bit() { + self.read_cell_mut(loc).set_forwarding_bit(true); + } + } + _ => {} + ); } #[inline] - pub fn stack_last(&self) -> Option { + pub fn push_stack(&mut self, h: IterStackLoc) { + self.stack.push(h); + } + + #[inline] + pub fn read_cell_mut(&mut self, loc: IterStackLoc) -> &mut HeapCellValue { + match loc.heap_or_stack() { + HeapOrStackTag::Heap => { + &mut self.heap[loc.value() as usize] + } + HeapOrStackTag::Stack => { + &mut self.machine_stack[loc.value() as usize] + } + } + } + + #[inline] + pub fn read_cell(&self, loc: IterStackLoc) -> HeapCellValue { + match loc.heap_or_stack() { + HeapOrStackTag::Heap => { + self.heap[loc.value() as usize] + } + HeapOrStackTag::Stack => { + self.machine_stack[loc.value() as usize] + } + } + } + + #[inline] + pub fn stack_last(&self) -> Option { for h in self.stack.iter().rev() { let is_readable_marked = h.is_marked(); - let h = h.value() as usize; - let cell = self.heap[h]; + let cell = self.read_cell(*h); if cell.get_forwarding_bit() { - return Some(h); + return Some(*h); } else if cell.get_mark_bit() && !is_readable_marked { continue; } - return Some(h); + return Some(*h); } None @@ -141,10 +201,9 @@ impl<'a> StackfulPreOrderHeapIter<'a> { pub fn pop_stack(&mut self) -> Option { while let Some(h) = self.stack.pop() { let is_readable_marked = h.is_marked(); - let h = h.value() as usize; - self.h = h; - let cell = &mut self.heap[h]; + self.h = h; + let cell = self.read_cell_mut(h); if cell.get_forwarding_bit() { cell.set_forwarding_bit(false); @@ -159,30 +218,29 @@ impl<'a> StackfulPreOrderHeapIter<'a> { None } - fn push_if_unmarked(&mut self, h: usize) { - if !self.heap[h].get_mark_bit() { - self.heap[h].set_mark_bit(true); - self.stack.push(IterStackLoc::iterable_heap_loc(h)); + fn push_if_unmarked(&mut self, loc: IterStackLoc) { + let cell = self.read_cell_mut(loc); + + if !cell.get_mark_bit() { + cell.set_mark_bit(true); + self.stack.push(IterStackLoc::iterable_loc(loc.value() as usize, loc.heap_or_stack())); } } fn follow(&mut self) -> Option { while let Some(h) = self.stack.pop() { if h.is_pending_mark() { - let h = h.value() as usize; - self.push_if_unmarked(h); - self.stack.push(IterStackLoc::mark_heap_loc(h)); + self.stack.push(IterStackLoc::mark_loc(h.value() as usize, h.heap_or_stack())); - forward_if_referent_marked(&mut self.heap, h); + self.forward_if_referent_marked(h); continue; } - let is_readable_marked = h.is_marked(); - let h = h.value() as usize; - self.h = h; - let cell = &mut self.heap[h]; + + let is_readable_marked = h.is_marked(); + let cell = self.read_cell_mut(h); if cell.get_forwarding_bit() { let copy = *cell; @@ -195,50 +253,68 @@ impl<'a> StackfulPreOrderHeapIter<'a> { read_heap_cell!(*cell, (HeapCellValueTag::Str | HeapCellValueTag::PStrLoc, vh) => { - self.push_if_unmarked(vh); - self.stack.push(IterStackLoc::mark_heap_loc(vh)); + let loc = IterStackLoc::iterable_loc(vh, HeapOrStackTag::Heap); + + self.push_if_unmarked(loc); + self.stack.push(IterStackLoc::mark_loc(vh, HeapOrStackTag::Heap)); } (HeapCellValueTag::Lis, vh) => { - self.push_if_unmarked(vh); + let loc = IterStackLoc::iterable_loc(vh, HeapOrStackTag::Heap); - self.stack.push(IterStackLoc::pending_mark_heap_loc(vh + 1)); - self.stack.push(IterStackLoc::mark_heap_loc(vh)); + self.push_if_unmarked(loc); - forward_if_referent_marked(&mut self.heap, vh); + self.stack.push(IterStackLoc::pending_mark_loc(vh + 1, HeapOrStackTag::Heap)); + self.stack.push(IterStackLoc::mark_loc(vh, HeapOrStackTag::Heap)); - return Some(self.heap[h]); + self.forward_if_referent_marked(loc); + + return Some(self.read_cell(h)); } (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, vh) => { - self.push_if_unmarked(vh); - self.stack.push(IterStackLoc::mark_heap_loc(vh)); - forward_if_referent_marked(&mut self.heap, vh); + let loc = IterStackLoc::iterable_loc(vh, HeapOrStackTag::Heap); + + self.push_if_unmarked(loc); + self.stack.push(IterStackLoc::mark_loc(vh, HeapOrStackTag::Heap)); + self.forward_if_referent_marked(loc); + } + (HeapCellValueTag::StackVar, vs) => { + let loc = IterStackLoc::iterable_loc(vs, HeapOrStackTag::Stack); + + self.push_if_unmarked(loc); + self.stack.push(IterStackLoc::mark_loc(vs, HeapOrStackTag::Stack)); + self.forward_if_referent_marked(loc); } (HeapCellValueTag::PStrOffset, offset) => { - self.push_if_unmarked(offset); - self.stack.push(IterStackLoc::iterable_heap_loc(h+1)); + self.push_if_unmarked(IterStackLoc::iterable_loc(offset, HeapOrStackTag::Heap)); + self.stack.push(IterStackLoc::iterable_loc((h.value()+1) as usize, HeapOrStackTag::Heap)); - return Some(self.heap[h]); + return Some(self.read_cell(h)); } (HeapCellValueTag::PStr) => { - self.push_if_unmarked(h); + let tail_loc = IterStackLoc::iterable_loc((h.value()+1) as usize, HeapOrStackTag::Heap); - self.stack.push(IterStackLoc::iterable_heap_loc(h+1)); - forward_if_referent_marked(&mut self.heap, h+1); + self.push_if_unmarked(IterStackLoc::iterable_loc(h.value() as usize, HeapOrStackTag::Heap)); + self.stack.push(tail_loc); + self.forward_if_referent_marked(tail_loc); - return Some(self.heap[h]); + return Some(self.read_cell(h)); } (HeapCellValueTag::Atom, (_name, arity)) => { - for h in (h + 2 .. h + arity + 1).rev() { - self.stack.push(IterStackLoc::pending_mark_heap_loc(h)); + let l = h.value() as usize; + + for l in (l + 2 .. l + arity + 1).rev() { + self.stack.push(IterStackLoc::pending_mark_loc(l, HeapOrStackTag::Heap)); } if arity > 0 { - self.push_if_unmarked(h+1); - self.stack.push(IterStackLoc::mark_heap_loc(h+1)); - forward_if_referent_marked(&mut self.heap, h+1); + let first_arg_loc = IterStackLoc::iterable_loc(l+1, HeapOrStackTag::Heap); + + self.push_if_unmarked(first_arg_loc); + self.stack.push(IterStackLoc::mark_loc(l+1, HeapOrStackTag::Heap)); + self.forward_if_referent_marked(first_arg_loc); } - return Some(self.heap[h]); + return Some(self.read_cell(h)); } _ => { return Some(*cell); @@ -269,19 +345,20 @@ pub(crate) fn stackless_preorder_iter( } #[inline(always)] -pub(crate) fn stackful_preorder_iter( - heap: &mut Vec, +pub(crate) fn stackful_preorder_iter<'a>( + heap: &'a mut Vec, + stack: &'a mut Stack, cell: HeapCellValue, -) -> StackfulPreOrderHeapIter { - StackfulPreOrderHeapIter::new(heap, cell) +) -> StackfulPreOrderHeapIter<'a> { + StackfulPreOrderHeapIter::new(heap, stack, cell) } #[derive(Debug)] pub(crate) struct PostOrderIterator { - focus: usize, + focus: IterStackLoc, base_iter: Iter, base_iter_valid: bool, - parent_stack: Vec<(usize, HeapCellValue, usize)>, // number of children, parent node, focus. + parent_stack: Vec<(usize, HeapCellValue, IterStackLoc)>, // number of children, parent node, focus. } impl Deref for PostOrderIterator { @@ -295,7 +372,7 @@ impl Deref for PostOrderIterator { impl PostOrderIterator { pub(crate) fn new(base_iter: Iter) -> Self { PostOrderIterator { - focus: 0, + focus: IterStackLoc::iterable_loc(0, HeapOrStackTag::Heap), base_iter, base_iter_valid: true, parent_stack: vec![], @@ -352,7 +429,7 @@ impl Iterator for PostOrderIterator { impl FocusedHeapIter for PostOrderIterator { #[inline(always)] - fn focus(&self) -> usize { + fn focus(&self) -> IterStackLoc { self.focus } } @@ -368,7 +445,8 @@ impl PostOrderIterator { if let Some((_child_count, item, focus)) = self.parent_stack.last() { read_heap_cell!(item, (HeapCellValueTag::Atom, (_name, arity)) => { - return focus + arity >= idx_loc && *focus < idx_loc; + let focus = focus.value() as usize; + return focus + arity >= idx_loc && focus < idx_loc; } _ => {} ); @@ -401,9 +479,10 @@ impl<'a> LeftistPostOrderHeapIter<'a> { #[inline] pub(crate) fn stackful_post_order_iter<'a>( heap: &'a mut Heap, + stack: &'a mut Stack, cell: HeapCellValue, ) -> LeftistPostOrderHeapIter<'a> { - PostOrderIterator::new(StackfulPreOrderHeapIter::new(heap, cell)) + PostOrderIterator::new(StackfulPreOrderHeapIter::new(heap, stack, cell)) } #[cfg(test)] @@ -424,6 +503,7 @@ mod tests { use super::*; use crate::machine::mock_wam::*; + #[test] fn heap_stackless_iter_tests() { let mut wam = MockWAM::new(); @@ -1381,7 +1461,11 @@ mod tests { .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + str_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1412,7 +1496,11 @@ mod tests { )); for _ in 0..20 { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + str_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1440,7 +1528,12 @@ mod tests { { wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); + let mut var = heap_loc_as_cell!(0); // self-referencing variables are copied with their forwarding @@ -1462,7 +1555,11 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1482,7 +1579,11 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1514,7 +1615,11 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(0)); { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); // the cycle will be iterated twice before being detected. assert_eq!( @@ -1542,7 +1647,11 @@ mod tests { } { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); // cut the iteration short to check that all cells are // unmarked and unforwarded by the Drop instance of @@ -1576,7 +1685,11 @@ mod tests { let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!( @@ -1596,7 +1709,11 @@ mod tests { let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; { - let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_preorder_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); @@ -1615,7 +1732,12 @@ mod tests { wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0i64))); { - let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_preorder_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); + let pstr_offset_cell = pstr_offset_as_cell!(0); // pstr_offset_cell.set_forwarding_bit(true); @@ -1640,7 +1762,12 @@ mod tests { wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(1i64))); { - let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_preorder_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); + let pstr_offset_cell = pstr_offset_as_cell!(0); // pstr_offset_cell.set_forwarding_bit(true); @@ -1653,7 +1780,7 @@ mod tests { let h = iter.focus(); - assert_eq!(h, 5); + assert_eq!(h.value(), 5); assert_eq!(unmark_cell_bits!(iter.heap[4]), pstr_offset_as_cell!(0)); assert_eq!(unmark_cell_bits!(iter.heap[5]), fixnum_as_cell!(Fixnum::build_with(1i64))); @@ -1673,7 +1800,11 @@ mod tests { wam.machine_st.heap.extend(functor); { - let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = StackfulPreOrderHeapIter::new( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1732,7 +1863,11 @@ mod tests { wam.machine_st.heap[4] = list_loc_as_cell!(1); { - let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_preorder_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1799,6 +1934,7 @@ mod tests { { let mut iter = StackfulPreOrderHeapIter::new( &mut wam.machine_st.heap, + &mut wam.machine_st.stack, heap_loc_as_cell!(0), ); @@ -1830,6 +1966,7 @@ mod tests { { let mut iter = stackful_preorder_iter( &mut wam.machine_st.heap, + &mut wam.machine_st.stack, heap_loc_as_cell!(0), ); @@ -1864,6 +2001,7 @@ mod tests { { let mut iter = stackful_preorder_iter( &mut wam.machine_st.heap, + &mut wam.machine_st.stack, heap_loc_as_cell!(0), ); @@ -1898,7 +2036,11 @@ mod tests { .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)])); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + str_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1929,7 +2071,11 @@ mod tests { )); for _ in 0..20 { // 0000 { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + str_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -1959,7 +2105,12 @@ mod tests { { wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); + let mut var = heap_loc_as_cell!(0); // self-referencing variables are copied with their forwarding @@ -1981,7 +2132,11 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2001,7 +2156,11 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2033,7 +2192,11 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(0)); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); // the cycle will be iterated twice before being detected. assert_eq!( @@ -2063,6 +2226,7 @@ mod tests { { let mut iter = stackful_post_order_iter( &mut wam.machine_st.heap, + &mut wam.machine_st.stack, heap_loc_as_cell!(0), ); @@ -2098,7 +2262,11 @@ mod tests { let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2117,7 +2285,11 @@ mod tests { let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize]; { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2136,7 +2308,11 @@ mod tests { wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0i64))); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(0i64))); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0)); @@ -2151,7 +2327,11 @@ mod tests { wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(1i64))); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + pstr_loc_as_cell!(0), + ); assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(1i64))); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0)); @@ -2175,7 +2355,11 @@ mod tests { wam.machine_st.heap.extend(functor); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2235,7 +2419,11 @@ mod tests { wam.machine_st.heap[4] = list_loc_as_cell!(1); { - let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackful_post_order_iter( + &mut wam.machine_st.heap, + &mut wam.machine_st.stack, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2342,7 +2530,10 @@ mod tests { )); for _ in 0..20 { - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter( + &mut wam.machine_st.heap, + str_loc_as_cell!(0), + ); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(0)); @@ -2372,7 +2563,10 @@ mod tests { { wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter( + &mut wam.machine_st.heap, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), @@ -2388,7 +2582,10 @@ mod tests { wam.machine_st.heap.push(heap_loc_as_cell!(1)); wam.machine_st.heap.push(heap_loc_as_cell!(0)); - let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0)); + let mut iter = stackless_post_order_iter( + &mut wam.machine_st.heap, + heap_loc_as_cell!(0), + ); assert_eq!( unmark_cell_bits!(iter.next().unwrap()), diff --git a/src/heap_print.rs b/src/heap_print.rs index dfb2efde..26f3f5d2 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -14,6 +14,7 @@ use crate::machine::heap::*; use crate::machine::machine_indices::*; use crate::machine::machine_state::pstr_loc_and_offset; use crate::machine::partial_string::*; +use crate::machine::stack::*; use crate::machine::streams::*; use crate::types::*; @@ -474,6 +475,7 @@ pub struct HCPrinter<'a, Outputter> { outputter: Outputter, iter: StackfulPreOrderHeapIter<'a>, atom_tbl: &'a mut AtomTable, + stack: &'a Stack, op_dir: &'a OpDir, state_stack: Vec, toplevel_spec: Option, @@ -539,6 +541,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { pub fn new( heap: &'a mut Heap, atom_tbl: &'a mut AtomTable, + stack: &'a Stack, op_dir: &'a OpDir, output: Outputter, cell: HeapCellValue, @@ -547,6 +550,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { outputter: output, iter: stackful_preorder_iter(heap, cell), atom_tbl, + stack, op_dir, state_stack: vec![], toplevel_spec: None, @@ -1443,12 +1447,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { ) { let negated_operand = negated_op_needs_bracketing(&self.iter, self.op_dir, &op); - let addr = match self.check_for_seen() { - Some(addr) => addr, - None => return, - }; - - let print_atom = |printer: &mut Self, name: Atom, arity: usize| { + let print_struct = |printer: &mut Self, name: Atom, arity: usize| { if name == atom!("[]") && arity == 0 { if !printer.at_cdr("") { append_str!(printer, "[]"); @@ -1496,29 +1495,33 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } }; + let addr = match self.check_for_seen() { + Some(addr) => addr, + None => return, + }; + read_heap_cell!(addr, (HeapCellValueTag::Atom, (name, arity)) => { - print_atom(self, name, arity); + print_struct(self, name, arity); } (HeapCellValueTag::Char, c) => { let name = self.atom_tbl.build_with(&String::from(c)); - print_atom(self, name, 0); - // print_char!(self, self.quoted, c); + print_struct(self, name, 0); } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.iter.heap[s]) .get_name_and_arity(); if let Some(spec) = fetch_op_spec(name, arity, self.op_dir) { - self.handle_op_as_struct( - name, - arity, - &op, - is_functor_redirect, - spec, - negated_operand, - max_depth, - ); + self.handle_op_as_struct( + name, + arity, + &op, + is_functor_redirect, + spec, + negated_operand, + max_depth, + ); } else { push_space_if_amb!(self, name.as_str(), { self.format_clause(max_depth, arity, name, None); @@ -1553,27 +1556,27 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } (HeapCellValueTag::Cons, c) => { match_untyped_arena_ptr!(c, - (ArenaHeaderTag::Integer, n) => { - self.print_number(max_depth, NumberFocus::Unfocused(Number::Integer(n)), &op); - } - (ArenaHeaderTag::Rational, r) => { - self.print_number(max_depth, NumberFocus::Unfocused(Number::Rational(r)), &op); - } - (ArenaHeaderTag::Stream, stream) => { - self.print_stream(stream, max_depth); - } - (ArenaHeaderTag::OssifiedOpDir, _op_dir) => { - self.print_impromptu_atom(atom!("$ossified_op_dir")); - } - (ArenaHeaderTag::Dropped, _value) => { - self.print_impromptu_atom(atom!("$dropped_value")); - } - (ArenaHeaderTag::IndexPtr, index_ptr) => { - self.print_index_ptr(*index_ptr, max_depth); - } - _ => { - } - ); + (ArenaHeaderTag::Integer, n) => { + self.print_number(max_depth, NumberFocus::Unfocused(Number::Integer(n)), &op); + } + (ArenaHeaderTag::Rational, r) => { + self.print_number(max_depth, NumberFocus::Unfocused(Number::Rational(r)), &op); + } + (ArenaHeaderTag::Stream, stream) => { + self.print_stream(stream, max_depth); + } + (ArenaHeaderTag::OssifiedOpDir, _op_dir) => { + self.print_impromptu_atom(atom!("$ossified_op_dir")); + } + (ArenaHeaderTag::Dropped, _value) => { + self.print_impromptu_atom(atom!("$dropped_value")); + } + (ArenaHeaderTag::IndexPtr, index_ptr) => { + self.print_index_ptr(*index_ptr, max_depth); + } + _ => { + } + ); } _ => { unreachable!() @@ -1596,6 +1599,8 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { pub fn print(mut self) -> Outputter { let spec = self.toplevel_spec.take(); + + self.iter.iterate_over_machine_stack(self.stack); self.handle_heap_term(spec, false, self.max_depth); while let Some(loc_data) = self.state_stack.pop() { @@ -1667,6 +1672,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1695,6 +1701,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1718,6 +1725,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1730,6 +1738,7 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1760,6 +1769,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), @@ -1778,6 +1788,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), @@ -1794,6 +1805,7 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1823,6 +1835,7 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1845,6 +1858,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), pstr_loc_as_cell!(0) @@ -1872,6 +1886,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, + &wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 51815c7e..6c98024c 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -1404,7 +1404,7 @@ impl MachineState { let term_addr = self[r]; let mut term_stack = vec![]; - let mut iter = stackful_post_order_iter(&mut self.heap, term_addr); + 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); diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 7d0f6c77..0203f62f 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -765,6 +765,7 @@ 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, diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index 2ddde129..f71f32e3 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -62,6 +62,7 @@ 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), From 73ca37eccad4393d7f1b088295cfd693fe6adad1 Mon Sep 17 00:00:00 2001 From: notoria Date: Sat, 27 May 2023 13:08:25 +0200 Subject: [PATCH 212/361] Remove and move comments --- src/lib/clpz.pl | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index b6ad08b3..43b8f26e 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -4990,7 +4990,6 @@ run_propagator(ptzdiv(X,Y,Z), MState) --> run_propagator(pmod(X,Y,Z), MState) --> ( Y == 0 -> { false } ; Y == Z -> { false } - % ; nonvar(Y), Z == X -> true ; X == Y -> kill(MState), queue_goal(Z = 0) ; true ), @@ -5008,7 +5007,7 @@ run_propagator(pmod(X,Y,Z), MState) --> ), { fd_get(X, XD0, XPs), domain_remove_smaller_than(XD0, XMin, XD2) }, - fd_put(X, XD2, XPs) + fd_put(X, XD2, XPs) % queue_goal(X #>= XMin) ; true ), @@ -5016,7 +5015,7 @@ run_propagator(pmod(X,Y,Z), MState) --> XMax is Z + Y * ((XU - Z) div Y), { fd_get(X, XD1, XPs), domain_remove_greater_than(XD1, XMax, XD3) }, - fd_put(X, XD3, XPs) + fd_put(X, XD3, XPs) % queue_goal(X #=< XMax) ; true ) @@ -5041,13 +5040,13 @@ run_propagator(pmod(X,Y,Z), MState) --> { fd_get(Y, YD, YPs), YMin is Z + 1, domain_remove_smaller_than(YD, YMin, YD1) }, - fd_put(Y, YD1, YPs) + fd_put(Y, YD1, YPs) % queue_goal(Y #> Z) ; Z < 0 -> { fd_get(Y, YD, YPs), YMax is Z - 1, domain_remove_greater_than(YD, YMax, YD1) }, - fd_put(Y, YD1, YPs) + fd_put(Y, YD1, YPs) % queue_goal(Y #< Z) ; true ) @@ -5067,7 +5066,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> queue_goal(Z = X) ; { fd_get(Z, ZD0, ZPs), domain_remove_greater_than(ZD0, X, ZD2) }, - fd_put(Z, ZD2, ZPs) + fd_put(Z, ZD2, ZPs) % queue_goal(Z #=< X) ) ; X < 0 -> @@ -5076,7 +5075,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> queue_goal(Z = X) ; { fd_get(Z, ZD0, ZPs), domain_remove_smaller_than(ZD0, X, ZD2) }, - fd_put(Z, ZD2, ZPs) + fd_put(Z, ZD2, ZPs) % queue_goal(Z #>= X) ) ), @@ -5085,14 +5084,14 @@ run_propagator(pmodz(X,Y,Z), MState) --> { fd_get(Z, ZD1, ZPs), domain_remove_smaller_than(ZD1, 0, ZD3), domain_remove_greater_than(ZD3, ZMax, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in 0..ZMax) ; { fd_get(Y, _, n(YL), n(YU), _), YU < 0 } -> ZMin is YL + 1, { fd_get(Z, ZD1, ZPs), domain_remove_greater_than(ZD1, 0, ZD3), domain_remove_smaller_than(ZD3, ZMin, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in ZMin..0) ; true ) @@ -5107,7 +5106,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> { fd_get(Z, ZD1, ZPs), domain_remove_greater_than(ZD1, 0, ZD3), domain_remove_smaller_than(ZD3, ZMin, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in ZMin..0) ) ; Y > 0 -> @@ -5118,7 +5117,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> { fd_get(Z, ZD1, ZPs), domain_remove_smaller_than(ZD1, 0, ZD3), domain_remove_greater_than(ZD3, ZMax, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in 0..ZMax) ) ) @@ -5133,12 +5132,12 @@ run_propagator(pmodz(X,Y,Z), MState) --> ; ( { fd_get(X, _, n(XL), n(XU), _), XL >= 0 } -> { fd_get(Z, ZD0, ZPs), domain_remove_greater_than(ZD0, XU, ZD2) }, - fd_put(Z, ZD2, ZPs) + fd_put(Z, ZD2, ZPs) % queue_goal(Z #=< XU) ; { fd_get(X, _, n(XL), n(XU), _), XU =< 0 } -> { fd_get(Z, ZD0, ZPs), domain_remove_smaller_than(ZD0, XL, ZD2) }, - fd_put(Z, ZD2, ZPs) + fd_put(Z, ZD2, ZPs) % queue_goal(Z #>= XL) ; true ), @@ -5147,14 +5146,14 @@ run_propagator(pmodz(X,Y,Z), MState) --> { fd_get(Z, ZD1, ZPs), domain_remove_smaller_than(ZD1, 0, ZD3), domain_remove_greater_than(ZD3, ZMax, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in 0..ZMax) ; { fd_get(Y, _, n(YL), n(YU), _), YU < 0 } -> ZMin is YL + 1, { fd_get(Z, ZD1, ZPs), domain_remove_greater_than(ZD1, 0, ZD3), domain_remove_smaller_than(ZD3, ZMin, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in ZMin..0) ; { fd_get(Y, _, n(YL), n(YU), _) } -> ZMin is YL + 1, @@ -5162,19 +5161,19 @@ run_propagator(pmodz(X,Y,Z), MState) --> { fd_get(Z, ZD1, ZPs), domain_remove_greater_than(ZD1, ZMax, ZD3), domain_remove_smaller_than(ZD3, ZMin, ZD5) }, - fd_put(Z, ZD5, ZPs) + fd_put(Z, ZD5, ZPs) % queue_goal(Z in ZMin..ZMax) ; { fd_get(Y, _, _, n(YU), _), YU > 0 } -> { fd_get(Z, ZD1, ZPs), ZMax is YU - 1, domain_remove_greater_than(ZD1, ZMax, ZD3) }, - fd_put(Z, ZD3, ZPs) + fd_put(Z, ZD3, ZPs) % queue_goal(Z #< YU) ; { fd_get(Y, _, n(YL), _, _), YL < 0 } -> { fd_get(Z, ZD1, ZPs), ZMin is YL + 1, domain_remove_smaller_than(ZD1, ZMin, ZD3) }, - fd_put(Z, ZD3, ZPs) + fd_put(Z, ZD3, ZPs) % queue_goal(Z #> YL) ; true ) @@ -5185,29 +5184,31 @@ run_propagator(pmody(X,Y,Z), MState) --> ( nonvar(Y) -> true % Nothing to do. % ; nonvar(X) -> true ; nonvar(Z) -> - ( Z > 0 -> % queue_goal(Y #> Z) + ( Z > 0 -> { fd_get(Y, YD, YPs), YMin is Z + 1, domain_remove_smaller_than(YD, YMin, YD1) }, - fd_put(Y, YD1, YPs) - ; Z < 0 -> % queue_goal(Y #< Z) + fd_put(Y, YD1, YPs) + % queue_goal(Y #> Z) + ; Z < 0 -> { fd_get(Y, YD, YPs), YMax is Z - 1, domain_remove_greater_than(YD, YMax, YD1) }, - fd_put(Y, YD1, YPs) + fd_put(Y, YD1, YPs) + % queue_goal(Y #< Z) ; Z =:= 0 -> kill(MState), queue_goal(X / Y #= _) ) ; ( { fd_get(Z, _, n(ZL), _, _), ZL > 0 } -> { fd_get(Y, YD, YPs), YMin is ZL + 1, domain_remove_smaller_than(YD, YMin, YD1) }, - fd_put(Y, YD1, YPs) + fd_put(Y, YD1, YPs) % queue_goal(Y #> ZL) ; { fd_get(Z, _, _, n(ZU), _), ZU < 0 } -> { fd_get(Y, YD, YPs), YMax is ZU - 1, domain_remove_greater_than(YD, YMax, YD1) }, - fd_put(Y, YD1, YPs) + fd_put(Y, YD1, YPs) % queue_goal(Y #< ZU) ; true ) From 770a682d8bb6b660f31f1b1c8b6426b609b13489 Mon Sep 17 00:00:00 2001 From: notoria Date: Sat, 27 May 2023 13:19:19 +0200 Subject: [PATCH 213/361] Don't add variable ?- Z #= 0, Z #= X mod Y. Z = 0, clpz:(_A*Y#=X), clpz:(Y in inf.. -1\/1..sup) % Unexpected. The expected result: Z = 0, clpz:(X mod Y#=0), clpz:(Y in inf.. -1\/1..sup). --- src/lib/clpz.pl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 43b8f26e..4cdcdc9a 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -5019,8 +5019,6 @@ run_propagator(pmod(X,Y,Z), MState) --> % queue_goal(X #=< XMax) ; true ) - % kill(MState), - % queue_goal(X #= Z + Y * _) % Add a variable to be efficient. ; nonvar(Z), nonvar(X) -> ( Z > 0 -> ( X < 0 -> true @@ -5180,7 +5178,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> ) ). -run_propagator(pmody(X,Y,Z), MState) --> +run_propagator(pmody(_X,Y,Z), _MState) --> ( nonvar(Y) -> true % Nothing to do. % ; nonvar(X) -> true ; nonvar(Z) -> @@ -5196,7 +5194,7 @@ run_propagator(pmody(X,Y,Z), MState) --> domain_remove_greater_than(YD, YMax, YD1) }, fd_put(Y, YD1, YPs) % queue_goal(Y #< Z) - ; Z =:= 0 -> kill(MState), queue_goal(X / Y #= _) + ; Z =:= 0 % Multiple solutions so do nothing special. ) ; ( { fd_get(Z, _, n(ZL), _, _), ZL > 0 } -> { fd_get(Y, YD, YPs), From 911c49c43f5e1005baf5fb4fef5829629addc923 Mon Sep 17 00:00:00 2001 From: notoria Date: Sat, 27 May 2023 13:47:14 +0200 Subject: [PATCH 214/361] Compute correctly the domain of the remainder --- src/lib/clpz.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 4cdcdc9a..8f9c6bf8 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -5153,7 +5153,7 @@ run_propagator(pmodz(X,Y,Z), MState) --> domain_remove_smaller_than(ZD3, ZMin, ZD5) }, fd_put(Z, ZD5, ZPs) % queue_goal(Z in ZMin..0) - ; { fd_get(Y, _, n(YL), n(YU), _) } -> + ; { fd_get(Y, _, n(YL), n(YU), _), YL < 0, YU > 0 } -> ZMin is YL + 1, ZMax is YU - 1, { fd_get(Z, ZD1, ZPs), From 749dedf47773be2b36fadf990aa4ebced1564064 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 26 May 2023 15:19:07 -0600 Subject: [PATCH 215/361] read from machine stack in stackful pre-order iterator (#1812) --- src/heap_print.rs | 80 ++++++++++++++--------------- src/machine/arithmetic_ops.rs | 2 +- src/machine/attributed_variables.rs | 6 +-- src/machine/gc.rs | 6 +-- src/machine/machine_state.rs | 6 +-- src/machine/machine_state_impl.rs | 4 +- src/machine/system_calls.rs | 4 +- src/machine/unify.rs | 8 +-- 8 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 26f3f5d2..d4b74378 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -116,7 +116,9 @@ impl<'a> StackfulPreOrderHeapIter<'a> { let mut parent_spec = DirectedOp::Left(atom!("-"), OpDesc::build_with(200, FY as u8)); loop { - read_heap_cell!(self.heap[h], + let cell = self.read_cell(h); + + read_heap_cell!(cell, (HeapCellValueTag::Str, s) => { read_heap_cell!(self.heap[s], (HeapCellValueTag::Atom, (name, _arity)) => { @@ -125,7 +127,7 @@ impl<'a> StackfulPreOrderHeapIter<'a> { if needs_bracketing(spec, &parent_spec) { return false; } else { - h = s + 1; + h = IterStackLoc::iterable_loc(s + 1, HeapOrStackTag::Heap); parent_spec = DirectedOp::Right(name, spec); continue; } @@ -140,7 +142,7 @@ impl<'a> StackfulPreOrderHeapIter<'a> { ) } _ => { - return property_check(self.heap[h]); + return property_check(cell); } ) } @@ -150,12 +152,12 @@ impl<'a> StackfulPreOrderHeapIter<'a> { where P: Fn(HeapCellValue) -> bool, { - let addr = match self.stack_last() { - Some(h) => self.heap[h], + let cell = match self.stack_last() { + Some(h) => self.read_cell(h), None => return false, }; - property_check(addr) + property_check(cell) } } @@ -475,7 +477,6 @@ pub struct HCPrinter<'a, Outputter> { outputter: Outputter, iter: StackfulPreOrderHeapIter<'a>, atom_tbl: &'a mut AtomTable, - stack: &'a Stack, op_dir: &'a OpDir, state_stack: Vec, toplevel_spec: Option, @@ -541,16 +542,15 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { pub fn new( heap: &'a mut Heap, atom_tbl: &'a mut AtomTable, - stack: &'a Stack, + stack: &'a mut Stack, op_dir: &'a OpDir, output: Outputter, cell: HeapCellValue, ) -> Self { HCPrinter { outputter: output, - iter: stackful_preorder_iter(heap, cell), + iter: stackful_preorder_iter(heap, stack, cell), atom_tbl, - stack, op_dir, state_stack: vec![], toplevel_spec: None, @@ -758,14 +758,14 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { fn format_numbered_vars(&mut self) -> bool { let h = self.iter.stack_last().unwrap(); - let addr = self.iter.heap[h]; - let addr = heap_bound_store( + let cell = self.iter.read_cell(h); + let cell = heap_bound_store( &self.iter.heap, - heap_bound_deref(&self.iter.heap, addr), + heap_bound_deref(&self.iter.heap, cell), ); // 7.10.4 - if let Some(var) = numbervar(&self.numbervars_offset, addr) { + if let Some(var) = numbervar(&self.numbervars_offset, cell) { self.iter.pop_stack(); self.state_stack.push(TokenOrRedirect::NumberedVar(var)); return true; @@ -809,11 +809,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { }; } - fn offset_as_string(&mut self, h: usize) -> Option { - let addr = self.iter.heap[h]; + fn offset_as_string(&mut self, h: IterStackLoc) -> Option { + let cell = self.iter.read_cell(h); - if let Some(var) = self.var_names.get(&addr) { - read_heap_cell!(addr, + if let Some(var) = self.var_names.get(&cell) { + read_heap_cell!(cell, (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => { return Some(var.borrow().to_string()); } @@ -824,7 +824,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { ); } - read_heap_cell!(addr, + read_heap_cell!(cell, (HeapCellValueTag::Lis | HeapCellValueTag::Str, h) => { Some(format!("{}", h)) } @@ -1169,7 +1169,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { fn print_list_like(&mut self, mut max_depth: usize) { let focus = self.iter.focus(); - let mut heap_pstr_iter = HeapPStrIter::new(self.iter.heap, focus); + let mut heap_pstr_iter = HeapPStrIter::new(self.iter.heap, focus.value() as usize); if heap_pstr_iter.next().is_some() { while let Some(_) = heap_pstr_iter.next() {} @@ -1181,7 +1181,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let end_cell = heap_pstr_iter.focus; if self.check_max_depth(&mut max_depth) { - self.remove_list_children(focus); + self.remove_list_children(focus.value() as usize); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); return; } @@ -1189,26 +1189,26 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let at_cdr = self.outputter.ends_with("|"); if !at_cdr && !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) { - self.remove_list_children(focus); - return self.print_proper_string(focus, max_depth); + self.remove_list_children(focus.value() as usize); + return self.print_proper_string(focus.value() as usize, max_depth); } if self.ignore_ops { self.at_cdr(","); - self.remove_list_children(focus); + self.remove_list_children(focus.value() as usize); - if !self.print_string_as_functor(focus, max_depth) { + if !self.print_string_as_functor(focus.value() as usize, max_depth) { if end_cell == empty_list_as_cell!() { append_str!(self, "[]"); } else { self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); - self.iter.push_stack(end_h); + self.iter.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap)); } } } else { let value = heap_bound_store( self.iter.heap, - heap_bound_deref(self.iter.heap, self.iter.heap[focus]), + heap_bound_deref(self.iter.heap, self.iter.read_cell(focus)), ); read_heap_cell!(value, @@ -1219,7 +1219,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let switch = Rc::new(Cell::new((!at_cdr, 0))); self.state_stack.push(TokenOrRedirect::CloseList(switch.clone())); - let (h, offset) = pstr_loc_and_offset(self.iter.heap, focus); + let (h, offset) = pstr_loc_and_offset(self.iter.heap, focus.value() as usize); let pstr = cell_as_string!(self.iter.heap[h]); let pstr = pstr.as_str_from(offset.get_num() as usize); @@ -1241,7 +1241,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.state_stack.push(TokenOrRedirect::HeadTailSeparator); } else if end_cell != empty_list_as_cell!() { if tag == HeapCellValueTag::PStrOffset { - self.iter.push_stack(end_h); + self.iter.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap)); } self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); @@ -1599,8 +1599,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { pub fn print(mut self) -> Outputter { let spec = self.toplevel_spec.take(); - - self.iter.iterate_over_machine_stack(self.stack); self.handle_heap_term(spec, false, self.max_depth); while let Some(loc_data) = self.state_stack.pop() { @@ -1672,7 +1670,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1701,7 +1699,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1725,7 +1723,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1738,7 +1736,7 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1769,7 +1767,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), @@ -1788,7 +1786,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), @@ -1805,7 +1803,7 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1835,7 +1833,7 @@ mod tests { let mut printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0) @@ -1858,7 +1856,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), pstr_loc_as_cell!(0) @@ -1886,7 +1884,7 @@ mod tests { let printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, - &wam.machine_st.stack, + &mut wam.machine_st.stack, &wam.op_dir, PrinterOutputter::new(), heap_loc_as_cell!(0), diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index 774b848f..f5aa982f 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -1106,7 +1106,7 @@ impl MachineState { pub(crate) fn arith_eval_by_metacall(&mut self, value: HeapCellValue) -> Result { 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() { diff --git a/src/machine/attributed_variables.rs b/src/machine/attributed_variables.rs index 57ea1c22..633378a0 100644 --- a/src/machine/attributed_variables.rs +++ b/src/machine/attributed_variables.rs @@ -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,7 +147,7 @@ impl MachineState { let value = unmark_cell_bits!(value); - if h != iter.focus() { + 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) { @@ -167,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; } diff --git a/src/machine/gc.rs b/src/machine/gc.rs index 8a884950..1de28ffe 100644 --- a/src/machine/gc.rs +++ b/src/machine/gc.rs @@ -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) } } diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 0203f62f..de034374 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -557,10 +557,10 @@ impl MachineState { let mut singleton_var_set: IndexMap = IndexMap::new(); - for addr in stackful_preorder_iter(&mut self.heap, term) { - let addr = unmark_cell_bits!(addr); + for cell in stackful_preorder_iter(&mut self.heap, &mut self.stack, term) { + let cell = unmark_cell_bits!(cell); - if let Some(var) = addr.as_var() { + if let Some(var) = cell.as_var() { if !singleton_var_set.contains_key(&var) { singleton_var_set.insert(var, true); } else { diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index b457ecae..b4dc3fea 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -1125,7 +1125,7 @@ impl MachineState { return false; } - let mut iter = stackful_preorder_iter(&mut self.heap, value); + let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, value); while let Some(value) = iter.next() { if value.get_forwarding_bit() { @@ -1626,7 +1626,7 @@ impl MachineState { return true; } - let mut iter = stackful_preorder_iter(&mut self.heap, value); + let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, value); while let Some(value) = iter.next() { let value = unmark_cell_bits!(value); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 7985f5fe..a871750a 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -531,7 +531,7 @@ impl MachineState { seen_set: &mut IndexSet, value: HeapCellValue, ) { - let mut iter = stackful_preorder_iter(&mut self.heap, value); + let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, value); while let Some(value) = iter.next() { let value = unmark_cell_bits!(value); @@ -721,7 +721,7 @@ impl MachineState { let mut seen_set = IndexSet::new(); { - let mut iter = stackful_post_order_iter(&mut self.heap, term); + let mut iter = stackful_post_order_iter(&mut self.heap, &mut self.stack, term); while let Some(value) = iter.next() { if iter.parent_stack_len() >= max_depth { diff --git a/src/machine/unify.rs b/src/machine/unify.rs index 19445fe4..d6401b92 100644 --- a/src/machine/unify.rs +++ b/src/machine/unify.rs @@ -651,10 +651,12 @@ fn bind_with_occurs_check(unifier: &mut U, r: Ref, value: HeapCellVa let mut occurs_triggered = false; if !value.is_constant() { - for addr in stackful_preorder_iter(&mut unifier.heap, value) { - let addr = unmark_cell_bits!(addr); + let machine_st: &mut MachineState = unifier.deref_mut(); - if let Some(inner_r) = addr.as_var() { + 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; From c5c83d724a920bebd54397d3999f303c31acf982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Mon, 29 May 2023 00:29:53 +0200 Subject: [PATCH 216/361] Rename INDEX.md to INDEX.dj and add banner about Scryer Prolog Meetup --- INDEX.md => INDEX.dj | 7 +++++++ 1 file changed, 7 insertions(+) rename INDEX.md => INDEX.dj (87%) diff --git a/INDEX.md b/INDEX.dj similarity index 87% rename from INDEX.md rename to INDEX.dj index 907d1e9c..c7cf6a11 100644 --- a/INDEX.md +++ b/INDEX.dj @@ -5,6 +5,13 @@ X = "Scryer Prolog!". ``` +``` =html +
+

Scryer Prolog Meetup 2023

+

The first annual Scryer Prolog meetup is going to happen in Düsseldorf (Germany) on the 9th and 10th of November 2023. Join us to discover the present and future of Scryer Prolog! Participation is free, registration not required. More details here.

+
+``` + ![scryer](scryer.png){width=128 style=float:right;} [Scryer Prolog](https://github.com/mthom/scryer-prolog) is a free software ISO Prolog system intended to be an industrial strength production environment *and* a testbed for bleeding edge research in logic and constraint programming. From 2716381e7b20fae2b0fb8e8e80f26d4582b345f9 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Mon, 29 May 2023 11:12:00 +0200 Subject: [PATCH 217/361] FIXED: correct dereferencing in atom_codes/2 and number_codes/2. This addresses #1818. Test case: run :- length(Ls, L), portray_clause(L), maplist(=(X), Ls), X = Y, Y = 12, atom_codes(_, Ls), false. --- src/machine/system_calls.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index a871750a..5fc5d451 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1010,6 +1010,8 @@ impl MachineState { let mut string = String::new(); for addr in addrs { + let addr = self.store(self.deref(addr)); + match Number::try_from(addr) { Ok(Number::Fixnum(n)) => { match u32::try_from(n.get_num()) { From 5ed1802f0fb5ce6202af79fac23a988d8bc445a1 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 29 May 2023 20:49:54 -0600 Subject: [PATCH 218/361] read set_value args from temp regs of put_unsafe_value (#1812) --- src/fixtures.rs | 439 ++++++++++++++++++++++++++++++++++++++++++++++ src/heap_print.rs | 20 +-- 2 files changed, 449 insertions(+), 10 deletions(-) create mode 100644 src/fixtures.rs diff --git a/src/fixtures.rs b/src/fixtures.rs new file mode 100644 index 00000000..01a5e385 --- /dev/null +++ b/src/fixtures.rs @@ -0,0 +1,439 @@ +use crate::parser::ast::*; + +use crate::forms::*; +use crate::instructions::*; +use crate::iterators::*; + +use indexmap::{IndexMap, IndexSet}; + +use std::cell::Cell; +use std::collections::BTreeSet; +use std::mem::swap; +use std::rc::Rc; +use std::vec::Vec; + +// labeled with chunk numbers. +#[derive(Debug)] +pub(crate) enum VarStatus { + Perm(usize), + Temp(usize, TempVarData), // Perm(chunk_num) | Temp(chunk_num, _) +} + +pub(crate) type OccurrenceSet = BTreeSet<(GenContext, usize)>; + +// Perm: 0 initially, a stack register once processed. +// Temp: labeled with chunk_num and temp offset (unassigned if 0). +#[derive(Debug)] +pub(crate) enum VarData { + Perm(usize), + Temp(usize, usize, TempVarData), +} + +impl VarData { + pub(crate) fn as_reg_type(&self) -> RegType { + match self { + &VarData::Temp(_, r, _) => RegType::Temp(r), + &VarData::Perm(r) => RegType::Perm(r), + } + } +} + +#[derive(Debug)] +pub(crate) struct TempVarData { + pub(crate) last_term_arity: usize, + pub(crate) use_set: OccurrenceSet, + pub(crate) no_use_set: BTreeSet, + pub(crate) conflict_set: BTreeSet, +} + +impl TempVarData { + pub(crate) fn new(last_term_arity: usize) -> Self { + TempVarData { + last_term_arity: last_term_arity, + use_set: BTreeSet::new(), + no_use_set: BTreeSet::new(), + conflict_set: BTreeSet::new(), + } + } + + pub(crate) fn uses_reg(&self, reg: usize) -> bool { + for &(_, nreg) in self.use_set.iter() { + if reg == nreg { + return true; + } + } + + return false; + } + + pub(crate) fn populate_conflict_set(&mut self) { + if self.last_term_arity > 0 { + let arity = self.last_term_arity; + let mut conflict_set: BTreeSet = (1..arity).collect(); + + for &(_, reg) in self.use_set.iter() { + conflict_set.remove(®); + } + + self.conflict_set = conflict_set; + } + } +} + +type VariableFixture<'a> = (VarStatus, Vec<&'a Cell>); + +#[derive(Debug)] +pub(crate) struct VariableFixtures<'a> { + perm_vars: IndexMap, VariableFixture<'a>>, + last_chunk_temp_vars: IndexSet>, +} + +impl<'a> VariableFixtures<'a> { + pub(crate) fn new() -> Self { + VariableFixtures { + perm_vars: IndexMap::new(), + last_chunk_temp_vars: IndexSet::new(), + } + } + + pub(crate) fn insert(&mut self, var: Rc, vs: VariableFixture<'a>) { + self.perm_vars.insert(var, vs); + } + + pub(crate) fn insert_last_chunk_temp_var(&mut self, var: Rc) { + self.last_chunk_temp_vars.insert(var); + } + + // computes no_use and conflict sets for all temp vars. + pub(crate) fn populate_restricting_sets(&mut self) { + // three stages: + // 1. move the use sets of each variable to a local IndexMap, use_set + // (iterate mutably, swap mutable refs). + // 2. drain use_set. For each use set of U, add into the + // no-use sets of appropriate variables T =/= U. + // 3. Move the use sets back to their original locations in the fixture. + // Compute the conflict set of u. + + // 1. + let mut use_sets: IndexMap, OccurrenceSet> = IndexMap::new(); + + for (var, &mut (ref mut var_status, _)) in self.iter_mut() { + if let &mut VarStatus::Temp(_, ref mut var_data) = var_status { + let mut use_set = OccurrenceSet::new(); + + swap(&mut var_data.use_set, &mut use_set); + use_sets.insert((*var).clone(), use_set); + } + } + + for (u, use_set) in use_sets.drain(..) { + // 2. + for &(term_loc, reg) in use_set.iter() { + if let GenContext::Last(cn_u) = term_loc { + for (ref t, &mut (ref mut var_status, _)) in self.iter_mut() { + if let &mut VarStatus::Temp(cn_t, ref mut t_data) = var_status { + if cn_u == cn_t && *u != ***t { + if !t_data.uses_reg(reg) { + t_data.no_use_set.insert(reg); + } + } + } + } + } + } + + // 3. + match self.get_mut(u).unwrap() { + &mut (VarStatus::Temp(_, ref mut u_data), _) => { + u_data.use_set = use_set; + u_data.populate_conflict_set(); + } + _ => {} + }; + } + } + + fn get_mut(&mut self, u: Rc) -> Option<&mut VariableFixture<'a>> { + self.perm_vars.get_mut(&u) + } + + fn iter_mut(&mut self) -> indexmap::map::IterMut, VariableFixture<'a>> { + self.perm_vars.iter_mut() + } + + fn record_temp_info(&mut self, tvd: &mut TempVarData, arg_c: usize, term_loc: GenContext) { + match term_loc { + GenContext::Head | GenContext::Last(_) => { + tvd.use_set.insert((term_loc, arg_c)); + } + _ => {} + }; + } + + pub(crate) fn vars_above_threshold(&self, index: usize) -> usize { + let mut var_count = 0; + + for &(ref var_status, _) in self.values() { + if let &VarStatus::Perm(i) = var_status { + if i > index { + var_count += 1; + } + } + } + + var_count + } + + pub(crate) fn mark_vars_in_chunk(&mut self, iter: I, lt_arity: usize, term_loc: GenContext) + where + I: Iterator>, + { + let chunk_num = term_loc.chunk_num(); + let mut arg_c = 1; + + for term_ref in iter { + if let &TermRef::Var(lvl, cell, ref var) = &term_ref { + let mut status = self.perm_vars.swap_remove(var).unwrap_or(( + VarStatus::Temp(chunk_num, TempVarData::new(lt_arity)), + Vec::new(), + )); + + status.1.push(cell); + + match status.0 { + VarStatus::Temp(cn, ref mut tvd) if cn == chunk_num => { + if let Level::Shallow = lvl { + self.record_temp_info(tvd, arg_c, term_loc); + } + } + _ => status.0 = VarStatus::Perm(chunk_num), + }; + + self.perm_vars.insert(var.clone(), status); + } + + if let Level::Shallow = term_ref.level() { + arg_c += 1; + } + } + } + + pub(crate) fn into_iter(self) -> indexmap::map::IntoIter, VariableFixture<'a>> { + self.perm_vars.into_iter() + } + + fn values(&self) -> indexmap::map::Values, VariableFixture<'a>> { + self.perm_vars.values() + } + + pub(crate) fn size(&self) -> usize { + self.perm_vars.len() + } + + pub(crate) fn set_perm_vals(&self, has_deep_cuts: bool) { + let mut values_vec: Vec<_> = self + .values() + .filter_map(|ref v| match &v.0 { + &VarStatus::Perm(i) => Some((i, &v.1)), + _ => None, + }) + .collect(); + + values_vec.sort_by_key(|ref v| v.0); + + let offset = has_deep_cuts as usize; + + for (i, (_, cells)) in values_vec.into_iter().rev().enumerate() { + for cell in cells { + cell.set(VarReg::Norm(RegType::Perm(i + 1 + offset))); + } + } + } +} + +#[derive(Debug)] +pub(crate) struct UnsafeVarMarker { + pub(crate) unsafe_perm_vars: IndexMap, + pub(crate) unsafe_temp_vars: IndexSet, + pub(crate) safe_perm_vars: IndexSet, + pub(crate) safe_temp_vars: IndexSet, + pub(crate) temp_vars_to_perm_vars: IndexMap, + pub(crate) perm_vars_to_temp_vars: IndexMap, +} + +impl UnsafeVarMarker { + pub(crate) fn new() -> Self { + UnsafeVarMarker { + unsafe_perm_vars: IndexMap::new(), + unsafe_temp_vars: IndexSet::new(), + safe_perm_vars: IndexSet::new(), + safe_temp_vars: IndexSet::new(), + temp_vars_to_perm_vars: IndexMap::new(), + perm_vars_to_temp_vars: IndexMap::new(), + } + } + + pub(crate) fn from_fact_vars(safe_vars: IndexSet) -> Self { + let mut unsafe_var_marker = Self::new(); + + for r in safe_vars { + unsafe_var_marker.mark_var_as_safe(r); + } + + unsafe_var_marker + } + + fn mark_var_as_safe(&mut self, r: RegType) { + match r { + RegType::Temp(t) => { + self.safe_temp_vars.insert(t); + } + RegType::Perm(p) => { + self.safe_perm_vars.insert(p); + } + }; + } + + fn mark_var_as_unsafe(&mut self, r: RegType, phase: usize) { + match r { + RegType::Temp(t) => { + self.unsafe_temp_vars.insert(t); + } + RegType::Perm(p) => { + self.unsafe_perm_vars.insert(p, phase); + } + } + } + + // returns true if the instruction at *query_instr cannot be + // changed by mark_unsafe_vars. + fn mark_safe_vars(&mut self, query_instr: &Instruction) -> bool { + match query_instr { + &Instruction::PutVariable(r @ RegType::Temp(_), _) | + &Instruction::SetVariable(r) => { + self.mark_var_as_safe(r); + true + } + &Instruction::PutVariable(RegType::Perm(p), t) => { + self.temp_vars_to_perm_vars.insert(t, p); + true + } + &Instruction::CallIs(RegType::Temp(t), ..) => { + if let Some(p) = self.temp_vars_to_perm_vars.get(&t) { + self.mark_var_as_safe(RegType::Perm(*p)); + } + + true + } + _ => false, + } + } + + fn mark_phase(&mut self, query_instr: &Instruction, phase: usize) { + match query_instr { + &Instruction::PutValue(r @ RegType::Perm(_), _) | + &Instruction::SetValue(r) => { + self.mark_var_as_unsafe(r, phase); + } + _ => {} + } + } + + fn mark_unsafe_perm_vars(&mut self, query_instr: &mut Instruction, phase: usize) { + match query_instr { + &mut Instruction::PutValue(RegType::Perm(p), arg) + if !self.safe_perm_vars.contains(&p) => { + if let Some(ph) = self.unsafe_perm_vars.swap_remove(&p) { + if ph == phase { + *query_instr = Instruction::PutUnsafeValue(p, arg); + self.perm_vars_to_temp_vars.insert(p, arg); + } else { + self.unsafe_perm_vars.insert(p, ph); + } + } + } + &mut Instruction::SetValue(r @ RegType::Perm(p)) => + if let Some(t) = self.perm_vars_to_temp_vars.get(&p) { + *query_instr = Instruction::SetValue(RegType::Temp(*t)); + } else { + *query_instr = Instruction::SetLocalValue(r); + + self.safe_perm_vars.insert(p); + self.unsafe_perm_vars.remove(&p); + } + _ => {} + } + } + + fn mark_unsafe_temp_vars(&mut self, query_instr: &mut Instruction) { + match query_instr { + &mut Instruction::SetValue(r @ RegType::Temp(t)) + if !self.safe_temp_vars.contains(&t) => { + *query_instr = Instruction::SetLocalValue(r); + + self.safe_temp_vars.insert(t); + self.unsafe_temp_vars.remove(&t); + } + _ => { + } + } + } + + fn clear_temp_vars(&mut self) { + self.safe_temp_vars.clear(); + self.unsafe_temp_vars.clear(); + self.temp_vars_to_perm_vars.clear(); + } + + pub(crate) fn mark_unsafe_instrs(&mut self, code: &mut Code) { + if code.is_empty() { + return; + } + + let mut code_index = 0; + + for phase in 0.. { + while code[code_index].is_query_instr() { + let query_instr = &mut code[code_index]; + + if !self.mark_safe_vars(query_instr) { + self.mark_phase(query_instr, phase); + self.mark_unsafe_temp_vars(query_instr); + } + + code_index += 1; + } + + while code_index < code.len() && !code[code_index].is_query_instr() { + self.mark_safe_vars(&code[code_index]); + code_index += 1; + } + + self.clear_temp_vars(); + + if code_index >= code.len() { + break; + } + } + + code_index = 0; + + for phase in 0.. { + while code[code_index].is_query_instr() { + let query_instr = &mut code[code_index]; + self.mark_unsafe_perm_vars(query_instr, phase); + code_index += 1; + } + + // ensure phase->instruction assignments match those of + // the previous for loop. + while code_index < code.len() && !code[code_index].is_query_instr() { + code_index += 1; + } + + if code_index >= code.len() { + break; + } + } + } +} diff --git a/src/heap_print.rs b/src/heap_print.rs index d4b74378..5b0b40b4 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -841,19 +841,19 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } fn check_for_seen(&mut self) -> Option { - if let Some(addr) = self.iter.next() { - let is_cyclic = addr.get_forwarding_bit(); + if let Some(cell) = self.iter.next() { + let is_cyclic = cell.get_forwarding_bit(); - let addr = heap_bound_store( + let cell = heap_bound_store( self.iter.heap, - heap_bound_deref(self.iter.heap, addr), + heap_bound_deref(self.iter.heap, cell), ); - let addr = unmark_cell_bits!(addr); + let cell = unmark_cell_bits!(cell); - match self.var_names.get(&addr).cloned() { - Some(var) if addr.is_var() => { - // If addr is an unbound variable and maps to + match self.var_names.get(&cell).cloned() { + Some(var) if cell.is_var() => { + // If cell is an unbound variable and maps to // a name via heap_locs, append the name to // the current output, and return None. None // short-circuits handle_heap_term. @@ -868,7 +868,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { None } var_opt => { - if is_cyclic && addr.is_compound(self.iter.heap) { + if is_cyclic && cell.is_compound(self.iter.heap) { // self-referential variables are marked "cyclic". match var_opt { Some(var) => { @@ -891,7 +891,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { return None; } - Some(addr) + Some(cell) } } } else { From 4d982d22c140bea42e350ba4b423d15ad27f9b34 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 6 Jun 2023 00:58:44 -0600 Subject: [PATCH 219/361] set_local_value does not make values safe (#1812) --- src/fixtures.rs | 3 --- src/heap_print.rs | 1 - 2 files changed, 4 deletions(-) diff --git a/src/fixtures.rs b/src/fixtures.rs index 01a5e385..1f812e2c 100644 --- a/src/fixtures.rs +++ b/src/fixtures.rs @@ -357,9 +357,6 @@ impl UnsafeVarMarker { *query_instr = Instruction::SetValue(RegType::Temp(*t)); } else { *query_instr = Instruction::SetLocalValue(r); - - self.safe_perm_vars.insert(p); - self.unsafe_perm_vars.remove(&p); } _ => {} } diff --git a/src/heap_print.rs b/src/heap_print.rs index 5b0b40b4..f846dea1 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -848,7 +848,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.iter.heap, heap_bound_deref(self.iter.heap, cell), ); - let cell = unmark_cell_bits!(cell); match self.var_names.get(&cell).cloned() { From d7f56757272f83139e91c2ddeb489c581e8a0c5f Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 10 Jun 2023 01:25:47 -0600 Subject: [PATCH 220/361] improve call/N implementation (#1829) --- build/instructions_template.rs | 12 +- src/loader.pl | 1225 +++++++++++--------------------- src/machine/dispatch.rs | 8 +- src/machine/loader.rs | 15 + src/machine/machine_indices.rs | 10 +- src/machine/system_calls.rs | 273 +++---- src/macros.rs | 1 + src/toplevel.pl | 3 +- 8 files changed, 596 insertions(+), 951 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 25036607..7687d4e0 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -560,8 +560,8 @@ enum SystemClauseType { StripModule, #[strum_discriminants(strum(props(Arity = "4", Name = "$compile_inline_or_expanded_goal")))] CompileInlineOrExpandedGoal, - #[strum_discriminants(strum(props(Arity = "arity", Name = "$call_inline")))] - InlineCallN(usize), + #[strum_discriminants(strum(props(Arity = "arity", Name = "$fast_call")))] + FastCallN(usize), #[strum_discriminants(strum(props(Arity = "1", Name = "$is_expanded_or_inlined")))] IsExpandedOrInlined, #[strum_discriminants(strum(props(Arity = "3", Name = "$get_clause_p")))] @@ -1472,11 +1472,11 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::DefaultExecuteN(arity) => { functor!(atom!("execute_default_n"), [fixnum(arity)]) } - &Instruction::CallInlineCallN(arity) => { - functor!(atom!("call_n_inline"), [fixnum(arity)]) + &Instruction::CallFastCallN(arity) => { + functor!(atom!("call_fast_call_n"), [fixnum(arity)]) } - &Instruction::ExecuteInlineCallN(arity) => { - functor!(atom!("call_n_inline"), [fixnum(arity)]) + &Instruction::ExecuteFastCallN(arity) => { + functor!(atom!("execute_fast_call_n"), [fixnum(arity)]) } &Instruction::CallTermGreaterThan | &Instruction::CallTermLessThan | diff --git a/src/loader.pl b/src/loader.pl index 5c35822e..f809cd1a 100644 --- a/src/loader.pl +++ b/src/loader.pl @@ -758,7 +758,6 @@ expand_goal(UnexpandedGoals, Module, ExpandedGoals) :- UnexpandedGoals = ExpandedGoals), !. - :- non_counted_backtracking expand_goal/4. expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars) :- @@ -779,7 +778,6 @@ expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars) :- ) ). - /* * private predicate for use in call/N. it doesn't specially consider * control predicates as expand_goal does with expand_goal_cases. @@ -790,27 +788,20 @@ expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars) :- expand_call_goal(UnexpandedGoals, Module, ExpandedGoals) :- % if a goal isn't callable, defer to call/N to report the error. - catch(loader:expand_call_goal_(UnexpandedGoals, Module, ExpandedGoals), + catch('$call'(loader:expand_call_goal_(UnexpandedGoals, Module, ExpandedGoals)), error(type_error(callable, _), _), - UnexpandedGoals = ExpandedGoals), + '$call'(UnexpandedGoals = ExpandedGoals)), !. - :- non_counted_backtracking expand_call_goal_/3. expand_call_goal_(UnexpandedGoals, Module, ExpandedGoals) :- ( var(UnexpandedGoals) -> - expand_module_names(call(UnexpandedGoals), [0], Module, ExpandedGoals, []) + UnexpandedGoals = ExpandedGoals ; goal_expansion(UnexpandedGoals, Module, UnexpandedGoals1), ( Module \== user -> - goal_expansion(UnexpandedGoals1, user, Goals) - ; Goals = UnexpandedGoals1 - ), - ( predicate_property(Module:Goals, meta_predicate(MetaSpecs0)), - MetaSpecs0 =.. [_ | MetaSpecs] -> - expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, []) - ; thread_goals(Goals, ExpandedGoals, (',')) - ; Goals = ExpandedGoals + goal_expansion(UnexpandedGoals1, user, ExpandedGoals) + ; ExpandedGoals = UnexpandedGoals1 ) ). @@ -838,7 +829,6 @@ expand_goal_cases((Module:Goals0), _, ExpandedGoals, HeadVars) :- expand_goal(Goals0, Module, Goals1, HeadVars), ExpandedGoals = (Module:Goals1). - :- non_counted_backtracking thread_goals/3. thread_goals(Goals0, Goals1, Functor) :- @@ -853,7 +843,6 @@ thread_goals(Goals0, Goals1, Functor) :- ; Goals1 = Goals0 ). - :- non_counted_backtracking thread_goals/4. thread_goals(Goals0, Goals1, Hole, Functor) :- @@ -872,8 +861,6 @@ thread_goals(Goals0, Goals1, Hole, Functor) :- % % call/{1-64} with dynamic goal expansion. % -% The program used to generate the call/N predicates: -% % :- use_module(library(between)). % :- use_module(library(error)). % :- use_module(library(lists)). @@ -884,22 +871,18 @@ thread_goals(Goals0, Goals1, Hole, Functor) :- % Head =.. [call, G | Args], % CallNHead =.. [call, '$call'(G) | Args], % N1 is N + 1, -% InlineCall =.. ['$call_inline', G0 | Args], -% CallClause =.. ['$prepare_call_clause', G1, M1, G | Args], -% ModuleCallClause0 =.. ['$module_call', M1, G1], -% ModuleCallClause1 =.. ['$module_call', M2, G3], +% StripModule =.. ['$strip_module', G, M1, G1], +% FastCall =.. ['$fast_call', G | Args], +% PrepareCallClause =.. [ '$prepare_call_clause', G2, G1 | Args], +% ModuleCall =.. ['$module_call', M2, G4], % Clauses = [(Head :- var(G), % instantiation_error(call/N1)), -% (Head :- '$strip_module'(G, _, G0), InlineCall), -% (CallNHead :- !, -% CallClause, -% '$call_with_inference_counting'(ModuleCallClause0)), -% (Head :- CallClause, -% ( '$call_inline'(G1) -% ; expand_call_goal(G1, M1, G2), -% strip_subst_module(G2, M1, M2, G3), -% '$call_with_inference_counting'(ModuleCallClause1) -% ))]. +% (Head :- FastCall), +% (Head :- StripModule, +% PrepareCallClause, +% expand_call_goal(G2, M1, G3), +% strip_subst_module(G3, M1, M2, G4), +% '$call_with_inference_counting'(ModuleCall))]. % % generate_call_forms :- % between(1, 64, N), @@ -915,1237 +898,847 @@ thread_goals(Goals0, Goals1, Hole, Functor) :- % The '$call' functor is an escape hatch from goal expansion. So far, % it is used only to avoid infinite recursion into expand_call_goal/3. -:-non_counted_backtracking call/1. +:- non_counted_backtracking call/1. + call(G) :- - var(G), - instantiation_error(call/1). + var(G), + instantiation_error(call/1). call(G) :- - '$strip_module'(G, _, G0), - '$call_inline'(G0). -call('$call'(G0)) :- - !, - '$prepare_call_clause'(G,M,G0), - '$call_with_inference_counting'('$module_call'(M, G)). -call(G) :- - '$prepare_call_clause'(G0,M1,G), - ( '$call_inline'(G0) %% '$call_inline' cuts (only) after succeeding. - ; expand_call_goal(G0, M1, G1), - strip_subst_module(G1, M1, M2, G2), - '$call_with_inference_counting'('$module_call'(M2, G2)) - ). + '$fast_call'(G). +call(G0) :- + '$strip_module'(G0, M0, G1), + expand_call_goal(G1, M0, G2), + strip_subst_module(G2, M0, M1, G3), + '$call_with_inference_counting'('$module_call'(M1, G3)). :-non_counted_backtracking call/2. call(A,B) :- var(A), instantiation_error(call/2). call(A,B) :- - '$strip_module'(A,C,D), - '$call_inline'(D,B). -call('$call'(A),B) :- - !, - '$prepare_call_clause'(C,D,A,B), - '$call_with_inference_counting'('$module_call'(D,C)). + '$fast_call'(A,B). call(A,B) :- - '$prepare_call_clause'(C,D,A,B), - ( '$call_inline'(C) - ; expand_call_goal(C,D,E), - strip_subst_module(E,D,F,G), - '$call_with_inference_counting'('$module_call'(F,G)) - ). + '$strip_module'(A,C,D), + '$prepare_call_clause'(E,D,B), + expand_call_goal(E,C,F), + strip_subst_module(F,C,G,H), + '$call_with_inference_counting'('$module_call'(G,H)). :-non_counted_backtracking call/3. call(A,B,C) :- var(A), instantiation_error(call/3). call(A,B,C) :- - '$strip_module'(A,D,E), - '$call_inline'(E,B,C). -call('$call'(A),B,C) :- - !, - '$prepare_call_clause'(D,E,A,B,C), - '$call_with_inference_counting'('$module_call'(E,D)). + '$fast_call'(A,B,C). call(A,B,C) :- - '$prepare_call_clause'(D,E,A,B,C), - ( '$call_inline'(D) - ; expand_call_goal(D,E,F), - strip_subst_module(F,E,G,H), - '$call_with_inference_counting'('$module_call'(G,H)) - ). + '$strip_module'(A,D,E), + '$prepare_call_clause'(F,E,B,C), + expand_call_goal(F,D,G), + strip_subst_module(G,D,H,I), + '$call_with_inference_counting'('$module_call'(H,I)). :-non_counted_backtracking call/4. call(A,B,C,D) :- var(A), instantiation_error(call/4). call(A,B,C,D) :- - '$strip_module'(A,E,F), - '$call_inline'(F,B,C,D). -call('$call'(A),B,C,D) :- - !, - '$prepare_call_clause'(E,F,A,B,C,D), - '$call_with_inference_counting'('$module_call'(F,E)). + '$fast_call'(A,B,C,D). call(A,B,C,D) :- - '$prepare_call_clause'(E,F,A,B,C,D), - ( '$call_inline'(E) - ; expand_call_goal(E,F,G), - strip_subst_module(G,F,H,I), - '$call_with_inference_counting'('$module_call'(H,I)) - ). + '$strip_module'(A,E,F), + '$prepare_call_clause'(G,F,B,C,D), + expand_call_goal(G,E,H), + strip_subst_module(H,E,I,J), + '$call_with_inference_counting'('$module_call'(I,J)). :-non_counted_backtracking call/5. call(A,B,C,D,E) :- var(A), instantiation_error(call/5). call(A,B,C,D,E) :- - '$strip_module'(A,F,G), - '$call_inline'(G,B,C,D,E). -call('$call'(A),B,C,D,E) :- - !, - '$prepare_call_clause'(F,G,A,B,C,D,E), - '$call_with_inference_counting'('$module_call'(G,F)). + '$fast_call'(A,B,C,D,E). call(A,B,C,D,E) :- - '$prepare_call_clause'(F,G,A,B,C,D,E), - ( '$call_inline'(F) - ; expand_call_goal(F,G,H), - strip_subst_module(H,G,I,J), - '$call_with_inference_counting'('$module_call'(I,J)) - ). + '$strip_module'(A,F,G), + '$prepare_call_clause'(H,G,B,C,D,E), + expand_call_goal(H,F,I), + strip_subst_module(I,F,J,K), + '$call_with_inference_counting'('$module_call'(J,K)). :-non_counted_backtracking call/6. call(A,B,C,D,E,F) :- var(A), instantiation_error(call/6). call(A,B,C,D,E,F) :- - '$strip_module'(A,G,H), - '$call_inline'(H,B,C,D,E,F). -call('$call'(A),B,C,D,E,F) :- - !, - '$prepare_call_clause'(G,H,A,B,C,D,E,F), - '$call_with_inference_counting'('$module_call'(H,G)). + '$fast_call'(A,B,C,D,E,F). call(A,B,C,D,E,F) :- - '$prepare_call_clause'(G,H,A,B,C,D,E,F), - ( '$call_inline'(G) - ; expand_call_goal(G,H,I), - strip_subst_module(I,H,J,K), - '$call_with_inference_counting'('$module_call'(J,K)) - ). + '$strip_module'(A,G,H), + '$prepare_call_clause'(I,H,B,C,D,E,F), + expand_call_goal(I,G,J), + strip_subst_module(J,G,K,L), + '$call_with_inference_counting'('$module_call'(K,L)). :-non_counted_backtracking call/7. call(A,B,C,D,E,F,G) :- var(A), instantiation_error(call/7). call(A,B,C,D,E,F,G) :- - '$strip_module'(A,H,I), - '$call_inline'(I,B,C,D,E,F,G). -call('$call'(A),B,C,D,E,F,G) :- - !, - '$prepare_call_clause'(H,I,A,B,C,D,E,F,G), - '$call_with_inference_counting'('$module_call'(I,H)). + '$fast_call'(A,B,C,D,E,F,G). call(A,B,C,D,E,F,G) :- - '$prepare_call_clause'(H,I,A,B,C,D,E,F,G), - ( '$call_inline'(H) - ; expand_call_goal(H,I,J), - strip_subst_module(J,I,K,L), - '$call_with_inference_counting'('$module_call'(K,L)) - ). + '$strip_module'(A,H,I), + '$prepare_call_clause'(J,I,B,C,D,E,F,G), + expand_call_goal(J,H,K), + strip_subst_module(K,H,L,M), + '$call_with_inference_counting'('$module_call'(L,M)). :-non_counted_backtracking call/8. call(A,B,C,D,E,F,G,H) :- var(A), instantiation_error(call/8). call(A,B,C,D,E,F,G,H) :- - '$strip_module'(A,I,J), - '$call_inline'(J,B,C,D,E,F,G,H). -call('$call'(A),B,C,D,E,F,G,H) :- - !, - '$prepare_call_clause'(I,J,A,B,C,D,E,F,G,H), - '$call_with_inference_counting'('$module_call'(J,I)). + '$fast_call'(A,B,C,D,E,F,G,H). call(A,B,C,D,E,F,G,H) :- - '$prepare_call_clause'(I,J,A,B,C,D,E,F,G,H), - ( '$call_inline'(I) - ; expand_call_goal(I,J,K), - strip_subst_module(K,J,L,M), - '$call_with_inference_counting'('$module_call'(L,M)) - ). + '$strip_module'(A,I,J), + '$prepare_call_clause'(K,J,B,C,D,E,F,G,H), + expand_call_goal(K,I,L), + strip_subst_module(L,I,M,N), + '$call_with_inference_counting'('$module_call'(M,N)). :-non_counted_backtracking call/9. call(A,B,C,D,E,F,G,H,I) :- var(A), instantiation_error(call/9). call(A,B,C,D,E,F,G,H,I) :- - '$strip_module'(A,J,K), - '$call_inline'(K,B,C,D,E,F,G,H,I). -call('$call'(A),B,C,D,E,F,G,H,I) :- - !, - '$prepare_call_clause'(J,K,A,B,C,D,E,F,G,H,I), - '$call_with_inference_counting'('$module_call'(K,J)). + '$fast_call'(A,B,C,D,E,F,G,H,I). call(A,B,C,D,E,F,G,H,I) :- - '$prepare_call_clause'(J,K,A,B,C,D,E,F,G,H,I), - ( '$call_inline'(J) - ; expand_call_goal(J,K,L), - strip_subst_module(L,K,M,N), - '$call_with_inference_counting'('$module_call'(M,N)) - ). + '$strip_module'(A,J,K), + '$prepare_call_clause'(L,K,B,C,D,E,F,G,H,I), + expand_call_goal(L,J,M), + strip_subst_module(M,J,N,O), + '$call_with_inference_counting'('$module_call'(N,O)). :-non_counted_backtracking call/10. call(A,B,C,D,E,F,G,H,I,J) :- var(A), instantiation_error(call/10). call(A,B,C,D,E,F,G,H,I,J) :- - '$strip_module'(A,K,L), - '$call_inline'(L,B,C,D,E,F,G,H,I,J). -call('$call'(A),B,C,D,E,F,G,H,I,J) :- - !, - '$prepare_call_clause'(K,L,A,B,C,D,E,F,G,H,I,J), - '$call_with_inference_counting'('$module_call'(L,K)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J). call(A,B,C,D,E,F,G,H,I,J) :- - '$prepare_call_clause'(K,L,A,B,C,D,E,F,G,H,I,J), - ( '$call_inline'(K) - ; expand_call_goal(K,L,M), - strip_subst_module(M,L,N,O), - '$call_with_inference_counting'('$module_call'(N,O)) - ). + '$strip_module'(A,K,L), + '$prepare_call_clause'(M,L,B,C,D,E,F,G,H,I,J), + expand_call_goal(M,K,N), + strip_subst_module(N,K,O,P), + '$call_with_inference_counting'('$module_call'(O,P)). :-non_counted_backtracking call/11. call(A,B,C,D,E,F,G,H,I,J,K) :- var(A), instantiation_error(call/11). call(A,B,C,D,E,F,G,H,I,J,K) :- - '$strip_module'(A,L,M), - '$call_inline'(M,B,C,D,E,F,G,H,I,J,K). -call('$call'(A),B,C,D,E,F,G,H,I,J,K) :- - !, - '$prepare_call_clause'(L,M,A,B,C,D,E,F,G,H,I,J,K), - '$call_with_inference_counting'('$module_call'(M,L)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K). call(A,B,C,D,E,F,G,H,I,J,K) :- - '$prepare_call_clause'(L,M,A,B,C,D,E,F,G,H,I,J,K), - ( '$call_inline'(L) - ; expand_call_goal(L,M,N), - strip_subst_module(N,M,O,P), - '$call_with_inference_counting'('$module_call'(O,P)) - ). + '$strip_module'(A,L,M), + '$prepare_call_clause'(N,M,B,C,D,E,F,G,H,I,J,K), + expand_call_goal(N,L,O), + strip_subst_module(O,L,P,Q), + '$call_with_inference_counting'('$module_call'(P,Q)). :-non_counted_backtracking call/12. call(A,B,C,D,E,F,G,H,I,J,K,L) :- var(A), instantiation_error(call/12). call(A,B,C,D,E,F,G,H,I,J,K,L) :- - '$strip_module'(A,M,N), - '$call_inline'(N,B,C,D,E,F,G,H,I,J,K,L). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L) :- - !, - '$prepare_call_clause'(M,N,A,B,C,D,E,F,G,H,I,J,K,L), - '$call_with_inference_counting'('$module_call'(N,M)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L). call(A,B,C,D,E,F,G,H,I,J,K,L) :- - '$prepare_call_clause'(M,N,A,B,C,D,E,F,G,H,I,J,K,L), - ( '$call_inline'(M) - ; expand_call_goal(M,N,O), - strip_subst_module(O,N,P,Q), - '$call_with_inference_counting'('$module_call'(P,Q)) - ). + '$strip_module'(A,M,N), + '$prepare_call_clause'(O,N,B,C,D,E,F,G,H,I,J,K,L), + expand_call_goal(O,M,P), + strip_subst_module(P,M,Q,R), + '$call_with_inference_counting'('$module_call'(Q,R)). :-non_counted_backtracking call/13. call(A,B,C,D,E,F,G,H,I,J,K,L,M) :- var(A), instantiation_error(call/13). call(A,B,C,D,E,F,G,H,I,J,K,L,M) :- - '$strip_module'(A,N,O), - '$call_inline'(O,B,C,D,E,F,G,H,I,J,K,L,M). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M) :- - !, - '$prepare_call_clause'(N,O,A,B,C,D,E,F,G,H,I,J,K,L,M), - '$call_with_inference_counting'('$module_call'(O,N)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M). call(A,B,C,D,E,F,G,H,I,J,K,L,M) :- - '$prepare_call_clause'(N,O,A,B,C,D,E,F,G,H,I,J,K,L,M), - ( '$call_inline'(N) - ; expand_call_goal(N,O,P), - strip_subst_module(P,O,Q,R), - '$call_with_inference_counting'('$module_call'(Q,R)) - ). + '$strip_module'(A,N,O), + '$prepare_call_clause'(P,O,B,C,D,E,F,G,H,I,J,K,L,M), + expand_call_goal(P,N,Q), + strip_subst_module(Q,N,R,S), + '$call_with_inference_counting'('$module_call'(R,S)). :-non_counted_backtracking call/14. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N) :- var(A), instantiation_error(call/14). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N) :- - '$strip_module'(A,O,P), - '$call_inline'(P,B,C,D,E,F,G,H,I,J,K,L,M,N). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N) :- - !, - '$prepare_call_clause'(O,P,A,B,C,D,E,F,G,H,I,J,K,L,M,N), - '$call_with_inference_counting'('$module_call'(P,O)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N) :- - '$prepare_call_clause'(O,P,A,B,C,D,E,F,G,H,I,J,K,L,M,N), - ( '$call_inline'(O) - ; expand_call_goal(O,P,Q), - strip_subst_module(Q,P,R,S), - '$call_with_inference_counting'('$module_call'(R,S)) - ). + '$strip_module'(A,O,P), + '$prepare_call_clause'(Q,P,B,C,D,E,F,G,H,I,J,K,L,M,N), + expand_call_goal(Q,O,R), + strip_subst_module(R,O,S,T), + '$call_with_inference_counting'('$module_call'(S,T)). :-non_counted_backtracking call/15. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) :- var(A), instantiation_error(call/15). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) :- - '$strip_module'(A,P,Q), - '$call_inline'(Q,B,C,D,E,F,G,H,I,J,K,L,M,N,O). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O) :- - !, - '$prepare_call_clause'(P,Q,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O), - '$call_with_inference_counting'('$module_call'(Q,P)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) :- - '$prepare_call_clause'(P,Q,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O), - ( '$call_inline'(P) - ; expand_call_goal(P,Q,R), - strip_subst_module(R,Q,S,T), - '$call_with_inference_counting'('$module_call'(S,T)) - ). + '$strip_module'(A,P,Q), + '$prepare_call_clause'(R,Q,B,C,D,E,F,G,H,I,J,K,L,M,N,O), + expand_call_goal(R,P,S), + strip_subst_module(S,P,T,U), + '$call_with_inference_counting'('$module_call'(T,U)). :-non_counted_backtracking call/16. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :- var(A), instantiation_error(call/16). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :- - '$strip_module'(A,Q,R), - '$call_inline'(R,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :- - !, - '$prepare_call_clause'(Q,R,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P), - '$call_with_inference_counting'('$module_call'(R,Q)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :- - '$prepare_call_clause'(Q,R,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P), - ( '$call_inline'(Q) - ; expand_call_goal(Q,R,S), - strip_subst_module(S,R,T,U), - '$call_with_inference_counting'('$module_call'(T,U)) - ). + '$strip_module'(A,Q,R), + '$prepare_call_clause'(S,R,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P), + expand_call_goal(S,Q,T), + strip_subst_module(T,Q,U,V), + '$call_with_inference_counting'('$module_call'(U,V)). :-non_counted_backtracking call/17. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :- var(A), instantiation_error(call/17). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :- - '$strip_module'(A,R,S), - '$call_inline'(S,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :- - !, - '$prepare_call_clause'(R,S,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q), - '$call_with_inference_counting'('$module_call'(S,R)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :- - '$prepare_call_clause'(R,S,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q), - ( '$call_inline'(R) - ; expand_call_goal(R,S,T), - strip_subst_module(T,S,U,V), - '$call_with_inference_counting'('$module_call'(U,V)) - ). + '$strip_module'(A,R,S), + '$prepare_call_clause'(T,S,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q), + expand_call_goal(T,R,U), + strip_subst_module(U,R,V,W), + '$call_with_inference_counting'('$module_call'(V,W)). :-non_counted_backtracking call/18. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :- var(A), instantiation_error(call/18). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :- - '$strip_module'(A,S,T), - '$call_inline'(T,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :- - !, - '$prepare_call_clause'(S,T,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R), - '$call_with_inference_counting'('$module_call'(T,S)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :- - '$prepare_call_clause'(S,T,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R), - ( '$call_inline'(S) - ; expand_call_goal(S,T,U), - strip_subst_module(U,T,V,W), - '$call_with_inference_counting'('$module_call'(V,W)) - ). + '$strip_module'(A,S,T), + '$prepare_call_clause'(U,T,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R), + expand_call_goal(U,S,V), + strip_subst_module(V,S,W,X), + '$call_with_inference_counting'('$module_call'(W,X)). :-non_counted_backtracking call/19. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :- var(A), instantiation_error(call/19). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :- - '$strip_module'(A,T,U), - '$call_inline'(U,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :- - !, - '$prepare_call_clause'(T,U,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S), - '$call_with_inference_counting'('$module_call'(U,T)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :- - '$prepare_call_clause'(T,U,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S), - ( '$call_inline'(T) - ; expand_call_goal(T,U,V), - strip_subst_module(V,U,W,X), - '$call_with_inference_counting'('$module_call'(W,X)) - ). + '$strip_module'(A,T,U), + '$prepare_call_clause'(V,U,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S), + expand_call_goal(V,T,W), + strip_subst_module(W,T,X,Y), + '$call_with_inference_counting'('$module_call'(X,Y)). :-non_counted_backtracking call/20. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :- var(A), instantiation_error(call/20). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :- - '$strip_module'(A,U,V), - '$call_inline'(V,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :- - !, - '$prepare_call_clause'(U,V,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T), - '$call_with_inference_counting'('$module_call'(V,U)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :- - '$prepare_call_clause'(U,V,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T), - ( '$call_inline'(U) - ; expand_call_goal(U,V,W), - strip_subst_module(W,V,X,Y), - '$call_with_inference_counting'('$module_call'(X,Y)) - ). + '$strip_module'(A,U,V), + '$prepare_call_clause'(W,V,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T), + expand_call_goal(W,U,X), + strip_subst_module(X,U,Y,Z), + '$call_with_inference_counting'('$module_call'(Y,Z)). :-non_counted_backtracking call/21. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :- var(A), instantiation_error(call/21). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :- - '$strip_module'(A,V,W), - '$call_inline'(W,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :- - !, - '$prepare_call_clause'(V,W,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U), - '$call_with_inference_counting'('$module_call'(W,V)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :- - '$prepare_call_clause'(V,W,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U), - ( '$call_inline'(V) - ; expand_call_goal(V,W,X), - strip_subst_module(X,W,Y,Z), - '$call_with_inference_counting'('$module_call'(Y,Z)) - ). + '$strip_module'(A,V,W), + '$prepare_call_clause'(X,W,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U), + expand_call_goal(X,V,Y), + strip_subst_module(Y,V,Z,A1), + '$call_with_inference_counting'('$module_call'(Z,A1)). :-non_counted_backtracking call/22. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :- var(A), instantiation_error(call/22). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :- - '$strip_module'(A,W,X), - '$call_inline'(X,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :- - !, - '$prepare_call_clause'(W,X,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V), - '$call_with_inference_counting'('$module_call'(X,W)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :- - '$prepare_call_clause'(W,X,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V), - ( '$call_inline'(W) - ; expand_call_goal(W,X,Y), - strip_subst_module(Y,X,Z,A1), - '$call_with_inference_counting'('$module_call'(Z,A1)) - ). + '$strip_module'(A,W,X), + '$prepare_call_clause'(Y,X,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V), + expand_call_goal(Y,W,Z), + strip_subst_module(Z,W,A1,B1), + '$call_with_inference_counting'('$module_call'(A1,B1)). :-non_counted_backtracking call/23. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :- var(A), instantiation_error(call/23). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :- - '$strip_module'(A,X,Y), - '$call_inline'(Y,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :- - !, - '$prepare_call_clause'(X,Y,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W), - '$call_with_inference_counting'('$module_call'(Y,X)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :- - '$prepare_call_clause'(X,Y,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W), - ( '$call_inline'(X) - ; expand_call_goal(X,Y,Z), - strip_subst_module(Z,Y,A1,B1), - '$call_with_inference_counting'('$module_call'(A1,B1)) - ). + '$strip_module'(A,X,Y), + '$prepare_call_clause'(Z,Y,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W), + expand_call_goal(Z,X,A1), + strip_subst_module(A1,X,B1,C1), + '$call_with_inference_counting'('$module_call'(B1,C1)). :-non_counted_backtracking call/24. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :- var(A), instantiation_error(call/24). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :- - '$strip_module'(A,Y,Z), - '$call_inline'(Z,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :- - !, - '$prepare_call_clause'(Y,Z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X), - '$call_with_inference_counting'('$module_call'(Z,Y)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :- - '$prepare_call_clause'(Y,Z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X), - ( '$call_inline'(Y) - ; expand_call_goal(Y,Z,A1), - strip_subst_module(A1,Z,B1,C1), - '$call_with_inference_counting'('$module_call'(B1,C1)) - ). + '$strip_module'(A,Y,Z), + '$prepare_call_clause'(A1,Z,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X), + expand_call_goal(A1,Y,B1), + strip_subst_module(B1,Y,C1,D1), + '$call_with_inference_counting'('$module_call'(C1,D1)). :-non_counted_backtracking call/25. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :- var(A), instantiation_error(call/25). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :- - '$strip_module'(A,Z,A1), - '$call_inline'(A1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :- - !, - '$prepare_call_clause'(Z,A1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y), - '$call_with_inference_counting'('$module_call'(A1,Z)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :- - '$prepare_call_clause'(Z,A1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y), - ( '$call_inline'(Z) - ; expand_call_goal(Z,A1,B1), - strip_subst_module(B1,A1,C1,D1), - '$call_with_inference_counting'('$module_call'(C1,D1)) - ). + '$strip_module'(A,Z,A1), + '$prepare_call_clause'(B1,A1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y), + expand_call_goal(B1,Z,C1), + strip_subst_module(C1,Z,D1,E1), + '$call_with_inference_counting'('$module_call'(D1,E1)). :-non_counted_backtracking call/26. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :- var(A), instantiation_error(call/26). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :- - '$strip_module'(A,A1,B1), - '$call_inline'(B1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :- - !, - '$prepare_call_clause'(A1,B1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z), - '$call_with_inference_counting'('$module_call'(B1,A1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :- - '$prepare_call_clause'(A1,B1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z), - ( '$call_inline'(A1) - ; expand_call_goal(A1,B1,C1), - strip_subst_module(C1,B1,D1,E1), - '$call_with_inference_counting'('$module_call'(D1,E1)) - ). + '$strip_module'(A,A1,B1), + '$prepare_call_clause'(C1,B1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z), + expand_call_goal(C1,A1,D1), + strip_subst_module(D1,A1,E1,F1), + '$call_with_inference_counting'('$module_call'(E1,F1)). :-non_counted_backtracking call/27. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :- var(A), instantiation_error(call/27). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :- - '$strip_module'(A,B1,C1), - '$call_inline'(C1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :- - !, - '$prepare_call_clause'(B1,C1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1), - '$call_with_inference_counting'('$module_call'(C1,B1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :- - '$prepare_call_clause'(B1,C1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1), - ( '$call_inline'(B1) - ; expand_call_goal(B1,C1,D1), - strip_subst_module(D1,C1,E1,F1), - '$call_with_inference_counting'('$module_call'(E1,F1)) - ). + '$strip_module'(A,B1,C1), + '$prepare_call_clause'(D1,C1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1), + expand_call_goal(D1,B1,E1), + strip_subst_module(E1,B1,F1,G1), + '$call_with_inference_counting'('$module_call'(F1,G1)). :-non_counted_backtracking call/28. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :- var(A), instantiation_error(call/28). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :- - '$strip_module'(A,C1,D1), - '$call_inline'(D1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :- - !, - '$prepare_call_clause'(C1,D1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1), - '$call_with_inference_counting'('$module_call'(D1,C1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :- - '$prepare_call_clause'(C1,D1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1), - ( '$call_inline'(C1) - ; expand_call_goal(C1,D1,E1), - strip_subst_module(E1,D1,F1,G1), - '$call_with_inference_counting'('$module_call'(F1,G1)) - ). + '$strip_module'(A,C1,D1), + '$prepare_call_clause'(E1,D1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1), + expand_call_goal(E1,C1,F1), + strip_subst_module(F1,C1,G1,H1), + '$call_with_inference_counting'('$module_call'(G1,H1)). :-non_counted_backtracking call/29. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :- var(A), instantiation_error(call/29). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :- - '$strip_module'(A,D1,E1), - '$call_inline'(E1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :- - !, - '$prepare_call_clause'(D1,E1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1), - '$call_with_inference_counting'('$module_call'(E1,D1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :- - '$prepare_call_clause'(D1,E1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1), - ( '$call_inline'(D1) - ; expand_call_goal(D1,E1,F1), - strip_subst_module(F1,E1,G1,H1), - '$call_with_inference_counting'('$module_call'(G1,H1)) - ). + '$strip_module'(A,D1,E1), + '$prepare_call_clause'(F1,E1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1), + expand_call_goal(F1,D1,G1), + strip_subst_module(G1,D1,H1,I1), + '$call_with_inference_counting'('$module_call'(H1,I1)). :-non_counted_backtracking call/30. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :- var(A), instantiation_error(call/30). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :- - '$strip_module'(A,E1,F1), - '$call_inline'(F1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :- - !, - '$prepare_call_clause'(E1,F1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1), - '$call_with_inference_counting'('$module_call'(F1,E1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :- - '$prepare_call_clause'(E1,F1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1), - ( '$call_inline'(E1) - ; expand_call_goal(E1,F1,G1), - strip_subst_module(G1,F1,H1,I1), - '$call_with_inference_counting'('$module_call'(H1,I1)) - ). + '$strip_module'(A,E1,F1), + '$prepare_call_clause'(G1,F1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1), + expand_call_goal(G1,E1,H1), + strip_subst_module(H1,E1,I1,J1), + '$call_with_inference_counting'('$module_call'(I1,J1)). :-non_counted_backtracking call/31. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :- var(A), instantiation_error(call/31). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :- - '$strip_module'(A,F1,G1), - '$call_inline'(G1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :- - !, - '$prepare_call_clause'(F1,G1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1), - '$call_with_inference_counting'('$module_call'(G1,F1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :- - '$prepare_call_clause'(F1,G1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1), - ( '$call_inline'(F1) - ; expand_call_goal(F1,G1,H1), - strip_subst_module(H1,G1,I1,J1), - '$call_with_inference_counting'('$module_call'(I1,J1)) - ). + '$strip_module'(A,F1,G1), + '$prepare_call_clause'(H1,G1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1), + expand_call_goal(H1,F1,I1), + strip_subst_module(I1,F1,J1,K1), + '$call_with_inference_counting'('$module_call'(J1,K1)). :-non_counted_backtracking call/32. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :- var(A), instantiation_error(call/32). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :- - '$strip_module'(A,G1,H1), - '$call_inline'(H1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :- - !, - '$prepare_call_clause'(G1,H1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1), - '$call_with_inference_counting'('$module_call'(H1,G1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :- - '$prepare_call_clause'(G1,H1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1), - ( '$call_inline'(G1) - ; expand_call_goal(G1,H1,I1), - strip_subst_module(I1,H1,J1,K1), - '$call_with_inference_counting'('$module_call'(J1,K1)) - ). + '$strip_module'(A,G1,H1), + '$prepare_call_clause'(I1,H1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1), + expand_call_goal(I1,G1,J1), + strip_subst_module(J1,G1,K1,L1), + '$call_with_inference_counting'('$module_call'(K1,L1)). :-non_counted_backtracking call/33. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :- var(A), instantiation_error(call/33). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :- - '$strip_module'(A,H1,I1), - '$call_inline'(I1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :- - !, - '$prepare_call_clause'(H1,I1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1), - '$call_with_inference_counting'('$module_call'(I1,H1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :- - '$prepare_call_clause'(H1,I1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1), - ( '$call_inline'(H1) - ; expand_call_goal(H1,I1,J1), - strip_subst_module(J1,I1,K1,L1), - '$call_with_inference_counting'('$module_call'(K1,L1)) - ). + '$strip_module'(A,H1,I1), + '$prepare_call_clause'(J1,I1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1), + expand_call_goal(J1,H1,K1), + strip_subst_module(K1,H1,L1,M1), + '$call_with_inference_counting'('$module_call'(L1,M1)). :-non_counted_backtracking call/34. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :- var(A), instantiation_error(call/34). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :- - '$strip_module'(A,I1,J1), - '$call_inline'(J1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :- - !, - '$prepare_call_clause'(I1,J1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1), - '$call_with_inference_counting'('$module_call'(J1,I1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :- - '$prepare_call_clause'(I1,J1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1), - ( '$call_inline'(I1) - ; expand_call_goal(I1,J1,K1), - strip_subst_module(K1,J1,L1,M1), - '$call_with_inference_counting'('$module_call'(L1,M1)) - ). + '$strip_module'(A,I1,J1), + '$prepare_call_clause'(K1,J1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1), + expand_call_goal(K1,I1,L1), + strip_subst_module(L1,I1,M1,N1), + '$call_with_inference_counting'('$module_call'(M1,N1)). :-non_counted_backtracking call/35. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :- var(A), instantiation_error(call/35). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :- - '$strip_module'(A,J1,K1), - '$call_inline'(K1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :- - !, - '$prepare_call_clause'(J1,K1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1), - '$call_with_inference_counting'('$module_call'(K1,J1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :- - '$prepare_call_clause'(J1,K1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1), - ( '$call_inline'(J1) - ; expand_call_goal(J1,K1,L1), - strip_subst_module(L1,K1,M1,N1), - '$call_with_inference_counting'('$module_call'(M1,N1)) - ). + '$strip_module'(A,J1,K1), + '$prepare_call_clause'(L1,K1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1), + expand_call_goal(L1,J1,M1), + strip_subst_module(M1,J1,N1,O1), + '$call_with_inference_counting'('$module_call'(N1,O1)). :-non_counted_backtracking call/36. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :- var(A), instantiation_error(call/36). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :- - '$strip_module'(A,K1,L1), - '$call_inline'(L1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :- - !, - '$prepare_call_clause'(K1,L1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1), - '$call_with_inference_counting'('$module_call'(L1,K1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :- - '$prepare_call_clause'(K1,L1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1), - ( '$call_inline'(K1) - ; expand_call_goal(K1,L1,M1), - strip_subst_module(M1,L1,N1,O1), - '$call_with_inference_counting'('$module_call'(N1,O1)) - ). + '$strip_module'(A,K1,L1), + '$prepare_call_clause'(M1,L1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1), + expand_call_goal(M1,K1,N1), + strip_subst_module(N1,K1,O1,P1), + '$call_with_inference_counting'('$module_call'(O1,P1)). :-non_counted_backtracking call/37. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :- var(A), instantiation_error(call/37). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :- - '$strip_module'(A,L1,M1), - '$call_inline'(M1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :- - !, - '$prepare_call_clause'(L1,M1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1), - '$call_with_inference_counting'('$module_call'(M1,L1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :- - '$prepare_call_clause'(L1,M1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1), - ( '$call_inline'(L1) - ; expand_call_goal(L1,M1,N1), - strip_subst_module(N1,M1,O1,P1), - '$call_with_inference_counting'('$module_call'(O1,P1)) - ). + '$strip_module'(A,L1,M1), + '$prepare_call_clause'(N1,M1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1), + expand_call_goal(N1,L1,O1), + strip_subst_module(O1,L1,P1,Q1), + '$call_with_inference_counting'('$module_call'(P1,Q1)). :-non_counted_backtracking call/38. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :- var(A), instantiation_error(call/38). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :- - '$strip_module'(A,M1,N1), - '$call_inline'(N1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :- - !, - '$prepare_call_clause'(M1,N1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1), - '$call_with_inference_counting'('$module_call'(N1,M1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :- - '$prepare_call_clause'(M1,N1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1), - ( '$call_inline'(M1) - ; expand_call_goal(M1,N1,O1), - strip_subst_module(O1,N1,P1,Q1), - '$call_with_inference_counting'('$module_call'(P1,Q1)) - ). + '$strip_module'(A,M1,N1), + '$prepare_call_clause'(O1,N1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1), + expand_call_goal(O1,M1,P1), + strip_subst_module(P1,M1,Q1,R1), + '$call_with_inference_counting'('$module_call'(Q1,R1)). :-non_counted_backtracking call/39. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :- var(A), instantiation_error(call/39). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :- - '$strip_module'(A,N1,O1), - '$call_inline'(O1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :- - !, - '$prepare_call_clause'(N1,O1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1), - '$call_with_inference_counting'('$module_call'(O1,N1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :- - '$prepare_call_clause'(N1,O1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1), - ( '$call_inline'(N1) - ; expand_call_goal(N1,O1,P1), - strip_subst_module(P1,O1,Q1,R1), - '$call_with_inference_counting'('$module_call'(Q1,R1)) - ). + '$strip_module'(A,N1,O1), + '$prepare_call_clause'(P1,O1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1), + expand_call_goal(P1,N1,Q1), + strip_subst_module(Q1,N1,R1,S1), + '$call_with_inference_counting'('$module_call'(R1,S1)). :-non_counted_backtracking call/40. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :- var(A), instantiation_error(call/40). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :- - '$strip_module'(A,O1,P1), - '$call_inline'(P1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :- - !, - '$prepare_call_clause'(O1,P1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1), - '$call_with_inference_counting'('$module_call'(P1,O1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :- - '$prepare_call_clause'(O1,P1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1), - ( '$call_inline'(O1) - ; expand_call_goal(O1,P1,Q1), - strip_subst_module(Q1,P1,R1,S1), - '$call_with_inference_counting'('$module_call'(R1,S1)) - ). + '$strip_module'(A,O1,P1), + '$prepare_call_clause'(Q1,P1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1), + expand_call_goal(Q1,O1,R1), + strip_subst_module(R1,O1,S1,T1), + '$call_with_inference_counting'('$module_call'(S1,T1)). :-non_counted_backtracking call/41. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :- var(A), instantiation_error(call/41). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :- - '$strip_module'(A,P1,Q1), - '$call_inline'(Q1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :- - !, - '$prepare_call_clause'(P1,Q1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1), - '$call_with_inference_counting'('$module_call'(Q1,P1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :- - '$prepare_call_clause'(P1,Q1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1), - ( '$call_inline'(P1) - ; expand_call_goal(P1,Q1,R1), - strip_subst_module(R1,Q1,S1,T1), - '$call_with_inference_counting'('$module_call'(S1,T1)) - ). + '$strip_module'(A,P1,Q1), + '$prepare_call_clause'(R1,Q1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1), + expand_call_goal(R1,P1,S1), + strip_subst_module(S1,P1,T1,U1), + '$call_with_inference_counting'('$module_call'(T1,U1)). :-non_counted_backtracking call/42. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :- var(A), instantiation_error(call/42). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :- - '$strip_module'(A,Q1,R1), - '$call_inline'(R1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :- - !, - '$prepare_call_clause'(Q1,R1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1), - '$call_with_inference_counting'('$module_call'(R1,Q1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :- - '$prepare_call_clause'(Q1,R1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1), - ( '$call_inline'(Q1) - ; expand_call_goal(Q1,R1,S1), - strip_subst_module(S1,R1,T1,U1), - '$call_with_inference_counting'('$module_call'(T1,U1)) - ). + '$strip_module'(A,Q1,R1), + '$prepare_call_clause'(S1,R1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1), + expand_call_goal(S1,Q1,T1), + strip_subst_module(T1,Q1,U1,V1), + '$call_with_inference_counting'('$module_call'(U1,V1)). :-non_counted_backtracking call/43. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :- var(A), instantiation_error(call/43). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :- - '$strip_module'(A,R1,S1), - '$call_inline'(S1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :- - !, - '$prepare_call_clause'(R1,S1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1), - '$call_with_inference_counting'('$module_call'(S1,R1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :- - '$prepare_call_clause'(R1,S1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1), - ( '$call_inline'(R1) - ; expand_call_goal(R1,S1,T1), - strip_subst_module(T1,S1,U1,V1), - '$call_with_inference_counting'('$module_call'(U1,V1)) - ). + '$strip_module'(A,R1,S1), + '$prepare_call_clause'(T1,S1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1), + expand_call_goal(T1,R1,U1), + strip_subst_module(U1,R1,V1,W1), + '$call_with_inference_counting'('$module_call'(V1,W1)). :-non_counted_backtracking call/44. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :- var(A), instantiation_error(call/44). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :- - '$strip_module'(A,S1,T1), - '$call_inline'(T1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :- - !, - '$prepare_call_clause'(S1,T1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1), - '$call_with_inference_counting'('$module_call'(T1,S1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :- - '$prepare_call_clause'(S1,T1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1), - ( '$call_inline'(S1) - ; expand_call_goal(S1,T1,U1), - strip_subst_module(U1,T1,V1,W1), - '$call_with_inference_counting'('$module_call'(V1,W1)) - ). + '$strip_module'(A,S1,T1), + '$prepare_call_clause'(U1,T1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1), + expand_call_goal(U1,S1,V1), + strip_subst_module(V1,S1,W1,X1), + '$call_with_inference_counting'('$module_call'(W1,X1)). :-non_counted_backtracking call/45. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :- var(A), instantiation_error(call/45). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :- - '$strip_module'(A,T1,U1), - '$call_inline'(U1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :- - !, - '$prepare_call_clause'(T1,U1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1), - '$call_with_inference_counting'('$module_call'(U1,T1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :- - '$prepare_call_clause'(T1,U1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1), - ( '$call_inline'(T1) - ; expand_call_goal(T1,U1,V1), - strip_subst_module(V1,U1,W1,X1), - '$call_with_inference_counting'('$module_call'(W1,X1)) - ). + '$strip_module'(A,T1,U1), + '$prepare_call_clause'(V1,U1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1), + expand_call_goal(V1,T1,W1), + strip_subst_module(W1,T1,X1,Y1), + '$call_with_inference_counting'('$module_call'(X1,Y1)). :-non_counted_backtracking call/46. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :- var(A), instantiation_error(call/46). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :- - '$strip_module'(A,U1,V1), - '$call_inline'(V1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :- - !, - '$prepare_call_clause'(U1,V1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1), - '$call_with_inference_counting'('$module_call'(V1,U1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :- - '$prepare_call_clause'(U1,V1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1), - ( '$call_inline'(U1) - ; expand_call_goal(U1,V1,W1), - strip_subst_module(W1,V1,X1,Y1), - '$call_with_inference_counting'('$module_call'(X1,Y1)) - ). + '$strip_module'(A,U1,V1), + '$prepare_call_clause'(W1,V1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1), + expand_call_goal(W1,U1,X1), + strip_subst_module(X1,U1,Y1,Z1), + '$call_with_inference_counting'('$module_call'(Y1,Z1)). :-non_counted_backtracking call/47. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :- var(A), instantiation_error(call/47). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :- - '$strip_module'(A,V1,W1), - '$call_inline'(W1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :- - !, - '$prepare_call_clause'(V1,W1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1), - '$call_with_inference_counting'('$module_call'(W1,V1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :- - '$prepare_call_clause'(V1,W1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1), - ( '$call_inline'(V1) - ; expand_call_goal(V1,W1,X1), - strip_subst_module(X1,W1,Y1,Z1), - '$call_with_inference_counting'('$module_call'(Y1,Z1)) - ). + '$strip_module'(A,V1,W1), + '$prepare_call_clause'(X1,W1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1), + expand_call_goal(X1,V1,Y1), + strip_subst_module(Y1,V1,Z1,A2), + '$call_with_inference_counting'('$module_call'(Z1,A2)). :-non_counted_backtracking call/48. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :- var(A), instantiation_error(call/48). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :- - '$strip_module'(A,W1,X1), - '$call_inline'(X1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :- - !, - '$prepare_call_clause'(W1,X1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1), - '$call_with_inference_counting'('$module_call'(X1,W1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :- - '$prepare_call_clause'(W1,X1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1), - ( '$call_inline'(W1) - ; expand_call_goal(W1,X1,Y1), - strip_subst_module(Y1,X1,Z1,A2), - '$call_with_inference_counting'('$module_call'(Z1,A2)) - ). + '$strip_module'(A,W1,X1), + '$prepare_call_clause'(Y1,X1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1), + expand_call_goal(Y1,W1,Z1), + strip_subst_module(Z1,W1,A2,B2), + '$call_with_inference_counting'('$module_call'(A2,B2)). :-non_counted_backtracking call/49. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :- var(A), instantiation_error(call/49). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :- - '$strip_module'(A,X1,Y1), - '$call_inline'(Y1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :- - !, - '$prepare_call_clause'(X1,Y1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1), - '$call_with_inference_counting'('$module_call'(Y1,X1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :- - '$prepare_call_clause'(X1,Y1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1), - ( '$call_inline'(X1) - ; expand_call_goal(X1,Y1,Z1), - strip_subst_module(Z1,Y1,A2,B2), - '$call_with_inference_counting'('$module_call'(A2,B2)) - ). + '$strip_module'(A,X1,Y1), + '$prepare_call_clause'(Z1,Y1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1), + expand_call_goal(Z1,X1,A2), + strip_subst_module(A2,X1,B2,C2), + '$call_with_inference_counting'('$module_call'(B2,C2)). :-non_counted_backtracking call/50. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :- var(A), instantiation_error(call/50). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :- - '$strip_module'(A,Y1,Z1), - '$call_inline'(Z1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :- - !, - '$prepare_call_clause'(Y1,Z1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1), - '$call_with_inference_counting'('$module_call'(Z1,Y1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :- - '$prepare_call_clause'(Y1,Z1,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1), - ( '$call_inline'(Y1) - ; expand_call_goal(Y1,Z1,A2), - strip_subst_module(A2,Z1,B2,C2), - '$call_with_inference_counting'('$module_call'(B2,C2)) - ). + '$strip_module'(A,Y1,Z1), + '$prepare_call_clause'(A2,Z1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1), + expand_call_goal(A2,Y1,B2), + strip_subst_module(B2,Y1,C2,D2), + '$call_with_inference_counting'('$module_call'(C2,D2)). :-non_counted_backtracking call/51. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :- var(A), instantiation_error(call/51). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :- - '$strip_module'(A,Z1,A2), - '$call_inline'(A2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :- - !, - '$prepare_call_clause'(Z1,A2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1), - '$call_with_inference_counting'('$module_call'(A2,Z1)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :- - '$prepare_call_clause'(Z1,A2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1), - ( '$call_inline'(Z1) - ; expand_call_goal(Z1,A2,B2), - strip_subst_module(B2,A2,C2,D2), - '$call_with_inference_counting'('$module_call'(C2,D2)) - ). + '$strip_module'(A,Z1,A2), + '$prepare_call_clause'(B2,A2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1), + expand_call_goal(B2,Z1,C2), + strip_subst_module(C2,Z1,D2,E2), + '$call_with_inference_counting'('$module_call'(D2,E2)). :-non_counted_backtracking call/52. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :- var(A), instantiation_error(call/52). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :- - '$strip_module'(A,A2,B2), - '$call_inline'(B2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :- - !, - '$prepare_call_clause'(A2,B2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1), - '$call_with_inference_counting'('$module_call'(B2,A2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :- - '$prepare_call_clause'(A2,B2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1), - ( '$call_inline'(A2) - ; expand_call_goal(A2,B2,C2), - strip_subst_module(C2,B2,D2,E2), - '$call_with_inference_counting'('$module_call'(D2,E2)) - ). + '$strip_module'(A,A2,B2), + '$prepare_call_clause'(C2,B2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1), + expand_call_goal(C2,A2,D2), + strip_subst_module(D2,A2,E2,F2), + '$call_with_inference_counting'('$module_call'(E2,F2)). :-non_counted_backtracking call/53. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :- var(A), instantiation_error(call/53). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :- - '$strip_module'(A,B2,C2), - '$call_inline'(C2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :- - !, - '$prepare_call_clause'(B2,C2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2), - '$call_with_inference_counting'('$module_call'(C2,B2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :- - '$prepare_call_clause'(B2,C2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2), - ( '$call_inline'(B2) - ; expand_call_goal(B2,C2,D2), - strip_subst_module(D2,C2,E2,F2), - '$call_with_inference_counting'('$module_call'(E2,F2)) - ). + '$strip_module'(A,B2,C2), + '$prepare_call_clause'(D2,C2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2), + expand_call_goal(D2,B2,E2), + strip_subst_module(E2,B2,F2,G2), + '$call_with_inference_counting'('$module_call'(F2,G2)). :-non_counted_backtracking call/54. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2) :- var(A), instantiation_error(call/54). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2) :- - '$strip_module'(A,C2,D2), - '$call_inline'(D2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2) :- - !, - '$prepare_call_clause'(C2,D2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2), - '$call_with_inference_counting'('$module_call'(D2,C2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2) :- - '$prepare_call_clause'(C2,D2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2), - ( '$call_inline'(C2) - ; expand_call_goal(C2,D2,E2), - strip_subst_module(E2,D2,F2,G2), - '$call_with_inference_counting'('$module_call'(F2,G2)) - ). + '$strip_module'(A,C2,D2), + '$prepare_call_clause'(E2,D2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2), + expand_call_goal(E2,C2,F2), + strip_subst_module(F2,C2,G2,H2), + '$call_with_inference_counting'('$module_call'(G2,H2)). :-non_counted_backtracking call/55. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2) :- var(A), instantiation_error(call/55). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2) :- - '$strip_module'(A,D2,E2), - '$call_inline'(E2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2) :- - !, - '$prepare_call_clause'(D2,E2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2), - '$call_with_inference_counting'('$module_call'(E2,D2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2) :- - '$prepare_call_clause'(D2,E2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2), - ( '$call_inline'(D2) - ; expand_call_goal(D2,E2,F2), - strip_subst_module(F2,E2,G2,H2), - '$call_with_inference_counting'('$module_call'(G2,H2)) - ). + '$strip_module'(A,D2,E2), + '$prepare_call_clause'(F2,E2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2), + expand_call_goal(F2,D2,G2), + strip_subst_module(G2,D2,H2,I2), + '$call_with_inference_counting'('$module_call'(H2,I2)). :-non_counted_backtracking call/56. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2) :- var(A), instantiation_error(call/56). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2) :- - '$strip_module'(A,E2,F2), - '$call_inline'(F2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2) :- - !, - '$prepare_call_clause'(E2,F2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2), - '$call_with_inference_counting'('$module_call'(F2,E2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2) :- - '$prepare_call_clause'(E2,F2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2), - ( '$call_inline'(E2) - ; expand_call_goal(E2,F2,G2), - strip_subst_module(G2,F2,H2,I2), - '$call_with_inference_counting'('$module_call'(H2,I2)) - ). + '$strip_module'(A,E2,F2), + '$prepare_call_clause'(G2,F2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2), + expand_call_goal(G2,E2,H2), + strip_subst_module(H2,E2,I2,J2), + '$call_with_inference_counting'('$module_call'(I2,J2)). :-non_counted_backtracking call/57. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2) :- var(A), instantiation_error(call/57). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2) :- - '$strip_module'(A,F2,G2), - '$call_inline'(G2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2) :- - !, - '$prepare_call_clause'(F2,G2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2), - '$call_with_inference_counting'('$module_call'(G2,F2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2) :- - '$prepare_call_clause'(F2,G2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2), - ( '$call_inline'(F2) - ; expand_call_goal(F2,G2,H2), - strip_subst_module(H2,G2,I2,J2), - '$call_with_inference_counting'('$module_call'(I2,J2)) - ). + '$strip_module'(A,F2,G2), + '$prepare_call_clause'(H2,G2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2), + expand_call_goal(H2,F2,I2), + strip_subst_module(I2,F2,J2,K2), + '$call_with_inference_counting'('$module_call'(J2,K2)). :-non_counted_backtracking call/58. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2) :- var(A), instantiation_error(call/58). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2) :- - '$strip_module'(A,G2,H2), - '$call_inline'(H2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2) :- - !, - '$prepare_call_clause'(G2,H2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2), - '$call_with_inference_counting'('$module_call'(H2,G2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2) :- - '$prepare_call_clause'(G2,H2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2), - ( '$call_inline'(G2) - ; expand_call_goal(G2,H2,I2), - strip_subst_module(I2,H2,J2,K2), - '$call_with_inference_counting'('$module_call'(J2,K2)) - ). + '$strip_module'(A,G2,H2), + '$prepare_call_clause'(I2,H2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2), + expand_call_goal(I2,G2,J2), + strip_subst_module(J2,G2,K2,L2), + '$call_with_inference_counting'('$module_call'(K2,L2)). :-non_counted_backtracking call/59. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2) :- var(A), instantiation_error(call/59). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2) :- - '$strip_module'(A,H2,I2), - '$call_inline'(I2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2) :- - !, - '$prepare_call_clause'(H2,I2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2), - '$call_with_inference_counting'('$module_call'(I2,H2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2) :- - '$prepare_call_clause'(H2,I2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2), - ( '$call_inline'(H2) - ; expand_call_goal(H2,I2,J2), - strip_subst_module(J2,I2,K2,L2), - '$call_with_inference_counting'('$module_call'(K2,L2)) - ). + '$strip_module'(A,H2,I2), + '$prepare_call_clause'(J2,I2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2), + expand_call_goal(J2,H2,K2), + strip_subst_module(K2,H2,L2,M2), + '$call_with_inference_counting'('$module_call'(L2,M2)). :-non_counted_backtracking call/60. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2) :- var(A), instantiation_error(call/60). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2) :- - '$strip_module'(A,I2,J2), - '$call_inline'(J2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2) :- - !, - '$prepare_call_clause'(I2,J2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2), - '$call_with_inference_counting'('$module_call'(J2,I2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2) :- - '$prepare_call_clause'(I2,J2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2), - ( '$call_inline'(I2) - ; expand_call_goal(I2,J2,K2), - strip_subst_module(K2,J2,L2,M2), - '$call_with_inference_counting'('$module_call'(L2,M2)) - ). + '$strip_module'(A,I2,J2), + '$prepare_call_clause'(K2,J2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2), + expand_call_goal(K2,I2,L2), + strip_subst_module(L2,I2,M2,N2), + '$call_with_inference_counting'('$module_call'(M2,N2)). :-non_counted_backtracking call/61. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2) :- var(A), instantiation_error(call/61). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2) :- - '$strip_module'(A,J2,K2), - '$call_inline'(K2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2) :- - !, - '$prepare_call_clause'(J2,K2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2), - '$call_with_inference_counting'('$module_call'(K2,J2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2) :- - '$prepare_call_clause'(J2,K2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2), - ( '$call_inline'(J2) - ; expand_call_goal(J2,K2,L2), - strip_subst_module(L2,K2,M2,N2), - '$call_with_inference_counting'('$module_call'(M2,N2)) - ). + '$strip_module'(A,J2,K2), + '$prepare_call_clause'(L2,K2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2), + expand_call_goal(L2,J2,M2), + strip_subst_module(M2,J2,N2,O2), + '$call_with_inference_counting'('$module_call'(N2,O2)). :-non_counted_backtracking call/62. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2) :- var(A), instantiation_error(call/62). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2) :- - '$strip_module'(A,K2,L2), - '$call_inline'(L2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2) :- - !, - '$prepare_call_clause'(K2,L2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2), - '$call_with_inference_counting'('$module_call'(L2,K2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2) :- - '$prepare_call_clause'(K2,L2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2), - ( '$call_inline'(K2) - ; expand_call_goal(K2,L2,M2), - strip_subst_module(M2,L2,N2,O2), - '$call_with_inference_counting'('$module_call'(N2,O2)) - ). + '$strip_module'(A,K2,L2), + '$prepare_call_clause'(M2,L2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2), + expand_call_goal(M2,K2,N2), + strip_subst_module(N2,K2,O2,P2), + '$call_with_inference_counting'('$module_call'(O2,P2)). :-non_counted_backtracking call/63. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2) :- var(A), instantiation_error(call/63). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2) :- - '$strip_module'(A,L2,M2), - '$call_inline'(M2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2) :- - !, - '$prepare_call_clause'(L2,M2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2), - '$call_with_inference_counting'('$module_call'(M2,L2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2) :- - '$prepare_call_clause'(L2,M2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2), - ( '$call_inline'(L2) - ; expand_call_goal(L2,M2,N2), - strip_subst_module(N2,M2,O2,P2), - '$call_with_inference_counting'('$module_call'(O2,P2)) - ). + '$strip_module'(A,L2,M2), + '$prepare_call_clause'(N2,M2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2), + expand_call_goal(N2,L2,O2), + strip_subst_module(O2,L2,P2,Q2), + '$call_with_inference_counting'('$module_call'(P2,Q2)). :-non_counted_backtracking call/64. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2) :- var(A), instantiation_error(call/64). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2) :- - '$strip_module'(A,M2,N2), - '$call_inline'(N2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2) :- - !, - '$prepare_call_clause'(M2,N2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2), - '$call_with_inference_counting'('$module_call'(N2,M2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2) :- - '$prepare_call_clause'(M2,N2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2), - ( '$call_inline'(M2) - ; expand_call_goal(M2,N2,O2), - strip_subst_module(O2,N2,P2,Q2), - '$call_with_inference_counting'('$module_call'(P2,Q2)) - ). + '$strip_module'(A,M2,N2), + '$prepare_call_clause'(O2,N2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2), + expand_call_goal(O2,M2,P2), + strip_subst_module(P2,M2,Q2,R2), + '$call_with_inference_counting'('$module_call'(Q2,R2)). :-non_counted_backtracking call/65. call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2) :- var(A), instantiation_error(call/65). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2) :- - '$strip_module'(A,N2,O2), - '$call_inline'(O2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2). -call('$call'(A),B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2) :- - !, - '$prepare_call_clause'(N2,O2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2), - '$call_with_inference_counting'('$module_call'(O2,N2)). + '$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2). call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2) :- - '$prepare_call_clause'(N2,O2,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2), - ( '$call_inline'(N2) - ; expand_call_goal(N2,O2,P2), - strip_subst_module(P2,O2,Q2,R2), - '$call_with_inference_counting'('$module_call'(Q2,R2)) - ). + '$strip_module'(A,N2,O2), + '$prepare_call_clause'(P2,O2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2), + expand_call_goal(P2,N2,Q2), + strip_subst_module(Q2,N2,R2,S2), + '$call_with_inference_counting'('$module_call'(R2,S2)). diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index a6e7963a..9c83272d 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -5058,12 +5058,12 @@ impl Machine { self.machine_st.fail = !self.is_expanded_or_inlined(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallInlineCallN(arity) => { + &Instruction::CallFastCallN(arity) => { let call_at_index = |wam: &mut Machine, name, arity, ptr| { wam.try_call(name, arity, ptr) }; - try_or_throw!(self.machine_st, self.call_inline(arity, call_at_index)); + try_or_throw!(self.machine_st, self.fast_call(arity, call_at_index)); if self.machine_st.fail { self.machine_st.backtrack(); @@ -5074,12 +5074,12 @@ impl Machine { ); } } - &Instruction::ExecuteInlineCallN(arity) => { + &Instruction::ExecuteFastCallN(arity) => { let call_at_index = |wam: &mut Machine, name, arity, ptr| { wam.try_execute(name, arity, ptr) }; - try_or_throw!(self.machine_st, self.call_inline(arity, call_at_index)); + try_or_throw!(self.machine_st, self.fast_call(arity, call_at_index)); if self.machine_st.fail { self.machine_st.backtrack(); diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 6c98024c..3c838b8b 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -1698,6 +1698,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, diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index fdc60e0b..ca31e4bd 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -8,7 +8,7 @@ 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::*; @@ -243,12 +243,15 @@ pub(crate) type LocalExtensiblePredicates = pub(crate) type CodeDir = IndexMap; +pub(crate) type GoalExpansionIndices = IndexSet; + #[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, @@ -257,6 +260,11 @@ pub struct IndexStore { } impl IndexStore { + #[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, diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 5fc5d451..94f166f2 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1202,29 +1202,29 @@ impl Machine { #[inline(always)] pub(crate) fn deref_register(&mut self, i: usize) -> HeapCellValue { - self.machine_st.store(self.machine_st.deref(self.machine_st.registers[i])) + self.machine_st.store(self.machine_st.deref(self.machine_st.registers[i])) } #[inline(always)] - pub(crate) fn call_inline( + pub(crate) fn fast_call( &mut self, arity: usize, call_at_index: impl Fn(&mut Machine, Atom, usize, IndexPtr) -> CallResult, ) -> CallResult { let arity = arity - 1; - let goal = self.deref_register(1); + let (mut module_name, mut goal) = self.machine_st.strip_module( + self.machine_st.registers[1], + heap_loc_as_cell!(0), + ); - let load_registers = |machine_st: &mut MachineState, goal: HeapCellValue| -> Option { + let load_registers = |machine_st: &mut MachineState, goal: HeapCellValue, goal_arity: usize| { read_heap_cell!(goal, - (HeapCellValueTag::Str, s) => { - let (name, goal_arity) = cell_as_atom_cell!(machine_st.heap[s]) - .get_name_and_arity(); - - if goal_arity > 0 { + (HeapCellValueTag::Str | HeapCellValueTag::Atom, s) => { + if goal_arity > 1 { for idx in (1 .. arity + 1).rev() { machine_st.registers[idx + goal_arity] = machine_st.registers[idx + 1]; } - } else { + } else if goal_arity == 0 { for idx in 1 .. arity + 1 { machine_st.registers[idx] = machine_st.registers[idx + 1]; } @@ -1233,8 +1233,6 @@ impl Machine { for idx in 1 .. goal_arity + 1 { machine_st.registers[idx] = machine_st.heap[s+idx]; } - - Some((name, goal_arity)) } _ => { unreachable!() @@ -1242,35 +1240,70 @@ impl Machine { ) }; - read_heap_cell!(goal, + let (mut name, mut goal_arity, index_cell_opt) = read_heap_cell!(goal, (HeapCellValueTag::Str, s) => { - let goal_arity = cell_as_atom_cell!(self.machine_st.heap[s]).get_arity(); + let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity(); - if self.machine_st.heap.len() > s + goal_arity + 1 { - let index_cell = self.machine_st.heap[s+goal_arity+1]; - - if let Some(code_index) = get_structure_index(index_cell) { - if code_index.is_undefined() { - self.machine_st.fail = true; - return Ok(()); - } - - match load_registers(&mut self.machine_st, goal) { - Some((name, goal_arity)) => { - let arity = goal_arity + arity; - self.machine_st.neck_cut(); - return call_at_index(self, name, arity, code_index.get()); - } - None => { - } - } - } - } + (name, arity, if self.machine_st.heap.len() > s + arity + 1 { + get_structure_index(self.machine_st.heap[s + arity + 1]) + } else { + None + }) + } + (HeapCellValueTag::Atom, (name, arity)) => { + debug_assert_eq!(arity, 0); + (name, arity, None) } _ => { + self.machine_st.fail = true; + return Ok(()); } ); + let mut arity = arity + goal_arity; + + let index_cell = index_cell_opt.or_else(|| { + let is_internal_call = name == atom!("$call") && goal_arity > 0; + + if !is_internal_call && self.indices.goal_expansion_defined((name, arity)) { + None + } else { + if is_internal_call { + debug_assert_eq!(goal.get_tag(), HeapCellValueTag::Str); + goal = self.machine_st.heap[goal.get_value()+1]; + (module_name, goal) = self.machine_st.strip_module(goal, module_name); + + if let Some((inner_name, inner_arity)) = self.machine_st.name_and_arity_from_heap(goal) { + arity -= goal_arity; + (name, goal_arity) = (inner_name, inner_arity); + arity += goal_arity; + } else { + return None; + } + } + + let module_name = if module_name.get_tag() != HeapCellValueTag::Atom { + if let Some(load_context) = self.load_contexts.last() { + load_context.module + } else { + atom!("user") + } + } else { + cell_as_atom!(module_name) + }; + + self.indices.get_predicate_code_index(name, arity, module_name) + } + }); + + if let Some(code_index) = index_cell { + if !code_index.is_undefined() { + load_registers(&mut self.machine_st, goal, goal_arity); + self.machine_st.neck_cut(); + return call_at_index(self, name, arity, code_index.get()); + } + } + self.machine_st.fail = true; Ok(()) } @@ -1489,35 +1522,12 @@ impl Machine { } #[inline(always)] - pub(crate) fn prepare_call_clause(&mut self, arity: usize) -> CallResult { + pub(crate) fn strip_module(&mut self) { let (module_loc, qualified_goal) = self.machine_st.strip_module( - self.machine_st.registers[3], + self.machine_st.registers[1], self.machine_st.registers[2], ); - // the first three arguments don't belong to the containing call/N. - let arity = arity - 3; - - let (name, narity, s) = self.machine_st.setup_call_n_init_goal_info( - qualified_goal, - arity, - )?; - - let module_loc = self.machine_st.store(self.machine_st.deref(module_loc)); - - if module_loc.is_var() { - self.load_context_module(module_loc); - - if self.machine_st.fail { - self.machine_st.fail = false; - self.machine_st.unify_atom(atom!("user"), module_loc); - - if self.machine_st.fail { - return Ok(()); - } - } - } - let target_module_loc = self.machine_st.registers[2]; unify_fn!( @@ -1526,9 +1536,26 @@ impl Machine { target_module_loc ); - if self.machine_st.fail { - return Ok(()); - } + let target_qualified_goal = self.machine_st.registers[3]; + + unify_fn!( + &mut self.machine_st, + qualified_goal, + target_qualified_goal + ); + } + + #[inline(always)] + pub(crate) fn prepare_call_clause(&mut self, arity: usize) -> CallResult { + let qualified_goal = self.deref_register(2); + + // the first two arguments don't belong to the containing call/N. + let arity = arity - 2; + + let (name, narity, s) = self.machine_st.setup_call_n_init_goal_info( + qualified_goal, + arity, + )?; // assemble goal from pre-loaded (narity) and supplementary // (arity) arguments. @@ -1544,15 +1571,10 @@ impl Machine { } for idx in 1 .. arity + 1 { - self.machine_st.heap.push(self.machine_st.registers[3 + idx]); + self.machine_st.heap.push(self.machine_st.registers[2 + idx]); } - let index_cell = self.machine_st.heap[s + narity + 1]; - - if get_structure_index(index_cell).is_some() { - self.machine_st.heap.push(index_cell); - str_loc_as_cell!(h) - } else if narity + arity > 0 { + if narity + arity > 0 { str_loc_as_cell!(h) } else { heap_loc_as_cell!(h) @@ -1570,6 +1592,65 @@ impl Machine { Ok(()) } + #[inline(always)] + pub(crate) fn dynamic_module_resolution( + &mut self, + narity: usize, + ) -> Result<(Atom, PredicateKey), MachineStub> { + let module_name = self.deref_register(1); + + let module_name = read_heap_cell!(module_name, + (HeapCellValueTag::Atom, (name, _arity)) => { + debug_assert_eq!(_arity, 0); + name + } + (HeapCellValueTag::Str, s) => { + let (module_name, _arity) = cell_as_atom_cell!(self.machine_st.heap[s]) + .get_name_and_arity(); + + debug_assert_eq!(_arity, 0); + module_name + } + _ if module_name.is_var() => { + if let Some(load_context) = self.load_contexts.last() { + load_context.module + } else { + atom!("user") + } + } + _ => { + unreachable!() + } + ); + + let goal = self.deref_register(2); + + let (name, arity, s) = self.machine_st.setup_call_n_init_goal_info(goal, narity)?; + + // TODO: think we just need the 'Greater' branch here. + match arity.cmp(&2) { + Ordering::Less => { + for i in arity + 1..arity + narity + 1 { + self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity]; + } + } + Ordering::Greater => { + for i in (arity + 1..arity + narity + 1).rev() { + self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity]; + } + } + Ordering::Equal => {} + } + + let key = (name, arity + narity); + + for i in 1..arity + 1 { + self.machine_st.registers[i] = self.machine_st.heap[s + i]; + } + + Ok((module_name, key)) + } + #[inline(always)] pub(crate) fn is_reset_cont_marker(&self, p: usize) -> bool { match &self.code[p] { @@ -3606,60 +3687,6 @@ impl Machine { } } - #[inline(always)] - pub(crate) fn dynamic_module_resolution( - &mut self, - narity: usize, - ) -> Result<(Atom, PredicateKey), MachineStub> { - let module_name = self.deref_register(1); - - let module_name = read_heap_cell!(module_name, - (HeapCellValueTag::Atom, (name, _arity)) => { - debug_assert_eq!(_arity, 0); - name - } - (HeapCellValueTag::Str, s) => { - let (module_name, _arity) = cell_as_atom_cell!(self.machine_st.heap[s]) - .get_name_and_arity(); - - debug_assert_eq!(_arity, 0); - module_name - } - _ if module_name.is_var() => { - atom!("user") - } - _ => { - unreachable!() - } - ); - - let goal = self.deref_register(2); - - let (name, arity, s) = self.machine_st.setup_call_n_init_goal_info(goal, narity)?; - - match arity.cmp(&2) { - Ordering::Less => { - for i in arity + 1..arity + narity + 1 { - self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity]; - } - } - Ordering::Greater => { - for i in (arity + 1..arity + narity + 1).rev() { - self.machine_st.registers[i] = self.machine_st.registers[i + 2 - arity]; - } - } - Ordering::Equal => {} - } - - let key = (name, arity + narity); - - for i in 1..arity + 1 { - self.machine_st.registers[i] = self.machine_st.heap[s + i]; - } - - Ok((module_name, key)) - } - #[inline(always)] pub(crate) fn lookup_db_ref(&mut self) { let name = cell_as_atom!(self.deref_register(1)); diff --git a/src/macros.rs b/src/macros.rs index c1f1552f..c0c929c8 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -574,6 +574,7 @@ macro_rules! index_store { extensible_predicates: ExtensiblePredicates::with_hasher(FxBuildHasher::default()), local_extensible_predicates: LocalExtensiblePredicates::with_hasher(FxBuildHasher::default()), global_variables: GlobalVarDir::with_hasher(FxBuildHasher::default()), + goal_expansion_indices: GoalExpansionIndices::with_hasher(FxBuildHasher::default()), meta_predicates: MetaPredicateDir::with_hasher(FxBuildHasher::default()), modules: $modules, op_dir: $op_dir, diff --git a/src/toplevel.pl b/src/toplevel.pl index 0bab4415..b554e52c 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -181,7 +181,8 @@ submit_query_and_print_results_(Term, VarList) :- '$get_b_value'(B), bb_put('$report_all', false), bb_put('$report_n_more', 0), - atts:call_residue_vars(user:Term, AttrVars), + expand_goal(Term, user, Term0), + atts:call_residue_vars(user:Term0, AttrVars), write_eqs_and_read_input(B, VarList, AttrVars), !. submit_query_and_print_results_(_, _) :- From 89ed1aa8de253c75c4ac2b145c18639281a8a41c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 22:54:36 +0000 Subject: [PATCH 221/361] Bump openssl from 0.10.48 to 0.10.55 Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.48 to 0.10.55. - [Release notes](https://github.com/sfackler/rust-openssl/releases) - [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.48...openssl-v0.10.55) --- updated-dependencies: - dependency-name: openssl dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Cargo.lock | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 364b4891..28421131 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1248,9 +1248,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "openssl" -version = "0.10.48" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518915b97df115dd36109bfa429a48b8f737bd05508cf9588977b599648926d2" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ "bitflags", "cfg-if", @@ -1280,11 +1280,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.83" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", From 4ad113a6f8180fd3839b93eec09f49a4ff457702 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 23 Jun 2023 11:13:51 -0600 Subject: [PATCH 222/361] mark is/2 allocated permanent variables as safe, add CompareNumber terms to ClauseType::is_inlined --- build/instructions_template.rs | 6 ++++++ src/codegen.rs | 3 ++- src/debray_allocator.rs | 29 ++++++++++------------------- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 7687d4e0..8e61ad23 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -2311,6 +2311,12 @@ pub fn generate_instructions_rs() -> TokenStream { (atom!(#name), #arity) => true } ); + + is_inlined_arms.push( + quote! { + (atom!(#name), #arity) => true + } + ); } for (name, arity, variant) in instr_data.compare_term_variants { diff --git a/src/codegen.rs b/src/codegen.rs index 000c0e65..dca532ce 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -803,7 +803,6 @@ impl<'b> CodeGenerator<'b> { let at = match &terms[0] { &Term::Var(ref vr, ref name) => { let var_num = name.to_var_num().unwrap(); - self.marker.mark_temp_to_safe_perm(var_num); self.marker.mark_var::( var_num, @@ -813,6 +812,8 @@ impl<'b> CodeGenerator<'b> { code, ); + self.marker.mark_safe_var_unconditionally(var_num); + compile_expr!(self, &terms[1], term_loc, code) } &Term::Literal(_, c @ Literal::Integer(_) | diff --git a/src/debray_allocator.rs b/src/debray_allocator.rs index 2cd853c7..6ad47cfc 100644 --- a/src/debray_allocator.rs +++ b/src/debray_allocator.rs @@ -414,8 +414,7 @@ impl DebrayAllocator { self.perm_free_list.pop_front(); match &mut self.var_data.records[var_num].allocation { - &mut VarAlloc::Perm(p, ref mut allocation) => { - *allocation = PermVarAllocation::Pending; + &mut VarAlloc::Perm(p, _) => { Some(p) } _ => unreachable!() @@ -426,21 +425,18 @@ impl DebrayAllocator { } } - pub(crate) fn mark_temp_to_safe_perm(&mut self, var_num: usize) { - match &self.var_data.records[var_num].allocation { - &VarAlloc::Temp { to_perm_var_num: Some(perm_var_num), .. } => { - let branch_designator = self.current_branch_designator(); + pub(crate) fn mark_safe_var_unconditionally(&mut self, var_num: usize) { + let branch_designator = self.current_branch_designator(); - match &mut self.var_data.records[perm_var_num].allocation { - VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, shallow_safety, .. }) => { - *deep_safety = VarSafetyStatus::unneeded(branch_designator); - *shallow_safety = VarSafetyStatus::unneeded(branch_designator); - } - _ => unreachable!() - } + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, shallow_safety, .. }) => { + *deep_safety = VarSafetyStatus::unneeded(branch_designator); + *shallow_safety = VarSafetyStatus::unneeded(branch_designator); } - _ => { + VarAlloc::Temp { safety, .. } => { + *safety = VarSafetyStatus::unneeded(branch_designator); } + _ => unreachable!(), } } @@ -708,11 +704,6 @@ impl Allocator for DebrayAllocator { if record.running_count < record.num_occurrences { record.running_count += 1; } else if r.is_perm() { - match &mut self.var_data.records[var_num].allocation { - VarAlloc::Perm(_, allocation) => *allocation = PermVarAllocation::Pending, - _ => unreachable!(), - } - self.perm_free_list.push_back((term_loc.chunk_num(), var_num)); } From 92853a6a1276d746b27baa249f08c1a35cb2089e Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 23 Jun 2023 12:24:34 -0600 Subject: [PATCH 223/361] free local cut variables after cut --- src/codegen.rs | 8 ++++---- src/debray_allocator.rs | 43 ++++++++++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index dca532ce..87345494 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -332,7 +332,7 @@ trait AddToFreeList<'a, Target: CompilationTarget<'a>> { impl<'a, 'b> AddToFreeList<'a, FactInstruction> for CodeGenerator<'b> { fn add_term_to_free_list(&mut self, r: RegType) { - self.marker.add_to_free_list(r); + self.marker.add_reg_to_free_list(r); } fn add_subterm_to_free_list(&mut self, _term: &Term) {} @@ -345,7 +345,7 @@ impl<'a, 'b> AddToFreeList<'a, QueryInstruction> for CodeGenerator<'b> { #[inline(always)] fn add_subterm_to_free_list(&mut self, term: &Term) { if let Some(cell) = structure_cell(term) { - self.marker.add_to_free_list(cell.get()); + self.marker.add_reg_to_free_list(cell.get()); } } } @@ -881,7 +881,6 @@ impl<'b> CodeGenerator<'b> { code.push_back(instr!("neck_cut")); } else { let r = self.marker.get_binding(var_num); - // let r = self.marker.mark_cut_var(var_num, chunk_num); code.push_back(instr!("cut", r)); } @@ -896,7 +895,6 @@ impl<'b> CodeGenerator<'b> { &QueryTerm::LocalCut(var_num) => { let code = branch_code_stack.code(code); let r = self.marker.get_binding(var_num); - // let r = self.marker.mark_cut_var(var_num, chunk_num); code.push_back(instr!("cut", r)); if self.marker.in_tail_position { @@ -905,6 +903,8 @@ impl<'b> CodeGenerator<'b> { } code.push_back(instr!("proceed")); + } else { + self.marker.free_cut_var(chunk_num, var_num); } } &QueryTerm::Clause( diff --git a/src/debray_allocator.rs b/src/debray_allocator.rs index 6ad47cfc..6bfbfb5e 100644 --- a/src/debray_allocator.rs +++ b/src/debray_allocator.rs @@ -382,7 +382,7 @@ impl DebrayAllocator { p } - pub fn add_to_free_list(&mut self, r: RegType) { + pub(crate) fn add_reg_to_free_list(&mut self, r: RegType) { if let RegType::Temp(r) = r { self.in_use.remove(r); self.temp_free_list.push(r); @@ -406,22 +406,43 @@ impl DebrayAllocator { self.var_data.records[var_num].running_count += 1; } + fn add_perm_to_free_list(&mut self, chunk_num: usize, var_num: usize) { + match &self.var_data.records[var_num].allocation { + VarAlloc::Perm(..) => { + self.perm_free_list.push_back((chunk_num, var_num)); + } + _ => {} + } + } + fn pop_free_perm(&mut self, chunk_num: usize) -> Option { - if let Some((perm_chunk_num, var_num)) = self.perm_free_list.front().cloned() { - if chunk_num == perm_chunk_num { - None - } else { + while let Some((perm_chunk_num, var_num)) = self.perm_free_list.front().cloned() { + if chunk_num > perm_chunk_num { self.perm_free_list.pop_front(); match &mut self.var_data.records[var_num].allocation { - &mut VarAlloc::Perm(p, _) => { - Some(p) + VarAlloc::Perm(p, PermVarAllocation::Pending) if *p > 0 => { + return Some(std::mem::replace(p, 0)); + } + _ => { } - _ => unreachable!() } + } else { + return None; + } + } + + None + } + + pub(crate) fn free_cut_var(&mut self, chunk_num: usize, var_num: usize) { + match &mut self.var_data.records[var_num].allocation { + VarAlloc::Perm(_, allocation) => { + *allocation = PermVarAllocation::Pending; + self.add_perm_to_free_list(chunk_num, var_num); + } + _ => { } - } else { - None } } @@ -704,7 +725,7 @@ impl Allocator for DebrayAllocator { if record.running_count < record.num_occurrences { record.running_count += 1; } else if r.is_perm() { - self.perm_free_list.push_back((term_loc.chunk_num(), var_num)); + self.add_perm_to_free_list(term_loc.chunk_num(), var_num); } self.in_use.insert(o); From f4469397707ac75f29e17a05c1d309990fbc7f15 Mon Sep 17 00:00:00 2001 From: infogulch Date: Thu, 22 Jun 2023 22:14:44 -0500 Subject: [PATCH 224/361] Add steps to publish binaries when releases are tagged --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73296baf..87a3331b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,8 @@ on: pull_request: schedule: - cron: '0 0 * * 3' # At 12:00 AM, only on Wednesday + label: + types: [created, edited] jobs: build-test: @@ -53,12 +55,12 @@ jobs: if: "!matrix.extra" run: cargo test --all --verbose - # Extra steps + # Extra steps only run once to avoid duplication, when matrix.extra is true - name: Test and report if: matrix.extra run: | cargo install cargo2junit --force - cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml + RUSTC_BOOTSTRAP=1 cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml - name: Publish cargo test results artifact if: matrix.extra uses: actions/upload-artifact@v3 @@ -99,6 +101,7 @@ jobs: runs-on: ubuntu-20.04 needs: [build-test] steps: + # Download prebuilt ubuntu binary from build-test job, setup logtalk - uses: actions/download-artifact@v3 with: name: scryer-prolog_ubuntu-20.04 @@ -139,3 +142,23 @@ jobs: files: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml' fail_on: nothing comment_mode: off + + # Publish binaries when building for a tag + release: + runs-on: ubuntu-20.04 + needs: [build-test] + if: startsWith(github.ref, 'refs/tags/v') + steps: + - uses: actions/download-artifact@v3 + - name: Zip binaries for release + run: | + zip scryer-prolog_macos-11.zip ./scryer-prolog_macos-11/scryer-prolog + zip scryer-prolog_ubuntu-20.04.zip ./scryer-prolog_ubuntu-20.04/scryer-prolog + zip scryer-prolog_windows-latest.zip ./scryer-prolog_windows-latest/scryer-prolog.exe + - name: Release + uses: softprops/action-gh-release@v1 + with: + files: | + scryer-prolog_macos-11.zip + scryer-prolog_ubuntu-20.04.zip + scryer-prolog_windows-latest.zip From fcae0d9fcfb77e564f0f5e122b261b20aa1bbaf7 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 23 Jun 2023 12:53:21 -0600 Subject: [PATCH 225/361] polish perm free list management --- src/codegen.rs | 2 +- src/debray_allocator.rs | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 87345494..33ac87d9 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -904,7 +904,7 @@ impl<'b> CodeGenerator<'b> { code.push_back(instr!("proceed")); } else { - self.marker.free_cut_var(chunk_num, var_num); + self.marker.free_var(chunk_num, var_num); } } &QueryTerm::Clause( diff --git a/src/debray_allocator.rs b/src/debray_allocator.rs index 6bfbfb5e..c1f32c49 100644 --- a/src/debray_allocator.rs +++ b/src/debray_allocator.rs @@ -151,8 +151,11 @@ impl DebrayAllocator { }; for var_num in subsumed_hits.iter().cloned() { + let running_count = self.var_data.records[var_num].running_count; + let num_occurrences = self.var_data.records[var_num].num_occurrences; + match &mut self.var_data.records[var_num].allocation { - VarAlloc::Perm(_, ref mut allocation) => { + VarAlloc::Perm(_, allocation) => { let shallow_safety = VarSafetyStatus::needed_if( shallow_safety.contains(var_num), branch_designator, @@ -163,7 +166,9 @@ impl DebrayAllocator { branch_designator, ); - *allocation = PermVarAllocation::Done { shallow_safety, deep_safety }; + if running_count < num_occurrences { + *allocation = PermVarAllocation::Done { shallow_safety, deep_safety }; + } } _ => unreachable!() } @@ -435,7 +440,7 @@ impl DebrayAllocator { None } - pub(crate) fn free_cut_var(&mut self, chunk_num: usize, var_num: usize) { + pub(crate) fn free_var(&mut self, chunk_num: usize, var_num: usize) { match &mut self.var_data.records[var_num].allocation { VarAlloc::Perm(_, allocation) => { *allocation = PermVarAllocation::Pending; @@ -724,8 +729,8 @@ impl Allocator for DebrayAllocator { if record.running_count < record.num_occurrences { record.running_count += 1; - } else if r.is_perm() { - self.add_perm_to_free_list(term_loc.chunk_num(), var_num); + } else { + self.free_var(term_loc.chunk_num(), var_num); } self.in_use.insert(o); From 612861e010b53d8ede949a02bec261152e5e49b8 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 23 Jun 2023 14:13:40 -0600 Subject: [PATCH 226/361] correct reversions after rebase --- src/machine/dispatch.rs | 44 +------ src/machine/loader.rs | 2 +- src/variable_records.rs | 248 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 251 insertions(+), 43 deletions(-) create mode 100644 src/variable_records.rs diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 9c83272d..686ec71b 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4987,51 +4987,11 @@ impl Machine { step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallStripModule => { - let (module_loc, qualified_goal) = self.machine_st.strip_module( - self.machine_st.registers[1], - self.machine_st.registers[2], - ); - - let target_module_loc = self.machine_st.registers[2]; - - unify_fn!( - &mut self.machine_st, - module_loc, - target_module_loc - ); - - let target_qualified_goal = self.machine_st.registers[3]; - - unify_fn!( - &mut self.machine_st, - qualified_goal, - target_qualified_goal - ); - + self.strip_module(); step_or_fail!(self, self.machine_st.p += 1); } &Instruction::ExecuteStripModule => { - let (module_loc, qualified_goal) = self.machine_st.strip_module( - self.machine_st.registers[1], - self.machine_st.registers[2], - ); - - let target_module_loc = self.machine_st.registers[2]; - - unify_fn!( - &mut self.machine_st, - module_loc, - target_module_loc - ); - - let target_qualified_goal = self.machine_st.registers[3]; - - unify_fn!( - &mut self.machine_st, - qualified_goal, - target_qualified_goal - ); - + self.strip_module(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallPrepareCallClause(arity) => { diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 3c838b8b..3e9ae50e 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -1435,7 +1435,7 @@ impl MachineState { term_stack.push(Term::Literal(Cell::default(), Literal::try_from(addr).unwrap())); } (HeapCellValueTag::Atom, (name, arity)) => { - let h = iter.focus(); + let h = iter.focus().value() as usize; let mut arity = arity; if iter.heap.len() > h + arity + 1 { diff --git a/src/variable_records.rs b/src/variable_records.rs new file mode 100644 index 00000000..f301d909 --- /dev/null +++ b/src/variable_records.rs @@ -0,0 +1,248 @@ +use crate::parser::ast::*; + +use bit_set::*; +use fxhash::FxBuildHasher; +use indexmap::{IndexMap, IndexSet}; +use std::ops::{Deref, DerefMut}; + +#[derive(Debug, Clone)] +pub struct TempVarData { + pub(crate) use_set: IndexSet<(GenContext, usize), FxBuildHasher>, + pub(crate) no_use_set: BitSet, + pub(crate) conflict_set: BitSet, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct BranchDesignator(pub (usize, usize)); + +impl BranchDesignator { + #[inline] + pub fn is_subbranch(&self) -> bool { + (self.0).0 > 0 + } + + #[inline] + pub fn subsumes(&self, branch_designator: &Self) -> bool { + (self.0).0 < (branch_designator.0).0 || self == branch_designator + } +} + +#[derive(Debug, Clone, Copy)] +pub enum VarSafetyStatus { + Needed, + // which branch planted the last unsafe guarded instruction? It may still be needed. + LocallyUnneeded(BranchDesignator), + GloballyUnneeded, +} + +impl VarSafetyStatus { + pub(crate) fn unneeded(current_branch: BranchDesignator) -> Self { + if current_branch.is_subbranch() { + VarSafetyStatus::LocallyUnneeded(current_branch) + } else { + VarSafetyStatus::GloballyUnneeded + } + } + + #[inline] + pub(crate) fn is_unneeded(&self, current_branch: BranchDesignator) -> bool { + match self { + &VarSafetyStatus::Needed => false, + &VarSafetyStatus::LocallyUnneeded(planter_branch) => planter_branch.subsumes(¤t_branch), + &VarSafetyStatus::GloballyUnneeded => true, + } + } + + #[inline] + pub(crate) fn needed_if(needed: bool, branch_designator: BranchDesignator) -> Self { + if needed { + VarSafetyStatus::Needed + } else if (branch_designator.0).0 == 0 { + VarSafetyStatus::GloballyUnneeded + } else { + VarSafetyStatus::LocallyUnneeded(branch_designator) + } + } +} + +#[derive(Debug, Clone, Copy)] +pub enum PermVarAllocation { + Done { shallow_safety: VarSafetyStatus, + deep_safety: VarSafetyStatus }, + Pending, +} + +impl PermVarAllocation { + #[inline] + pub(crate) fn done() -> Self { + PermVarAllocation::Done { + shallow_safety: VarSafetyStatus::Needed, + deep_safety: VarSafetyStatus::Needed, + } + } + + #[inline] + pub(crate) fn pending(&self) -> bool { + match self { + &PermVarAllocation::Pending => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub enum VarAlloc { + Temp { term_loc: GenContext, + temp_reg: usize, + temp_var_data: TempVarData, + safety: VarSafetyStatus, + to_perm_var_num: Option }, + Perm(usize, PermVarAllocation), // stack offset, allocation info +} + +impl VarAlloc { + #[inline] + pub(crate) fn as_reg_type(&self) -> RegType { + match self { + &VarAlloc::Temp { temp_reg, .. } => RegType::Temp(temp_reg), + &VarAlloc::Perm(r, _) => RegType::Perm(r), + } + } + + #[inline] + pub(crate) fn set_register(&mut self, reg_num: usize) { + match self { + VarAlloc::Perm(ref mut p, _) => *p = reg_num, + VarAlloc::Temp { ref mut temp_reg, .. } => *temp_reg = reg_num, + }; + } +} + +impl TempVarData { + pub(crate) fn new() -> Self { + TempVarData { + use_set: IndexSet::with_hasher(FxBuildHasher::default()), + no_use_set: BitSet::default(), + conflict_set: BitSet::default(), + } + } + + pub(crate) fn uses_reg(&self, reg: usize) -> bool { + for &(_, nreg) in self.use_set.iter() { + if reg == nreg { + return true; + } + } + + return false; + } + + pub(crate) fn populate_conflict_set(&mut self) { + let arity = self.use_set.len(); + let mut conflict_set: BitSet = (1..arity).collect(); + + for &(_, idx) in &self.use_set { + conflict_set.remove(idx); + } + + self.conflict_set = conflict_set; + } +} + +#[derive(Debug, Clone)] +pub struct VariableRecord { + pub allocation: VarAlloc, + pub num_occurrences: usize, + pub running_count: usize, +} + +impl Default for VariableRecord { + fn default() -> Self { + VariableRecord { + allocation: VarAlloc::Perm(0, PermVarAllocation::Pending), + num_occurrences: 0, + running_count: 0, + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct VariableRecords(Vec); + +impl Deref for VariableRecords { + type Target = Vec; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for VariableRecords { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl VariableRecords { + #[inline] + pub(crate) fn new(num_records: usize) -> Self { + Self(vec![VariableRecord::default(); num_records]) + } + + // computes no_use and conflict sets for all temp vars. + pub(crate) fn populate_restricting_sets(&mut self) { + // three stages: + // 1. move the use sets of each variable to a local IndexMap, use_set + // (iterate mutably, swap mutable refs). + // 2. drain use_set. For each use set of U, add into the + // no-use sets of appropriate variables T =/= U. + // 3. Move the use sets back to their original locations in the fixture. + // Compute the conflict set of u. + + // 1. + let mut use_sets: IndexMap> = IndexMap::new(); + + for (var_gen_index, record) in self.0.iter_mut().enumerate() { + match &mut record.allocation { + VarAlloc::Temp { temp_var_data, .. } => { + let use_set = std::mem::replace( + &mut temp_var_data.use_set, + IndexSet::with_hasher(FxBuildHasher::default()), + ); + + use_sets.insert(var_gen_index, use_set); + } + _ => { + } + } + } + + for (u, use_set) in use_sets.drain(..) { + // 2. + for &(term_loc, reg) in &use_set { + if let GenContext::Last(cn_u) = term_loc { + for (var_gen_index, record) in self.0.iter_mut().enumerate() { + match &mut record.allocation { + VarAlloc::Temp { term_loc, temp_var_data, .. } => { + if cn_u == term_loc.chunk_num() && u != var_gen_index { + if !temp_var_data.uses_reg(reg) { + temp_var_data.no_use_set.insert(reg); + } + } + } + _ => {} + } + } + } + } + + // 3. + if let VarAlloc::Temp{ temp_var_data, .. } = &mut self[u].allocation { + temp_var_data.use_set = use_set; + temp_var_data.populate_conflict_set(); + } + } + } +} From 7a188744da4bf34f68d4a5aa6afa824d3d172fb6 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 23 Jun 2023 16:49:27 -0600 Subject: [PATCH 227/361] correct code_walker.rs in light of compilation improvements --- src/machine/code_walker.rs | 45 +++++++------------------------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/src/machine/code_walker.rs b/src/machine/code_walker.rs index 1244eb20..c2032727 100644 --- a/src/machine/code_walker.rs +++ b/src/machine/code_walker.rs @@ -1,5 +1,6 @@ use crate::instructions::*; +use fxhash::FxBuildHasher; use indexmap::IndexSet; fn capture_offset(line: &Instruction, index: usize, stack: &mut Vec) -> bool { @@ -7,34 +8,24 @@ fn capture_offset(line: &Instruction, index: usize, stack: &mut Vec) -> 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::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; @@ -51,7 +42,7 @@ fn capture_offset(line: &Instruction, index: usize, stack: &mut Vec) -> 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) { @@ -69,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); - } - } -} -*/ From 18d0a74f2343104d0272da3ffd8bca78b918d69f Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 24 Jun 2023 11:47:32 +0200 Subject: [PATCH 228/361] MODIFIED: read_line_to_chars/3 is now called get_line_to_chars/3 This is for consistency with other I/O predicates, where "read" always indicates Prolog terms. Please adjust your programs accordingly. --- src/lib/charsio.pl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/charsio.pl b/src/lib/charsio.pl index 0f19a8db..0b69ca26 100644 --- a/src/lib/charsio.pl +++ b/src/lib/charsio.pl @@ -10,7 +10,7 @@ read and write chars. chars_utf8bytes/2, get_single_char/1, get_n_chars/3, - read_line_to_chars/3, + get_line_to_chars/3, read_from_chars/2, write_term_to_chars/3, chars_base64/3]). @@ -277,17 +277,17 @@ continuation(Code, Chars, Nb) --> [Byte], % each remaining continuation byte (if any) will raise 0xFFFD too continuation(_, ['\xFFFD\'|T], _) --> [_], decode_utf8(T). -%% read_line_to_chars(+Stream, -Chars, +InitialChars). +%% get_line_to_chars(+Stream, -Chars, +InitialChars). % % Reads chars from stream Stream until it finds a `\n` character. % InitialChars will be appended at the end of Chars -read_line_to_chars(Stream, Cs0, Cs) :- +get_line_to_chars(Stream, Cs0, Cs) :- '$get_n_chars'(Stream, 1, Char), % this also works for binary streams ( Char == [] -> Cs0 = Cs ; Char = [C], Cs0 = [C|Rest], ( C == '\n' -> Rest = Cs - ; read_line_to_chars(Stream, Rest, Cs) + ; get_line_to_chars(Stream, Rest, Cs) ) ). From 9bc3757a9e48e9fca4ae61627db0a92864eb43b1 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 24 Jun 2023 11:48:28 +0200 Subject: [PATCH 229/361] another case of "read" --> "get", for an only internally used predicate --- src/lib/charsio.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/charsio.pl b/src/lib/charsio.pl index 0b69ca26..1fc120f0 100644 --- a/src/lib/charsio.pl +++ b/src/lib/charsio.pl @@ -299,17 +299,17 @@ get_line_to_chars(Stream, Cs0, Cs) :- get_n_chars(Stream, N, Cs) :- can_be(integer, N), ( var(N) -> - read_to_eof(Stream, Cs), + get_to_eof(Stream, Cs), length(Cs, N) ; N >= 0, '$get_n_chars'(Stream, N, Cs) ). -read_to_eof(Stream, Cs) :- +get_to_eof(Stream, Cs) :- '$get_n_chars'(Stream, 512, Cs0), ( Cs0 == [] -> Cs = [] ; partial_string(Cs0, Cs, Rest), - read_to_eof(Stream, Rest) + get_to_eof(Stream, Rest) ). %% chars_base64(?Chars, ?Base64, +Options). From bf581879e61904861d9a54f39a239bd02b5e7e04 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 24 Jun 2023 09:11:30 -0600 Subject: [PATCH 230/361] update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 93d7a43c..caff6a91 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Extend Scryer Prolog to include the following, among other features: - [ ] Replacing choice points pivoting on inlined semi-deterministic predicates (`atom`, `var`, etc) with if/else ladders. (_in progress_) - [ ] Inlining all built-ins and system call instructions. - - [ ] Greatly reducing the number of instructions used to compile disjunctives. + - [x] Greatly reducing the number of instructions used to compile disjunctives. - [ ] Storing short atoms to heap cells without writing them to the atom table. - [ ] A compacting garbage collector satisfying the five properties of "[Precise Garbage Collection in Prolog](https://www.complang.tuwien.ac.at/ulrich/papers/PDF/2008-ciclops.pdf)." (_in progress_) From 9f209dadd9689f562e5b55a01c0ac2c6156e01c3 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 24 Jun 2023 10:59:29 -0600 Subject: [PATCH 231/361] fix branch subsumption bug (#1840, #1841) --- src/codegen.rs | 7 +- src/debray_allocator.rs | 154 +++++++++++++++++++++++++++------------- src/variable_records.rs | 27 +++---- 3 files changed, 116 insertions(+), 72 deletions(-) diff --git a/src/codegen.rs b/src/codegen.rs index 33ac87d9..68374c58 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -937,13 +937,14 @@ impl<'b> CodeGenerator<'b> { branch_code_stack.add_new_branch_stack(); branch_code_stack.add_new_branch(); - self.marker.add_branch_stack(num_branches); + self.marker.branch_stack.add_branch_stack(num_branches); self.marker.add_branch(); } ClauseItem::NextBranch => { branch_code_stack.add_new_branch(); + self.marker.add_branch(); - self.marker.incr_current_branch(); + self.marker.branch_stack.incr_current_branch(); } ClauseItem::BranchEnd(depth) => { if !clause_iter.in_tail_position() { @@ -951,7 +952,7 @@ impl<'b> CodeGenerator<'b> { self.marker.pop_branch(depth, subsumed_hits); branch_code_stack.push_jump_instrs(depth); } else { - self.marker.drain_branches(depth); + self.marker.branch_stack.drain_branches(depth); } let settings = CodeGenSettings { diff --git a/src/debray_allocator.rs b/src/debray_allocator.rs index c1f32c49..0337ed4d 100644 --- a/src/debray_allocator.rs +++ b/src/debray_allocator.rs @@ -14,6 +14,7 @@ use indexmap::IndexMap; use std::cell::Cell; use std::collections::VecDeque; +use std::ops::{Deref, DerefMut}; pub type BranchHits = IndexMap; // key: var_num, value: branch arm occurrences. @@ -41,24 +42,51 @@ impl BranchOccurrences { } #[derive(Debug)] -pub(crate) struct DebrayAllocator { - pub(crate) var_data: VarData, // var_data replaces bindings. - pub(crate) branch_stack: Vec, - pub(crate) in_tail_position: bool, - // bindings: IndexMap, // VarNum -> VarWitness - arg_c: usize, - temp_lb: usize, - perm_lb: usize, - arity: usize, // 0 if not at head. - shallow_temp_mappings: IndexMap, - in_use: BitSet, // deep and non-var allocations - temp_free_list: Vec, - perm_free_list: VecDeque<(usize, usize)>, // chunk_num, var_num +pub(crate) struct BranchStack { + stack: Vec, } -impl DebrayAllocator { +impl Deref for BranchStack { + type Target = Vec; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.stack + } +} + +impl DerefMut for BranchStack { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.stack + } +} + +impl BranchStack { + fn branch_subsumes(&self, branch: &BranchDesignator, sub_branch: &BranchDesignator) -> bool { + if branch.branch_stack_num < sub_branch.branch_stack_num { + if branch.branch_stack_num == 0 { + true + } else { + let idx = branch.branch_stack_num - 1; + self[idx].current_branch == branch.branch_num + } + } else { + branch == sub_branch + } + } + + fn safety_unneeded_in_branch(&self, safety: &VarSafetyStatus, branch: &BranchDesignator) -> bool { + match safety { + VarSafetyStatus::Needed => false, + VarSafetyStatus::LocallyUnneeded(planter_branch) => + self.branch_subsumes(planter_branch, branch), + VarSafetyStatus::GloballyUnneeded => true, + } + } + pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) { - if let Some(occurrences) = self.branch_stack.last_mut() { + if let Some(occurrences) = self.last_mut() { debug_assert!(occurrences.current_branch < occurrences.num_branches); let num_branches = occurrences.num_branches; @@ -72,32 +100,70 @@ impl DebrayAllocator { } pub(crate) fn add_branch_stack(&mut self, num_branches: usize) { - self.branch_stack.push(BranchOccurrences::new(num_branches)); + self.push(BranchOccurrences::new(num_branches)); } pub(crate) fn current_branch_designator(&self) -> BranchDesignator { - let num_branches = self.branch_stack.len(); - let current_branch = self.branch_stack.last() + let branch_stack_num = self.len(); + let branch_num = self.last() .map(|occurrences| occurrences.current_branch) .unwrap_or(0); - BranchDesignator((num_branches, current_branch)) + BranchDesignator { branch_stack_num, branch_num } } - pub(crate) fn add_branch(&mut self) { - let branch_designator = self.current_branch_designator(); - let branch_occurrences = self.branch_stack.last_mut().unwrap(); + #[inline] + pub(crate) fn incr_current_branch(&mut self) { + let branch_occurrences = self.last_mut().unwrap(); + branch_occurrences.current_branch += 1; + } - for var_num in branch_occurrences.subsumed_hits.drain(..) { + #[inline] + pub(crate) fn drain_branches(&mut self, depth: usize) -> std::vec::Drain { + let start_idx = self.len() - depth; + self.drain(start_idx ..) + } +} + +#[derive(Debug)] +pub(crate) struct DebrayAllocator { + pub(crate) var_data: VarData, // var_data replaces bindings. + pub(crate) branch_stack: BranchStack, + pub(crate) in_tail_position: bool, + arg_c: usize, + temp_lb: usize, + perm_lb: usize, + arity: usize, // 0 if not at head. + shallow_temp_mappings: IndexMap, + in_use: BitSet, // deep and non-var allocations + temp_free_list: Vec, + perm_free_list: VecDeque<(usize, usize)>, // chunk_num, var_num +} + +impl DebrayAllocator { + pub(crate) fn add_branch(&mut self) { + let branch_designator = self.branch_stack.current_branch_designator(); + let subsumed_hits = { + let branch_occurrences = self.branch_stack.last_mut().unwrap(); + + std::mem::replace( + &mut branch_occurrences.subsumed_hits, + SubsumedBranchHits::with_hasher(FxBuildHasher::default()), + ) + }; + + for var_num in subsumed_hits { match &mut self.var_data.records[var_num].allocation { VarAlloc::Perm(_, ref mut allocation) => { match allocation { PermVarAllocation::Done { shallow_safety, deep_safety, .. } => { - if !shallow_safety.is_unneeded(branch_designator) { + if !self.branch_stack.safety_unneeded_in_branch(shallow_safety, &branch_designator) { + let branch_occurrences = self.branch_stack.last_mut().unwrap(); branch_occurrences.shallow_safety.insert(var_num); } - if !deep_safety.is_unneeded(branch_designator) { + if !self.branch_stack.safety_unneeded_in_branch(deep_safety, &branch_designator) { + let branch_occurrences = self.branch_stack.last_mut().unwrap(); branch_occurrences.deep_safety.insert(var_num); } } @@ -113,20 +179,8 @@ impl DebrayAllocator { } } - #[inline] - pub(crate) fn incr_current_branch(&mut self) { - let branch_occurrences = self.branch_stack.last_mut().unwrap(); - branch_occurrences.current_branch += 1; - } - - #[inline] - pub(crate) fn drain_branches(&mut self, depth: usize) -> std::vec::Drain { - let start_idx = self.branch_stack.len() - depth; - self.branch_stack.drain(start_idx ..) - } - pub(crate) fn pop_branch(&mut self, depth: usize, subsumed_hits: SubsumedBranchHits) { - let removed_branches = self.drain_branches(depth); + let removed_branches = self.branch_stack.drain_branches(depth); let (deep_safety, shallow_safety) = removed_branches .into_iter() @@ -138,7 +192,7 @@ impl DebrayAllocator { (deep_safety, shallow_safety) }); - let branch_designator = self.current_branch_designator(); + let branch_designator = self.branch_stack.current_branch_designator(); let (deep_safety, shallow_safety) = match self.branch_stack.last_mut() { Some(latest_branch) => { @@ -176,7 +230,7 @@ impl DebrayAllocator { if self.branch_stack.len() > 0 { for var_num in subsumed_hits { - self.add_branch_occurrence(var_num); + self.branch_stack.add_branch_occurrence(var_num); } } } @@ -452,7 +506,7 @@ impl DebrayAllocator { } pub(crate) fn mark_safe_var_unconditionally(&mut self, var_num: usize) { - let branch_designator = self.current_branch_designator(); + let branch_designator = self.branch_stack.current_branch_designator(); match &mut self.var_data.records[var_num].allocation { VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, shallow_safety, .. }) => { @@ -467,7 +521,7 @@ impl DebrayAllocator { } fn mark_safe_var(&mut self, var_num: usize, lvl: Level, term_loc: GenContext) { - let branch_designator = self.current_branch_designator(); + let branch_designator = self.branch_stack.current_branch_designator(); match &mut self.var_data.records[var_num].allocation { VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, shallow_safety, .. }) => { @@ -503,11 +557,11 @@ impl DebrayAllocator { r: RegType, arg_c: usize, ) -> Instruction { - let branch_designator = self.current_branch_designator(); + let branch_designator = self.branch_stack.current_branch_designator(); match &mut self.var_data.records[var_num].allocation { VarAlloc::Perm(_, PermVarAllocation::Done { ref mut shallow_safety, .. }) => { - if !self.in_tail_position || shallow_safety.is_unneeded(branch_designator) { + if !self.in_tail_position || self.branch_stack.safety_unneeded_in_branch(shallow_safety, &branch_designator) { Target::argument_to_value(r, arg_c) } else { *shallow_safety = VarSafetyStatus::unneeded(branch_designator); @@ -515,7 +569,7 @@ impl DebrayAllocator { } } VarAlloc::Temp { ref mut safety, .. } => { - if safety.is_unneeded(branch_designator) { + if self.branch_stack.safety_unneeded_in_branch(safety, &branch_designator) { Target::argument_to_value(r, arg_c) } else { *safety = VarSafetyStatus::GloballyUnneeded; @@ -533,11 +587,11 @@ impl DebrayAllocator { var_num: usize, r: RegType, ) -> Instruction { - let branch_designator = self.current_branch_designator(); + let branch_designator = self.branch_stack.current_branch_designator(); match &mut self.var_data.records[var_num].allocation { VarAlloc::Perm(_, PermVarAllocation::Done { ref mut deep_safety, .. }) => { - if deep_safety.is_unneeded(branch_designator) { + if self.branch_stack.safety_unneeded_in_branch(deep_safety, &branch_designator) { Target::subterm_to_value(r) } else { *deep_safety = VarSafetyStatus::unneeded(branch_designator); @@ -545,7 +599,7 @@ impl DebrayAllocator { } } VarAlloc::Temp { ref mut safety, .. } => { - if safety.is_unneeded(branch_designator) { + if self.branch_stack.safety_unneeded_in_branch(safety, &branch_designator) { Target::subterm_to_value(r) } else { *safety = VarSafetyStatus::unneeded(branch_designator); @@ -572,7 +626,7 @@ impl Allocator for DebrayAllocator { in_use: BitSet::default(), temp_free_list: vec![], perm_free_list: VecDeque::new(), - branch_stack: vec![], + branch_stack: BranchStack { stack: vec![] } } } @@ -720,7 +774,7 @@ impl Allocator for DebrayAllocator { if !r.is_perm() { self.shallow_temp_mappings.insert(o, var_num); } else if r.is_perm() && is_new_var { - self.add_branch_occurrence(var_num); + self.branch_stack.add_branch_occurrence(var_num); } let record = &mut self.var_data.records[var_num]; diff --git a/src/variable_records.rs b/src/variable_records.rs index f301d909..2d19ec08 100644 --- a/src/variable_records.rs +++ b/src/variable_records.rs @@ -13,17 +13,15 @@ pub struct TempVarData { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct BranchDesignator(pub (usize, usize)); +pub struct BranchDesignator { + pub branch_stack_num: usize, + pub branch_num: usize, +} impl BranchDesignator { #[inline] - pub fn is_subbranch(&self) -> bool { - (self.0).0 > 0 - } - - #[inline] - pub fn subsumes(&self, branch_designator: &Self) -> bool { - (self.0).0 < (branch_designator.0).0 || self == branch_designator + pub fn is_sub_branch(&self) -> bool { + self.branch_stack_num > 0 } } @@ -37,27 +35,18 @@ pub enum VarSafetyStatus { impl VarSafetyStatus { pub(crate) fn unneeded(current_branch: BranchDesignator) -> Self { - if current_branch.is_subbranch() { + if current_branch.is_sub_branch() { VarSafetyStatus::LocallyUnneeded(current_branch) } else { VarSafetyStatus::GloballyUnneeded } } - #[inline] - pub(crate) fn is_unneeded(&self, current_branch: BranchDesignator) -> bool { - match self { - &VarSafetyStatus::Needed => false, - &VarSafetyStatus::LocallyUnneeded(planter_branch) => planter_branch.subsumes(¤t_branch), - &VarSafetyStatus::GloballyUnneeded => true, - } - } - #[inline] pub(crate) fn needed_if(needed: bool, branch_designator: BranchDesignator) -> Self { if needed { VarSafetyStatus::Needed - } else if (branch_designator.0).0 == 0 { + } else if branch_designator.branch_stack_num == 0 { VarSafetyStatus::GloballyUnneeded } else { VarSafetyStatus::LocallyUnneeded(branch_designator) From c05afb470523a76cbfce62b575074eb705ca4cdf Mon Sep 17 00:00:00 2001 From: infogulch Date: Sat, 24 Jun 2023 13:05:57 -0500 Subject: [PATCH 232/361] Fix tags trigger --- .github/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87a3331b..f79952bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,14 @@ name: CI + on: push: branches: [master] + tags: + - "v**" pull_request: schedule: - cron: '0 0 * * 3' # At 12:00 AM, only on Wednesday - label: - types: [created, edited] + workflow_dispatch: jobs: build-test: From e52a4fbfc008ab0f6e4cf9e999ec1d0e827b7925 Mon Sep 17 00:00:00 2001 From: infogulch Date: Sat, 24 Jun 2023 13:21:42 -0500 Subject: [PATCH 233/361] Bump msrv to 1.65 due to bumping rug to 1.19 https://gitlab.com/tspiteri/rug#version-1190-news-2023-01-06 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f79952bd..24fb4b81 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: - { os: windows-latest, rust-version: stable, shell: 'msys2 {0}' } - { os: macos-11, rust-version: stable, shell: bash } - { os: ubuntu-20.04, rust-version: stable, shell: bash, extra: true } - - { os: ubuntu-20.04, rust-version: 1.63, shell: bash } + - { os: ubuntu-20.04, rust-version: 1.65, shell: bash } - { os: ubuntu-20.04, rust-version: beta, shell: bash } - { os: ubuntu-20.04, rust-version: nightly, shell: bash } defaults: From d9829a3606a422b04d8c76d1a148ec97fad6778a Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 24 Jun 2023 14:12:04 -0600 Subject: [PATCH 234/361] fix string incompleteness (#1828) --- src/machine/partial_string.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/partial_string.rs b/src/machine/partial_string.rs index fce47204..812941fc 100644 --- a/src/machine/partial_string.rs +++ b/src/machine/partial_string.rs @@ -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]; } _ => { From 0b45d4291240592d942ca8514c5606ecd3731131 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 24 Jun 2023 17:03:46 -0600 Subject: [PATCH 235/361] mark chunk boundary at beginning of disjunct in disjuncts.rs (#1843) --- src/machine/disjuncts.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index 3ef2bbf8..f2a66851 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -598,6 +598,9 @@ impl VariableClassifier { 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(); @@ -632,6 +635,9 @@ impl VariableClassifier { 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 => { From a6522d6317f783ff3ac88ebb623f102d99815f57 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 26 Jun 2023 16:32:30 -0600 Subject: [PATCH 236/361] properly account for partial string offsets in '$skip_max_list' (#1827) --- src/machine/machine_errors.rs | 2 +- src/machine/system_calls.rs | 175 ++++++++++++++++++++++------------ 2 files changed, 116 insertions(+), 61 deletions(-) diff --git a/src/machine/machine_errors.rs b/src/machine/machine_errors.rs index 194d3ccc..dc9f8f13 100644 --- a/src/machine/machine_errors.rs +++ b/src/machine/machine_errors.rs @@ -799,7 +799,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), } diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 94f166f2..df89c520 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -120,6 +120,7 @@ pub struct BrentAlgState { pub power: usize, pub lam: usize, pub pstr_chars: usize, + max_steps: i64, } impl BrentAlgState { @@ -130,6 +131,7 @@ impl BrentAlgState { power: 1, lam: 0, pstr_chars: 0, + max_steps: -1, } } @@ -161,72 +163,95 @@ impl BrentAlgState { return self.lam + self.pstr_chars + self.power - 1; } + #[inline(always)] + pub fn exhausted_max_steps(&self) -> bool { + self.max_steps > -1 && self.num_steps() as i64 >= self.max_steps + } + pub fn to_result(mut self, heap: &[HeapCellValue]) -> CycleSearchResult { + /* if let Some(var) = heap[self.hare].as_var() { return CycleSearchResult::PartialList(self.num_steps(), var); } + */ - read_heap_cell!(heap[self.hare], - (HeapCellValueTag::PStrOffset) => { - let n = cell_as_fixnum!(heap[self.hare+1]).get_num() as usize; + loop { + read_heap_cell!(heap[self.hare], + (HeapCellValueTag::PStrOffset) => { + let (pstr_loc, offset) = pstr_loc_and_offset(heap, self.hare); + let offset = offset.get_num() as usize; - let pstr = cell_as_string!(heap[self.hare]); - self.pstr_chars += pstr.as_str_from(n).chars().count(); + let pstr = cell_as_string!(heap[self.hare]); + self.pstr_chars += pstr.as_str_from(offset).chars().count(); - return CycleSearchResult::PStrLocation(self.num_steps(), n); - } - (HeapCellValueTag::Atom, (name, arity)) => { - return if name == atom!("[]") && arity == 0 { - CycleSearchResult::ProperList(self.num_steps()) - } else { - CycleSearchResult::NotList(self.num_steps(), heap[self.hare]) - }; - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(heap[s]) - .get_name_and_arity(); + return CycleSearchResult::PStrLocation(self.num_steps(), pstr_loc, offset); + } + (HeapCellValueTag::PStrLoc, l) => { + let (_pstr_loc, offset) = pstr_loc_and_offset(heap, l); + let offset = offset.get_num() as usize; + return CycleSearchResult::PStrLocation(self.num_steps(), l, offset); + } + (HeapCellValueTag::Atom, (name, arity)) => { + return if name == atom!("[]") && arity == 0 { + CycleSearchResult::ProperList(self.num_steps()) + } else { + CycleSearchResult::NotList(self.num_steps(), heap[self.hare]) + }; + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(heap[s]) + .get_name_and_arity(); - return if name == atom!("[]") && arity == 0 { - CycleSearchResult::ProperList(self.num_steps()) - } else { - CycleSearchResult::NotList(self.num_steps(), heap[self.hare]) - }; - } - (HeapCellValueTag::Lis, l) => { - return CycleSearchResult::UntouchedList(self.num_steps(), l); - } - _ => { - return CycleSearchResult::NotList(self.num_steps(), heap[self.hare]); - } - ); + return if name == atom!("[]") && arity == 0 { + CycleSearchResult::ProperList(self.num_steps()) + } else { + CycleSearchResult::NotList(self.num_steps(), heap[self.hare]) + }; + } + (HeapCellValueTag::Lis, l) => { + return CycleSearchResult::UntouchedList(self.num_steps(), l); + } + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { + if h == self.hare { + let var = heap[self.hare].as_var().unwrap(); + return CycleSearchResult::PartialList(self.num_steps(), var); + } else { + self.hare = h; + } + } + _ => { + return CycleSearchResult::NotList(self.num_steps(), heap[self.hare]); + } + ); + } } - fn add_pstr_chars_and_step(&mut self, heap: &[HeapCellValue], h: usize) -> Option { + fn add_pstr_offset_chars(&mut self, heap: &[HeapCellValue], h: usize, offset: usize) -> Option { read_heap_cell!(heap[h], (HeapCellValueTag::CStr, cstr_atom) => { let cstr = PartialString::from(cstr_atom); + let num_chars = cstr.as_str_from(offset).chars().count(); - self.pstr_chars += cstr.as_str_from(0).chars().count(); - Some(CycleSearchResult::ProperList(self.num_steps())) + if self.max_steps == -1 || self.num_steps() + num_chars < self.max_steps as usize { + self.pstr_chars += num_chars; + Some(CycleSearchResult::ProperList(self.num_steps())) + } else { + let offset = self.num_steps() + num_chars - self.max_steps as usize; + self.pstr_chars += offset; + Some(CycleSearchResult::PStrLocation(self.max_steps as usize, h, offset)) + } } (HeapCellValueTag::PStr, pstr_atom) => { let pstr = PartialString::from(pstr_atom); + let num_chars = pstr.as_str_from(offset).chars().count(); - self.pstr_chars += pstr.as_str_from(0).chars().count() - 1; - self.step(h+1) - } - (HeapCellValueTag::PStrOffset, offset) => { - let pstr = cell_as_string!(heap[offset]); - let n = cell_as_fixnum!(heap[h+1]).get_num() as usize; - - self.pstr_chars += pstr.as_str_from(n).chars().count(); - - if let HeapCellValueTag::PStr = heap[offset].get_tag() { - self.pstr_chars -= 1; - self.step(offset+1) + if self.max_steps == -1 || self.num_steps() + num_chars < self.max_steps as usize { + self.pstr_chars += num_chars - 1; + self.step(h+1) } else { - debug_assert!(heap[offset].get_tag() == HeapCellValueTag::CStr); - Some(CycleSearchResult::ProperList(self.num_steps())) + let offset = self.num_steps() + num_chars - self.max_steps as usize; + self.pstr_chars += offset; + Some(CycleSearchResult::PStrLocation(self.max_steps as usize, h, offset)) } } _ => { @@ -235,6 +260,18 @@ impl BrentAlgState { ) } + fn add_pstr_chars_and_step(&mut self, heap: &[HeapCellValue], h: usize) -> Option { + read_heap_cell!(heap[h], + (HeapCellValueTag::PStrOffset, l) => { + let (pstr_loc, offset) = pstr_loc_and_offset(heap, l); + self.add_pstr_offset_chars(heap, pstr_loc, offset.get_num() as usize) + } + _ => { + self.add_pstr_offset_chars(heap, h, 0) + } + ) + } + #[inline(always)] fn cycle_step(&mut self, heap: &[HeapCellValue]) -> Option { loop { @@ -388,7 +425,7 @@ impl BrentAlgState { } if pstr_chars + 1 > max_steps { - return CycleSearchResult::PStrLocation(max_steps, h_offset); + return CycleSearchResult::PStrLocation(max_steps, h_offset, max_steps); } h_offset+1 @@ -444,9 +481,10 @@ impl BrentAlgState { brent_st.power += 1; // advance a step. brent_st.pstr_chars = pstr_chars; + brent_st.max_steps = max_steps as i64; loop { - if brent_st.num_steps() >= max_steps { + if brent_st.exhausted_max_steps() { return brent_st.to_result(&heap); } @@ -638,8 +676,25 @@ impl MachineState { }; match search_result { - CycleSearchResult::PStrLocation(steps, pstr_loc) => { - self.finalize_skip_max_list(steps as i64, pstr_loc_as_cell!(pstr_loc)); + CycleSearchResult::PStrLocation(steps, pstr_loc, offset) => { + let steps = if max_steps > - 1 { + std::cmp::min(max_steps, steps as i64) + } else { + steps as i64 + }; + + let cell = if offset > 0 { + let h = self.heap.len(); + + self.heap.push(pstr_offset_as_cell!(pstr_loc)); + self.heap.push(fixnum_as_cell!(Fixnum::build_with(offset as i64))); + + pstr_loc_as_cell!(h) + } else { + pstr_loc_as_cell!(pstr_loc) + }; + + self.finalize_skip_max_list(steps, cell); } CycleSearchResult::UntouchedList(n, l) => { self.finalize_skip_max_list(n as i64, list_loc_as_cell!(l)); @@ -2805,7 +2860,7 @@ impl Machine { unreachable!() } ); - + self.machine_st.fail = true; // This predicate fails by default. read_heap_cell!(a2, @@ -2868,12 +2923,12 @@ impl Machine { macro_check!(symbolic_control_char, atom!("symbolic_control")); method_check!(is_uppercase, atom!("upper")); // macro_check!(variable_indicator_char, atom!("variable_indicator")); - method_check!(is_whitespace, atom!("whitespace")); + method_check!(is_whitespace, atom!("whitespace")); } (HeapCellValueTag::Str, s) => { let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]) .get_name_and_arity(); - + match (name, arity) { (atom!("to_upper"), 1) => { let reg = self.machine_st.deref(self.machine_st.heap[s+1]); @@ -2888,7 +2943,7 @@ impl Machine { let lower_str = string_as_cstr_cell!(atom); unify!(self.machine_st, reg, lower_str); self.machine_st.fail = false; - } + } _ => { unreachable!() } @@ -2898,9 +2953,9 @@ impl Machine { unreachable!() } ); - - + + } #[inline(always)] @@ -4238,7 +4293,7 @@ impl Machine { let query_str = request.request.uri().query().unwrap_or(""); let query_atom = self.machine_st.atom_tbl.build_with(query_str); let query_cell = string_as_cstr_cell!(query_atom); - + let hyper_req = request.request; let buf = self.runtime.block_on(async {hyper::body::aggregate(hyper_req).await.unwrap()}); let reader = buf.reader(); @@ -4433,7 +4488,7 @@ impl Machine { } } } - + match self.machine_st.try_from_list(args_reg, stub_gen) { Ok(args) => { let args: Vec<_> = args.into_iter().map(|x| map_arg(&mut self.machine_st, x)).collect(); @@ -4482,7 +4537,7 @@ impl Machine { Value::Struct(name, struct_args) => self.build_struct(&name, struct_args), } }).collect(); - + heap_loc_as_cell!( iter_to_heap_list( &mut self.machine_st.heap, From aa65287c3b1c5c3528bd1dab24f595b76ddb3770 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 27 Jun 2023 10:30:55 -0600 Subject: [PATCH 237/361] remove tabling attributes in each module's attribute_goals//1 (#1825) --- src/lib/tabling/batched_worklist.pl | 8 ++++++++ src/lib/tabling/double_linked_list.pl | 6 ++++++ src/lib/tabling/global_worklist.pl | 3 +++ src/lib/tabling/table_data_structure.pl | 5 +++++ src/lib/tabling/table_link_manager.pl | 4 ++++ src/lib/tabling/trie.pl | 5 +++++ 6 files changed, 31 insertions(+) diff --git a/src/lib/tabling/batched_worklist.pl b/src/lib/tabling/batched_worklist.pl index 8a8b4117..ce63981e 100644 --- a/src/lib/tabling/batched_worklist.pl +++ b/src/lib/tabling/batched_worklist.pl @@ -49,12 +49,20 @@ :- use_module(library(tabling/double_linked_list)). :- use_module(library(atts)). +:- use_module(library(dcgs)). :- use_module(library(lists)). :- attribute executing_all_work/1, worklist_presence/1, wkl_answer_cluster/1, wkl_suspension_cluster/1, wkl_answer_cluster_pointer_flag/1. verify_attributes(_, _, []). +attribute_goals(X) --> + { put_atts(X, -executing_all_work(_)), + put_atts(X, -worklist_presence(_)), + put_atts(X, -wkl_answer_cluster(_)), + put_atts(X, -wkl_suspension_cluster(_)), + put_atts(X, -wkl_answer_cluster_pointer_flag(_)) }. + /** Tabling Worklist management A batched worklist: a worklist that clusters suspensions and answers as diff --git a/src/lib/tabling/double_linked_list.pl b/src/lib/tabling/double_linked_list.pl index d80ee6db..08a71ae7 100644 --- a/src/lib/tabling/double_linked_list.pl +++ b/src/lib/tabling/double_linked_list.pl @@ -49,9 +49,15 @@ ]). :- use_module(library(atts)). +:- use_module(library(dcgs)). :- attribute dll_element/1, dll_next/1, dll_prev/1. +attribute_goals(X) --> + { put_atts(X, -dll_element(_)), + put_atts(X, -dll_next(_)), + put_atts(X, -dll_prev(_)) }. + % A circular double linked list % ============================= diff --git a/src/lib/tabling/global_worklist.pl b/src/lib/tabling/global_worklist.pl index 55c437f1..124638c3 100644 --- a/src/lib/tabling/global_worklist.pl +++ b/src/lib/tabling/global_worklist.pl @@ -9,12 +9,15 @@ ]). :- use_module(library(atts)). +:- use_module(library(dcgs)). :- use_module(library(iso_ext)). :- attribute table_global_worklist/1. verify_attributes(_, _, []). +attribute_goals(X) --> { put_atts(X, -table_global_worklist(_)) }. + put_new_global_worklist :- ( bb_get(table_global_worklist_initialized, _) -> true diff --git a/src/lib/tabling/table_data_structure.pl b/src/lib/tabling/table_data_structure.pl index 69a057e6..2f36c0e7 100644 --- a/src/lib/tabling/table_data_structure.pl +++ b/src/lib/tabling/table_data_structure.pl @@ -56,6 +56,7 @@ :- use_module(library(tabling/batched_worklist)). :- use_module(library(atts)). +:- use_module(library(dcgs)). :- use_module(library(gensym)). :- use_module(library(iso_ext)). @@ -63,6 +64,10 @@ verify_attributes(_, _, []). +attribute_goals(X) --> + { put_atts(X, -table_status(_)), + put_atts(X, -newly_created_table_identifiers(_)) }. + % This file defines the table datastructure. % % The table datastructure contains the following sub-structures: diff --git a/src/lib/tabling/table_link_manager.pl b/src/lib/tabling/table_link_manager.pl index c346ea10..8d2b2c65 100644 --- a/src/lib/tabling/table_link_manager.pl +++ b/src/lib/tabling/table_link_manager.pl @@ -43,6 +43,7 @@ ]). :- use_module(library(atts)). +:- use_module(library(dcgs)). :- use_module(library(lists)). :- use_module(library(iso_ext)). :- use_module(library(terms)). @@ -53,6 +54,9 @@ verify_attributes(_, _, []). +attribute_goals(X) --> + { put_atts(X, -trie_table_link(_)) }. + % This file defines a call pattern trie. % % This data structure keeps the relation between a variant and the diff --git a/src/lib/tabling/trie.pl b/src/lib/tabling/trie.pl index 2460f70f..6a9024fc 100644 --- a/src/lib/tabling/trie.pl +++ b/src/lib/tabling/trie.pl @@ -45,12 +45,17 @@ :- use_module(library(assoc)). :- use_module(library(atts)). +:- use_module(library(dcgs)). :- use_module(library(lists)). :- attribute maybe_just/1, children/1. verify_attributes(_, _, []). +attribute_goals(X) --> + { put_atts(X, -maybe_just(_)), + put_atts(X, -children(_)) }. + % Implementation of a prefix tree, a.k.a. trie % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From ce890799bc438bee87a95edc36701014be9836c8 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 27 Jun 2023 10:41:15 -0600 Subject: [PATCH 238/361] fix builtin_predicate (#1819) --- src/machine/loader.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 3e9ae50e..5a309197 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -2475,11 +2475,10 @@ impl Machine { 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; + } else { + self.machine_st.fail = true; } } - - self.machine_st.fail = true; } } From b593fffc7d05f0c7ca2e150a7a750eb9719bf3b3 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 27 Jun 2023 11:08:29 -0600 Subject: [PATCH 239/361] support module resolution in current_predicate/1 (#1817) --- build/instructions_template.rs | 4 +-- src/lib/builtins.pl | 14 ++++++--- src/machine/machine_indices.rs | 1 - src/machine/system_calls.rs | 53 +++++++++++++++++++++++++++++----- 4 files changed, 57 insertions(+), 15 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 8e61ad23..7f559b8e 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -308,7 +308,7 @@ enum SystemClauseType { GetContinuationChunk, #[strum_discriminants(strum(props(Arity = "7", Name = "$get_next_op_db_ref")))] GetNextOpDBRef, - #[strum_discriminants(strum(props(Arity = "2", Name = "$lookup_db_ref")))] + #[strum_discriminants(strum(props(Arity = "3", Name = "$lookup_db_ref")))] LookupDBRef, #[strum_discriminants(strum(props(Arity = "1", Name = "$is_partial_string")))] IsPartialString, @@ -578,7 +578,7 @@ enum SystemClauseType { DeleteAllAttributesFromVar, #[strum_discriminants(strum(props(Arity = "1", Name = "$unattributed_var")))] UnattributedVar, - #[strum_discriminants(strum(props(Arity = "3", Name = "$get_db_refs")))] + #[strum_discriminants(strum(props(Arity = "4", Name = "$get_db_refs")))] GetDBRefs, REPL(REPLCodePtr), } diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 1c183a0c..ab821f4b 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -1254,7 +1254,13 @@ current_predicate(Pred) :- ( var(Pred) -> '$get_db_refs'(_, _, PIs), lists:member(Pred, PIs) - ; Pred = Name/Arity -> + ; '$strip_module'(Pred, Module, UnqualifiedPred), + ( var(Module), + \+ functor(Pred, (:), 2) + ; atom(Module) + ), + nonvar(UnqualifiedPred), + UnqualifiedPred = Name/Arity -> ( ( nonvar(Name), \+ atom(Name) ; nonvar(Arity), \+ integer(Arity) ; integer(Arity), Arity < 0 @@ -1262,9 +1268,9 @@ current_predicate(Pred) :- throw(error(type_error(predicate_indicator, Pred), current_predicate/1)) ; nonvar(Name), nonvar(Arity) -> - '$lookup_db_ref'(Name, Arity) - ; '$get_db_refs'(Name, Arity, PIs), - lists:member(Pred, PIs) + '$lookup_db_ref'(Module, Name, Arity) + ; '$get_db_refs'(Module, Name, Arity, PIs), + lists:member(UnqualifiedPred, PIs) ) ; throw(error(type_error(predicate_indicator, Pred), current_predicate/1)) ). diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index ca31e4bd..df880447 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -227,7 +227,6 @@ impl CodeIndex { } pub(crate) type HeapVarDict = IndexMap; -// pub(crate) type AllocVarDict = IndexMap; pub(crate) type GlobalVarDir = IndexMap), FxBuildHasher>; diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index df89c520..48741974 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -3744,12 +3744,25 @@ impl Machine { #[inline(always)] pub(crate) fn lookup_db_ref(&mut self) { - let name = cell_as_atom!(self.deref_register(1)); - let arity = cell_as_fixnum!(self.deref_register(2)).get_num() as usize; + let module_name = self.deref_register(1); + let name = cell_as_atom!(self.deref_register(2)); + let arity = cell_as_fixnum!(self.deref_register(3)).get_num() as usize; - if self.indices.code_dir.get(&(name, arity)).is_none() { - self.machine_st.fail = true; - } + let module_name = read_heap_cell!(module_name, + (HeapCellValueTag::Atom, (module_name, _arity)) => { + module_name + } + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var) => { + atom!("user") + } + _ => { + unreachable!() + } + ); + + self.machine_st.fail = self.indices + .get_predicate_code_index(name, arity, module_name) + .is_none(); } #[inline(always)] @@ -3757,7 +3770,19 @@ impl Machine { let name_match: fn(Atom, Atom) -> bool; let arity_match: fn(usize, usize) -> bool; - let atom = self.deref_register(1); + let module_name = read_heap_cell!(self.deref_register(1), + (HeapCellValueTag::Atom, (module_name, _arity)) => { + module_name + } + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var) => { + atom!("user") + } + _ => { + unreachable!() + } + ); + + let atom = self.deref_register(2); let pred_atom = if atom.is_var() { name_match = |_, _| true; @@ -3767,7 +3792,7 @@ impl Machine { cell_as_atom!(atom) }; - let arity = self.deref_register(2); + let arity = self.deref_register(3); let pred_arity = if arity.is_var() { arity_match = |_, _| true; @@ -3792,7 +3817,19 @@ impl Machine { let h = self.machine_st.heap.len(); let mut num_functors = 0; - for (name, arity) in self.indices.code_dir.keys() { + let code_dir = if module_name == atom!("user") { + &self.indices.code_dir + } else { + match self.indices.modules.get(&module_name).map(|module| &module.code_dir) { + Some(code_dir) => code_dir, + None => { + self.machine_st.fail = true; + return; + } + } + }; + + for (name, arity) in code_dir.keys() { if name_match(pred_atom, *name) && arity_match(pred_arity, *arity) { self.machine_st.heap.extend( functor!(atom!("/"), [cell(atom_as_cell!(name)), fixnum(*arity)]), From 16f281e3d125420fc60126192bf5d93ea4c1fd97 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 27 Jun 2023 12:23:40 -0600 Subject: [PATCH 240/361] enable unification of streams to alias atoms (#1823) --- src/machine/streams.rs | 364 ++++++++++++++++++++--------------------- src/machine/unify.rs | 23 +++ 2 files changed, 205 insertions(+), 182 deletions(-) diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 932e7f78..8d9f8407 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -281,24 +281,24 @@ pub struct HttpWriteStream { 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 { - 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)) - } + 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)) + } } #[inline] fn flush(&mut self) -> std::io::Result<()> { - Ok(()) + Ok(()) } } @@ -512,7 +512,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 _)) @@ -566,7 +566,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(), @@ -583,7 +583,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, @@ -600,7 +600,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, @@ -618,7 +618,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, @@ -636,7 +636,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, @@ -654,7 +654,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, @@ -676,7 +676,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, @@ -696,7 +696,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, @@ -716,7 +716,7 @@ impl CharRead for Stream { Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) | - Stream::HttpWrite(_) | + Stream::HttpWrite(_) | Stream::Null(_) => {} } } @@ -733,7 +733,7 @@ impl CharRead for Stream { Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) | - Stream::HttpWrite(_) | + Stream::HttpWrite(_) | Stream::Null(_) => {} } } @@ -751,13 +751,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 @@ -773,7 +773,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(_) | @@ -793,7 +793,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(_) | @@ -879,10 +879,10 @@ impl Stream { file_stream.position() } Stream::NamedTcp(..) - | Stream::NamedTls(..) - | Stream::Readline(..) - | Stream::StaticString(..) - | Stream::Byte(..) => Some(0), + | Stream::NamedTls(..) + | Stream::Readline(..) + | Stream::StaticString(..) + | Stream::Byte(..) => Some(0), _ => None, }; @@ -920,7 +920,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, @@ -943,7 +943,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, @@ -1007,10 +1007,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"), @@ -1084,15 +1084,15 @@ impl Stream { #[inline] pub(crate) fn from_http_sender( - body_writer: Sender, - arena: &mut Arena, + body_writer: Sender, + arena: &mut Arena, ) -> Self { - Stream::HttpWrite(arena_alloc!( - StreamLayout::new(CharReader::new(HttpWriteStream { - body_writer - })), - arena - )) + Stream::HttpWrite(arena_alloc!( + StreamLayout::new(CharReader::new(HttpWriteStream { + body_writer + })), + arena + )) } #[inline] @@ -1182,12 +1182,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, } } @@ -1196,12 +1196,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, } } @@ -1320,101 +1320,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(); @@ -1437,60 +1437,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); diff --git a/src/machine/unify.rs b/src/machine/unify.rs index d6401b92..e386b4b0 100644 --- a/src/machine/unify.rs +++ b/src/machine/unify.rs @@ -494,6 +494,29 @@ pub(crate) trait Unifier: DerefMut { (ArenaHeaderTag::Rational, rat_ptr) => { Self::unify_big_num(self, rat_ptr, value); } + (ArenaHeaderTag::Stream, stream) => { + read_heap_cell!(value, + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var) => { + 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)); From ceb276b2498d304d507f62281e52c575ce9b82f4 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Tue, 27 Jun 2023 22:37:23 +0200 Subject: [PATCH 241/361] use copy_term_nat/2 --- src/lib/clpz.pl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 8f9c6bf8..9b162a19 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -6168,8 +6168,7 @@ with_local_attributes(Vars, Goal, Result) :- % we made during propagation, and unify the variables % in the thrown copy with Vars in order to get the % intended variables in Result. - asserta(nat_copy(Vars-Result)), - retract(nat_copy(Copy)), + copy_term_nat(Vars-Result, Copy), throw(local_attributes(Copy))), local_attributes(Vars-Result), true). From 039fffb33964f29258716710dee996fda959742e Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 27 Jun 2023 16:39:10 -0600 Subject: [PATCH 242/361] better detect syntax errors in lexer.rs (#1771) --- src/lib/charsio.pl | 1 + src/parser/lexer.rs | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib/charsio.pl b/src/lib/charsio.pl index 1fc120f0..99b5c281 100644 --- a/src/lib/charsio.pl +++ b/src/lib/charsio.pl @@ -193,6 +193,7 @@ get_single_char(C) :- % ``` read_from_chars(Chars, Term) :- must_be(chars, Chars), + must_be(var, Term), '$read_term_from_chars'(Chars, Term). %% write_term_to_chars(+Term, +Options, -Chars). diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index 9aeacd82..236585d9 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -859,7 +859,13 @@ impl<'a, R: CharRead> Lexer<'a, R> { self.get_single_quoted_char() .map(|c| Token::Literal(Literal::Fixnum(Fixnum::build_with(c as i64)))) - .or_else(|_| { + .or_else(|err| { + match err { + ParserError::UnexpectedChar('\'', ..) => { + } + err => return Err(err), + } + self.return_char(c); i64::from_str_radix(&token, 10) From c4b13a217608e1a612a8c4972ed69de7b7bd3b62 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 27 Jun 2023 17:35:10 -0600 Subject: [PATCH 243/361] unify stack variables to streams in unify_constant (#1845) --- src/machine/unify.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/unify.rs b/src/machine/unify.rs index e386b4b0..e73fa4a1 100644 --- a/src/machine/unify.rs +++ b/src/machine/unify.rs @@ -496,7 +496,7 @@ pub(crate) trait Unifier: DerefMut { } (ArenaHeaderTag::Stream, stream) => { read_heap_cell!(value, - (HeapCellValueTag::AttrVar | HeapCellValueTag::Var) => { + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { Self::bind(self, value.as_var().unwrap(), untyped_arena_ptr_as_cell!(ptr)); } (HeapCellValueTag::Atom, (name, arity)) => { From 58af615dd4d5223b1d47caa383b24dd79479a5d4 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 28 Jun 2023 17:31:43 -0600 Subject: [PATCH 244/361] correct and generalize current_predicate/1 --- src/lib/builtins.pl | 3 +-- src/lib/files.pl | 6 +++--- src/lib/iso_ext.pl | 4 ++-- src/machine/system_calls.rs | 6 +++--- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index ab821f4b..904d2f69 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -1252,14 +1252,13 @@ abolish(Pred) :- % It can be used to check for existence of a predicate or to enumerate all loaded predicates current_predicate(Pred) :- ( var(Pred) -> - '$get_db_refs'(_, _, PIs), + '$get_db_refs'(_, _, _, PIs), lists:member(Pred, PIs) ; '$strip_module'(Pred, Module, UnqualifiedPred), ( var(Module), \+ functor(Pred, (:), 2) ; atom(Module) ), - nonvar(UnqualifiedPred), UnqualifiedPred = Name/Arity -> ( ( nonvar(Name), \+ atom(Name) ; nonvar(Arity), \+ integer(Arity) diff --git a/src/lib/files.pl b/src/lib/files.pl index 90c1307d..6d89eaad 100644 --- a/src/lib/files.pl +++ b/src/lib/files.pl @@ -70,9 +70,9 @@ _lists of characters_. This is an ideal representation: file_exists/1, directory_exists/1, delete_file/1, - rename_file/2, - file_copy/2, - delete_directory/1, + rename_file/2, + file_copy/2, + delete_directory/1, make_directory/1, make_directory_path/1, working_directory/2, diff --git a/src/lib/iso_ext.pl b/src/lib/iso_ext.pl index d6f13df0..0af55c99 100644 --- a/src/lib/iso_ext.pl +++ b/src/lib/iso_ext.pl @@ -16,8 +16,8 @@ but they're not part of the ISO Prolog standard at the moment. setup_call_cleanup/3, call_nth/2, copy_term_nat/2, - asserta/2, - assertz/2]). + asserta/2, + assertz/2]). :- use_module(library(error), [can_be/2, domain_error/3, diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 48741974..7dbaf43b 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -3774,7 +3774,7 @@ impl Machine { (HeapCellValueTag::Atom, (module_name, _arity)) => { module_name } - (HeapCellValueTag::AttrVar | HeapCellValueTag::Var) => { + (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => { atom!("user") } _ => { @@ -3845,9 +3845,9 @@ impl Machine { (0 .. num_functors).map(|i| str_loc_as_cell!(h + 3 * i)), ); - unify!(self.machine_st, heap_loc_as_cell!(h), self.machine_st.registers[3]); + unify!(self.machine_st, heap_loc_as_cell!(h), self.machine_st.registers[4]); } else { - unify!(self.machine_st, empty_list_as_cell!(), self.machine_st.registers[3]); + unify!(self.machine_st, empty_list_as_cell!(), self.machine_st.registers[4]); } } From c84a5c3282af2d3e986aba578aa172ca22fcea4f Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 29 Jun 2023 19:25:23 +0200 Subject: [PATCH 245/361] remove unneeded single quotes --- tools/showterm.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/showterm.pl b/tools/showterm.pl index 3193802c..899adb68 100644 --- a/tools/showterm.pl +++ b/tools/showterm.pl @@ -75,7 +75,7 @@ dot(Term) :- dot(Term, []). dot(Term, NVs) :- - phrase(term_labels(Term, NVs, 'c'), Ls), + phrase(term_labels(Term, NVs, c), Ls), phrase(("graph G {\n", dots(Ls), "}\n"), DOT), From c36bd4dc07630d511624cacb0fd6a59c59f1d36d Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 29 Jun 2023 12:14:06 -0600 Subject: [PATCH 246/361] introduce CutPoint heap tag so that they can be offset by call_continuation/1 --- src/arithmetic.rs | 2 +- src/machine/dispatch.rs | 6 +++--- src/machine/machine_state.rs | 2 +- src/machine/system_calls.rs | 16 +++++++++++----- src/macros.rs | 17 ++++++++++++++++- src/parser/ast.rs | 9 +++++++++ src/types.rs | 23 +++++++++++++---------- 7 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 0fbd91d5..515dba4b 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -685,7 +685,7 @@ impl TryFrom for Number { (HeapCellValueTag::F64, n) => { Ok(Number::Float(*n)) } - (HeapCellValueTag::Fixnum, n) => { + (HeapCellValueTag::Fixnum | HeapCellValueTag::CutPoint, n) => { Ok(Number::Fixnum(n)) } _ => { diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 686ec71b..8029bb70 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -1149,17 +1149,17 @@ impl Machine { &Instruction::GetLevel(r) => { let b0 = self.machine_st.b0; - self.machine_st[r] = fixnum_as_cell!(Fixnum::build_with(b0 as i64)); + self.machine_st[r] = fixnum_as_cell!(Fixnum::as_cutpoint(b0 as i64)); self.machine_st.p += 1; } &Instruction::GetPrevLevel(r) => { let prev_b = self.machine_st.stack.index_or_frame(self.machine_st.b).prelude.b; - self.machine_st[r] = fixnum_as_cell!(Fixnum::build_with(prev_b as i64)); + self.machine_st[r] = fixnum_as_cell!(Fixnum::as_cutpoint(prev_b as i64)); self.machine_st.p += 1; } &Instruction::GetCutPoint(r) => { - self.machine_st[r] = fixnum_as_cell!(Fixnum::build_with(self.machine_st.b as i64)); + self.machine_st[r] = fixnum_as_cell!(Fixnum::as_cutpoint(self.machine_st.b as i64)); self.machine_st.p += 1; } &Instruction::Cut(r) => { diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index de034374..29702c4a 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -821,7 +821,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 { diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 7dbaf43b..6a676ae9 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -981,7 +981,7 @@ impl MachineState { self.p = cp + 1; - // adjust cut point to occur after call_continuation. + /* if num_cells > 0 { if let HeapCellValueTag::Fixnum = self.heap[s + 2].get_tag() { and_frame[1] = fixnum_as_cell!(Fixnum::build_with(self.b as i64)); @@ -989,9 +989,15 @@ impl MachineState { and_frame[1] = self.heap[s + 2]; } } + */ - for index in s + 3..s + 2 + num_cells { - and_frame[index - (s + 1)] = self.heap[index]; + for index in s + 2..s + 2 + num_cells { + if let HeapCellValueTag::CutPoint = self.heap[index].get_tag() { + // adjust cut point to occur after call_continuation. + and_frame[index - (s + 1)] = fixnum_as_cell!(Fixnum::as_cutpoint(self.b as i64)); + } else { + and_frame[index - (s + 1)] = self.heap[index]; + } } self.e = e; @@ -5557,13 +5563,13 @@ impl Machine { #[inline(always)] pub(crate) fn get_b_value(&mut self) { - let n = Fixnum::build_with(i64::try_from(self.machine_st.b).unwrap()); + let n = Fixnum::as_cutpoint(i64::try_from(self.machine_st.b).unwrap()); self.machine_st.unify_fixnum(n, self.machine_st.registers[1]); } #[inline(always)] pub(crate) fn get_cut_point(&mut self) { - let n = Fixnum::build_with(i64::try_from(self.machine_st.b0).unwrap()); + let n = Fixnum::as_cutpoint(i64::try_from(self.machine_st.b0).unwrap()); self.machine_st.unify_fixnum(n, self.machine_st.registers[1]); } diff --git a/src/macros.rs b/src/macros.rs index c0c929c8..8a3ede55 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -15,7 +15,7 @@ macro_rules! char_as_cell { macro_rules! fixnum_as_cell { ($n: expr) => { - HeapCellValue::from_bytes($n.into_bytes()) //HeapCellValueTag::Fixnum, $n.get_num() as u64) + HeapCellValue::from_bytes($n.into_bytes()) }; } @@ -378,6 +378,21 @@ macro_rules! read_heap_cell_pat_body { #[allow(unused_braces)] $code }); + ($cell:ident, CutPoint, $value:ident, $code:expr) => ({ + let $value = Fixnum::from_bytes($cell.into_bytes()); + #[allow(unused_braces)] + $code + }); + ($cell:ident, Fixnum | CutPoint, $value:ident, $code:expr) => ({ + let $value = Fixnum::from_bytes($cell.into_bytes()); + #[allow(unused_braces)] + $code + }); + ($cell:ident, CutPoint | Fixnum, $value:ident, $code:expr) => ({ + let $value = Fixnum::from_bytes($cell.into_bytes()); + #[allow(unused_braces)] + $code + }); ($cell:ident, Char, $value:ident, $code:expr) => ({ let $value = unsafe { char::from_u32_unchecked($cell.get_value() as u32) }; #[allow(unused_braces)] diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 283a9dc0..31f1a702 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -493,6 +493,15 @@ impl Fixnum { .with_f(false) } + #[inline] + pub fn as_cutpoint(num: i64) -> Self { + Fixnum::new() + .with_num(u64::from_ne_bytes(num.to_ne_bytes()) & ((1 << 56) - 1)) + .with_tag(HeapCellValueTag::CutPoint as u8) + .with_m(false) + .with_f(false) + } + #[inline] pub fn build_with_checked(num: i64) -> Result { const UPPER_BOUND: i64 = (1 << 55) - 1; diff --git a/src/types.rs b/src/types.rs index a8b66b35..12add4ef 100644 --- a/src/types.rs +++ b/src/types.rs @@ -30,6 +30,7 @@ pub enum HeapCellValueTag { Atom = 0b010111, PStr = 0b011001, CStr = 0b011011, + CutPoint = 0b011111, } #[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -50,13 +51,15 @@ pub enum HeapCellValueView { Atom = 0b010111, PStr = 0b011001, CStr = 0b011011, + CutPoint = 0b011111, // trail elements. - TrailedHeapVar = 0b011101, - TrailedStackVar = 0b011111, + TrailedHeapVar = 0b101111, + TrailedStackVar = 0b101011, + TrailedAttrVar = 0b100001, TrailedAttrVarListLink = 0b100011, TrailedAttachedValue = 0b100101, TrailedBlackboardEntry = 0b100111, - TrailedBlackboardOffset = 0b101001, + TrailedBlackboardOffset = 0b110011, } #[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -189,13 +192,13 @@ pub enum TrailRef { #[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[bits = 6] pub(crate) enum TrailEntryTag { - TrailedHeapVar = 0b011110, - TrailedStackVar = 0b011111, - TrailedAttrVar = 0b101110, - TrailedAttrVarListLink = 0b100011, - TrailedAttachedValue = 0b101010, - TrailedBlackboardEntry = 0b100110, - TrailedBlackboardOffset = 0b100111, + TrailedHeapVar = 0b101111, + TrailedStackVar = 0b101011, + TrailedAttrVar = 0b100001, + TrailedAttrVarListLink = 0b100011, + TrailedAttachedValue = 0b100101, + TrailedBlackboardEntry = 0b100111, + TrailedBlackboardOffset = 0b110011, } #[bitfield] From a6a0cef9fc7730b7763945bf5b55499f3dcf43be Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 29 Jun 2023 12:58:15 -0600 Subject: [PATCH 247/361] read the cell written to by mark_var when needed in compile_is (#1846) --- src/arithmetic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 515dba4b..7f15a7f1 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -335,11 +335,11 @@ impl<'a> ArithmeticEvaluator<'a> { term_loc, &mut code, ); + cell.get().norm() } else { self.marker.increment_running_count(var_num); + r } - - r } else { self.marker.increment_running_count(var_num); cell.get().norm() From bb09de18059953dd45ac2168e63680072bbdc530 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 29 Jun 2023 14:58:15 -0600 Subject: [PATCH 248/361] fix ReadlineStream peek_char using CharReader --- src/parser/char_reader.rs | 2 +- src/read.rs | 55 +++++++++++++++++++++++---------------- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/parser/char_reader.rs b/src/parser/char_reader.rs index dddabb19..a2a7c2e2 100644 --- a/src/parser/char_reader.rs +++ b/src/parser/char_reader.rs @@ -111,7 +111,7 @@ impl CharReader { } impl CharReader { - fn refresh_buffer(&mut self) -> io::Result<&[u8]> { + pub fn refresh_buffer(&mut self) -> io::Result<&[u8]> { // If we've reached the end of our internal buffer then we need to fetch // some more data from the underlying reader. // Branch using `>=` instead of the more correct `==` diff --git a/src/read.rs b/src/read.rs index 8f70eeec..e42e3439 100644 --- a/src/read.rs +++ b/src/read.rs @@ -83,7 +83,7 @@ fn get_prompt() -> &'static str { #[derive(Debug)] pub struct ReadlineStream { rl: Editor, - pending_input: Cursor, + pending_input: CharReader>, add_history: bool, } @@ -107,7 +107,7 @@ impl ReadlineStream { ReadlineStream { rl, - pending_input: Cursor::new(pending_input.to_owned()), + pending_input: CharReader::new(Cursor::new(pending_input.to_owned())), add_history: add_history, } } @@ -119,29 +119,35 @@ impl ReadlineStream { #[inline] pub fn reset(&mut self) { - self.pending_input.get_mut().clear(); - self.pending_input.set_position(0); + self.pending_input.reset_buffer(); + + let pending_input = self.pending_input.get_mut(); + + pending_input.get_mut().clear(); + pending_input.set_position(0); } fn call_readline(&mut self) -> std::io::Result { match self.rl.readline(get_prompt()) { Ok(text) => { - *self.pending_input.get_mut() = text; - self.pending_input.set_position(0); + self.pending_input.reset_buffer(); + + *self.pending_input.get_mut().get_mut() = text; + self.pending_input.get_mut().set_position(0); unsafe { if PROMPT { - self.rl.history_mut().add(self.pending_input.get_ref()); + self.rl.history_mut().add(self.pending_input.get_ref().get_ref()); self.save_history(); PROMPT = false; } } - if self.pending_input.get_ref().chars().last() != Some('\n') { - *self.pending_input.get_mut() += "\n"; + if self.pending_input.get_ref().get_ref().chars().last() != Some('\n') { + *self.pending_input.get_mut().get_mut() += "\n"; } - Ok(self.pending_input.get_ref().len()) + Ok(self.pending_input.get_ref().get_ref().len()) } Err(ReadlineError::Eof) => Ok(0), Err(e) => Err(Error::new(ErrorKind::InvalidInput, e)), @@ -164,9 +170,13 @@ impl ReadlineStream { } } + #[inline] pub(crate) fn peek_byte(&mut self) -> std::io::Result { + let bytes = self.pending_input.refresh_buffer()?; + let byte = bytes.iter().next().cloned(); + loop { - match self.pending_input.get_ref().bytes().next() { + match byte { Some(0) => { return Ok(0); } @@ -178,7 +188,7 @@ impl ReadlineStream { return Err(e); } Ok(0) => { - self.pending_input.get_mut().push('\u{0}'); + self.pending_input.get_mut().get_mut().push('\u{0}'); return Ok(0); } _ => { @@ -203,24 +213,23 @@ impl Read for ReadlineStream { } impl CharRead for ReadlineStream { + #[inline] fn peek_char(&mut self) -> Option> { loop { - let pos = self.pending_input.position() as usize; - - match self.pending_input.get_ref()[pos ..].chars().next() { - Some('\u{0}') => { + match self.pending_input.peek_char() { + Some(Ok('\u{0}')) => { return Some(Ok('\u{0}')); } - Some(c) => { + Some(Ok(c)) => { return Some(Ok(c)); } - None => { + _ => { match self.call_readline() { Err(e) => { return Some(Err(e)); } Ok(0) => { - self.pending_input.get_mut().push('\u{0}'); + self.pending_input.get_mut().get_mut().push('\u{0}'); return Some(Ok('\u{0}')); } _ => { @@ -232,14 +241,14 @@ impl CharRead for ReadlineStream { } } + #[inline] fn consume(&mut self, nread: usize) { - let offset = self.pending_input.position() as usize; - self.pending_input.set_position((offset + nread) as u64); + self.pending_input.consume(nread); } + #[inline] fn put_back_char(&mut self, c: char) { - let offset = self.pending_input.position() as usize; - self.pending_input.set_position((offset - c.len_utf8()) as u64); + self.pending_input.put_back_char(c); } } From 330e9ba4efe4a1cb459ffc858db863b20c5b0e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Mon, 19 Jun 2023 19:18:14 +0200 Subject: [PATCH 249/361] Multiple fixes for http libraries * use reqwest for http_open (still uses Hyper underneath) * use Hyper 1.0.0-rc3 for server * Modify all internal handling of server --- Cargo.lock | 414 +++++++++++++++++++++++++++++++----- Cargo.toml | 8 +- src/http.rs | 57 +++-- src/lib/http/http_server.pl | 39 ++-- src/machine/streams.rs | 57 +++-- src/machine/system_calls.rs | 160 +++++++------- 6 files changed, 545 insertions(+), 190 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51a8fa49..15599610 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -49,12 +64,33 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + [[package]] name = "bit-set" version = "0.5.3" @@ -76,6 +112,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "bitvec" version = "1.0.1" @@ -253,7 +295,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ebde6a9dd5e331cd6c6f48253254d117642c31653baa475e394657c59c1f7d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossterm_winapi", "libc", "mio 0.7.14", @@ -398,6 +440,15 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + [[package]] name = "endian-type" version = "0.1.2" @@ -446,12 +497,12 @@ dependencies = [ [[package]] name = "fd-lock" -version = "3.0.12" +version = "3.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ae6b3d9530211fb3b12a95374b8b0823be812f53d09e18c5675c0146b09642" +checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" dependencies = [ "cfg-if", - "rustix", + "rustix 0.38.1", "windows-sys 0.48.0", ] @@ -476,6 +527,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + [[package]] name = "funty" version = "2.0.0" @@ -548,7 +608,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.22", ] [[package]] @@ -620,6 +680,12 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "git-version" version = "0.3.5" @@ -654,9 +720,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -686,15 +752,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.1" @@ -748,6 +805,29 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-body" +version = "1.0.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951dfc2e32ac02d67c90c0d65bd27009a635dc9b381a2cc7d284ab01e3a0150d" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92445bc9cc14bfa0a3ce56817dc3b5bcc227a168781a356b702410789cec0d10" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body 1.0.0-rc.2", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.8.0" @@ -762,9 +842,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -772,7 +852,7 @@ dependencies = [ "futures-util", "h2", "http", - "http-body", + "http-body 0.4.5", "httparse", "httpdate", "itoa", @@ -784,6 +864,28 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75264b2003a3913f118d35c586e535293b3e22e41f074930762929d071e092" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body 1.0.0-rc.2", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", + "tracing", + "want", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -791,7 +893,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.27", "native-tls", "tokio", "tokio-native-tls", @@ -820,6 +922,16 @@ dependencies = [ "cc", ] +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -845,11 +957,17 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi", "libc", "windows-sys 0.48.0", ] +[[package]] +name = "ipnet" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + [[package]] name = "itertools" version = "0.10.5" @@ -906,7 +1024,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" dependencies = [ "arrayvec", - "bitflags", + "bitflags 1.3.2", "cfg-if", "ryu", "static_assertions", @@ -914,9 +1032,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.146" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libffi" @@ -965,6 +1083,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + [[package]] name = "lock_api" version = "0.4.10" @@ -1034,6 +1158,21 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.7.14" @@ -1125,7 +1264,7 @@ version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cc", "cfg-if", "libc", @@ -1138,7 +1277,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "static_assertions", @@ -1164,14 +1303,23 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -1190,7 +1338,7 @@ version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -1207,7 +1355,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.22", ] [[package]] @@ -1285,6 +1433,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + [[package]] name = "phf" version = "0.9.0" @@ -1432,18 +1586,18 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.28" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] @@ -1500,7 +1654,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1509,7 +1663,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1535,6 +1689,43 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +[[package]] +name = "reqwest" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +dependencies = [ + "base64 0.21.2", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body 0.4.5", + "hyper 0.14.27", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "ring" version = "0.16.20" @@ -1582,16 +1773,35 @@ dependencies = [ ] [[package]] -name = "rustix" -version = "0.37.20" +name = "rustc-demangle" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.37.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25693a73057a1b4cb56179dd3c7ea21a7c6c5ee7d85781f5749b46f34b79c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc6396159432b5c8490d4e301d8c705f61860b8b6c863bf79942ce5401968f3" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys 0.4.3", "windows-sys 0.48.0", ] @@ -1607,7 +1817,7 @@ version = "9.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db7826789c0e25614b03e5a54a0717a86f9ff6e6e5247f92b369472869320039" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "clipboard-win", "dirs-next", @@ -1660,10 +1870,11 @@ name = "scryer-prolog" version = "0.9.1" dependencies = [ "assert_cmd", - "base64", + "base64 0.12.3", "bit-set", "bitvec", "blake2 0.8.1", + "bytes", "chrono", "cpu-time", "crossterm", @@ -1676,8 +1887,8 @@ dependencies = [ "fxhash", "git-version", "hostname", - "hyper", - "hyper-tls", + "http-body-util", + "hyper 1.0.0-rc.3", "indexmap", "lazy_static", "lexical", @@ -1692,6 +1903,7 @@ dependencies = [ "proc-macro2", "quote", "ref_thread_local", + "reqwest", "ring", "ripemd160", "roxmltree", @@ -1719,7 +1931,7 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -1753,6 +1965,29 @@ version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +[[package]] +name = "serde_json" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serial_test" version = "0.5.1" @@ -1966,9 +2201,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.18" +version = "2.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" dependencies = [ "proc-macro2", "quote", @@ -1991,7 +2226,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.3.5", - "rustix", + "rustix 0.37.21", "windows-sys 0.48.0", ] @@ -2029,7 +2264,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.22", ] [[package]] @@ -2043,6 +2278,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "to-syn-value" version = "0.1.0" @@ -2066,11 +2316,12 @@ dependencies = [ [[package]] name = "tokio" -version = "1.28.2" +version = "1.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +checksum = "374442f06ee49c3a28a8fc9f01a2596fed7559c6b99b31279c3261778e77d84f" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", "mio 0.8.8", @@ -2091,7 +2342,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.22", ] [[package]] @@ -2156,12 +2407,27 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + [[package]] name = "unicode-ident" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-segmentation" version = "1.10.1" @@ -2180,6 +2446,17 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "utf-8" version = "0.7.6" @@ -2265,10 +2542,22 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.22", "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.87" @@ -2287,7 +2576,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.22", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2374,9 +2663,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm 0.48.0", "windows_aarch64_msvc 0.48.0", @@ -2471,6 +2760,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 99218c71..c24d66f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,13 +61,15 @@ smallvec = "1.8.0" sodiumoxide = "0.2.6" static_assertions = "1.1.0" ryu = "1.0.9" -hyper = { version = "0.14", features = ["full"] } -hyper-tls = "0.5.0" -tokio = { version = "1.24.2", features = ["full"] } +hyper = { version = "1.0.0-rc.3", features = ["full"] } +tokio = { version = "1.28.2", features = ["full"] } futures = "0.3" libffi = "3.1.0" libloading = "0.7" derive_deref = "1.1.1" +http-body-util = "0.1.0-rc.2" +bytes = "1" +reqwest = { version = "0.11.18", features = ["blocking"] } [dev-dependencies] assert_cmd = "1.0.3" diff --git a/src/http.rs b/src/http.rs index 887366f8..71d1682c 100644 --- a/src/http.rs +++ b/src/http.rs @@ -1,25 +1,54 @@ -use std::sync::Arc; -use std::convert::Infallible; - -use hyper::{Response, Request, Body}; -use tokio::sync::Mutex; -use tokio::sync::mpsc::{channel, Receiver, Sender}; +use std::sync::{Arc, Mutex, Condvar}; +use std::future::Future; +use std::pin::Pin; +use http_body_util::Full; +use bytes::Bytes; +use hyper::service::Service; +use hyper::{body::Incoming as IncomingBody, Request, Response}; pub struct HttpListener { - pub incoming: Receiver + pub incoming: std::sync::mpsc::Receiver } #[derive(Debug)] pub struct HttpRequest { - pub request: Request, + pub request: Request, pub response: HttpResponse, } -pub type HttpResponse = Sender>; +pub type HttpResponse = Arc<(Mutex, Mutex>>>, Condvar)>; -pub async fn serve_req(req: Request, tx: Arc>>) -> Result, Infallible> { - let (response_tx, mut rx) = channel(1); - let http_request = HttpRequest { request: req, response: response_tx }; - tx.lock().await.send(http_request).await.unwrap(); - Ok(rx.recv().await.unwrap()) +pub struct HttpService { + pub tx: std::sync::mpsc::SyncSender, +} + +impl Service> for HttpService { + type Response = Response>; + type Error = hyper::Error; + type Future = Pin> + Send>>; + + fn call(&mut self, req: Request) -> Self::Future { + // new connection! + // we send the Request info to Prolog + let response = Arc::new((Mutex::new(false), Mutex::new(None), Condvar::new())); + let http_request = HttpRequest { request: req, response: Arc::clone(&response) }; + self.tx.send(http_request).unwrap(); + + // we wait for the Response info from Prolog + { + let (ready, _response, cvar) = &*response; + let mut ready = ready.lock().unwrap(); + while !*ready { + ready = cvar.wait(ready).unwrap(); + } + } + { + let (_, response, _) = &*response; + let response = response.lock().unwrap().take(); + let res = response.expect("Data race error in HTTP Server"); + Box::pin(async move { + Ok(res) + }) + } + } } diff --git a/src/lib/http/http_server.pl b/src/lib/http/http_server.pl index 7f2de002..381d5607 100644 --- a/src/lib/http/http_server.pl +++ b/src/lib/http/http_server.pl @@ -121,37 +121,44 @@ http_loop(HttpListener, Handlers) :- send_response(ResponseHandle, http_response(StatusCode0, text(ResponseText), ResponseHeaders0)) :- default(StatusCode0, 200, StatusCode), maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0), - '$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream), - call_cleanup( - format(ResponseStream, "~s", [ResponseText]), - close(ResponseStream) + '$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream0), + open(stream(ResponseStream0), write, ResponseStream, [type(text)]), + catch( + call_cleanup(format(ResponseStream, "~s", [ResponseText]),close(ResponseStream)), + error(existence_error(stream, _), _), + true ). send_response(ResponseHandle, http_response(StatusCode0, bytes(ResponseBytes), ResponseHeaders0)) :- default(StatusCode0, 200, StatusCode), maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0), '$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream), - call_cleanup( - format(ResponseStream, "~s", [ResponseBytes]), - close(ResponseStream) + catch( + call_cleanup(format(ResponseStream, "~s", [ResponseBytes]),close(ResponseStream)), + error(existence_error(stream, _), _), + true ). send_response(ResponseHandle, http_response(StatusCode0, file(Filename), ResponseHeaders0)) :- default(StatusCode0, 200, StatusCode), maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0), '$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream), - call_cleanup( - setup_call_cleanup( - open(Filename, read, FileStream, [type(binary)]), - ( - get_n_chars(FileStream, _, FileCs), - format(ResponseStream, "~s", [FileCs]) + catch( + call_cleanup( + setup_call_cleanup( + open(Filename, read, FileStream, [type(binary)]), + ( + get_n_chars(FileStream, _, FileCs), + format(ResponseStream, "~s", [FileCs]) + ), + close(FileStream) ), - close(FileStream) + close(ResponseStream) ), - close(ResponseStream) + error(existence_error(stream, _), _), + true ). - + default(Var, Default, Out) :- (var(Var) -> Out = Default diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 8d9f8407..27942cb2 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -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] @@ -276,7 +276,10 @@ impl Read for HttpReadStream { } pub struct HttpWriteStream { - body_writer: Sender, + status_code: u16, + headers: hyper::HeaderMap, + response: TypedArenaPtr, + buffer: Vec, } impl Debug for HttpWriteStream { @@ -288,17 +291,28 @@ impl Debug for HttpWriteStream { impl Write for HttpWriteStream { #[inline] fn write(&mut self, buf: &[u8]) -> std::io::Result { - 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<()> { - Ok(()) + 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(()) } } @@ -1084,15 +1098,20 @@ impl Stream { #[inline] pub(crate) fn from_http_sender( - body_writer: Sender, - arena: &mut Arena, + response: TypedArenaPtr, + status_code: u16, + headers: hyper::HeaderMap, + arena: &mut Arena, ) -> Self { - Stream::HttpWrite(arena_alloc!( - StreamLayout::new(CharReader::new(HttpWriteStream { - body_writer - })), - arena - )) + Stream::HttpWrite(arena_alloc!( + StreamLayout::new(CharReader::new(HttpWriteStream { + response, + status_code, + headers, + buffer: Vec::new(), + })), + arena + )) } #[inline] @@ -1139,10 +1158,10 @@ impl Stream { Ok(()) } - Stream::HttpWrite(ref mut http_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(()) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 6a676ae9..2cfa521e 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -9,7 +9,7 @@ use crate::forms::*; use crate::ffi::*; use crate::heap_iter::*; use crate::heap_print::*; -use crate::http::{self, HttpListener, HttpResponse}; +use crate::http::{HttpService, HttpListener, HttpResponse}; use crate::instructions::*; use crate::machine; use crate::machine::{Machine, VERIFY_ATTR_INTERRUPT_LOC, get_structure_index}; @@ -39,7 +39,7 @@ use ref_thread_local::{RefThreadLocal, ref_thread_local}; use std::cell::Cell; use std::cmp::Ordering; use std::collections::BTreeSet; -use std::convert::{TryFrom, Infallible}; +use std::convert::{TryFrom}; use std::env; use std::ffi::CString; use std::fs; @@ -52,7 +52,6 @@ use std::num::NonZeroU32; use std::ops::Sub; use std::process; use std::str::FromStr; -use std::sync::Arc; use chrono::{offset::Local, DateTime}; use cpu_time::ProcessTime; @@ -80,13 +79,12 @@ use base64; use roxmltree; use select; -use hyper::{Body, Server, Client, HeaderMap, Method, Request, Response, Uri}; -use hyper::header::{HeaderName, HeaderValue}; -use hyper::body::Buf; -use hyper::service::{make_service_fn, service_fn}; -use hyper_tls::HttpsConnector; -use tokio::sync::Mutex; -use tokio::sync::mpsc::channel; +use hyper::server::conn::http1; +use hyper::header::{HeaderValue, HeaderName}; +use hyper::{HeaderMap, Method}; +use http_body_util::BodyExt; +use bytes::Buf; +use reqwest::Url; ref_thread_local! { pub(crate) static managed RANDOM_STATE: RandState<'static> = RandState::new(); @@ -3125,7 +3123,7 @@ impl Machine { } bytes.push(c as u8); - } + } } else { bytes = string.as_str().bytes().collect(); } @@ -4184,63 +4182,65 @@ impl Machine { }; if let Some(address_sink) = self.machine_st.value_to_str_like(address_sink) { let address_string = address_sink.as_str(); //to_string(); - let address: Uri = address_string.parse().unwrap(); + let address: Url = address_string.parse().unwrap(); - let stream = self.runtime.block_on(async { - let https = HttpsConnector::new(); - let client = Client::builder() - .build::<_, hyper::Body>(https); + let client = reqwest::blocking::Client::builder() + .build() + .unwrap(); - // request - let mut req = Request::builder() - .method(method) - .uri(address) - .body(Body::from(bytes)) - .unwrap(); - // request headers - *req.headers_mut() = headers; - // do it! - let resp = client.request(req).await.unwrap(); - // status code - let status = resp.status().as_u16(); - self.machine_st.unify_fixnum(Fixnum::build_with(status as i64), address_status); - // headers - let headers: Vec = resp.headers().iter().map(|(header_name, header_value)| { - let h = self.machine_st.heap.len(); + // request + let mut req = reqwest::blocking::Request::new(method, address); - let header_term = functor!( - self.machine_st.atom_tbl.build_with(header_name.as_str()), - [cell(string_as_cstr_cell!(self.machine_st.atom_tbl.build_with(header_value.to_str().unwrap())))] - ); + *req.headers_mut() = headers; + if bytes.len() > 0 { + *req.body_mut() = Some(reqwest::blocking::Body::from(bytes)); + } - self.machine_st.heap.extend(header_term.into_iter()); - str_loc_as_cell!(h) - }).collect(); + // do it! + match client.execute(req) { + Ok(resp) => { + // status code + let status = resp.status().as_u16(); + self.machine_st.unify_fixnum(Fixnum::build_with(status as i64), address_status); + // headers + let headers: Vec = resp.headers().iter().map(|(header_name, header_value)| { + let h = self.machine_st.heap.len(); - let headers_list = iter_to_heap_list(&mut self.machine_st.heap, headers.into_iter()); - unify!(self.machine_st, heap_loc_as_cell!(headers_list), self.machine_st.registers[6]); - // body - let buf = hyper::body::aggregate(resp).await.unwrap(); - let reader = buf.reader(); + let header_term = functor!( + self.machine_st.atom_tbl.build_with(header_name.as_str()), + [cell(string_as_cstr_cell!(self.machine_st.atom_tbl.build_with(header_value.to_str().unwrap())))] + ); - let mut stream = Stream::from_http_stream( - self.machine_st.atom_tbl.build_with(&address_string), - Box::new(reader), - &mut self.machine_st.arena - ); - *stream.options_mut() = StreamOptions::default(); - if let Some(alias) = stream.options().get_alias() { - self.indices.stream_aliases.insert(alias, stream); - } + self.machine_st.heap.extend(header_term.into_iter()); + str_loc_as_cell!(h) + }).collect(); - self.indices.streams.insert(stream); + let headers_list = iter_to_heap_list(&mut self.machine_st.heap, headers.into_iter()); + unify!(self.machine_st, heap_loc_as_cell!(headers_list), self.machine_st.registers[6]); + // body + let reader = resp.bytes().unwrap().reader(); - stream_as_cell!(stream) - }); + let mut stream = Stream::from_http_stream( + self.machine_st.atom_tbl.build_with(&address_string), + Box::new(reader), + &mut self.machine_st.arena + ); + *stream.options_mut() = StreamOptions::default(); + if let Some(alias) = stream.options().get_alias() { + self.indices.stream_aliases.insert(alias, stream); + } - let stream_addr = self.deref_register(2); - self.machine_st.bind(stream_addr.as_var().unwrap(), stream); + self.indices.streams.insert(stream); + let stream = stream_as_cell!(stream); + + let stream_addr = self.deref_register(2); + self.machine_st.bind(stream_addr.as_var().unwrap(), stream); + }, + Err(_) => { + self.machine_st.fail = true; + } + } } else { let err = self.machine_st.domain_error(DomainErrorType::SourceSink, address_sink); let stub = functor_stub(atom!("http_open"), 3); @@ -4264,26 +4264,31 @@ impl Machine { } }; - let (tx, rx) = channel(1); - let tx = Arc::new(Mutex::new(tx)); + let (tx, rx) = std::sync::mpsc::sync_channel(1024); let _guard = self.runtime.enter(); - let server = match Server::try_bind(&addr) { - Ok(server) => server, + let listener = match self.runtime.block_on(async { tokio::net::TcpListener::bind(addr).await }) { + Ok(listener) => listener, Err(_) => { return Err(self.machine_st.open_permission_error(address_sink, atom!("http_listen"), 2)); } }; self.runtime.spawn(async move { - let make_svc = make_service_fn(move |_conn| { + loop { let tx = tx.clone(); - async move { Ok::<_, Infallible>(service_fn(move |req| http::serve_req(req, tx.clone()))) } - }); - let server = server.serve(make_svc); + let (stream, _) = listener.accept().await.unwrap(); - if let Err(_) = server.await { - eprintln!("server error"); + tokio::task::spawn(async move { + if let Err(err) = http1::Builder::new() + .serve_connection(stream, HttpService { + tx + }) + .await + { + eprintln!("Error serving connection: {:?}", err); + } + }); } }); let http_listener = HttpListener { incoming: rx }; @@ -4306,8 +4311,8 @@ impl Machine { (HeapCellValueTag::Cons, cons_ptr) => { match_untyped_arena_ptr!(cons_ptr, (ArenaHeaderTag::HttpListener, http_listener) => { - match http_listener.incoming.blocking_recv() { - Some(request) => { + match http_listener.incoming.recv() { + Ok(request) => { let method_atom = match *request.request.method() { Method::GET => atom!("get"), Method::POST => atom!("post"), @@ -4338,7 +4343,7 @@ impl Machine { let query_cell = string_as_cstr_cell!(query_atom); let hyper_req = request.request; - let buf = self.runtime.block_on(async {hyper::body::aggregate(hyper_req).await.unwrap()}); + let buf = self.runtime.block_on(async {hyper_req.collect().await.unwrap().aggregate()}); let reader = buf.reader(); let mut stream = Stream::from_http_stream( @@ -4360,7 +4365,7 @@ impl Machine { self.machine_st.bind(stream_addr.as_var().unwrap(), stream); self.machine_st.bind(handle_addr.as_var().unwrap(), typed_arena_ptr_as_cell!(handle)); } - None => { + Err(_) => { self.machine_st.fail = true; } } @@ -4418,15 +4423,10 @@ impl Machine { (HeapCellValueTag::Cons, cons_ptr) => { match_untyped_arena_ptr!(cons_ptr, (ArenaHeaderTag::HttpResponse, http_response) => { - let mut response = Response::builder() - .status(status_code); - *response.headers_mut().unwrap() = headers; - let (sender, body) = Body::channel(); - let response = response.body(body).unwrap(); - http_response.blocking_send(response).unwrap(); - let mut stream = Stream::from_http_sender( - sender, + http_response, + status_code, + headers, &mut self.machine_st.arena ); *stream.options_mut() = StreamOptions::default(); From db972de40c05a86ef3db277772b607f268f643dc Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Fri, 30 Jun 2023 07:34:15 +0200 Subject: [PATCH 250/361] bracket all operators that are direct operands of (=)/2 This addresses #804. --- src/toplevel.pl | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/toplevel.pl b/src/toplevel.pl index b554e52c..ff463833 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -205,12 +205,14 @@ submit_query_and_print_results(Term, VarList) :- needs_bracketing(Value, Op) :- - catch((functor(Value, F, _), - current_op(EqPrec, EqSpec, Op), - current_op(FPrec, _, F)), - _, - false), - ( EqPrec < FPrec -> + nonvar(Value), + \+ integer(Value), + functor(Value, F, Arity), + current_op(FPrec, _, F), + current_op(EqPrec, EqSpec, Op), + ( Arity =:= 0 -> + true + ; EqPrec < FPrec -> true ; FPrec > 0, F == Value, graphic_token_char(F) -> true @@ -228,7 +230,7 @@ write_goal(G, VarList, MaxDepth) :- ), write(Var), write(' = '), - ( needs_bracketing(Value, (=)) -> + ( needs_bracketing(Value, =) -> write('('), write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), write(')') @@ -247,7 +249,7 @@ write_last_goal(G, VarList, MaxDepth) :- ), write(Var), write(' = '), - ( needs_bracketing(Value, (=)) -> + ( needs_bracketing(Value, =) -> write('('), write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), write(')') @@ -271,8 +273,7 @@ write_eq(G, VarList, MaxDepth) :- write_last_goal(G, VarList, MaxDepth). graphic_token_char(C) :- - memberchk(C, ['#', '$', '&', '*', '+', '-', '.', ('/'), ':', - '<', '=', '>', '?', '@', '^', '~', ('\\')]). + memberchk(C, [#, $, &, *, +, -, ., /, :, <, =, >, ?, @, ^, ~, \\]). list_last_item([C], C) :- !. list_last_item([_|Cs], D) :- From 28065b0565ec9ec50b8802e5eddf7575533182a3 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Fri, 30 Jun 2023 18:01:17 +0200 Subject: [PATCH 251/361] constrain bracketing to operators with pertaining arity Example: ?- X = -->(a,b,c). X = -->(a,b,c). --- src/toplevel.pl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/toplevel.pl b/src/toplevel.pl index ff463833..d61e324f 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -208,8 +208,9 @@ needs_bracketing(Value, Op) :- nonvar(Value), \+ integer(Value), functor(Value, F, Arity), - current_op(FPrec, _, F), + current_op(FPrec, FSpec, F), current_op(EqPrec, EqSpec, Op), + arity_specifier(Arity, FSpec), ( Arity =:= 0 -> true ; EqPrec < FPrec -> @@ -222,6 +223,10 @@ needs_bracketing(Value, Op) :- memberchk(EqSpec, [fx,xfx,yfx]) ). +arity_specifier(0, _). +arity_specifier(1, S) :- atom_chars(S, [_,_]). +arity_specifier(2, S) :- atom_chars(S, [_,_,_]). + write_goal(G, VarList, MaxDepth) :- ( G = (Var = Value) -> ( var(Value) -> From bfe808a779252d98e23db1e5d4e0fa01a31ac625 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Fri, 30 Jun 2023 18:01:54 +0200 Subject: [PATCH 252/361] shorten needs_bracketing/2 --- src/toplevel.pl | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/toplevel.pl b/src/toplevel.pl index d61e324f..7d1e2569 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -211,16 +211,12 @@ needs_bracketing(Value, Op) :- current_op(FPrec, FSpec, F), current_op(EqPrec, EqSpec, Op), arity_specifier(Arity, FSpec), - ( Arity =:= 0 -> - true - ; EqPrec < FPrec -> - true - ; FPrec > 0, F == Value, graphic_token_char(F) -> - true - ; F \== '.', '$quoted_token'(F) -> - true - ; EqPrec == FPrec, - memberchk(EqSpec, [fx,xfx,yfx]) + ( Arity =:= 0 + ; EqPrec < FPrec + ; FPrec > 0, F == Value, graphic_token_char(F) + ; F \== '.', '$quoted_token'(F) + ; EqPrec =:= FPrec, + member(EqSpec, [fx,xfx,yfx]) ). arity_specifier(0, _). From a3f8ddd24a786e44d9c7183bb724e6d536d2bc9b Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Fri, 30 Jun 2023 18:02:43 +0200 Subject: [PATCH 253/361] remove subsumed case: F == Value means Arity =:= 0, now considered above --- src/toplevel.pl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/toplevel.pl b/src/toplevel.pl index 7d1e2569..8a0d44ea 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -213,7 +213,6 @@ needs_bracketing(Value, Op) :- arity_specifier(Arity, FSpec), ( Arity =:= 0 ; EqPrec < FPrec - ; FPrec > 0, F == Value, graphic_token_char(F) ; F \== '.', '$quoted_token'(F) ; EqPrec =:= FPrec, member(EqSpec, [fx,xfx,yfx]) From 42282c6e6ec0cff6fc110b0a6be23addc4b11088 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Fri, 30 Jun 2023 18:03:21 +0200 Subject: [PATCH 254/361] remove unneeded case: only operator definitions should count, not quoting --- src/toplevel.pl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/toplevel.pl b/src/toplevel.pl index 8a0d44ea..2cfe04f5 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -213,7 +213,6 @@ needs_bracketing(Value, Op) :- arity_specifier(Arity, FSpec), ( Arity =:= 0 ; EqPrec < FPrec - ; F \== '.', '$quoted_token'(F) ; EqPrec =:= FPrec, member(EqSpec, [fx,xfx,yfx]) ). From 1620824d3ad40be577ec356ebe3d194184d127c1 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 30 Jun 2023 12:06:07 -0600 Subject: [PATCH 255/361] do not enclose '(' as atom in brackets (#1487) --- src/parser/parser.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 021147ea..1d7e035d 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -827,6 +827,10 @@ impl<'a, R: CharRead> Parser<'a, R> { return false; } + if let Some(TokenType::Open | TokenType::OpenCT) = self.stack.last().map(|token| token.tt) { + return false; + } + let idx = self.stack.len() - 2; let td = self.stack.remove(idx); From caf84a259eb304607cffb06aeda4118c0d5db903 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 30 Jun 2023 13:45:45 -0600 Subject: [PATCH 256/361] check that F in needs_bracketing/2 is an atom --- src/toplevel.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/toplevel.pl b/src/toplevel.pl index 2cfe04f5..d9c1f5c4 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -208,6 +208,7 @@ needs_bracketing(Value, Op) :- nonvar(Value), \+ integer(Value), functor(Value, F, Arity), + atom(F), current_op(FPrec, FSpec, F), current_op(EqPrec, EqSpec, Op), arity_specifier(Arity, FSpec), From 31030738a4d1d771757c64ea1fc99d8cd4b7c4fe Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Fri, 30 Jun 2023 22:14:00 +0200 Subject: [PATCH 257/361] remove now unneeded check --- src/toplevel.pl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/toplevel.pl b/src/toplevel.pl index d9c1f5c4..30de5227 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -206,7 +206,6 @@ submit_query_and_print_results(Term, VarList) :- needs_bracketing(Value, Op) :- nonvar(Value), - \+ integer(Value), functor(Value, F, Arity), atom(F), current_op(FPrec, FSpec, F), From b0566e41503a6c8d29b792b560defca8ca028cf5 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 30 Jun 2023 17:13:38 -0600 Subject: [PATCH 258/361] use lexer to detect remaining layout in parse_number_from_string (#1773) --- src/machine/system_calls.rs | 130 ++++++++++++++++++++---------------- src/machine/term_stream.rs | 2 +- src/parser/lexer.rs | 2 +- src/parser/parser.rs | 30 +++++++-- src/read.rs | 3 +- 5 files changed, 104 insertions(+), 63 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 6a676ae9..4c9c0f2f 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -886,75 +886,92 @@ impl MachineState { indices: &IndexStore, stub_gen: impl Fn() -> FunctorStub, ) -> CallResult { + use crate::parser::lexer::*; + let nx = self.store(self.deref(self.registers[2])); + let add_dot = !string.ends_with("."); + let cursor = std::io::Cursor::new(string); - let mut charcode_space = false; - let mut cs = string.chars(); + let iter = std::io::Read::chain( + cursor, + { + let mut dot_buf: [u8; '.'.len_utf8()] = [0u8]; - loop { - let c = cs.next(); + if add_dot { + '.'.encode_utf8(&mut dot_buf); + } - if c == None { - break; + std::io::Cursor::new(dot_buf) + }, + ); + + let mut lexer = Lexer::new(CharReader::new(iter), self); + let mut tokens = vec![]; + + match lexer.next_token() { + Ok(token @ Token::Literal(Literal::Atom(atom!("-")) | Literal::Char('-'))) => { + tokens.push(token); + + if let Ok(token) = lexer.next_token() { + tokens.push(token); + } } - - if c == Some('0') - && cs.next() == Some('\'') - && cs.next() == Some(' ') - && cs.next() == None { - charcode_space = true; - break; + Ok(token) => { + tokens.push(token); + } + Err(err) => { + let err = self.syntax_error(err); + return Err(self.error_form(err, stub_gen())); } } - if !charcode_space { - if let Some(c) = string.chars().last() { - if layout_char!(c) { - let (line_num, col_num) = string.chars().fold((0, 0), |(line_num, col_num), c| { - if new_line_char!(c) { - (1 + line_num, 0) - } else { - (line_num, col_num + 1) + loop { + match lexer.lookahead_char() { + Err(ParserError::UnexpectedEOF) => { + let mut parser = Parser::from_lexer(lexer); + let op_dir = CompositeOpDir::new(&indices.op_dir, None); + + tokens.reverse(); + + match parser.read_term(&op_dir, Tokens::Provided(tokens)) { + Err(err) => { + let err = self.syntax_error(err); + return Err(self.error_form(err, stub_gen())); } - }); + Ok(Term::Literal(_, Literal::Rational(n))) => { + self.unify_rational(n, nx); + } + Ok(Term::Literal(_, Literal::Float(n))) => { + self.unify_f64(n.as_ptr(), nx); + } + Ok(Term::Literal(_, Literal::Integer(n))) => { + self.unify_big_int(n, nx); + } + Ok(Term::Literal(_, Literal::Fixnum(n))) => { + self.unify_fixnum(n, nx); + } + _ => { + let err = ParserError::ParseBigInt(0, 0); + let err = self.syntax_error(err); + + return Err(self.error_form(err, stub_gen())); + } + } + + break; + } + Ok('.') => { + lexer.skip_char('.'); + } + Ok(c) => { + let (line_num, col_num) = (lexer.line_num, lexer.col_num); + let err = ParserError::UnexpectedChar(c, line_num, col_num); let err = self.syntax_error(err); return Err(self.error_form(err, stub_gen())); } - } - } - - let mut dot_buf: [u8; '.'.len_utf8()] = [0u8]; - '.'.encode_utf8(&mut dot_buf); - - let cursor = std::io::Cursor::new(string); - let iter = std::io::Read::chain(cursor, std::io::Cursor::new(dot_buf)); - - let mut parser = Parser::new(CharReader::new(iter), self); - - match parser.read_term(&CompositeOpDir::new(&indices.op_dir, None)) { - Err(err) => { - let err = self.syntax_error(err); - return Err(self.error_form(err, stub_gen())); - } - Ok(Term::Literal(_, Literal::Rational(n))) => { - self.unify_rational(n, nx); - } - Ok(Term::Literal(_, Literal::Float(n))) => { - self.unify_f64(n.as_ptr(), nx); - } - Ok(Term::Literal(_, Literal::Integer(n))) => { - self.unify_big_int(n, nx); - } - Ok(Term::Literal(_, Literal::Fixnum(n))) => { - self.unify_fixnum(n, nx); - } - _ => { - let err = ParserError::ParseBigInt(0, 0); - let err = self.syntax_error(err); - - return Err(self.error_form(err, stub_gen())); + Err(_) => unreachable!(), } } @@ -5730,8 +5747,9 @@ impl Machine { if let Some(atom_or_string) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { let chars = CharReader::new(ByteStream::from_string(atom_or_string.to_string())); let mut parser = Parser::new(chars, &mut self.machine_st); + let op_dir = CompositeOpDir::new(&self.indices.op_dir, None); - let term_write_result = parser.read_term(&CompositeOpDir::new(&self.indices.op_dir, None)) + let term_write_result = parser.read_term(&op_dir, Tokens::Default) .map_err(CompilationError::from) .and_then(|term| { write_term_to_heap( diff --git a/src/machine/term_stream.rs b/src/machine/term_stream.rs index bb92c8d4..98d77627 100644 --- a/src/machine/term_stream.rs +++ b/src/machine/term_stream.rs @@ -52,7 +52,7 @@ impl<'a> TermStream for BootstrappingTermStream<'a> { fn next(&mut self, op_dir: &CompositeOpDir) -> Result { self.parser.reset(); self.parser - .read_term(op_dir) + .read_term(op_dir, Tokens::Default) .map_err(CompilationError::from) } diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index 236585d9..7f5d73ed 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -110,7 +110,7 @@ impl<'a, R: CharRead> Lexer<'a, R> { self.reader.put_back_char(c); } - fn skip_char(&mut self, c: char) { + pub fn skip_char(&mut self, c: char) { self.reader.consume(c.len_utf8()); if new_line_char!(c) { diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 1d7e035d..5bb600da 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -24,6 +24,16 @@ enum TokenType { End, } +/* +Specifies whether the token sequence should be read from the lexer or +provided via the Provided variant. +*/ +#[derive(Debug)] +pub enum Tokens { + Default, + Provided(Vec), +} + impl TokenType { fn is_sep(self) -> bool { matches!( @@ -302,8 +312,17 @@ impl<'a, R: CharRead> Parser<'a, R> { Parser { lexer: Lexer::new(stream, machine_st), tokens: vec![], - stack: Vec::new(), - terms: Vec::new(), + stack: vec![], + terms: vec![], + } + } + + pub fn from_lexer(lexer: Lexer<'a, R>) -> Self { + Parser { + lexer, + tokens: vec![], + stack: vec![], + terms: vec![], } } @@ -1048,8 +1067,11 @@ impl<'a, R: CharRead> Parser<'a, R> { } // on success, returns the parsed term and the number of lines read. - pub fn read_term(&mut self, op_dir: &CompositeOpDir) -> Result { - self.tokens = read_tokens(&mut self.lexer)?; + pub fn read_term(&mut self, op_dir: &CompositeOpDir, tokens: Tokens) -> Result { + self.tokens = match tokens { + Tokens::Default => read_tokens(&mut self.lexer)?, + Tokens::Provided(tokens) => tokens, + }; while let Some(token) = self.tokens.pop() { self.shift_token(token, op_dir)?; diff --git a/src/read.rs b/src/read.rs index e42e3439..085c867e 100644 --- a/src/read.rs +++ b/src/read.rs @@ -45,10 +45,11 @@ impl MachineState { let (term, num_lines_read) = { let prior_num_lines_read = inner.lines_read(); let mut parser = Parser::new(inner, self); + let op_dir = CompositeOpDir::new(op_dir, None); parser.add_lines_read(prior_num_lines_read); - let term = parser.read_term(&CompositeOpDir::new(op_dir, None)) + let term = parser.read_term(&op_dir, Tokens::Default) .map_err(CompilationError::from)?; (term, parser.lines_read() - prior_num_lines_read) From d079a18459feb29e24ab7863d784c3bc8177801a Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 2 Jul 2023 11:10:25 -0600 Subject: [PATCH 259/361] removing residual debugging comments from format.pl --- src/lib/format.pl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/format.pl b/src/lib/format.pl index be1cd532..32ad2ff9 100644 --- a/src/lib/format.pl +++ b/src/lib/format.pl @@ -513,12 +513,10 @@ portray_clause(Stream, Term) :- phrase_to_stream(portray_clause_(Term), Stream), flush_output(Stream). -% called once. portray_clause_(Term) --> { unique_variable_names(Term, VNs) }, portray_(Term, VNs), ".\n". -% mysteriously called twice, the second time with the truncated B3. unique_variable_names(Term, VNs) :- term_variables(Term, Vs), foldl(var_name, Vs, VNs, 0, _). From 699afb2c00358aa0e16f36c6c50ef07c64e90626 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 29 Jun 2023 12:18:14 -0600 Subject: [PATCH 260/361] introduce tests-pl/iso-conformity-tests.pl --- tests-pl/iso-conformity-tests.pl | 1023 ++++++++++++++++++++++++++++++ 1 file changed, 1023 insertions(+) create mode 100644 tests-pl/iso-conformity-tests.pl diff --git a/tests-pl/iso-conformity-tests.pl b/tests-pl/iso-conformity-tests.pl new file mode 100644 index 00000000..e263fa9f --- /dev/null +++ b/tests-pl/iso-conformity-tests.pl @@ -0,0 +1,1023 @@ +:- module(iso_conformity_tests, []). + +:- use_module(library(charsio)). +:- use_module(library(dcgs)). +:- use_module(library(files)). +:- use_module(library(format)). +:- use_module(library(iso_ext)). +:- use_module(library(lists), [append/3]). + +writeq_term_to_chars(Term, Chars) :- + Options = [ignore_ops(false), numbervars(true), quoted(true), variable_names([])], + write_term_to_chars(Term, Options, Chars). + +write_term_to_chars(Term, Chars) :- + Options = [ignore_ops(false), numbervars(false), quoted(false), variable_names([])], + write_term_to_chars(Term, Options, Chars). + +write_canonical_term_to_chars(Term, Chars) :- + Options = [ignore_ops(true), numbervars(false), quoted(true), variable_names([])], + write_term_to_chars(Term, Options, Chars). + +test_syntax_error(ReadString, Error) :- + catch((once(read_from_chars(ReadString, _)), + false), + error(Error, _), + true). + +test_1 :- write_term_to_chars('\n', Chars), + Chars = "\n". + +test_2 :- test_syntax_error("'\n", syntax_error(_)). + +test_3 :- test_syntax_error(")\n", syntax_error(incomplete_reduction)). + +test_261 :- test_syntax_error(")\n'\n", syntax_error(invalid_single_quoted_character)). + +test_4 :- test_syntax_error(".\n", syntax_error(incomplete_reduction)). + +test_177 :- test_syntax_error("0'\t=0' .", syntax_error(unexpected_char)). + +test_6 :- test_syntax_error("writeq('\n').", syntax_error(invalid_single_quoted_character)). + +test_7 :- read_from_chars("writeq('\\\n').", T), + T == writeq(''). + +test_8 :- read_from_chars("writeq('\\\na').", T), + T == writeq(a). + +test_9 :- read_from_chars("writeq('a\\\nb').", T), + T == writeq(ab). + +test_10 :- read_from_chars("writeq('a\\\n b').", T), + T == writeq('a b'). + +test_11 :- test_syntax_error("writeq('\\ ').", syntax_error(invalid_single_quoted_character)). + +test_193 :- test_syntax_error("writeq('\\ \n').", syntax_error(invalid_single_quoted_character)). + +test_12 :- test_syntax_error("writeq('\\\t').", syntax_error(invalid_single_quoted_character)). + +test_13 :- read_from_chars("writeq('\\t').", T), + T == writeq('\t'). + +test_14 :- read_from_chars("writeq('\\a').", T), + T == writeq('\a'). + +test_15 :- read_from_chars("writeq('\\7\\').", T), + T == writeq('\a'). + +test_16 :- test_syntax_error("writeq('\\ca').", syntax_error(invalid_single_quoted_character)). + +test_241 :- test_syntax_error("writeq('\\d').", syntax_error(invalid_single_quoted_character)). + +test_17 :- test_syntax_error("writeq('\\e').", syntax_error(invalid_single_quoted_character)). + +test_18 :- read_from_chars("writeq('\\033\\').", T), + T = writeq('\x1b\'). + +test_301 :- read_from_chars("writeq('\\0\\').", T), + T = writeq('\x0\'). + +test_19 :- test_syntax_error("char_code('\\e', C).", syntax_error(invalid_single_quoted_character)). + +test_21 :- test_syntax_error("char_code('\\d', C).", syntax_error(invalid_single_quoted_character)). + +test_22 :- test_syntax_error("writeq('\\u1').", syntax_error(invalid_single_quoted_character)). + +test_23 :- test_syntax_error("X = 0'\\u1.", syntax_error(unexpected_char)). + +test_24 :- test_syntax_error("writeq('\n", syntax_error(invalid_single_quoted_character)). + +test_25 :- test_syntax_error("writeq(.", syntax_error(incomplete_reduction)). + +test_26 :- test_syntax_error("'\\\n''.\n", syntax_error(invalid_single_quoted_character)). + +test_210 :- test_syntax_error("X = 0'\\.", syntax_error(unexpected_char)). + +test_211 :- test_syntax_error("X = 0'\\. .", syntax_error(unexpected_char)). + +test_222 :- writeq_term_to_chars((-)-(-), T), + T == "(-)-(-)". + +test_223 :- writeq_term_to_chars(((:-):-(:-)), T), + T == "(:-):-(:-)". + +test_27 :- writeq_term_to_chars((*)=(*), T), + T == "(*)=(*)". + +test_28 :- writeq_term_to_chars([:-,-], T), + T == "[:-,-]". + +test_29 :- writeq_term_to_chars(f(*), T), + T == "f(*)". + +test_30 :- writeq_term_to_chars(a*(b+c), T), + T == "a*(b+c)". + +test_31 :- writeq_term_to_chars(f(;,'|',';;'), T), + T == "f(;,'|',';;')". + +test_32 :- read_from_chars("[.,.(.,.,.)].", T), + writeq_term_to_chars(T, Chars), + Chars == "['.','.'('.','.','.')]". + +test_33 :- writeq_term_to_chars((a :- b,c), Chars), + Chars == "a:-b,c". + +test_34 :- write_canonical_term_to_chars([a], T), + T == "'.'(a,[])". + +test_35 :- writeq_term_to_chars('/*', Chars), + Chars == "'/*'". + +test_203 :- writeq_term_to_chars(//*, Chars), + Chars == "//*". + +test_282 :- writeq_term_to_chars(//*.*/, Chars), + Chars == "//*.*/". + +test_36 :- writeq_term_to_chars('/**', Chars), + Chars == "'/**'". + +test_37 :- writeq_term_to_chars('*/', Chars), + Chars == "*/". + +test_38 :- "\'\`\"" = "'`""". + +test_179 :- "\'\"" = "'""". + +test_178 :- "\`" = "`". + +test_39 :- '\'\`\"' = '''`"'. + +test_40 :- writeq_term_to_chars('\'\`\"\"', T), + T == "'\\'`\"\"'". + +test_41 :- ('\\') = (\). + +test_42 :- setup_call_cleanup(op(1,xf,xf1), + ( read_from_chars("1xf1 = xf1(1).", T), + call(T) + ), + op(0,xf,xf1)). + +test_43 :- test_syntax_error("X = 0X1.", syntax_error(incomplete_reduction)). + +test_44 :- test_syntax_error("float(.0).", syntax_error(incomplete_reduction)). + +test_45 :- setup_call_cleanup(op(100,xfx,.), + ( read_from_chars("functor(3 .2,F,A).", T), + call(T), + T == functor('.'(3,2),'.',2) + ), + op(0,xfx,.)). + +test_46 :- test_syntax_error("float(- .0).", syntax_error(incomplete_reduction)). + +test_47 :- test_syntax_error("float(1E9).", syntax_error(incomplete_reduction)). + +test_48 :- test_syntax_error("integer(1e).", syntax_error(incomplete_reduction)). + +test_49 :- setup_call_cleanup(op(9,xf,e9), + ( read_from_chars("1e9 = e9(1).", T), + call(T) + ), + op(0,xf,e9)). + +test_50_51_204_220 :- + setup_call_cleanup(op(9,xf,e), + ( read_from_chars("1e-9 = -(e(1),9).", T0), + call(T0), + read_from_chars("1.0e- 9 = -(e(1.0),9).", T1), + call(T1), + read_from_chars("1e.", T2), + writeq_term_to_chars(T2, T3), + T3 == "1 e", + read_from_chars("1.0e.", T4), + writeq_term_to_chars(T4, T5), + T5 == "1.0 e" + ), + op(0,xf,e)). + +test_52 :- setup_call_cleanup(op(9,xfy,e), + ( read_from_chars("1.2e 3 = e(X,Y).", T0), + call(T0) + ), + op(0,xfy,e)). + +test_53 :- writeq_term_to_chars(1.0e100, Chars), + Chars == "1.0e100". + +test_54 :- test_syntax_error("float(1.0ee9).", syntax_error(incomplete_reduction)). + +test_286 :- (- (1)) = -(1). + +test_287 :- (- -1) = -(-1). + +test_288 :- (- 1^2) = ^(-1,2). + +test_56 :- integer(- 1). + +test_57 :- integer('-'1). + +test_58 :- integer('-' 1). + +test_59 :- integer(- /*.*/1). + +test_60 :- test_syntax_error("integer(-/*.*/1).", syntax_error(incomplete_reduction)). + +test_61 :- integer('-'/*.*/1). + +test_62 :- atom(-/*.*/-). + +test_63_180_64 :- setup_call_cleanup(( current_op(P,fy,-), + op(0,fy,-) + ), + ( integer(-1), + integer(- 1) + ), + op(P,fy,-)). + +test_135 :- writeq_term_to_chars(-(1), Chars), + Chars == "- (1)". + +test_136 :- setup_call_cleanup(( current_op(P,fy,-), + op(0,fy,-) + ), + ( writeq_term_to_chars(-(1), Chars), + Chars == "-(1)" + ), + op(P,fy,-)). + +test_182 :- writeq_term_to_chars(-(-1), Chars), + Chars == "- -1". + +test_183 :- writeq_term_to_chars(-(1^2), Chars), + Chars == "- (1^2)". + +test_260 :- writeq_term_to_chars(-(a^2), Chars), + Chars == "- (a^2)". + +test_139 :- writeq_term_to_chars(-((a,b)), Chars), + Chars == "- (a,b)". + +test_218 :- writeq_term_to_chars(-(1*2), Chars), + Chars == "- (1*2)". + +test_140 :- writeq_term_to_chars(-a, Chars), + Chars == "- a". + +test_184 :- writeq_term_to_chars(-(-), Chars), + Chars == "- (-)". + +test_185 :- writeq_term_to_chars(-[-], Chars), + Chars == "- \"-\"". + +test_188 :- writeq_term_to_chars(-p(c), Chars), + Chars == "- p(c)". + +test_189 :- writeq_term_to_chars(-{}, Chars), + Chars == "- {}". + +test_190 :- writeq_term_to_chars(-{a}, Chars), + Chars == "- {a}". + +test_191 :- writeq_term_to_chars(-(-a), Chars), + Chars == "- - a". + +test_192 :- writeq_term_to_chars(-(-(-a)), Chars), + Chars == "- - - a". + +test_216 :- writeq_term_to_chars(-(-1), Chars), + Chars == "- -1". + +test_215_248_249 :- + setup_call_cleanup(op(100,yfx,~), + ( read_from_chars("-(1~2~3).", T0), + writeq_term_to_chars(T0, Chars0), + Chars0 == "- (1~2~3)", + read_from_chars("- (1~2).", T1), + writeq_term_to_chars(T1, Chars1), + Chars1 == "- (1~2)", + read_from_chars("1~2.", T2), + writeq_term_to_chars(T2, Chars2), + Chars2 == "1~2" + ), + op(0,yfx,~)). + +test_278 :- setup_call_cleanup(op(9,xfy,.), + ( writeq_term_to_chars(-[1], Chars), + Chars == "- [1]" + ), + op(0,xfy,.)). + +test_279_296 :- + setup_call_cleanup(op(9,xf,'$VAR'), + ( writeq_term_to_chars(-'$VAR'(0), Chars0), + Chars0 == "- A", + writeq_term_to_chars('$VAR'(0), Chars1), + Chars1 == "A" + ), + op(0,xf,'$VAR')). + +test_55 :- setup_call_cleanup(op(1,yf,yf1), + ( read_from_chars("{-1 yf1}={yf1(X)}.", T), + call(T), + T = (_ = { yf1(-1) }) + ), + op(0,yf,yf1)). + +test_65 :- compound(+1). + +test_66 :- compound(+ 1). + +test_277 :- writeq_term_to_chars(+ 1^2, _). + +test_67 :- setup_call_cleanup(( current_op(P,fy,+), + op(0,fy,+) + ), + compound(+1), + op(P,fy,+)). + +test_257 :- writeq_term_to_chars([+{a},+[]], Chars), + Chars == "[+{a},+[]]". + +test_68 :- [(:-)|(:-)]=[:-|:-]. + +test_69 :- test_syntax_error("X=[a|b,c].", syntax_error(incomplete_reduction)). + +test_70 :- catch((op(1000,xfy,','), + false), + error(permission_error(modify, operator, ','), op/3), + true). + +test_71 :- catch((op(1001,xfy,','), + false), + error(permission_error(modify, operator, ','), op/3), + true). + +test_72 :- catch((op(999,xfy,'|'), + false), + error(permission_error(create, operator, '|'), op/3), + true). + +test_73 :- _ = [a|b]. + +test_285 :- test_syntax_error("X = [(a|b)].", syntax_error(_)). + +test_219 :- [a|[]] = [a]. + +test_74 :- test_syntax_error("X = [a|b|c].", syntax_error(incomplete_reduction)). + +test_75 :- test_syntax_error("var(a:-b).", syntax_error(incomplete_reduction)). + +test_76 :- test_syntax_error(":- = :- .", syntax_error(incomplete_reduction)). + +test_77 :- test_syntax_error("- = - .", syntax_error(incomplete_reduction)). + +test_78 :- test_syntax_error("* = * .", syntax_error(incomplete_reduction)). + +test_79 :- current_op(200,fy,-), !. + +test_80 :- current_op(200,fy,+), !. + +test_81 :- {- - c}={-(-(c))}. + +test_82 :- test_syntax_error("(- -) = -(-). ", syntax_error(incomplete_reduction)). + +test_83 :- test_syntax_error("(- - -) = -(-(-)). ", syntax_error(incomplete_reduction)). + +test_84 :- test_syntax_error("(- - - -) = -(-(-(-))). ", syntax_error(incomplete_reduction)). + +test_85 :- test_syntax_error("{:- :- c} = {:-(:-,c)}.", syntax_error(incomplete_reduction)). + +test_86 :- test_syntax_error("{- = - 1}={(-(=)) - 1}. ", syntax_error(incomplete_reduction)). + +test_87 :- test_syntax_error("write_canonical((- = - 1)). ", syntax_error(incomplete_reduction)). + +test_88 :- test_syntax_error("write_canonical((- = -1)). ", syntax_error(incomplete_reduction)). + +test_89 :- test_syntax_error("write_canonical((-;)). ", syntax_error(incomplete_reduction)). + +test_90 :- test_syntax_error("write_canonical((-;-)). ", syntax_error(incomplete_reduction)). + +test_91 :- test_syntax_error("write_canonical((;-;-)). ", syntax_error(incomplete_reduction)). + +test_92 :- test_syntax_error("[:- -c] = [(:- -c)].", syntax_error(incomplete_reduction)). + +test_93 :- test_syntax_error("writeq([a,b|,]).", syntax_error(incomplete_reduction)). + +test_94 :- test_syntax_error("X = {,}.", syntax_error(incomplete_reduction)). + +test_95 :- {1} = {}(1). + +test_96 :- write_canonical_term_to_chars({1}, Chars), + Chars == "{}(1)". + +test_97 :- '[]'(1) = [ ](X), + X == 1. + +test_98 :- test_syntax_error("X = [] (1).", syntax_error(incomplete_reduction)). + +test_99 :- catch((op(100,yfy,op), + false), + error(domain_error(operator_specifier, yfy), op/3), + true). + +test_100 :- '''' = '\''. + +test_101 :- a = '\141\'. + +test_102 :- test_syntax_error("a = '\\141'.", syntax_error(incomplete_reduction)). + +test_103 :- X = '\141\141', + X == a141. + +test_104 :- test_syntax_error("X = '\\9'.", syntax_error(invalid_single_quoted_character)). + +test_105 :- test_syntax_error("X = '\\N'.", syntax_error(invalid_single_quoted_character)). + +test_106 :- test_syntax_error("X = '\\\\'.", syntax_error(incomplete_reduction)). + +test_107 :- test_syntax_error("X = '\\77777777777\\'.", syntax_error(cannot_parse_big_int)). + +test_108 :- a = '\x61\'. + +test_109 :- test_syntax_error("atom_codes('\\xG\\',Cs).", syntax_error(incomplete_reduction)). + +test_110 :- test_syntax_error("atom_codes('\\xG1\\',Cs).", syntax_error(incomplete_reduction)). + +test_111 :- test_syntax_error("atom(`).", syntax_error(incomplete_reduction)). + +test_112 :- test_syntax_error("atom(`+).", syntax_error(incomplete_reduction)). + +test_297 :- test_syntax_error("atom(`\n`).", syntax_error(missing_quote)). + +test_113 :- test_syntax_error("X =`a`.", syntax_error(back_quoted_string)). + +test_114 :- integer(0'\'). + +test_115 :- integer(0'''). + +test_116 :- 0''' = 0'\'. + +test_117 :- test_syntax_error("integer(0'').", syntax_error(incomplete_reduction)). + +test_195_205_196_197 :- + setup_call_cleanup(op(100,xf,''), + ( read_from_chars("(0 '') = ''(X).", T0), + call(T0), + T0 = (_ = ('')(0)), + read_from_chars("0 ''.", T1), + writeq_term_to_chars(T1, C0), + C0 == "0 ''", + read_from_chars("0''.", T2), + writeq_term_to_chars(T2, C1), + C1 == "0 ''" ), + op(0,xf,'')). + +test_118_119_120 :- + setup_call_cleanup(op(100,xfx,''), + ( read_from_chars("functor(0 ''1, F, A).", T0), + call(T0), + T0 = functor(_, (''), 2), + read_from_chars("functor(0''1, F, A).", T1), + call(T1), + T1 = functor(_, (''), 2) + ), + op(0,xfx,'')). + +test_206_207_209_256 :- + setup_call_cleanup(op(100,xf,f), + ( test_syntax_error("0'f'.", syntax_error(incomplete_reduction)), + read_from_chars("0'f'f'.", T0), + writeq_term_to_chars(T0, C0), + C0 == "102 f", + read_from_chars("0'ff.", T1), + writeq_term_to_chars(T1, C1), + C1 == "102 f", + read_from_chars("0f.", T2), + writeq_term_to_chars(T2, C2), + C2 == "0 f" + ), + op(0,xf,f)). + +test_208 :- setup_call_cleanup(op(100,xf,'f '), + ( read_from_chars("0 'f '.", T0), + writeq_term_to_chars(T0, C0), + C0 == "0 'f '"), + op(0,xf,'f ')). + +test_121 :- test_syntax_error("X = 2'1.", syntax_error(incomplete_reduction)). + +test_122_262 :- + setup_call_cleanup(op(100,xfx,'1 '), + ( read_from_chars("functor(2'1 'y, F, A).", T0), + call(T0), + T0 = functor(_, ('1 '), 2), + read_from_chars("functor(2 '1 'y, F, A).", T1), + call(T1), + T1 = functor(_, ('1 '), 2) + ), + op(0,xfx,'1 ')). + +test_123 :- read_from_chars("X = 0'\\x41\\ .", T), + T = (_ = A), + A == 65. + +test_124 :- X =0'\x41\, + X == 65. + +test_125 :- X =0'\x1\, + X == 1. + +test_127 :- X is 16'mod'2, + X == 0. + +test_128 :- X is 37'mod'2, + X == 1. + +test_129 :- test_syntax_error("X is 0'mod'1.", syntax_error(incomplete_reduction)). + +test_130 :- X is 1'+'1, + X == 2. + +test_212 :- read_from_chars("X is 1'\\\n+'1.", T), + T = (_ is 1+1), + call(T). + +test_213 :- read_from_chars("X is 0'\\\n+'1.", T), + T = (_ is 0+1), + call(T). + +test_259 :- read_from_chars("X is 0'\\\n+'/*'. % */1.", T), + T = (_ is 0+1), + call(T). + +test_303 :- test_syntax_error("X = 0'\\\na.", syntax_error(incomplete_reduction)). + +test_214 :- test_syntax_error("X is 0'\\", syntax_error(incomplete_reduction)). + +test_126 :- test_syntax_error("X = 0'\\\n.\\", syntax_error(incomplete_reduction)). + +test_131_132_133 :- + setup_call_cleanup(op(100,fx,' op'), + ( read_from_chars("' op' '1 '.", T0), + writeq_term_to_chars(T0, C0), + C0 == "' op' '1 '", + read_from_chars("' op'[].", T1), + writeq_term_to_chars(T1, C1), + C1 == "' op'[]" + ), + op(0, fx, ' op') + ). + +test_134 :- + setup_call_cleanup(op(1,xf,xf1), + test_syntax_error("{- =xf1}.", syntax_error(incomplete_reduction)), + op(0,xf,xf1)). + +test_137 :- writeq_term_to_chars(- (a*b), Chars), + Chars == "- (a*b)". + +test_138 :- writeq_term_to_chars(\ (a*b), Chars), + Chars == "\\ (a*b)". + +test_141 :- \+ current_op(_,xfy,.). + +test_142_143_144_221_258 :- + setup_call_cleanup(op(100,xfy,.), + ( read_from_chars("1 .2.", T0), + writeq_term_to_chars(T0, C0), + C0 == "[1|2]", + read_from_chars("[1].", T1), + writeq_term_to_chars(T1, C1), + C1 == "[1]", + read_from_chars("-[1].", T2), + writeq_term_to_chars(T2, C2), + C2 == "- [1]", + read_from_chars("X = 1.e.", T3), + writeq_term_to_chars(T3, C3), + C3 == "A=[1|e]", + read_from_chars("writeq(ok).%\n1=X.", T4), + T4 = writeq(ok) + ), + op(0,xfy,.)). + +test_145 :- write_canonical_term_to_chars('$VAR'(0), Cs), + Cs == "'$VAR'(0)". + +test_146 :- write_term_to_chars('$VAR'(0), [], Cs), + Cs == "$VAR(0)". + +test_244 :- writeq_term_to_chars('$VAR'(0), Cs), + Cs == "A". + +test_245 :- writeq_term_to_chars('$VAR'(-1), Cs), + Cs == "'$VAR'(-1)". + +test_246 :- writeq_term_to_chars('$VAR'(-2), Cs), + Cs == "'$VAR'(-2)". + +test_247 :- writeq_term_to_chars('$VAR'(x), Cs), + Cs == "'$VAR'(x)". + +test_289 :- writeq_term_to_chars('$VAR'('A'), Cs), + Cs == "'$VAR'('A')". + +test_147_148_149_150 :- + setup_call_cleanup(( op(9,fy,fy), + op(9,yf,yf)), + ( read_from_chars("fy 1 yf.", T0), + write_canonical_term_to_chars(T0, C0), + C0 == "fy(yf(1))", + test_syntax_error("fy yf.", syntax_error(incomplete_reduction)), + read_from_chars("fy(yf(1)).", T1), + writeq_term_to_chars(T1, C1), + C1 == "fy 1 yf", + read_from_chars("yf(fy(1)).", T2), + writeq_term_to_chars(T2, C2), + C2 == "(fy 1)yf" + ), + ( op(0,fy,fy), + op(0,yf,yf))). + +test_151_152_153 :- + setup_call_cleanup(( op(9,fy,fy), + op(9,yfx,yfx)), + ( read_from_chars("fy 1 yfx 2.", T0), + write_canonical_term_to_chars(T0, C0), + C0 == "fy(yfx(1,2))", + read_from_chars("fy(yfx(1,2)).", T1), + writeq_term_to_chars(T1, C1), + C1 == "fy 1 yfx 2", + read_from_chars("yfx(fy(1),2).", T2), + writeq_term_to_chars(T2, C2), + C2 == "(fy 1)yfx 2" + ), + ( op(0,fy,fy), + op(0,yfx,yfx))). + +test_154_155_156 :- + setup_call_cleanup(( op(9,yf,yf), + op(9,xfy,xfy)), + ( read_from_chars("1 xfy 2 yf.", T0), + write_canonical_term_to_chars(T0, C0), + C0 == "xfy(1,yf(2))", + read_from_chars("xfy(1,yf(2)).", T1), + writeq_term_to_chars(T1, C1), + C1 == "1 xfy 2 yf", + read_from_chars("yf(xfy(1,2)).", T2), + writeq_term_to_chars(T2, C2), + C2 == "(1 xfy 2)yf" + ), + ( op(0,yf,yf), + op(0,xfy,xfy)) + ). + +test_157 :- setup_call_cleanup((( current_op(P,xfy,:-) -> + true + ; P = 0 + ), + op(0,xfy,:-) + ), + \+ current_op(_,xfx,:-), + ( op(P,xfy,:-), + op(1200,xfx,:-) ) + ). + +test_158 :- catch((op(0,xfy,','), + false), + error(permission_error(modify, operator, (',')), op/3), + true). + +test_159_201_202_160_161 :- + setup_call_cleanup(( op(9,fy,f), + op(9,yf,f)), + ( read_from_chars("f f 0.", T0), + write_canonical_term_to_chars(T0, C0), + C0 == "f(f(0))", + read_from_chars("f(f(0)).", T1), + writeq_term_to_chars(T1, C1), + C1 == "f f 0", + read_from_chars("f 0 f.", T2), + write_canonical_term_to_chars(T2, C2), + C2 == "f(f(0))", + read_from_chars("0 f f.", T3), + write_canonical_term_to_chars(T3, C3), + C3 == "f(f(0))", + test_syntax_error("f f.", syntax_error(incomplete_reduction)) + ), + ( op(0,fy,f), + op(0,yf,f))). + +test_162 :- setup_call_cleanup((op(9,fy,p),op(9,yfx,p)), + test_syntax_error("1 p p p 2.", syntax_error(incomplete_reduction)), + (op(0,fy,p),op(0,yfx,p))). + +test_163 :- setup_call_cleanup((op(9,fy,p),op(9,xfy,p)), + ( read_from_chars("1 p p p 2.", T), + write_canonical_term_to_chars(T, C), + C == "p(1,p(p(2)))" + ), + (op(0,fy,p),op(0,xfy,p))). + +test_164 :- setup_call_cleanup((op(7,fy,p),op(9,yfx,p)), + ( read_from_chars("1 p p p 2.", T), + write_canonical_term_to_chars(T, C), + C == "p(1,p(p(2)))" + ), + (op(0,fy,p),op(0,yfx,p))). + +test_165 :- atom('.''-''.'). + +test_166_167 :- setup_call_cleanup(current_op(P,xfy,'|'), + ( op(0,xfy,'|'), + test_syntax_error("(a|b).", syntax_error(incomplete_reduction))), + op(P,xfy,'|')). + +test_168_169 :- call_cleanup(( op(0,xfy,.), + op(9,yf,.), + read_from_chars(".(.).", T), + writeq_term_to_chars(T, C), + C == "('.')'.'" ), + op(0,yf,.)). + +test_194 :- op(0,xfy,.), + writeq_term_to_chars((.)+(.), C), + C == "'.'+'.'". + +test_170 :- set_prolog_flag(double_quotes,chars). + +test_171 :- writeq_term_to_chars("a", C), + C == "\"a\"". + +test_229 :- test_syntax_error("\"\\z.\"", syntax_error(missing_quote)). + +test_300 :- writeq_term_to_chars("\0\", C), + C == "\"\\x0\\\"". + +test_172 :- X is 10.0** -323, + writeq_term_to_chars(X, C), + C == "1.0e-323". + +test_173 :- 1.0e-323=:=10.0** -323. + +test_174 :- -1 = -0x1. + +test_175 :- T = t(0b1,0o1,0x1), + T = t(1,1,1). + +test_176 :- X is 0b1mod 2, + X == 1. + +test_217_181_290 :- + setup_call_cleanup(( current_op(P, xfy, '|') -> + true + ; P = 0 + ), + ( op(1105,xfy,'|'), + read_from_chars("(a-->b,c|d).", T0), + writeq_term_to_chars(T0, C0), + C0 == "a-->b,c | d", + read_from_chars("[(a|b)].", T1), + writeq_term_to_chars(T1, C1), + C1 == "[(a | b)]" + ), + op(P, xfy, '|')). + +test_186 :- X/* /*/=7, + X == 7. + +test_187 :- X/*/*/=7, + X == 7. + +test_198 :- atom($-). + +test_199 :- atom(-$). + +test_200 :- setup_call_cleanup(op(900, fy, [$]), + ( read_from_chars("$a+b.", T), + write_canonical_term_to_chars(T, C), + C == "$(+(a,b))" + ), + op(0,fy,[$])). + +test_224 :- catch((read_from_chars("\\ .", T), + call(T), + false), + error(existence_error(procedure,(\)/0), _), + true). + +test_225 :- char_code(C,0), + writeq_term_to_chars(C, Cs), + Cs == "'\\x0\\'". + +test_250 :- writeq_term_to_chars('\0\', C), + C == "'\\x0\\'". + +test_226 :- write_canonical_term_to_chars(_+_, Cs), + Cs == "+(A,B)". % note that no variable names are supplied by write_canonical_term_to_chars/2. + +test_227 :- write_canonical_term_to_chars(A+A, Cs), + Cs == "+(A,A)". + +test_228 :- test_syntax_error("writeq(0'\\z).", syntax_error(unexpected_char)). + +test_230 :- test_syntax_error("char_code('\\^',X).", syntax_error(invalid_single_quoted_character)). + +test_231 :- test_syntax_error("writeq(0'\\c).", syntax_error(unexpected_char)). + +test_232 :- test_syntax_error("writeq(0'\\ ).", syntax_error(unexpected_char)). + +test_233 :- test_syntax_error("writeq(nop (1)).", syntax_error(incomplete_reduction)). + +test_234_235 :- setup_call_cleanup(op(400,fx,f), + ( read_from_chars("f/*.*/(1,2).", T), + writeq_term_to_chars(T, C), + C == "f (1,2)", + test_syntax_error("1 = f.", syntax_error(incomplete_reduction)) + ), + op(0,fx,f)). + +test_236 :- write_canonical_term_to_chars(a- - -b, Cs), + Cs == "-(a,-(-(b)))". + +test_237 :- catch((op(699,xf,>), + false), + error(permission_error(create,operator,>),op/3), + true). + +test_238 :- writeq_term_to_chars(>(>(a),b), Cs), + Cs == ">(a)>b". + +test_239 :- test_syntax_error("a> >b.", syntax_error(incomplete_reduction)). + +test_242 :- test_syntax_error("a> =b.", syntax_error(incomplete_reduction)). + +test_243 :- test_syntax_error("a>,b.", syntax_error(incomplete_reduction)). + +test_240 :- test_syntax_error("a>.", syntax_error(incomplete_reduction)). + +test_251_263_252_253_254_255 :- + setup_call_cleanup(op(9,yfx,[bop,bo,b,op,xor]), + ( read_from_chars("0 bop 2.", T0), + writeq_term_to_chars(T0, C0), + C0 == "0 bop 2", + read_from_chars("0bo 2.", T1), + writeq_term_to_chars(T1, C1), + C1 == "0 bo 2", + read_from_chars("0b 2.", T2), + writeq_term_to_chars(T2, C2), + C2 == "0 b 2", + read_from_chars("0op 2.", T3), + writeq_term_to_chars(T3, C3), + C3 == "0 op 2", + read_from_chars("0xor 2.", T4), + writeq_term_to_chars(T4, C4), + C4 == "0 xor 2" + ), + op(0,yfx,[bop,bo,b,op,xor])). + +test_264 :- writeq_term_to_chars('^`', C), + C == "'^`'". + +test_265_266_267 :- + setup_call_cleanup(op(9,yf,[b2,o8]), + ( read_from_chars("0b2.", T0), + writeq_term_to_chars(T0, C0), + C0 == "0 b2", + read_from_chars("0o8.", T1), + writeq_term_to_chars(T1, C1), + C1 == "0 o8" + ), + op(0,yf,[b2,o8])). + +test_268 :- catch((op(500,xfy,{}), + false), + error(permission_error(create, operator, {}), op/3), + true). + +test_269 :- writeq_term_to_chars('\b\r\f\t\n', C), + C == "'\\b\\r\\f\\t\\n'". + +test_270 :- + setup_call_cleanup((open("test_270.txt", write, WriteFile), + format(WriteFile, "get_char(Stream, C). %\n", []), + close(WriteFile), + open("test_270.txt", read, ReadFile)), + (read_term(ReadFile, T, []), + T = get_char(ReadFile, C), + call(T), + C == ' '), + (close(ReadFile), + delete_file("test_270.txt"))). + +test_271 :- + setup_call_cleanup((open("test_271.txt", write, WriteFile), + format(WriteFile, "get_char(Stream, C).%\n", []), + close(WriteFile), + open("test_271.txt", read, ReadFile)), + (read_term(ReadFile, T, []), + T = get_char(ReadFile, C), + call(T), + C == '%'), + (close(ReadFile), + delete_file("test_271.txt"))). + +test_272 :- test_syntax_error("writeq(0B1).", syntax_error(incomplete_reduction)). + +test_274_275 :- + setup_call_cleanup(op(20,fx,--), + ( read_from_chars("--(a).", T0), + writeq_term_to_chars(T0, C0), + C0 == "--a", + op(0,fx,--), + read_from_chars("--(a).", T1), + writeq_term_to_chars(T1, C1), + C1 == "--(a)" + ), + op(0,fx,--)). + +test_276 :- writeq_term_to_chars(0xamod 2, C), + C == "10 mod 2". + +test_280 :- writeq_term_to_chars(00'+'1, C), + C == "0+1". + +test_281 :- test_syntax_error("00'a.", syntax_error(incomplete_reduction)). + +test_284 :- test_syntax_error("'\\^J'.", syntax_error(invalid_single_quoted_character)). + +test_291 :- writeq_term_to_chars([(a,b)], C), + C == "[(a,b)]". + +test_292 :- writeq_term_to_chars(1 = \\, C), + C == "1= \\\\". + +test_293 :- test_syntax_error("writeq((,)).", syntax_error(incomplete_reduction)). + +test_294 :- test_syntax_error("writeq({[}).", syntax_error(incomplete_reduction)). + +test_295 :- test_syntax_error("writeq({(}).", syntax_error(incomplete_reduction)). + +test_298 :- writeq_term_to_chars([a,b|c], C), + C == "[a,b|c]". + +test_299 :- (\+ (a,b)) = \+(T), + T == (a,b). + +test_302 :- [] = '[]'. + +test_304 :- setup_call_cleanup(op(300,fy,~), + ( read_from_chars("~ (a = b).", T), + writeq_term_to_chars(T, C), + C == "~(a=b)" + ), + op(0,fy,~)). + +test_305 :- writeq_term_to_chars(\ (a = b), C), + C == "\\ (a=b)". + +test_306 :- writeq_term_to_chars(+ (a = b), C), + C == "+(a=b)". + +test_307 :- writeq_term_to_chars([/**/], C), + C == "[]". + +test_308 :- writeq_term_to_chars(.+, C), + C == ".+". + +test_309 :- writeq_term_to_chars({a,b}, C), + C == "{a,b}". + +test_310 :- test_syntax_error("writeq({\\+ (}).", syntax_error(incomplete_reduction)). + +test_311 :- test_syntax_error("Finis ().", syntax_error(incomplete_reduction)). + +run_tests([Test|Tests]) --> + ( { call(Test) } -> + [] + ; { format("~a failed!~n", [Test]) }, + [Test] + ), + run_tests(Tests). +run_tests([]) --> []. + +run_tests :- + findall(Test, + ( current_predicate(iso_conformity_tests:Test/0), + once(sub_atom(Test, 0, 5, _, test_)) + ), + Tests), + phrase(run_tests(Tests), FailedTests), + ( FailedTests == [] -> + write('All tests passed'), + nl + ; format("Failed ISO conformity tests: ~w~n", [FailedTests]), + false + ). + +% FIXME: enable once all tests pass. +% :- initialization_goals(run_tests). From 3cbe78cb9b77d4daa53f6cb8bdbbdae49144f19e Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 29 Jun 2023 14:57:29 -0600 Subject: [PATCH 261/361] correct tests 259 and 304 of tests-pl/iso-conformity-tests.pl --- tests-pl/iso-conformity-tests.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests-pl/iso-conformity-tests.pl b/tests-pl/iso-conformity-tests.pl index e263fa9f..87c6d066 100644 --- a/tests-pl/iso-conformity-tests.pl +++ b/tests-pl/iso-conformity-tests.pl @@ -551,8 +551,8 @@ test_213 :- read_from_chars("X is 0'\\\n+'1.", T), T = (_ is 0+1), call(T). -test_259 :- read_from_chars("X is 0'\\\n+'/*'. % */1.", T), - T = (_ is 0+1), +test_259 :- read_from_chars("X = 0'\\\n+'/*'. %*/1.", T), + T = (_ = 0+1), call(T). test_303 :- test_syntax_error("X = 0'\\\na.", syntax_error(incomplete_reduction)). @@ -973,7 +973,7 @@ test_302 :- [] = '[]'. test_304 :- setup_call_cleanup(op(300,fy,~), ( read_from_chars("~ (a = b).", T), writeq_term_to_chars(T, C), - C == "~(a=b)" + C == "~ (a=b)" ), op(0,fy,~)). From 8140ff9154b60665e0c0232e3ce2395bbf64ffc6 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 29 Jun 2023 17:35:27 -0600 Subject: [PATCH 262/361] always print a space between prefix operator and its operand --- src/heap_print.rs | 46 +++++++------------------------- tests-pl/iso-conformity-tests.pl | 8 +++--- 2 files changed, 14 insertions(+), 40 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index f846dea1..3f48aed7 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -590,31 +590,21 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { right_directed_op, )); } else if is_prefix!(spec.get_spec()) { - match name { - atom!("-") | atom!("\\") => { - self.format_prefix_op_with_space(max_depth, name, spec); - return; - } - _ => {} - }; - if self.check_max_depth(&mut max_depth) { self.iter.pop_stack(); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); - self.state_stack.push(TokenOrRedirect::Op(name, spec)); + self.state_stack.push(TokenOrRedirect::Space); + self.state_stack.push(TokenOrRedirect::Atom(name)); return; } - let left_directed_op = DirectedOp::Left(name, spec); + let op = DirectedOp::Left(name, spec); - self.state_stack.push(TokenOrRedirect::CompositeRedirect( - max_depth, - left_directed_op, - )); - - self.state_stack.push(TokenOrRedirect::Op(name, spec)); + self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, op)); + self.state_stack.push(TokenOrRedirect::Space); + self.state_stack.push(TokenOrRedirect::Atom(name)); } else { match name.as_str() { "|" => { @@ -687,24 +677,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { true } - fn format_prefix_op_with_space(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) { - if self.check_max_depth(&mut max_depth) { - self.iter.pop_stack(); - - self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); - self.state_stack.push(TokenOrRedirect::Space); - self.state_stack.push(TokenOrRedirect::Atom(name)); - - return; - } - - let op = DirectedOp::Left(name, spec); - - self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, op)); - self.state_stack.push(TokenOrRedirect::Space); - self.state_stack.push(TokenOrRedirect::Atom(name)); - } - fn format_bar_separator_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) { if self.check_max_depth(&mut max_depth) { self.iter.pop_stack(); @@ -1348,8 +1320,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.state_stack.push(TokenOrRedirect::Open); if let Some(ref op) = &op { - if op.is_left() && requires_space(op.as_atom().as_str(), "(") { - self.state_stack.push(TokenOrRedirect::Space); + if !self.outputter.ends_with(" ") { + if op.is_left() && requires_space(op.as_atom().as_str(), "(") { + self.state_stack.push(TokenOrRedirect::Space); + } } } } diff --git a/tests-pl/iso-conformity-tests.pl b/tests-pl/iso-conformity-tests.pl index 87c6d066..39626808 100644 --- a/tests-pl/iso-conformity-tests.pl +++ b/tests-pl/iso-conformity-tests.pl @@ -341,7 +341,7 @@ test_67 :- setup_call_cleanup(( current_op(P,fy,+), op(P,fy,+)). test_257 :- writeq_term_to_chars([+{a},+[]], Chars), - Chars == "[+{a},+[]]". + Chars == "[+ {a},+ []]". test_68 :- [(:-)|(:-)]=[:-|:-]. @@ -568,7 +568,7 @@ test_131_132_133 :- C0 == "' op' '1 '", read_from_chars("' op'[].", T1), writeq_term_to_chars(T1, C1), - C1 == "' op'[]" + C1 == "' op' []" ), op(0, fx, ' op') ). @@ -932,7 +932,7 @@ test_274_275 :- setup_call_cleanup(op(20,fx,--), ( read_from_chars("--(a).", T0), writeq_term_to_chars(T0, C0), - C0 == "--a", + C0 == "-- a", op(0,fx,--), read_from_chars("--(a).", T1), writeq_term_to_chars(T1, C1), @@ -981,7 +981,7 @@ test_305 :- writeq_term_to_chars(\ (a = b), C), C == "\\ (a=b)". test_306 :- writeq_term_to_chars(+ (a = b), C), - C == "+(a=b)". + C == "+ (a=b)". test_307 :- writeq_term_to_chars([/**/], C), C == "[]". From a09306c58524fb9f378d6df6cbe60a1b9922e08e Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 29 Jun 2023 17:59:25 -0600 Subject: [PATCH 263/361] check ambiguity of "'" against tail if atom token is about to be quoted --- src/heap_print.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 3f48aed7..4025339c 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -568,7 +568,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { #[inline] fn ambiguity_check(&self, atom: &str) -> bool { let tail = self.outputter.range_from(self.last_item_idx..); - requires_space(tail, atom) + + if !self.quoted || non_quoted_token(atom.chars()) { + requires_space(tail, atom) + } else { + requires_space(tail, "'") + } } fn enqueue_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) { From 521118265a94ea504cdbdd137c0fc30a5d462b5c Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 29 Jun 2023 18:01:09 -0600 Subject: [PATCH 264/361] make setup of test_166_167 pass --- tests-pl/iso-conformity-tests.pl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests-pl/iso-conformity-tests.pl b/tests-pl/iso-conformity-tests.pl index 39626808..31ddc4d3 100644 --- a/tests-pl/iso-conformity-tests.pl +++ b/tests-pl/iso-conformity-tests.pl @@ -732,7 +732,10 @@ test_164 :- setup_call_cleanup((op(7,fy,p),op(9,yfx,p)), test_165 :- atom('.''-''.'). -test_166_167 :- setup_call_cleanup(current_op(P,xfy,'|'), +test_166_167 :- setup_call_cleanup(( current_op(P,xfy,'|') -> + true + ; P = 0 + ), ( op(0,xfy,'|'), test_syntax_error("(a|b).", syntax_error(incomplete_reduction))), op(P,xfy,'|')). From 38a9d231746395b5725af47233bca9523cb2de67 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 29 Jun 2023 18:03:43 -0600 Subject: [PATCH 265/361] correct initialization_goals misnomer in iso-conformity-tests.pl --- tests-pl/iso-conformity-tests.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-pl/iso-conformity-tests.pl b/tests-pl/iso-conformity-tests.pl index 31ddc4d3..e78305a2 100644 --- a/tests-pl/iso-conformity-tests.pl +++ b/tests-pl/iso-conformity-tests.pl @@ -1023,4 +1023,4 @@ run_tests :- ). % FIXME: enable once all tests pass. -% :- initialization_goals(run_tests). +% :- initialization(run_tests). From cb25a9025018643f5e2da32f4474dbc23ca299c3 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 1 Jul 2023 15:38:05 -0600 Subject: [PATCH 266/361] add iso-conformity-tests.pl to test suite --- src/machine/mock_wam.rs | 2 +- tests-pl/iso-conformity-tests.pl | 9 +++------ tests/scryer/src_tests.rs | 9 +++++++++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index f71f32e3..70264ac9 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -239,7 +239,7 @@ impl Machine { user_error, load_contexts: vec![], runtime, - foreign_function_table: Default::default(), + foreign_function_table: Default::default(), }; let mut lib_path = current_dir(); diff --git a/tests-pl/iso-conformity-tests.pl b/tests-pl/iso-conformity-tests.pl index e78305a2..d72fbf38 100644 --- a/tests-pl/iso-conformity-tests.pl +++ b/tests-pl/iso-conformity-tests.pl @@ -5,7 +5,6 @@ :- use_module(library(files)). :- use_module(library(format)). :- use_module(library(iso_ext)). -:- use_module(library(lists), [append/3]). writeq_term_to_chars(Term, Chars) :- Options = [ignore_ops(false), numbervars(true), quoted(true), variable_names([])], @@ -1016,11 +1015,9 @@ run_tests :- Tests), phrase(run_tests(Tests), FailedTests), ( FailedTests == [] -> - write('All tests passed'), - nl - ; format("Failed ISO conformity tests: ~w~n", [FailedTests]), + write('All tests passed') + ; format("Failed ISO conformity tests: ~w", [FailedTests]), false ). -% FIXME: enable once all tests pass. -% :- initialization(run_tests). +:- initialization(run_tests). diff --git a/tests/scryer/src_tests.rs b/tests/scryer/src_tests.rs index c7f7c80a..38043098 100644 --- a/tests/scryer/src_tests.rs +++ b/tests/scryer/src_tests.rs @@ -69,3 +69,12 @@ fn setup_call_cleanup_process() { fn clpz_load() { load_module_test("src/tests/clpz/test_clpz.pl", ""); } + +#[serial] +#[test] +fn iso_conformity_tests() { + load_module_test( + "tests-pl/iso-conformity-tests.pl", + "All tests passed", + ); +} From e36f96fd475ff06173d5f1de8c6726f6303d4d53 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 3 Jul 2023 11:34:41 -0600 Subject: [PATCH 267/361] correct ISO conformity test #185 --- tests-pl/iso-conformity-tests.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests-pl/iso-conformity-tests.pl b/tests-pl/iso-conformity-tests.pl index d72fbf38..40242fca 100644 --- a/tests-pl/iso-conformity-tests.pl +++ b/tests-pl/iso-conformity-tests.pl @@ -271,7 +271,7 @@ test_184 :- writeq_term_to_chars(-(-), Chars), Chars == "- (-)". test_185 :- writeq_term_to_chars(-[-], Chars), - Chars == "- \"-\"". + Chars == "- [-]". test_188 :- writeq_term_to_chars(-p(c), Chars), Chars == "- p(c)". From 9cdad087ef2b064073faf650fa65912ade038e03 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 3 Jul 2023 11:35:07 -0600 Subject: [PATCH 268/361] add double_quotes write option for printing to strings, enable it at toplevel --- build/instructions_template.rs | 4 ++-- src/heap_print.rs | 13 ++++++++--- src/lib/builtins.pl | 40 +++++++++++++++++++--------------- src/lib/charsio.pl | 7 +++--- src/machine/machine_state.rs | 23 ++++++++++++++++++- src/machine/mock_wam.rs | 1 + src/parser/ast.rs | 2 +- src/toplevel.pl | 12 +++++----- 8 files changed, 68 insertions(+), 34 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 7f559b8e..dc88a827 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -472,9 +472,9 @@ enum SystemClauseType { WAMInstructions, #[strum_discriminants(strum(props(Arity = "2", Name = "$inlined_instructions")))] InlinedInstructions, - #[strum_discriminants(strum(props(Arity = "7", Name = "$write_term")))] + #[strum_discriminants(strum(props(Arity = "8", Name = "$write_term")))] WriteTerm, - #[strum_discriminants(strum(props(Arity = "7", Name = "$write_term_to_chars")))] + #[strum_discriminants(strum(props(Arity = "8", Name = "$write_term_to_chars")))] WriteTermToChars, #[strum_discriminants(strum(props(Arity = "1", Name = "$scryer_prolog_version")))] ScryerPrologVersion, diff --git a/src/heap_print.rs b/src/heap_print.rs index 4025339c..4aef1ab1 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -478,6 +478,7 @@ pub struct HCPrinter<'a, Outputter> { iter: StackfulPreOrderHeapIter<'a>, atom_tbl: &'a mut AtomTable, op_dir: &'a OpDir, + flags: MachineFlags, state_stack: Vec, toplevel_spec: Option, last_item_idx: usize, @@ -488,6 +489,7 @@ pub struct HCPrinter<'a, Outputter> { pub ignore_ops: bool, pub print_strings_as_strs: bool, pub max_depth: usize, + pub double_quotes: bool, } macro_rules! push_space_if_amb { @@ -544,6 +546,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { atom_tbl: &'a mut AtomTable, stack: &'a mut Stack, op_dir: &'a OpDir, + flags: MachineFlags, output: Outputter, cell: HeapCellValue, ) -> Self { @@ -552,6 +555,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { iter: stackful_preorder_iter(heap, stack, cell), atom_tbl, op_dir, + flags, state_stack: vec![], toplevel_spec: None, last_item_idx: 0, @@ -562,6 +566,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { var_names: IndexMap::new(), print_strings_as_strs: false, max_depth: 0, + double_quotes: false, } } @@ -1164,9 +1169,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let at_cdr = self.outputter.ends_with("|"); - if !at_cdr && !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) { - self.remove_list_children(focus.value() as usize); - return self.print_proper_string(focus.value() as usize, max_depth); + if self.double_quotes && self.flags.double_quotes == DoubleQuotes::Chars { + if !at_cdr && !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) { + self.remove_list_children(focus.value() as usize); + return self.print_proper_string(focus.value() as usize, max_depth); + } } if self.ignore_ops { diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 904d2f69..4794c603 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -528,30 +528,34 @@ parse_options_list(Options, Selector, DefaultPairs, OptionValues, Stub) :- parse_write_options(Options, OptionValues, Stub) :- - DefaultOptions = [ignore_ops-false, max_depth-0, numbervars-false, + DefaultOptions = [double_quotes-false, ignore_ops-false, max_depth-0, numbervars-false, quoted-false, variable_names-[]], parse_options_list(Options, builtins:parse_write_options_, DefaultOptions, OptionValues, Stub). + +parse_write_options_(double_quotes(DoubleQuotes), double_quotes-DoubleQuotes) :- + ( nonvar(DoubleQuotes), + lists:member(DoubleQuotes, [true, false]), + ! + ; throw(error(domain_error(write_option, double_quotes(DoubleQuotes)), _)) + ). parse_write_options_(ignore_ops(IgnoreOps), ignore_ops-IgnoreOps) :- ( nonvar(IgnoreOps), lists:member(IgnoreOps, [true, false]), ! - ; - throw(error(domain_error(write_option, ignore_ops(IgnoreOps)), _)) + ; throw(error(domain_error(write_option, ignore_ops(IgnoreOps)), _)) ). parse_write_options_(quoted(Quoted), quoted-Quoted) :- ( nonvar(Quoted), lists:member(Quoted, [true, false]), ! - ; - throw(error(domain_error(write_option, quoted(Quoted)), _)) + ; throw(error(domain_error(write_option, quoted(Quoted)), _)) ). parse_write_options_(numbervars(NumberVars), numbervars-NumberVars) :- ( nonvar(NumberVars), lists:member(NumberVars, [true, false]), ! - ; - throw(error(domain_error(write_option, numbervars(NumberVars)), _)) + ; throw(error(domain_error(write_option, numbervars(NumberVars)), _)) ). parse_write_options_(variable_names(VNNames), variable_names-VNNames) :- must_be_var_names_list(VNNames), @@ -560,8 +564,7 @@ parse_write_options_(max_depth(MaxDepth), max_depth-MaxDepth) :- ( integer(MaxDepth), MaxDepth >= 0, ! - ; - throw(error(domain_error(write_option, max_depth(MaxDepth)), _)) + ; throw(error(domain_error(write_option, max_depth(MaxDepth)), _)) ). parse_write_options_(E, _) :- throw(error(domain_error(write_option, E), _)). @@ -607,11 +610,12 @@ write_term(Term, Options) :- % * `max_depth(+N)` if the term is nested deeper than N, print the reminder as ellipses. % If N = 0 (default), there's no limit. % * `numbervars(+Boolean)` if true, replaces `$VAR(N)` variables with letters, in order. Default is false. -% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog synytax, are quoted. Default is false. +% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog syntax, are quoted. Default is false. % * `variable_names(+List)` assign names to variables in term. List should be a list of terms of format `Name=Var`. +% * `double_quotes(+Boolean)` if true, strings are printed in double quotes rather than with list notation. Default is false. write_term(Stream, Term, Options) :- - parse_write_options(Options, [IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], write_term/3), - '$write_term'(Stream, Term, IgnoreOps, NumberVars, Quoted, VNNames, MaxDepth). + parse_write_options(Options, [DoubleQuotes, IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], write_term/3), + '$write_term'(Stream, Term, IgnoreOps, NumberVars, Quoted, VNNames, MaxDepth, DoubleQuotes). %% write(+Term). @@ -619,26 +623,26 @@ write_term(Stream, Term, Options) :- % Write Term to the current output stream using a syntax similar to Prolog write(Term) :- current_output(Stream), - '$write_term'(Stream, Term, false, true, false, [], 0). + '$write_term'(Stream, Term, false, true, false, [], 0, false). %% write(+Stream, +Term). % % Write Term to the stream Stream using a syntax similar to Prolog write(Stream, Term) :- - '$write_term'(Stream, Term, false, true, false, [], 0). + '$write_term'(Stream, Term, false, true, false, [], 0, false). %% write_canonical(+Term). % % Write Term to the current output stream using canonical Prolog syntax. Can be read back as Prolog terms. write_canonical(Term) :- current_output(Stream), - '$write_term'(Stream, Term, true, false, true, [], 0). + '$write_term'(Stream, Term, true, false, true, [], 0, false). %% write_canonical(+Stream, +Term). % % Write Term to the stream Stream using canonical Prolog syntax. Can be read back as Prolog terms. write_canonical(Stream, Term) :- - '$write_term'(Stream, Term, true, false, true, [], 0). + '$write_term'(Stream, Term, true, false, true, [], 0, false). %% writeq(+Term). % @@ -646,14 +650,14 @@ write_canonical(Stream, Term) :- % quoted according to Prolog syntax. writeq(Term) :- current_output(Stream), - '$write_term'(Stream, Term, false, true, true, [], 0). + '$write_term'(Stream, Term, false, true, true, [], 0, false). %% writeq(+Stream, +Term). % % Write Term to the stream Stream using a syntax similar to `write/1` but quoting the atoms that need to be % quoted according to Prolog syntax. writeq(Stream, Term) :- - '$write_term'(Stream, Term, false, true, true, [], 0). + '$write_term'(Stream, Term, false, true, true, [], 0, false). select_rightmost_options([Option-Value | OptionPairs], OptionValues) :- ( pairs:same_key(Option, OptionPairs, OtherValues, _), diff --git a/src/lib/charsio.pl b/src/lib/charsio.pl index 99b5c281..128a6c50 100644 --- a/src/lib/charsio.pl +++ b/src/lib/charsio.pl @@ -206,13 +206,14 @@ read_from_chars(Chars, Term) :- % * `max_depth(+N)` if the term is nested deeper than N, print the reminder as ellipses. % If N = 0 (default), there's no limit. % * `numbervars(+Boolean)` if true, replaces `$VAR(N)` variables with letters, in order. Default is false. -% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog synytax, are quoted. Default is false. +% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog syntax, are quoted. Default is false. % * `variable_names(+List)` assign names to variables in term. List should be a list of terms of format `Name=Var`. +% * `double_quotes(+Boolean)` if true, strings are printed in double quotes rather than with list notation. Default is false. write_term_to_chars(_, Options, _) :- var(Options), instantiation_error(write_term_to_chars/3). write_term_to_chars(Term, Options, Chars) :- builtins:parse_write_options(Options, - [IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], + [DoubleQuotes, IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], write_term_to_chars/3), ( nonvar(Chars) -> throw(error(uninstantiation_error(Chars), write_term_to_chars/3)) @@ -221,7 +222,7 @@ write_term_to_chars(Term, Options, Chars) :- ), term_variables(Term, Vars), extend_var_list(Vars, VNNames, NewVarNames, numbervars), - '$write_term_to_chars'(Chars, Term, IgnoreOps, NumberVars, Quoted, NewVarNames, MaxDepth). + '$write_term_to_chars'(Chars, Term, IgnoreOps, NumberVars, Quoted, NewVarNames, MaxDepth, DoubleQuotes). % Encodes Ch character to list of Bytes. char_utf8bytes(Ch, Bytes) :- diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 29702c4a..78b1a7f5 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -666,6 +666,7 @@ 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); @@ -747,7 +748,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) => { @@ -767,6 +786,7 @@ impl MachineState { &mut self.atom_tbl, &mut self.stack, op_dir, + self.flags, PrinterOutputter::new(), term_to_be_printed, ); @@ -774,6 +794,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)) => { diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index 70264ac9..a4aad74c 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -64,6 +64,7 @@ impl MockWAM { &mut self.machine_st.atom_tbl, &mut self.machine_st.stack, &self.op_dir, + self.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(term_write_result.heap_loc), ); diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 31f1a702..272d5b7e 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -313,7 +313,7 @@ impl Default for MachineFlags { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum DoubleQuotes { Atom, Chars, diff --git a/src/toplevel.pl b/src/toplevel.pl index 30de5227..2df6d360 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -231,13 +231,13 @@ write_goal(G, VarList, MaxDepth) :- write(' = '), ( needs_bracketing(Value, =) -> write('('), - write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), write(')') - ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]) + ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]) ) ; G == [] -> write('true') - ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth)]) + ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth), double_quotes(true)]) ). write_last_goal(G, VarList, MaxDepth) :- @@ -250,9 +250,9 @@ write_last_goal(G, VarList, MaxDepth) :- write(' = '), ( needs_bracketing(Value, =) -> write('('), - write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), write(')') - ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]), + ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), ( trailing_period_is_ambiguous(Value) -> write(' ') ; true @@ -260,7 +260,7 @@ write_last_goal(G, VarList, MaxDepth) :- ) ; G == [] -> write('true') - ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth)]) + ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth), double_quotes(true)]) ). write_eq((G1, G2), VarList, MaxDepth) :- From f5e7573bd6078b56a7533f198a8a666c28fa6608 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 3 Jul 2023 12:09:26 -0600 Subject: [PATCH 269/361] correct tests #171 and #300 --- tests-pl/iso-conformity-tests.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests-pl/iso-conformity-tests.pl b/tests-pl/iso-conformity-tests.pl index 40242fca..630b2baa 100644 --- a/tests-pl/iso-conformity-tests.pl +++ b/tests-pl/iso-conformity-tests.pl @@ -753,12 +753,12 @@ test_194 :- op(0,xfy,.), test_170 :- set_prolog_flag(double_quotes,chars). test_171 :- writeq_term_to_chars("a", C), - C == "\"a\"". + C == "[a]". test_229 :- test_syntax_error("\"\\z.\"", syntax_error(missing_quote)). test_300 :- writeq_term_to_chars("\0\", C), - C == "\"\\x0\\\"". + C == "['\\x0\\']". test_172 :- X is 10.0** -323, writeq_term_to_chars(X, C), From ab893be41848f108f11721dd13d9d6cad0d6412a Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 3 Jul 2023 13:18:53 -0600 Subject: [PATCH 270/361] update tests --- src/heap_print.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 4aef1ab1..4e7781b7 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -1657,6 +1657,7 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, + wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0) ); @@ -1686,6 +1687,7 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, + wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0) ); @@ -1710,6 +1712,7 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, + wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0) ); @@ -1723,6 +1726,7 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, + wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0) ); @@ -1754,6 +1758,7 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, + wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0), ); @@ -1773,6 +1778,7 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, + wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0), ); @@ -1790,6 +1796,7 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, + wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0) ); @@ -1820,6 +1827,7 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, + wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0) ); @@ -1843,6 +1851,7 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, + wam.machine_st.flags, PrinterOutputter::new(), pstr_loc_as_cell!(0) ); @@ -1866,15 +1875,18 @@ mod tests { wam.machine_st.heap.push(empty_list_as_cell!()); { - let printer = HCPrinter::new( + let mut printer = HCPrinter::new( &mut wam.machine_st.heap, &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, + wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0), ); + printer.double_quotes = true; + let output = printer.print(); assert_eq!(output.result(), "\"abcabc\""); @@ -1893,7 +1905,7 @@ mod tests { assert_eq!( &wam.parse_and_print_term("[a,b,\"a\",[a,b,c]].").unwrap(), - "[a,b,\"a\",\"abc\"]" + "[a,b,[a],[a,b,c]]" ); all_cells_unmarked(&wam.machine_st.heap); @@ -1901,7 +1913,7 @@ mod tests { assert_eq!( &wam.parse_and_print_term("[\"abc\",e,f,[g,e,h,Y,v|[X,Y]]].") .unwrap(), - "[\"abc\",e,f,[g,e,h,Y,v,X,Y]]" + "[[a,b,c],e,f,[g,e,h,Y,v,X,Y]]" ); all_cells_unmarked(&wam.machine_st.heap); From 7f159a7ed227713937e096956452ff9d13e6ce7b Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Mon, 3 Jul 2023 22:04:26 +0200 Subject: [PATCH 271/361] advertise newly achieved strong syntactic conformance: all current tests pass This addresses an important aspect of #1777. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index caff6a91..f5c08e89 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ source industrial strength production environment that is also a testbed for bleeding edge research in logic and constraint programming, which is itself written in a high-level language. +As of July 2023, **Scryer Prolog passes all [syntactic conformity tests](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformity_testing)**. + The homepage of the project is: [**https://www.scryer.pl**](https://www.scryer.pl) ![Scryer Logo: Cryer](logo/scryer.png) From 076a75d1381926f83dd77150fdbda43969c168bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Arroyo=20Calle?= Date: Tue, 4 Jul 2023 17:33:48 +0200 Subject: [PATCH 272/361] Allow comparisons with stream terms --- src/machine/machine_state_impl.rs | 144 +++++++++++++++++++++++++++++- src/types.rs | 12 +++ 2 files changed, 155 insertions(+), 1 deletion(-) diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index b4dc3fea..5cc07b1f 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -11,6 +11,7 @@ use crate::machine::machine_indices::*; use crate::machine::machine_state::*; use crate::machine::partial_string::*; use crate::machine::stack::*; +use crate::machine::Stream; use crate::machine::unify::*; use crate::parser::ast::*; use crate::parser::rug::{Integer, Rational}; @@ -514,6 +515,14 @@ impl MachineState { return Some(n1.cmp(&n2)); } } + (HeapCellValueTag::Cons, ptr) => { + let stream = cell_as_stream!(ptr); + let n2 = stream.options().get_alias().unwrap(); + if n1 != n2 { + self.pdl.clear(); + return Some(n1.cmp(&n2)); + } + } _ => { unreachable!(); } @@ -558,7 +567,22 @@ impl MachineState { ); } } - _ => { + (HeapCellValueTag::Cons, ptr) => { + let stream = cell_as_stream!(ptr); + let n2 = stream.options().get_alias().unwrap(); + if let Some(c2) = n2.as_char() { + if c1 != c2 { + self.pdl.clear(); + return Some(c1.cmp(&c2)); + } + } else { + self.pdl.clear(); + return Some( + Some(c1).cmp(&n2.chars().next()) + .then(Ordering::Less) + ); + } + } _ => { unreachable!() } ) @@ -597,11 +621,65 @@ impl MachineState { return Some(n1.cmp(&n2)); } } + (HeapCellValueTag::Cons, ptr) => { + let stream = cell_as_stream!(ptr); + let n2 = stream.options().get_alias().unwrap(); + if n1 != n2 { + self.pdl.clear(); + return Some(n1.cmp(&n2)); + } + } _ => { unreachable!(); } ) } + (HeapCellValueTag::Cons, ptr) => { + let stream = cell_as_stream!(ptr); + let n1 = stream.options().get_alias().unwrap(); + read_heap_cell!(v2, + (HeapCellValueTag::Atom, (n2, _a2)) => { + if n1 != n2 { + self.pdl.clear(); + return Some(n1.cmp(&n2)); + } + } + (HeapCellValueTag::Char, c2) => { + if let Some(c1) = n1.as_char() { + if c1 != c2 { + self.pdl.clear(); + return Some(c1.cmp(&c2)); + } + } else { + self.pdl.clear(); + return Some( + n1.chars().next().cmp(&Some(c2)) + .then(Ordering::Greater) + ); + } + } + (HeapCellValueTag::Str, s) => { + let n2 = cell_as_atom_cell!(self.heap[s]) + .get_name(); + + if n1 != n2 { + self.pdl.clear(); + return Some(n1.cmp(&n2)); + } + } + (HeapCellValueTag::Cons, ptr) => { + let stream = cell_as_stream!(ptr); + let n2 = stream.options().get_alias().unwrap(); + if n1 != n2 { + self.pdl.clear(); + return Some(n1.cmp(&n2)); + } + } + _ => { + unreachable!(); + } + ) + } _ => { unreachable!() } @@ -658,6 +736,9 @@ impl MachineState { Some((2, atom!(".")).cmp(&(arity, name))) } } + (HeapCellValueTag::Cons, _s) => { + Some(Ordering::Greater) + } _ => { unreachable!() } @@ -761,6 +842,10 @@ impl MachineState { } } } + (HeapCellValueTag::Cons, _ptr) => { + self.pdl.clear(); + return Some(Ordering::Greater); + } _ => { unreachable!(); } @@ -862,11 +947,68 @@ impl MachineState { self.heap.pop(); self.heap.pop(); } + (HeapCellValueTag::Cons, s2) => { + let stream = cell_as_stream!(s2); + let ptr = stream.as_ptr() as u64; + + let (n1, a1) = cell_as_atom_cell!(self.heap[s1]) + .get_name_and_arity(); + + match (a1, n1).cmp(&(1, atom!("$stream"))) { + Ordering::Equal => { + self.pdl.push(HeapCellValue::from(ptr)); + self.pdl.push(self.heap[s1+1]); + } + ordering => { + self.pdl.clear(); + return Some(ordering); + } + } + } _ => { unreachable!() } ) } + (HeapCellValueTag::Cons, s1) => { + let stream = cell_as_stream!(s1); + let ptr = stream.as_ptr() as u64; + read_heap_cell!(v2, + (HeapCellValueTag::Str, s2) => { + let (n2, a2) = cell_as_atom_cell!(self.heap[s2]) + .get_name_and_arity(); + + match (1, atom!("$stream")).cmp(&(a2, n2)) { + Ordering::Equal => { + self.pdl.push(self.heap[s2+1]); + self.pdl.push(HeapCellValue::from(ptr)); + } + ordering => { + self.pdl.clear(); + return Some(ordering); + } + } + } + (HeapCellValueTag::Lis, _l2) => { + self.pdl.clear(); + return Some(Ordering::Less); + } + (HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc) => { + self.pdl.clear(); + return Some(Ordering::Less); + } + (HeapCellValueTag::Cons, s2) => { + let stream2 = cell_as_stream!(s2); + let ptr2 = stream2.as_ptr() as u64; + + self.pdl.clear(); + return Some(ptr.cmp(&ptr2)); + } + _ => { + unreachable!() + } + ) + } _ => { unreachable!() } diff --git a/src/types.rs b/src/types.rs index 12add4ef..aa8384fc 100644 --- a/src/types.rs +++ b/src/types.rs @@ -616,6 +616,18 @@ impl HeapCellValue { Some(TermOrderCategory::Compound) } } + HeapCellValueTag::Cons => { + let ptr = cell_as_untyped_arena_ptr!(self); + match_untyped_arena_ptr!(ptr, + (ArenaHeaderTag::Stream, stream) => { + match stream.options().get_alias() { + Some(_) => Some(TermOrderCategory::Atom), + None => Some(TermOrderCategory::Compound) + } + }, + _ => None + ) + } _ => { None } From 5d09449c95857bb7e37e1c3ed8c4ee6036f40d2f Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 4 Jul 2023 11:22:07 -0600 Subject: [PATCH 273/361] widen CharReader buffer (#1859) --- src/parser/char_reader.rs | 8 ++++---- src/parser/lexer.rs | 11 +++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/parser/char_reader.rs b/src/parser/char_reader.rs index a2a7c2e2..8a74db37 100644 --- a/src/parser/char_reader.rs +++ b/src/parser/char_reader.rs @@ -20,7 +20,7 @@ use std::str; pub struct CharReader { inner: R, - buf: SmallVec<[u8;4]>, + buf: SmallVec<[u8;32]>, pos: usize, } @@ -121,7 +121,7 @@ impl CharReader { self.buf.clear(); - let mut word = [0u8;4]; + let mut word = [0u8; std::mem::size_of::()]; let nread = self.inner.read(&mut word)?; self.buf.extend_from_slice(&word[..nread]); @@ -234,10 +234,10 @@ impl CharRead for CharReader { #[inline(always)] fn put_back_char(&mut self, c: char) { let src_len = self.buf.len() - self.pos; - debug_assert!(src_len <= 4); + debug_assert!(src_len <= self.buf.capacity()); let c_len = c.len_utf8(); - let mut shifted_slice = [0u8; 4]; + let mut shifted_slice = [0u8; 32]; shifted_slice[0..src_len].copy_from_slice(&self.buf[self.pos .. self.buf.len()]); diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index 7f5d73ed..3fbddc1d 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -988,6 +988,17 @@ impl<'a, R: CharRead> Lexer<'a, R> { return Ok(Token::End); } + Ok(c) if c == '\\' => { + self.skip_char(c); + + if self.lookahead_char().ok() == Some('n') { + self.skip_char('n'); + return Ok(Token::End); + } else { + self.return_char(c); + self.return_char('.'); + } + } Err(ParserError::UnexpectedEOF) => { return Ok(Token::End); } From c58d8804a1581b7ca65030be655fa505c4de9010 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 4 Jul 2023 11:22:07 -0600 Subject: [PATCH 274/361] widen CharReader buffer (#1859) --- src/parser/char_reader.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/parser/char_reader.rs b/src/parser/char_reader.rs index a2a7c2e2..8a74db37 100644 --- a/src/parser/char_reader.rs +++ b/src/parser/char_reader.rs @@ -20,7 +20,7 @@ use std::str; pub struct CharReader { inner: R, - buf: SmallVec<[u8;4]>, + buf: SmallVec<[u8;32]>, pos: usize, } @@ -121,7 +121,7 @@ impl CharReader { self.buf.clear(); - let mut word = [0u8;4]; + let mut word = [0u8; std::mem::size_of::()]; let nread = self.inner.read(&mut word)?; self.buf.extend_from_slice(&word[..nread]); @@ -234,10 +234,10 @@ impl CharRead for CharReader { #[inline(always)] fn put_back_char(&mut self, c: char) { let src_len = self.buf.len() - self.pos; - debug_assert!(src_len <= 4); + debug_assert!(src_len <= self.buf.capacity()); let c_len = c.len_utf8(); - let mut shifted_slice = [0u8; 4]; + let mut shifted_slice = [0u8; 32]; shifted_slice[0..src_len].copy_from_slice(&self.buf[self.pos .. self.buf.len()]); From 7683367c0e5a2716f6569f428a007fa3acaadb5a Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 4 Jul 2023 16:09:54 -0600 Subject: [PATCH 275/361] throw lexer errors from devour_whitespace (#1778) --- src/machine/system_calls.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 4c9c0f2f..9fef738c 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -7393,9 +7393,15 @@ impl Machine { match self.machine_st.devour_whitespace(stream) { Ok(false) => { // not at EOF. } - _ => { + Ok(true) => { self.machine_st.fail = true; } + Err(err) => { + let stub = functor_stub(atom!("load"), 1); + let err = self.machine_st.syntax_error(err); + + return Err(self.machine_st.error_form(err, stub)); + } } Ok(()) From 5ab087bc1eed33129ffd50a98004f98898a79cdf Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 4 Jul 2023 12:13:43 -0600 Subject: [PATCH 276/361] revise iso_conformity_tests.pl in response to new ambiguity check of #1860 --- src/heap_print.rs | 38 +++++++++++++++++++++++++------- tests-pl/iso-conformity-tests.pl | 30 ++++++++++++------------- tests/scryer/issues.rs | 6 ++--- 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 4e7781b7..7cd8b713 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -44,6 +44,15 @@ impl DirectedOp { } } + #[inline] + fn is_prefix(&self )-> bool { + match self { + &DirectedOp::Left(_name, cell) | &DirectedOp::Right(_name, cell) => { + is_prefix!(cell.get_spec() as u32) + } + } + } + #[inline] fn is_negative_sign(&self) -> bool { match self { @@ -604,7 +613,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.iter.pop_stack(); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); - self.state_stack.push(TokenOrRedirect::Space); self.state_stack.push(TokenOrRedirect::Atom(name)); return; @@ -613,7 +621,13 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let op = DirectedOp::Left(name, spec); self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, op)); - self.state_stack.push(TokenOrRedirect::Space); + + /* + if fetch_op_spec(name, 2, self.op_dir).is_some() { + self.state_stack.push(TokenOrRedirect::Space); + } + */ + self.state_stack.push(TokenOrRedirect::Atom(name)); } else { match name.as_str() { @@ -938,13 +952,17 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } fn print_number(&mut self, max_depth: usize, n: NumberFocus, op: &Option) { - let add_brackets = if let Some(op) = op { - op.is_negative_sign() && !n.is_negative() + let (add_brackets, op_is_prefix) = if let Some(op) = op { + (op.is_negative_sign() && !n.is_negative(), op.is_prefix()) } else { - false + (false, false) }; if add_brackets { + if op_is_prefix && !self.outputter.ends_with(" ") { + push_char!(self, ' '); + } + push_char!(self, '('); } @@ -1333,8 +1351,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { if let Some(ref op) = &op { if !self.outputter.ends_with(" ") { - if op.is_left() && requires_space(op.as_atom().as_str(), "(") { - self.state_stack.push(TokenOrRedirect::Space); + if op.is_left() { + if op.is_prefix() || requires_space(op.as_atom().as_str(), "(") { + self.state_stack.push(TokenOrRedirect::Space); + } } } } @@ -1457,7 +1477,9 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let mut result = String::new(); if let Some(ref op) = op { - if printer.outputter.ends_with(&format!(" {}", op.as_atom().as_str())) { + let op_is_prefix = op.is_prefix() && op.is_left(); + + if op_is_prefix || printer.outputter.ends_with(&format!(" {}", op.as_atom().as_str())) { result.push(' '); } diff --git a/tests-pl/iso-conformity-tests.pl b/tests-pl/iso-conformity-tests.pl index 630b2baa..c86620da 100644 --- a/tests-pl/iso-conformity-tests.pl +++ b/tests-pl/iso-conformity-tests.pl @@ -265,31 +265,31 @@ test_218 :- writeq_term_to_chars(-(1*2), Chars), Chars == "- (1*2)". test_140 :- writeq_term_to_chars(-a, Chars), - Chars == "- a". + Chars == "-a". test_184 :- writeq_term_to_chars(-(-), Chars), Chars == "- (-)". test_185 :- writeq_term_to_chars(-[-], Chars), - Chars == "- [-]". + Chars == "-[-]". test_188 :- writeq_term_to_chars(-p(c), Chars), - Chars == "- p(c)". + Chars == "-p(c)". test_189 :- writeq_term_to_chars(-{}, Chars), - Chars == "- {}". + Chars == "-{}". test_190 :- writeq_term_to_chars(-{a}, Chars), - Chars == "- {a}". + Chars == "-{a}". test_191 :- writeq_term_to_chars(-(-a), Chars), - Chars == "- - a". + Chars == "- -a". test_192 :- writeq_term_to_chars(-(-(-a)), Chars), - Chars == "- - - a". + Chars == "- - -a". -test_216 :- writeq_term_to_chars(-(-1), Chars), - Chars == "- -1". +test_216 :- writeq_term_to_chars(-(-(1)), Chars), + Chars == "- - (1)". test_215_248_249 :- setup_call_cleanup(op(100,yfx,~), @@ -307,14 +307,14 @@ test_215_248_249 :- test_278 :- setup_call_cleanup(op(9,xfy,.), ( writeq_term_to_chars(-[1], Chars), - Chars == "- [1]" + Chars == "-[1]" ), op(0,xfy,.)). test_279_296 :- setup_call_cleanup(op(9,xf,'$VAR'), ( writeq_term_to_chars(-'$VAR'(0), Chars0), - Chars0 == "- A", + Chars0 == "-A", writeq_term_to_chars('$VAR'(0), Chars1), Chars1 == "A" ), @@ -340,7 +340,7 @@ test_67 :- setup_call_cleanup(( current_op(P,fy,+), op(P,fy,+)). test_257 :- writeq_term_to_chars([+{a},+[]], Chars), - Chars == "[+ {a},+ []]". + Chars == "[+{a},+[]]". test_68 :- [(:-)|(:-)]=[:-|:-]. @@ -567,7 +567,7 @@ test_131_132_133 :- C0 == "' op' '1 '", read_from_chars("' op'[].", T1), writeq_term_to_chars(T1, C1), - C1 == "' op' []" + C1 == "' op'[]" ), op(0, fx, ' op') ). @@ -595,7 +595,7 @@ test_142_143_144_221_258 :- C1 == "[1]", read_from_chars("-[1].", T2), writeq_term_to_chars(T2, C2), - C2 == "- [1]", + C2 == "-[1]", read_from_chars("X = 1.e.", T3), writeq_term_to_chars(T3, C3), C3 == "A=[1|e]", @@ -934,7 +934,7 @@ test_274_275 :- setup_call_cleanup(op(20,fx,--), ( read_from_chars("--(a).", T0), writeq_term_to_chars(T0, C0), - C0 == "-- a", + C0 == "--a", op(0,fx,--), read_from_chars("--(a).", T1), writeq_term_to_chars(T1, C1), diff --git a/tests/scryer/issues.rs b/tests/scryer/issues.rs index af22252a..f1735e78 100644 --- a/tests/scryer/issues.rs +++ b/tests/scryer/issues.rs @@ -54,10 +54,10 @@ fn handle_residual_goal() { true.\n \ true.\n \ false.\n \ - X = - X.\n \ - dif:dif(- X,X).\n \ + X = -X.\n \ + dif:dif(-X,X).\n \ false.\n \ - Vars = [X], dif:dif(- X,X).\n \ + Vars = [X], dif:dif(-X,X).\n \ true.\n \ true.\n \ true.\n\ From b5b45dde9d748644e16cefac74deae66fa50f26e Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 4 Jul 2023 17:44:27 -0600 Subject: [PATCH 277/361] add missing self.pos to peek_char slices (#1726) --- src/parser/char_reader.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser/char_reader.rs b/src/parser/char_reader.rs index 8a74db37..9d6babf4 100644 --- a/src/parser/char_reader.rs +++ b/src/parser/char_reader.rs @@ -187,7 +187,7 @@ impl CharRead for CharReader { if self.pos >= self.buf.len() { return None; } else if self.buf.len() - self.pos >= 4 { - return match str::from_utf8(&self.buf[..e.valid_up_to()]) { + return match str::from_utf8(&self.buf[self.pos .. e.valid_up_to()]) { Ok(s) => { let mut chars = s.chars(); let c = chars.next().unwrap(); @@ -195,7 +195,7 @@ impl CharRead for CharReader { Some(Ok(c)) } Err(e) => { - let badbytes = self.buf[..e.valid_up_to()].to_vec(); + let badbytes = self.buf[self.pos .. e.valid_up_to()].to_vec(); Some(Err(io::Error::new(io::ErrorKind::InvalidData, BadUtf8Error { bytes: badbytes }))) From 3b9b9e75c41ed62e2e5d15f245324f2c36b48e60 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 5 Jul 2023 21:22:49 +0200 Subject: [PATCH 278/361] make double_quotes write option not dependent on double_quotes flag This gives consistent results without depending on another flag. --- src/heap_print.rs | 5 +---- src/machine/machine_state.rs | 1 - src/machine/mock_wam.rs | 1 - src/toplevel.pl | 19 +++++++++++++------ 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 7cd8b713..10a211a1 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -487,7 +487,6 @@ pub struct HCPrinter<'a, Outputter> { iter: StackfulPreOrderHeapIter<'a>, atom_tbl: &'a mut AtomTable, op_dir: &'a OpDir, - flags: MachineFlags, state_stack: Vec, toplevel_spec: Option, last_item_idx: usize, @@ -555,7 +554,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { atom_tbl: &'a mut AtomTable, stack: &'a mut Stack, op_dir: &'a OpDir, - flags: MachineFlags, output: Outputter, cell: HeapCellValue, ) -> Self { @@ -564,7 +562,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { iter: stackful_preorder_iter(heap, stack, cell), atom_tbl, op_dir, - flags, state_stack: vec![], toplevel_spec: None, last_item_idx: 0, @@ -1187,7 +1184,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let at_cdr = self.outputter.ends_with("|"); - if self.double_quotes && self.flags.double_quotes == DoubleQuotes::Chars { + if self.double_quotes { if !at_cdr && !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) { self.remove_list_children(focus.value() as usize); return self.print_proper_string(focus.value() as usize, max_depth); diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 78b1a7f5..5ef74003 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -786,7 +786,6 @@ impl MachineState { &mut self.atom_tbl, &mut self.stack, op_dir, - self.flags, PrinterOutputter::new(), term_to_be_printed, ); diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index a4aad74c..70264ac9 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -64,7 +64,6 @@ impl MockWAM { &mut self.machine_st.atom_tbl, &mut self.machine_st.stack, &self.op_dir, - self.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(term_write_result.heap_loc), ); diff --git a/src/toplevel.pl b/src/toplevel.pl index 2df6d360..870b6990 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -221,7 +221,13 @@ arity_specifier(0, _). arity_specifier(1, S) :- atom_chars(S, [_,_]). arity_specifier(2, S) :- atom_chars(S, [_,_,_]). +double_quotes_option(DQ) :- + ( current_prolog_flag(double_quotes, chars) -> DQ = true + ; DQ = false + ). + write_goal(G, VarList, MaxDepth) :- + double_quotes_option(DQ), ( G = (Var = Value) -> ( var(Value) -> select((Var = _), VarList, NewVarList) @@ -231,16 +237,17 @@ write_goal(G, VarList, MaxDepth) :- write(' = '), ( needs_bracketing(Value, =) -> write('('), - write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), + write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(DQ)]), write(')') - ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]) + ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(DQ)]) ) ; G == [] -> write('true') - ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth), double_quotes(true)]) + ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth), double_quotes(DQ)]) ). write_last_goal(G, VarList, MaxDepth) :- + double_quotes_option(DQ), ( G = (Var = Value) -> ( var(Value) -> select((Var = _), VarList, NewVarList) @@ -250,9 +257,9 @@ write_last_goal(G, VarList, MaxDepth) :- write(' = '), ( needs_bracketing(Value, =) -> write('('), - write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), + write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(DQ)]), write(')') - ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(true)]), + ; write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth), double_quotes(DQ)]), ( trailing_period_is_ambiguous(Value) -> write(' ') ; true @@ -260,7 +267,7 @@ write_last_goal(G, VarList, MaxDepth) :- ) ; G == [] -> write('true') - ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth), double_quotes(true)]) + ; write_term(G, [quoted(true), variable_names(VarList), max_depth(MaxDepth), double_quotes(DQ)]) ). write_eq((G1, G2), VarList, MaxDepth) :- From 5ffdd2d91ab9e5c4e3acb5accc02f54461b6f85e Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 5 Jul 2023 14:50:38 -0600 Subject: [PATCH 279/361] shrink scope of control_entry_point catch, add CutPoint tag to printer --- src/heap_print.rs | 2 +- src/lib/builtins.pl | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 10a211a1..7e628949 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -1532,7 +1532,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { }); } } - (HeapCellValueTag::Fixnum, n) => { + (HeapCellValueTag::Fixnum | HeapCellValueTag::CutPoint, n) => { self.print_number(max_depth, NumberFocus::Unfocused(Number::Fixnum(n)), &op); } (HeapCellValueTag::F64, f) => { diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 4794c603..db5282f7 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -301,19 +301,12 @@ set_cp(B) :- '$set_cp'(B). control_entry_point(G) :- functor(G, Name, Arity), - catch(builtins:control_entry_point_(G), - dispatch_prep_error, - builtins:throw(error(type_error(callable, G), Name/Arity))). - - -:- non_counted_backtracking control_entry_point_/1. - -control_entry_point_(G) :- '$get_cp'(B), - dispatch_prep(G,B,Conts), + catch('$call'(builtins:dispatch_prep(G,B,Conts)), + dispatch_prep_error, + '$call'(builtins:throw(error(type_error(callable, G), Name/Arity)))), dispatch_call_list(Conts). - :- non_counted_backtracking cont_list_to_goal/2. cont_list_goal([Cont], Cont) :- !. @@ -771,7 +764,7 @@ catch(G,C,R,Bb) :- end_block(Bb, NBb) :- '$clean_up_block'(NBb), '$reset_block'(Bb). -end_block(Bb, NBb) :- +end_block(_Bb, NBb) :- '$reset_block'(NBb), '$fail'. From 9ff1b660f1a9168d011fc663c3d399196dc558d1 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 5 Jul 2023 18:11:09 -0600 Subject: [PATCH 280/361] correct heap_print.rs tests --- src/heap_print.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 7e628949..e17eecd8 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -1676,7 +1676,6 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, - wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0) ); @@ -1706,7 +1705,6 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, - wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0) ); @@ -1731,7 +1729,6 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, - wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0) ); @@ -1745,7 +1742,6 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, - wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0) ); @@ -1777,7 +1773,6 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, - wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0), ); @@ -1797,7 +1792,6 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, - wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0), ); @@ -1815,7 +1809,6 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, - wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0) ); @@ -1846,7 +1839,6 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, - wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0) ); @@ -1870,7 +1862,6 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, - wam.machine_st.flags, PrinterOutputter::new(), pstr_loc_as_cell!(0) ); @@ -1899,7 +1890,6 @@ mod tests { &mut wam.machine_st.atom_tbl, &mut wam.machine_st.stack, &wam.op_dir, - wam.machine_st.flags, PrinterOutputter::new(), heap_loc_as_cell!(0), ); From 483e4568a29adfc5d0e0172db6a92be4626758f6 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 6 Jul 2023 11:20:49 -0600 Subject: [PATCH 281/361] add scc_block to MachineState to avoid SCC cleanup terms being deallocated too early (#1427) --- build/instructions_template.rs | 10 +++++- src/lib/iso_ext.pl | 11 +++---- src/machine/dispatch.rs | 16 ++++++++++ src/machine/machine_state.rs | 4 ++- src/machine/machine_state_impl.rs | 20 +++++------- src/machine/mod.rs | 2 +- src/machine/system_calls.rs | 52 +++++++++++++++++++++++++------ src/parser/ast.rs | 6 ++++ 8 files changed, 90 insertions(+), 31 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index dc88a827..2629f7b3 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -322,7 +322,7 @@ enum SystemClauseType { GetSCCCleaner, #[strum_discriminants(strum(props(Arity = "2", Name = "$head_is_dynamic")))] HeadIsDynamic, - #[strum_discriminants(strum(props(Arity = "2", Name = "$install_scc_cleaner")))] + #[strum_discriminants(strum(props(Arity = "1", Name = "$install_scc_cleaner")))] InstallSCCCleaner, #[strum_discriminants(strum(props(Arity = "3", Name = "$install_inference_counter")))] InstallInferenceCounter, @@ -404,6 +404,8 @@ enum SystemClauseType { GetBall, #[strum_discriminants(strum(props(Arity = "1", Name = "$get_current_block")))] GetCurrentBlock, + #[strum_discriminants(strum(props(Arity = "1", Name = "$get_current_scc_block")))] + GetCurrentSCCBlock, #[strum_discriminants(strum(props(Arity = "1", Name = "$get_cp")))] GetCutPoint, #[strum_discriminants(strum(props(Arity = "1", Name = "$get_double_quotes")))] @@ -420,6 +422,8 @@ enum SystemClauseType { ReadTermFromChars, #[strum_discriminants(strum(props(Arity = "1", Name = "$reset_block")))] ResetBlock, + #[strum_discriminants(strum(props(Arity = "1", Name = "$reset_scc_block")))] + ResetSCCBlock, #[strum_discriminants(strum(props(Arity = "0", Name = "$return_from_verify_attr")))] ReturnFromVerifyAttr, #[strum_discriminants(strum(props(Arity = "1", Name = "$set_ball")))] @@ -1713,6 +1717,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallFail | &Instruction::CallGetBall | &Instruction::CallGetCurrentBlock | + &Instruction::CallGetCurrentSCCBlock | &Instruction::CallGetCutPoint | &Instruction::CallGetDoubleQuotes | &Instruction::CallInstallNewBlock | @@ -1732,6 +1737,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallQuotedToken | &Instruction::CallReadTermFromChars | &Instruction::CallResetBlock | + &Instruction::CallResetSCCBlock | &Instruction::CallReturnFromVerifyAttr | &Instruction::CallSetBall | &Instruction::CallPushBallStack | @@ -1935,6 +1941,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteFail | &Instruction::ExecuteGetBall | &Instruction::ExecuteGetCurrentBlock | + &Instruction::ExecuteGetCurrentSCCBlock | &Instruction::ExecuteGetCutPoint | &Instruction::ExecuteGetDoubleQuotes | &Instruction::ExecuteInstallNewBlock | @@ -1954,6 +1961,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteQuotedToken | &Instruction::ExecuteReadTermFromChars | &Instruction::ExecuteResetBlock | + &Instruction::ExecuteResetSCCBlock | &Instruction::ExecuteReturnFromVerifyAttr | &Instruction::ExecuteSetBall | &Instruction::ExecutePushBallStack | diff --git a/src/lib/iso_ext.pl b/src/lib/iso_ext.pl index 0af55c99..8b3e8dc3 100644 --- a/src/lib/iso_ext.pl +++ b/src/lib/iso_ext.pl @@ -138,7 +138,7 @@ setup_call_cleanup(S, G, C) :- '$get_b_value'(B), '$call_with_inference_counting'(call(S)), '$set_cp_by_default'(B), - '$get_current_block'(Bb), + '$get_current_scc_block'(Bb), ( C = _:CC, var(CC) -> instantiation_error(setup_call_cleanup/3) @@ -151,17 +151,16 @@ setup_call_cleanup(S, G, C) :- scc_helper(C, G, Bb) :- '$get_cp'(Cp), - '$install_scc_cleaner'(C, NBb), + '$install_scc_cleaner'(C), '$call_with_inference_counting'(call(G)), ( '$check_cp'(Cp) -> - '$reset_block'(Bb), + '$reset_scc_block'(Bb), run_cleaners_without_handling(Cp) ; true - ; '$reset_block'(NBb), - '$fail' + ; '$fail' ). scc_helper(_, _, Bb) :- - '$reset_block'(Bb), + '$reset_scc_block'(Bb), '$push_ball_stack', run_cleaners_with_handling, '$pop_from_ball_stack', diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 8029bb70..ba13cf5b 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4114,6 +4114,14 @@ impl Machine { self.get_current_block(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallGetCurrentSCCBlock => { + self.get_current_scc_block(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteGetCurrentSCCBlock => { + self.get_current_scc_block(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } &Instruction::CallGetCutPoint => { self.get_cut_point(); step_or_fail!(self, self.machine_st.p += 1); @@ -4250,6 +4258,14 @@ impl Machine { self.reset_block(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallResetSCCBlock => { + self.reset_scc_block(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteResetSCCBlock => { + self.reset_scc_block(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } &Instruction::CallReturnFromVerifyAttr | &Instruction::ExecuteReturnFromVerifyAttr => { self.return_from_verify_attr(); diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 5ef74003..2d62d368 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -73,11 +73,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, // save current ball before jumping via, e.g., verify_attr interrupt. pub(super) lifted_heap: Heap, pub(super) interms: Vec, // 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, @@ -112,6 +113,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) diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index 5cc07b1f..436641cd 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -47,6 +47,7 @@ impl MachineState { tr: 0, hb: 0, block: 0, + scc_block: 0, ball: Ball::new(), ball_stack: vec![], lifted_heap: Heap::new(), @@ -328,6 +329,11 @@ impl MachineState { unifier.unify_internal(); } + #[inline(always)] + pub(super) fn effective_block(&self) -> usize { + std::cmp::max(self.block, self.scc_block) + } + pub(super) fn set_ball(&mut self) { self.ball.reset(); @@ -341,8 +347,9 @@ impl MachineState { ); } + #[inline(always)] pub(super) fn unwind_stack(&mut self) { - self.b = self.block; + self.b = self.effective_block(); self.fail = true; } @@ -1437,17 +1444,6 @@ impl MachineState { .unwrap_or(true) } - pub fn reset_block(&mut self, addr: HeapCellValue) { - read_heap_cell!(self.store(addr), - (HeapCellValueTag::Fixnum, n) => { - self.block = n.get_num() as usize; - } - _ => { - self.fail = true; - } - ) - } - #[inline(always)] fn try_functor_compound_case(&mut self, name: Atom, arity: usize) { self.try_functor_unify_components(atom_as_cell!(name), arity); diff --git a/src/machine/mod.rs b/src/machine/mod.rs index ddf64d34..add3be4e 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -828,7 +828,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!( diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 9fef738c..20b1e330 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -802,7 +802,7 @@ impl MachineState { unify_fn!(*self, list_of_vars, outcome); } - #[inline] + #[inline(always)] pub(crate) fn install_new_block(&mut self, value: HeapCellValue) -> usize { let value = self.store(self.deref(value)); @@ -5160,18 +5160,18 @@ impl Machine { pub(crate) fn get_scc_cleaner(&mut self) { let dest = self.machine_st.registers[1]; - if let Some((addr, b_cutoff, prev_b)) = self.machine_st.cont_pts.pop() { + if let Some((addr, b_cutoff, prev_block)) = self.machine_st.cont_pts.pop() { let b = self.machine_st.stack.index_or_frame(self.machine_st.b).prelude.b; if b <= b_cutoff { - self.machine_st.block = prev_b; + self.machine_st.scc_block = prev_block; if let Some(r) = dest.as_var() { self.machine_st.bind(r, addr); return; } } else { - self.machine_st.cont_pts.push((addr, b_cutoff, prev_b)); + self.machine_st.cont_pts.push((addr, b_cutoff, prev_block)); } } @@ -5203,11 +5203,11 @@ impl Machine { pub(crate) fn install_scc_cleaner(&mut self) { let addr = self.machine_st.registers[1]; let b = self.machine_st.b; - let prev_block = self.machine_st.block; + let prev_block = self.machine_st.scc_block; self.machine_st.run_cleaners_fn = Machine::run_cleaners; - self.machine_st.install_new_block(self.machine_st.registers[2]); + self.machine_st.scc_block = b; self.machine_st.cont_pts.push((addr, b, prev_block)); } @@ -5574,8 +5574,18 @@ impl Machine { #[inline(always)] pub(crate) fn get_current_block(&mut self) { - let n = Fixnum::build_with(i64::try_from(self.machine_st.block).unwrap()); - self.machine_st.unify_fixnum(n, self.machine_st.registers[1]); + let addr = self.machine_st.registers[1]; + let block = Fixnum::build_with(self.machine_st.block as i64); + + self.machine_st.unify_fixnum(block, addr); + } + + #[inline(always)] + pub(crate) fn get_current_scc_block(&mut self) { + let addr = self.machine_st.registers[1]; + let block = Fixnum::build_with(self.machine_st.scc_block as i64); + + self.machine_st.unify_fixnum(block, addr); } #[inline(always)] @@ -5782,8 +5792,30 @@ impl Machine { #[inline(always)] pub(crate) fn reset_block(&mut self) { - let addr = self.machine_st.deref(self.machine_st.registers[1]); - self.machine_st.reset_block(addr); + let addr = self.deref_register(1); + + read_heap_cell!(addr, + (HeapCellValueTag::Fixnum, block) => { + self.machine_st.block = block.get_num() as usize; + } + _ => { + self.machine_st.fail = true; + } + ); + } + + #[inline(always)] + pub(crate) fn reset_scc_block(&mut self) { + let addr = self.deref_register(1); + + read_heap_cell!(addr, + (HeapCellValueTag::Fixnum, block) => { + self.machine_st.scc_block = block.get_num() as usize; + } + _ => { + self.machine_st.fail = true; + } + ); } #[inline(always)] diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 272d5b7e..6ac5d05e 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -502,6 +502,12 @@ impl Fixnum { .with_f(false) } + #[inline] + pub fn get_tag(&self) -> HeapCellValueTag { + use modular_bitfield::Specifier; + HeapCellValueTag::from_bytes(self.tag()).unwrap() + } + #[inline] pub fn build_with_checked(num: i64) -> Result { const UPPER_BOUND: i64 = (1 << 55) - 1; From b746a8f9ab191da151ba06e2dd5fbe41f23382b2 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 6 Jul 2023 11:38:12 -0600 Subject: [PATCH 282/361] add stream alias check to atom/1 (#1855) --- src/machine/dispatch.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index ba13cf5b..11e6d95d 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -2376,6 +2376,20 @@ impl Machine { (HeapCellValueTag::Char) => { self.machine_st.p += 1; } + (HeapCellValueTag::Cons, c) => { + match_untyped_arena_ptr!(c, + (ArenaHeaderTag::Stream, stream) => { + if stream.options().get_alias().is_none() { + self.machine_st.backtrack(); + } else { + self.machine_st.p += 1; + } + } + _ => { + self.machine_st.backtrack(); + } + ); + } _ => { self.machine_st.backtrack(); } @@ -2405,6 +2419,20 @@ impl Machine { (HeapCellValueTag::Char) => { self.machine_st.p = self.machine_st.cp; } + (HeapCellValueTag::Cons, c) => { + match_untyped_arena_ptr!(c, + (ArenaHeaderTag::Stream, stream) => { + if stream.options().get_alias().is_none() { + self.machine_st.backtrack(); + } else { + self.machine_st.p = self.machine_st.cp; + } + } + _ => { + self.machine_st.backtrack(); + } + ); + } _ => { self.machine_st.backtrack(); } From 811ff652097849bbb05e0e6f51c186d65cd2e236 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 7 Jul 2023 10:38:05 -0600 Subject: [PATCH 283/361] add stream alias processing to atom_chars/2, atom_codes/2 --- src/machine/system_calls.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index b05dd102..f74ad4a8 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -2181,6 +2181,17 @@ impl Machine { self.machine_st.fail = true; } + (HeapCellValueTag::Cons, ptr) => { + match_untyped_arena_ptr!(ptr, + (ArenaHeaderTag::Stream, stream) => { + let alias = stream.options().get_alias().unwrap(); + self.machine_st.unify_complete_string(alias, a2); + } + _ => { + unreachable!(); + } + ); + } _ => { unreachable!(); } @@ -2240,6 +2251,22 @@ impl Machine { } } } + (HeapCellValueTag::Cons, ptr) => { + match_untyped_arena_ptr!(ptr, + (ArenaHeaderTag::Stream, stream) => { + let alias = stream.options().get_alias().unwrap(); + + let iter = alias.chars() + .map(|c| fixnum_as_cell!(Fixnum::build_with(c as i64))); + + let h = iter_to_heap_list(&mut self.machine_st.heap, iter); + unify!(self.machine_st, heap_loc_as_cell!(h), self.machine_st.registers[2]); + } + _ => { + unreachable!(); + } + ); + } _ => { unreachable!(); } From b7f77d1747a7c96f94818b146f6b62ba9a9c613d Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 7 Jul 2023 11:05:44 -0600 Subject: [PATCH 284/361] interpret '\u{0}' as end_of_file in get_char/1 --- src/machine/system_calls.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index f74ad4a8..13045f8c 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -3400,11 +3400,7 @@ impl Machine { let result = iter.read_char(); match result { - Some(Ok(c)) => { - self.machine_st.unify_char(c, addr); - break; - } - _ => { + Some(Ok('\u{0}')) | Some(Err(_)) | None => { self.machine_st.eof_action( self.machine_st.registers[2], stream, @@ -3418,6 +3414,10 @@ impl Machine { break; } } + Some(Ok(c)) => { + self.machine_st.unify_char(c, addr); + break; + } } } From 4db0b385f3e382cb00b78e6381b990389c9a5121 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 7 Jul 2023 13:04:27 -0600 Subject: [PATCH 285/361] treat unexpected EOF as incomplete reduction in bracketed_comment --- src/parser/char_reader.rs | 2 +- src/parser/lexer.rs | 27 +++++++++++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/parser/char_reader.rs b/src/parser/char_reader.rs index 9d6babf4..4b08e008 100644 --- a/src/parser/char_reader.rs +++ b/src/parser/char_reader.rs @@ -117,7 +117,7 @@ impl CharReader { // Branch using `>=` instead of the more correct `==` // to tell the compiler that the pos..cap slice is always valid. if self.pos >= self.buf.len() { - debug_assert!(self.pos == self.buf.len()); + debug_assert!(self.pos >= self.buf.len()); self.buf.clear(); diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index 7f5d73ed..47ff0a22 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -168,17 +168,32 @@ impl<'a, R: CharRead> Lexer<'a, R> { let mut c = self.lookahead_char()?; - loop { - while !comment_2_char!(c) { + let mut comment_loop = || { + loop { + while !comment_2_char!(c) { + self.skip_char(c); + c = self.lookahead_char()?; + } + self.skip_char(c); c = self.lookahead_char()?; + + if comment_1_char!(c) { + break; + } } - self.skip_char(c); - c = self.lookahead_char()?; + Ok(()) + }; - if comment_1_char!(c) { - break; + match comment_loop() { + Err(ParserError::UnexpectedEOF) => { + return Err(ParserError::IncompleteReduction(self.line_num, self.col_num)); + } + Err(e) => { + return Err(e); + } + Ok(_) => { } } From 6525c1f543128f4c153881a8dc0acf5ec1cbe05b Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 8 Jul 2023 08:19:05 +0200 Subject: [PATCH 286/361] print version more readably, addressing #1868 --- src/toplevel.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/toplevel.pl b/src/toplevel.pl index 870b6990..6a87df3c 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -83,7 +83,7 @@ print_help :- print_version :- '$scryer_prolog_version'(Version), - write(Version), nl, + maplist(put_char, Version), nl, halt. gather_goal(Type, Args0, Goals) :- From 067b5998ee557899247faab3914abcfdc8e75684 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 8 Jul 2023 13:38:32 -0600 Subject: [PATCH 287/361] clarify EOF error across stream types and predicates (#1867, #1870) --- src/machine/dispatch.rs | 2 +- src/machine/machine_errors.rs | 1 - src/machine/machine_state.rs | 27 +++++++++++--------- src/machine/streams.rs | 14 ++--------- src/machine/system_calls.rs | 47 ++++++++++++++++++++++++++--------- src/machine/term_stream.rs | 6 ++--- src/parser/ast.rs | 22 +++++++++++++--- src/parser/char_reader.rs | 2 -- src/parser/lexer.rs | 14 +++++------ src/parser/parser.rs | 4 +-- src/read.rs | 16 +----------- 11 files changed, 84 insertions(+), 71 deletions(-) diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 11e6d95d..047d5071 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -198,7 +198,7 @@ impl Machine { let value = self.machine_st.registers[2]; unify_fn!(&mut self.machine_st, value, heap_loc_as_cell!(offset.heap_loc)); } - Err(CompilationError::ParserError(ParserError::UnexpectedEOF)) => { + Err(CompilationError::ParserError(e)) if e.is_unexpected_eof() => { let value = self.machine_st.registers[2]; self.machine_st.unify_atom(atom!("end_of_file"), value); } diff --git a/src/machine/machine_errors.rs b/src/machine/machine_errors.rs index dc9f8f13..4422fdae 100644 --- a/src/machine/machine_errors.rs +++ b/src/machine/machine_errors.rs @@ -680,7 +680,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 => { diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 2d62d368..7b074a64 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -634,21 +634,24 @@ impl MachineState { return Ok(unify_fn!(*self, var_names_offset, var_names_addr)); } Err(err) => { - if let CompilationError::ParserError(ParserError::UnexpectedEOF) = err { - self.eof_action( - self.registers[2], - stream, - atom!("read_term"), - 3, - )?; + match err { + CompilationError::ParserError(e) if e.is_unexpected_eof() => { + self.eof_action( + self.registers[2], + stream, + atom!("read_term"), + 3, + )?; - if stream.options().eof_action() == EOFAction::Reset { - if self.fail == false { - continue; + if stream.options().eof_action() == EOFAction::Reset { + if self.fail == false { + continue; + } } - } - return Ok(()); + return Ok(()); + } + _ => {} } let stub = functor_stub(atom!("read_term"), 3); diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 27942cb2..80c51537 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -1523,20 +1523,10 @@ impl MachineState { } } - pub(crate) fn open_parsing_stream( - &mut self, - mut stream: Stream, - stub_name: Atom, - stub_arity: usize, - ) -> Result { + pub(crate) fn open_parsing_stream(&mut self, mut stream: Stream) -> Result { 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 diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 13045f8c..da2314d3 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -925,7 +925,7 @@ impl MachineState { loop { match lexer.lookahead_char() { - Err(ParserError::UnexpectedEOF) => { + Err(e) if e.is_unexpected_eof() => { let mut parser = Parser::from_lexer(lexer); let op_dir = CompositeOpDir::new(&indices.op_dir, None); @@ -3377,7 +3377,7 @@ impl Machine { } let stub_gen = || functor_stub(atom!("get_char"), 2); - let mut iter = self.machine_st.open_parsing_stream(stream, atom!("get_char"), 2)?; + let result = self.machine_st.open_parsing_stream(stream); let addr = if addr.is_var() { addr @@ -3396,11 +3396,26 @@ impl Machine { ) }; - loop { - let result = iter.read_char(); + let mut iter = match result { + Ok(iter) => iter, + Err(e) => { + if e.is_unexpected_eof() { + self.machine_st.unify_atom(atom!("end_of_file"), addr); + return Ok(()); + } else { + let err = self.machine_st.session_error(SessionError::from(e)); + return Err(self.machine_st.error_form(err, stub_gen())); + } + } + }; - match result { - Some(Ok('\u{0}')) | Some(Err(_)) | None => { + loop { + match iter.read_char() { + Some(Ok(c)) => { + self.machine_st.unify_char(c, addr); + break; + } + _ => { self.machine_st.eof_action( self.machine_st.registers[2], stream, @@ -3414,10 +3429,6 @@ impl Machine { break; } } - Some(Ok(c)) => { - self.machine_st.unify_char(c, addr); - break; - } } } @@ -3459,7 +3470,13 @@ impl Machine { string.push(c as char); } } else { - let mut iter = self.machine_st.open_parsing_stream(stream, atom!("get_n_chars"), 2)?; + let mut iter = self.machine_st.open_parsing_stream(stream) + .map_err(|e| { + let err = self.machine_st.session_error(SessionError::from(e)); + let stub = functor_stub(atom!("get_n_chars"), 2); + + self.machine_st.error_form(err, stub) + })?; for _ in 0..num { let result = iter.read_char(); @@ -3557,7 +3574,13 @@ impl Machine { } }; - let mut iter = self.machine_st.open_parsing_stream(stream.clone(), atom!("get_code"), 2)?; + let mut iter = self.machine_st.open_parsing_stream(stream) + .map_err(|e| { + let err = self.machine_st.session_error(SessionError::from(e)); + let stub = functor_stub(atom!("get_code"), 2); + + self.machine_st.error_form(err, stub) + })?; loop { let result = iter.read_char(); diff --git a/src/machine/term_stream.rs b/src/machine/term_stream.rs index 98d77627..079f5c49 100644 --- a/src/machine/term_stream.rs +++ b/src/machine/term_stream.rs @@ -125,15 +125,15 @@ pub struct InlineTermStream { impl TermStream for InlineTermStream { fn next(&mut self, _: &CompositeOpDir) -> Result { - Err(CompilationError::from(ParserError::UnexpectedEOF)) + Err(CompilationError::from(ParserError::unexpected_eof())) } fn eof(&mut self) -> Result { - Ok(true) + Ok(true) } fn listing_src(&self) -> &ListingSource { - &ListingSource::User + &ListingSource::User } } diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 6ac5d05e..caed5915 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -7,7 +7,7 @@ use crate::types::HeapCellValueTag; use std::cell::{Cell, Ref, RefCell, RefMut}; use std::fmt; use std::hash::{Hash, Hasher}; -use std::io::{Error as IOError}; +use std::io::{Error as IOError, ErrorKind}; use std::ops::{Deref, Neg}; use std::rc::Rc; use std::vec::Vec; @@ -380,7 +380,7 @@ pub enum ParserError { NonPrologChar(usize, usize), ParseBigInt(usize, usize), UnexpectedChar(char, usize, usize), - UnexpectedEOF, + // UnexpectedEOF, Utf8Error(usize, usize), } @@ -403,16 +403,30 @@ impl ParserError { ParserError::BackQuotedString(..) => atom!("back_quoted_string"), ParserError::IncompleteReduction(..) => atom!("incomplete_reduction"), ParserError::InvalidSingleQuotedCharacter(..) => atom!("invalid_single_quoted_character"), + ParserError::IO(e) if e.kind() == ErrorKind::UnexpectedEof => atom!("unexpected_end_of_file"), ParserError::IO(_) => atom!("input_output_error"), - ParserError::LexicalError(_) => atom!("lexical_error"), // TODO: ? + ParserError::LexicalError(_) => atom!("lexical_error"), ParserError::MissingQuote(..) => atom!("missing_quote"), ParserError::NonPrologChar(..) => atom!("non_prolog_character"), ParserError::ParseBigInt(..) => atom!("cannot_parse_big_int"), ParserError::UnexpectedChar(..) => atom!("unexpected_char"), - ParserError::UnexpectedEOF => atom!("unexpected_end_of_file"), ParserError::Utf8Error(..) => atom!("utf8_conversion_error"), } } + + #[inline] + pub fn unexpected_eof() -> Self { + ParserError::IO(std::io::Error::from(ErrorKind::UnexpectedEof)) + } + + #[inline] + pub fn is_unexpected_eof(&self) -> bool { + if let ParserError::IO(e) = self { + e.kind() == ErrorKind::UnexpectedEof + } else { + false + } + } } impl From for ParserError { diff --git a/src/parser/char_reader.rs b/src/parser/char_reader.rs index 4b08e008..32854b07 100644 --- a/src/parser/char_reader.rs +++ b/src/parser/char_reader.rs @@ -117,8 +117,6 @@ impl CharReader { // Branch using `>=` instead of the more correct `==` // to tell the compiler that the pos..cap slice is always valid. if self.pos >= self.buf.len() { - debug_assert!(self.pos >= self.buf.len()); - self.buf.clear(); let mut word = [0u8; std::mem::size_of::()]; diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index 47ff0a22..e3a81e6f 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -18,7 +18,7 @@ macro_rules! is_not_eof { return Ok(true); } Ok(c) => c, - Err($crate::parser::ast::ParserError::UnexpectedEOF) => return Ok(true), + Err(e) if e.is_unexpected_eof() => return Ok(true), Err(e) => return Err(e), } }; @@ -94,14 +94,14 @@ impl<'a, R: CharRead> Lexer<'a, R> { pub fn lookahead_char(&mut self) -> Result { match self.reader.peek_char() { Some(Ok(c)) => Ok(c), - _ => Err(ParserError::UnexpectedEOF) + _ => Err(ParserError::unexpected_eof()) } } pub fn read_char(&mut self) -> Result { match self.reader.read_char() { Some(Ok(c)) => Ok(c), - _ => Err(ParserError::UnexpectedEOF) + _ => Err(ParserError::unexpected_eof()) } } @@ -168,7 +168,7 @@ impl<'a, R: CharRead> Lexer<'a, R> { let mut c = self.lookahead_char()?; - let mut comment_loop = || { + let mut comment_loop = || -> Result<(), ParserError> { loop { while !comment_2_char!(c) { self.skip_char(c); @@ -187,7 +187,7 @@ impl<'a, R: CharRead> Lexer<'a, R> { }; match comment_loop() { - Err(ParserError::UnexpectedEOF) => { + Err(e) if e.is_unexpected_eof() => { return Err(ParserError::IncompleteReduction(self.line_num, self.col_num)); } Err(e) => { @@ -1003,7 +1003,7 @@ impl<'a, R: CharRead> Lexer<'a, R> { return Ok(Token::End); } - Err(ParserError::UnexpectedEOF) => { + Err(e) if e.is_unexpected_eof() => { return Ok(Token::End); } _ => { @@ -1055,7 +1055,7 @@ impl<'a, R: CharRead> Lexer<'a, R> { } if c == '\u{0}' { - return Err(ParserError::UnexpectedEOF); + return Err(ParserError::unexpected_eof()) } self.name_token(c) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 5bb600da..c170cfe0 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -275,7 +275,7 @@ fn read_tokens(lexer: &mut Lexer) -> Result, ParserEr break; } } - Err(ParserError::UnexpectedEOF) if !tokens.is_empty() => { + Err(e) if e.is_unexpected_eof() && !tokens.is_empty() => { return Err(ParserError::IncompleteReduction( lexer.line_num, lexer.col_num, @@ -883,7 +883,7 @@ impl<'a, R: CharRead> Parser<'a, R> { }) = get_op_desc(name, op_dir) { if (pre > 0 && inf + post > 0) || is_negate!(spec) { - match self.tokens.last().ok_or(ParserError::UnexpectedEOF)? { + match self.tokens.last().ok_or(ParserError::unexpected_eof())? { // do this when layout hasn't been inserted, // ie. why we don't match on Token::Open. Token::OpenCT => { diff --git a/src/read.rs b/src/read.rs index 085c867e..86fdcdbd 100644 --- a/src/read.rs +++ b/src/read.rs @@ -150,7 +150,7 @@ impl ReadlineStream { Ok(self.pending_input.get_ref().get_ref().len()) } - Err(ReadlineError::Eof) => Ok(0), + Err(ReadlineError::Eof) => Err(Error::from(ErrorKind::UnexpectedEof)), Err(e) => Err(Error::new(ErrorKind::InvalidInput, e)), } } @@ -178,9 +178,6 @@ impl ReadlineStream { loop { match byte { - Some(0) => { - return Ok(0); - } Some(b) => { return Ok(b); } @@ -188,10 +185,6 @@ impl ReadlineStream { Err(e) => { return Err(e); } - Ok(0) => { - self.pending_input.get_mut().get_mut().push('\u{0}'); - return Ok(0); - } _ => { set_prompt(false); } @@ -218,9 +211,6 @@ impl CharRead for ReadlineStream { fn peek_char(&mut self) -> Option> { loop { match self.pending_input.peek_char() { - Some(Ok('\u{0}')) => { - return Some(Ok('\u{0}')); - } Some(Ok(c)) => { return Some(Ok(c)); } @@ -229,10 +219,6 @@ impl CharRead for ReadlineStream { Err(e) => { return Some(Err(e)); } - Ok(0) => { - self.pending_input.get_mut().get_mut().push('\u{0}'); - return Some(Ok('\u{0}')); - } _ => { set_prompt(false); } From fb8e3071f2b6de2c5737686509029f2642741dec Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 8 Jul 2023 19:27:32 -0600 Subject: [PATCH 288/361] follow EOF action after open_parsing_stream in get_char if stream at EOF --- src/machine/system_calls.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index da2314d3..11a724cc 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -3400,8 +3400,12 @@ impl Machine { Ok(iter) => iter, Err(e) => { if e.is_unexpected_eof() { - self.machine_st.unify_atom(atom!("end_of_file"), addr); - return Ok(()); + return self.machine_st.eof_action( + self.machine_st.registers[2], + stream, + atom!("get_char"), + 2, + ); } else { let err = self.machine_st.session_error(SessionError::from(e)); return Err(self.machine_st.error_form(err, stub_gen())); From b8a6882a276c0c4723f013c69a66a002eb8ed3c5 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 8 Jul 2023 22:25:30 -0600 Subject: [PATCH 289/361] refine EOF handling --- scryer-prolog.wxs | 54 +++++++++++----------- src/machine/system_calls.rs | 8 +++- src/machine/term_stream.rs | 7 +-- src/parser/lexer.rs | 90 +++++++++++++++++-------------------- src/parser/parser.rs | 26 ++++++++--- src/read.rs | 22 ++++----- 6 files changed, 108 insertions(+), 99 deletions(-) diff --git a/scryer-prolog.wxs b/scryer-prolog.wxs index b69b1dff..fef55e49 100644 --- a/scryer-prolog.wxs +++ b/scryer-prolog.wxs @@ -1,28 +1,28 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 11a724cc..c6cf9254 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -7469,17 +7469,21 @@ impl Machine { #[inline(always)] pub(crate) fn devour_whitespace(&mut self) -> CallResult { - let stream = self.machine_st.get_stream_or_alias( + let mut stream = self.machine_st.get_stream_or_alias( self.machine_st.registers[1], &self.indices.stream_aliases, atom!("$devour_whitespace"), 1, )?; - match self.machine_st.devour_whitespace(stream) { + let mut parser = Parser::new(stream, &mut self.machine_st); + + match devour_whitespace(&mut parser) { Ok(false) => { // not at EOF. + stream.add_lines_read(parser.lines_read()); } Ok(true) => { + stream.add_lines_read(parser.lines_read()); self.machine_st.fail = true; } Err(err) => { diff --git a/src/machine/term_stream.rs b/src/machine/term_stream.rs index 079f5c49..8c6b055d 100644 --- a/src/machine/term_stream.rs +++ b/src/machine/term_stream.rs @@ -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; @@ -58,8 +59,8 @@ impl<'a> TermStream for BootstrappingTermStream<'a> { #[inline] fn eof(&mut self) -> Result { - 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 { - return Ok(self.term_queue.is_empty()); + Ok(self.term_queue.is_empty()) } #[inline] diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index e3a81e6f..1eacc80b 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -10,20 +10,6 @@ use crate::parser::rug::Integer; use std::convert::TryFrom; use std::fmt; -macro_rules! is_not_eof { - ($parser:expr, $c:expr) => { - match $c { - Ok('\u{0}') => { - $parser.consume('\u{0}'.len_utf8()); - return Ok(true); - } - Ok(c) => c, - Err(e) if e.is_unexpected_eof() => return Ok(true), - Err(e) => return Err(e), - } - }; -} - macro_rules! consume_chars_with { ($token:expr, $e:expr) => { loop { @@ -37,6 +23,12 @@ macro_rules! consume_chars_with { }; } +#[derive(Debug, Default)] +pub struct LayoutInfo { + pub inserted: bool, + pub more: bool, +} + #[derive(Debug, PartialEq)] pub enum Token { Literal(Literal), @@ -121,18 +113,6 @@ impl<'a, R: CharRead> Lexer<'a, R> { } } - pub fn eof(&mut self) -> Result { - let mut c = is_not_eof!(self.reader, self.lookahead_char()); - - while layout_char!(c) { - self.skip_char(c); - - c = is_not_eof!(self.reader, self.lookahead_char()); - } - - Ok(false) - } - fn single_line_comment(&mut self) -> Result<(), ParserError> { loop { if self.reader.peek_char().is_none() { @@ -929,38 +909,48 @@ impl<'a, R: CharRead> Lexer<'a, R> { } } - pub fn scan_for_layout(&mut self) -> Result { - let mut layout_inserted = false; - let mut more_layout = true; + pub fn consume_layout( + &mut self, + c: Option, + layout_info: &mut LayoutInfo, + ) -> Result<(), ParserError> { + match c { + Some(c) if layout_char!(c) => { + self.skip_char(c); + layout_info.inserted = true; + } + Some(c) if end_line_comment_char!(c) => { + self.single_line_comment()?; + layout_info.inserted = true; + } + Some(c) if comment_1_char!(c) => { + if self.bracketed_comment()? { + layout_info.inserted = true; + } else { + layout_info.more = false; + } + } + _ => { + layout_info.more = false; + } + } + + Ok(()) + } + + fn scan_for_layout(&mut self) -> Result { + let mut layout_info = LayoutInfo { inserted: false, more: true }; loop { let cr = self.lookahead_char(); + self.consume_layout(cr.ok(), &mut layout_info)?; - match cr { - Ok(c) if layout_char!(c) => { - self.skip_char(c); - layout_inserted = true; - } - Ok(c) if end_line_comment_char!(c) => { - self.single_line_comment()?; - layout_inserted = true; - } - Ok(c) if comment_1_char!(c) => { - if self.bracketed_comment()? { - layout_inserted = true; - } else { - more_layout = false; - } - } - _ => more_layout = false, - }; - - if !more_layout { + if !layout_info.more { break; } } - Ok(layout_inserted) + Ok(layout_info.inserted) } pub fn next_token(&mut self) -> Result { diff --git a/src/parser/parser.rs b/src/parser/parser.rs index c170cfe0..41cc108c 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -621,7 +621,26 @@ impl<'a, R: CharRead> Parser<'a, R> { } pub fn devour_whitespace(&mut self) -> Result<(), ParserError> { - self.lexer.scan_for_layout()?; + match self.lexer.lookahead_char() { + Err(e) => { // if e.is_unexpected_eof() => { + return Err(e); + } + Ok(c) => { + let mut layout_info = LayoutInfo { inserted: false, more: true }; + let mut cr = Some(c); + + loop { + self.lexer.consume_layout(cr, &mut layout_info)?; + + if !layout_info.more { + break; + } + + cr = self.lexer.lookahead_char().ok(); + } + } + } + Ok(()) } @@ -1051,11 +1070,6 @@ impl<'a, R: CharRead> Parser<'a, R> { Ok(()) } - #[inline] - pub fn eof(&mut self) -> Result { - self.lexer.eof() - } - #[inline] pub fn add_lines_read(&mut self, lines_read: usize) { self.lexer.line_num += lines_read; diff --git a/src/read.rs b/src/read.rs index 86fdcdbd..6094b38e 100644 --- a/src/read.rs +++ b/src/read.rs @@ -24,19 +24,19 @@ use std::io::{Cursor, Error, ErrorKind, Read}; type SubtermDeque = VecDeque<(usize, usize)>; -impl MachineState { - pub(crate) fn devour_whitespace( - &mut self, - mut inner: Stream, - ) -> Result { - let mut parser = Parser::new(inner, self); - - parser.devour_whitespace()?; - inner.add_lines_read(parser.lines_read()); - - parser.eof() +pub(crate) fn devour_whitespace<'a, R: CharRead>(parser: &mut Parser<'a, R>) -> Result { + match parser.devour_whitespace() { + Err(e) if e.is_unexpected_eof() => { + Ok(true) + } + Err(e) => Err(e), + Ok(()) => { + Ok(false) + } } +} +impl MachineState { pub(crate) fn read( &mut self, mut inner: Stream, From c8b90592897b7576b1947dcacdb5b1ef4fe354ce Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 9 Jul 2023 01:53:40 -0600 Subject: [PATCH 290/361] refine EOF handling more (#1873) --- src/parser/lexer.rs | 35 ++++++++++++++++++++++------------- src/parser/parser.rs | 24 ------------------------ src/read.rs | 4 ++-- src/toplevel.pl | 1 + 4 files changed, 25 insertions(+), 39 deletions(-) diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index 1eacc80b..664af935 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -24,9 +24,9 @@ macro_rules! consume_chars_with { } #[derive(Debug, Default)] -pub struct LayoutInfo { - pub inserted: bool, - pub more: bool, +struct LayoutInfo { + inserted: bool, + more: bool, } #[derive(Debug, PartialEq)] @@ -909,7 +909,7 @@ impl<'a, R: CharRead> Lexer<'a, R> { } } - pub fn consume_layout( + fn consume_layout( &mut self, c: Option, layout_info: &mut LayoutInfo, @@ -938,19 +938,28 @@ impl<'a, R: CharRead> Lexer<'a, R> { Ok(()) } - fn scan_for_layout(&mut self) -> Result { - let mut layout_info = LayoutInfo { inserted: false, more: true }; + pub fn scan_for_layout(&mut self) -> Result { + match self.lookahead_char() { + Err(e) => { + Err(e) + } + Ok(c) => { + let mut layout_info = LayoutInfo { inserted: false, more: true }; + let mut cr = Some(c); - loop { - let cr = self.lookahead_char(); - self.consume_layout(cr.ok(), &mut layout_info)?; + loop { + self.consume_layout(cr, &mut layout_info)?; - if !layout_info.more { - break; + if !layout_info.more { + break; + } + + cr = self.lookahead_char().ok(); + } + + Ok(layout_info.inserted) } } - - Ok(layout_info.inserted) } pub fn next_token(&mut self) -> Result { diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 41cc108c..a952f73f 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -620,30 +620,6 @@ impl<'a, R: CharRead> Parser<'a, R> { false } - pub fn devour_whitespace(&mut self) -> Result<(), ParserError> { - match self.lexer.lookahead_char() { - Err(e) => { // if e.is_unexpected_eof() => { - return Err(e); - } - Ok(c) => { - let mut layout_info = LayoutInfo { inserted: false, more: true }; - let mut cr = Some(c); - - loop { - self.lexer.consume_layout(cr, &mut layout_info)?; - - if !layout_info.more { - break; - } - - cr = self.lexer.lookahead_char().ok(); - } - } - } - - Ok(()) - } - pub fn reset(&mut self) { self.stack.clear() } diff --git a/src/read.rs b/src/read.rs index 6094b38e..9a191dad 100644 --- a/src/read.rs +++ b/src/read.rs @@ -25,12 +25,12 @@ use std::io::{Cursor, Error, ErrorKind, Read}; type SubtermDeque = VecDeque<(usize, usize)>; pub(crate) fn devour_whitespace<'a, R: CharRead>(parser: &mut Parser<'a, R>) -> Result { - match parser.devour_whitespace() { + match parser.lexer.scan_for_layout() { Err(e) if e.is_unexpected_eof() => { Ok(true) } Err(e) => Err(e), - Ok(()) => { + Ok(_) => { Ok(false) } } diff --git a/src/toplevel.pl b/src/toplevel.pl index 6a87df3c..989e083a 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -152,6 +152,7 @@ expand_op_list([Op | OtherOps], Pred, Spec, [(:- op(Pred, Spec, Op)) | OtherResu read_and_match :- + '$debug_hook', '$read_query_term'(_, Term, _, _, VarList), instruction_match(Term, VarList). From d18f128a3c0d7eb8b1fbf92f354cabf684d76907 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 9 Jul 2023 13:36:36 +0200 Subject: [PATCH 291/361] correct \\ to \, addressing #1865 --- src/toplevel.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/toplevel.pl b/src/toplevel.pl index 989e083a..baf7ca1d 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -280,7 +280,7 @@ write_eq(G, VarList, MaxDepth) :- write_last_goal(G, VarList, MaxDepth). graphic_token_char(C) :- - memberchk(C, [#, $, &, *, +, -, ., /, :, <, =, >, ?, @, ^, ~, \\]). + memberchk(C, [#, $, &, *, +, -, ., /, :, <, =, >, ?, @, ^, ~, \]). list_last_item([C], C) :- !. list_last_item([_|Cs], D) :- From 918dfca409a0f66c61f3c2c03e2c7ac34d2990e1 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 9 Jul 2023 14:24:21 +0200 Subject: [PATCH 292/361] DOC: new section on applications of Scryer Prolog This addresses an important aspect of #1777. --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index f5c08e89..0a78baa0 100644 --- a/README.md +++ b/README.md @@ -680,6 +680,26 @@ not need additional tools and formalisms for its application, and further, it encourages declarative reasoning that can in principle also be performed automatically. +## Applications + +Scryer Prolog's strong commitment to the Prolog ISO standard makes it +ideally suited for use in corporations and government agencies +that are subject to strict regulations pertaining to interoperability, +standards compliance and warranty. + +Successful existing applications of Scryer Prolog include the +[DocLog](https://github.com/aarroyoc/doclog) system which +generates Scryer's own documentation and homepage, [Symbolic +Analysis of Grants](https://www.brz.gv.at/en/BRZ-Tech-Blog/Tech-Blog-7-Symbolic-Analysis-of-Grants.html) +by the Austrian Federal Computing Center, and parts of the +[precautionary](https://github.com/dcnorris/precautionary/tree/main/exec/prolog) +package for the analysis of dose-escalation trials in the +safety-critical and highly regulated domain of clinical oncology. + +Scryer Prolog is also very well suited for teaching and learning +Prolog, and for testing syntactic conformance and hence portability of +existing Prolog programs. + ## Support and discussions If Scryer Prolog crashes or yields unexpected errors, consider filing From fba779063795fc8240fc0a0c97b4461d97206f60 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 9 Jul 2023 10:30:30 -0600 Subject: [PATCH 293/361] remove errant debug_hook from read_and_match --- src/toplevel.pl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/toplevel.pl b/src/toplevel.pl index baf7ca1d..8318f43b 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -152,7 +152,6 @@ expand_op_list([Op | OtherOps], Pred, Spec, [(:- op(Pred, Spec, Op)) | OtherResu read_and_match :- - '$debug_hook', '$read_query_term'(_, Term, _, _, VarList), instruction_match(Term, VarList). From 55a1f8d3daef309b5e73864bcbe6c307843e209e Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 10 Jul 2023 10:32:15 -0600 Subject: [PATCH 294/361] clean commented code from disjuncts.rs --- src/machine/disjuncts.rs | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index f2a66851..c1a71802 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -155,7 +155,6 @@ enum TraversalState { 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 - // SetChunkType(ChunkType), // consider remaining terms as belonging to a last chunk } #[derive(Debug)] @@ -214,25 +213,11 @@ impl VarData { pub type ClassifyFactResult = (Term, VarData); pub type ClassifyRuleResult = (Term, ChunkedTermVec, VarData); -fn merge_branch_seq>(branches: Iter) -> BranchInfo { +fn merge_branch_seq(branches: impl Iterator) -> BranchInfo { let mut branch_info = BranchInfo::new(BranchNumber::default()); for mut branch in branches { branch_info.branch_num = branch.branch_num; - - /* - if let Some(last_chunk) = branch_info.chunks.last_mut() { - if let Some(first_moved_chunk) = branch.chunks.first_mut() { - if last_chunk.chunk_num == first_moved_chunk.chunk_num { - last_chunk.vars.extend(first_moved_chunk.vars.drain(..)); - branch_info.chunks.extend(branch.chunks.drain(1 ..)); - - continue; - } - } - } - */ - branch_info.chunks.extend(branch.chunks.drain(..)); } From bb420e934716e6816efef494a60eb7eb9777ad60 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 11 Jul 2023 13:43:34 -0600 Subject: [PATCH 295/361] use indexing functions to set num_cells in allocate_and_frame/or_frame (#1877) --- src/machine/mod.rs | 4 ---- src/machine/stack.rs | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/machine/mod.rs b/src/machine/mod.rs index add3be4e..78c347f2 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -690,8 +690,6 @@ impl Machine { fn try_call(&mut self, name: Atom, arity: usize, idx: IndexPtr) -> CallResult { let compiled_tl_index = idx.p() as usize; - // println!("calling {}/{}", name.as_str(), arity); - match idx.tag() { IndexPtrTag::DynamicUndefined => { self.machine_st.fail = true; @@ -715,8 +713,6 @@ impl Machine { fn try_execute(&mut self, name: Atom, arity: usize, idx: IndexPtr) -> CallResult { let compiled_tl_index = idx.p() as usize; - // println!("executing {}/{}", name.as_str(), arity); - match idx.tag() { IndexPtrTag::DynamicUndefined => { self.machine_st.fail = true; diff --git a/src/machine/stack.rs b/src/machine/stack.rs index cc86aa34..1e15e69d 100644 --- a/src/machine/stack.rs +++ b/src/machine/stack.rs @@ -202,7 +202,7 @@ impl Stack { offset += mem::size_of::(); } - let and_frame = &mut *(new_ptr as *mut AndFrame); + let and_frame = self.index_and_frame_mut(e); and_frame.prelude.num_cells = num_cells; e @@ -226,7 +226,7 @@ impl Stack { offset += mem::size_of::(); } - let or_frame = &mut *(new_ptr as *mut OrFrame); + let or_frame = self.index_or_frame_mut(b); or_frame.prelude.num_cells = num_cells; b From ab80c847145316e61a0c5f651e9cdcbcfd671edf Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 12 Jul 2023 11:44:41 -0600 Subject: [PATCH 296/361] Revert "add stream alias processing to atom_chars/2, atom_codes/2" This reverts commit 811ff652097849bbb05e0e6f51c186d65cd2e236. --- src/machine/system_calls.rs | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index c6cf9254..88cdde16 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -2181,17 +2181,6 @@ impl Machine { self.machine_st.fail = true; } - (HeapCellValueTag::Cons, ptr) => { - match_untyped_arena_ptr!(ptr, - (ArenaHeaderTag::Stream, stream) => { - let alias = stream.options().get_alias().unwrap(); - self.machine_st.unify_complete_string(alias, a2); - } - _ => { - unreachable!(); - } - ); - } _ => { unreachable!(); } @@ -2251,22 +2240,6 @@ impl Machine { } } } - (HeapCellValueTag::Cons, ptr) => { - match_untyped_arena_ptr!(ptr, - (ArenaHeaderTag::Stream, stream) => { - let alias = stream.options().get_alias().unwrap(); - - let iter = alias.chars() - .map(|c| fixnum_as_cell!(Fixnum::build_with(c as i64))); - - let h = iter_to_heap_list(&mut self.machine_st.heap, iter); - unify!(self.machine_st, heap_loc_as_cell!(h), self.machine_st.registers[2]); - } - _ => { - unreachable!(); - } - ); - } _ => { unreachable!(); } From 8c33da11ce761d6e90446f20d4236da947cc30cb Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 12 Jul 2023 11:44:44 -0600 Subject: [PATCH 297/361] Revert "add stream alias check to atom/1 (#1855)" This reverts commit b746a8f9ab191da151ba06e2dd5fbe41f23382b2. --- src/machine/dispatch.rs | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 047d5071..e1ba0c0f 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -2376,20 +2376,6 @@ impl Machine { (HeapCellValueTag::Char) => { self.machine_st.p += 1; } - (HeapCellValueTag::Cons, c) => { - match_untyped_arena_ptr!(c, - (ArenaHeaderTag::Stream, stream) => { - if stream.options().get_alias().is_none() { - self.machine_st.backtrack(); - } else { - self.machine_st.p += 1; - } - } - _ => { - self.machine_st.backtrack(); - } - ); - } _ => { self.machine_st.backtrack(); } @@ -2419,20 +2405,6 @@ impl Machine { (HeapCellValueTag::Char) => { self.machine_st.p = self.machine_st.cp; } - (HeapCellValueTag::Cons, c) => { - match_untyped_arena_ptr!(c, - (ArenaHeaderTag::Stream, stream) => { - if stream.options().get_alias().is_none() { - self.machine_st.backtrack(); - } else { - self.machine_st.p = self.machine_st.cp; - } - } - _ => { - self.machine_st.backtrack(); - } - ); - } _ => { self.machine_st.backtrack(); } From 44052cb373f7f816f369b0632efe3efaabd98c82 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 12 Jul 2023 11:44:45 -0600 Subject: [PATCH 298/361] Revert "Allow comparisons with stream terms" This reverts commit 076a75d1381926f83dd77150fdbda43969c168bf. --- src/machine/machine_state_impl.rs | 144 +----------------------------- src/types.rs | 12 --- 2 files changed, 1 insertion(+), 155 deletions(-) diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index 436641cd..3bb118f9 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -11,7 +11,6 @@ use crate::machine::machine_indices::*; use crate::machine::machine_state::*; use crate::machine::partial_string::*; use crate::machine::stack::*; -use crate::machine::Stream; use crate::machine::unify::*; use crate::parser::ast::*; use crate::parser::rug::{Integer, Rational}; @@ -522,14 +521,6 @@ impl MachineState { return Some(n1.cmp(&n2)); } } - (HeapCellValueTag::Cons, ptr) => { - let stream = cell_as_stream!(ptr); - let n2 = stream.options().get_alias().unwrap(); - if n1 != n2 { - self.pdl.clear(); - return Some(n1.cmp(&n2)); - } - } _ => { unreachable!(); } @@ -574,22 +565,7 @@ impl MachineState { ); } } - (HeapCellValueTag::Cons, ptr) => { - let stream = cell_as_stream!(ptr); - let n2 = stream.options().get_alias().unwrap(); - if let Some(c2) = n2.as_char() { - if c1 != c2 { - self.pdl.clear(); - return Some(c1.cmp(&c2)); - } - } else { - self.pdl.clear(); - return Some( - Some(c1).cmp(&n2.chars().next()) - .then(Ordering::Less) - ); - } - } _ => { + _ => { unreachable!() } ) @@ -628,65 +604,11 @@ impl MachineState { return Some(n1.cmp(&n2)); } } - (HeapCellValueTag::Cons, ptr) => { - let stream = cell_as_stream!(ptr); - let n2 = stream.options().get_alias().unwrap(); - if n1 != n2 { - self.pdl.clear(); - return Some(n1.cmp(&n2)); - } - } _ => { unreachable!(); } ) } - (HeapCellValueTag::Cons, ptr) => { - let stream = cell_as_stream!(ptr); - let n1 = stream.options().get_alias().unwrap(); - read_heap_cell!(v2, - (HeapCellValueTag::Atom, (n2, _a2)) => { - if n1 != n2 { - self.pdl.clear(); - return Some(n1.cmp(&n2)); - } - } - (HeapCellValueTag::Char, c2) => { - if let Some(c1) = n1.as_char() { - if c1 != c2 { - self.pdl.clear(); - return Some(c1.cmp(&c2)); - } - } else { - self.pdl.clear(); - return Some( - n1.chars().next().cmp(&Some(c2)) - .then(Ordering::Greater) - ); - } - } - (HeapCellValueTag::Str, s) => { - let n2 = cell_as_atom_cell!(self.heap[s]) - .get_name(); - - if n1 != n2 { - self.pdl.clear(); - return Some(n1.cmp(&n2)); - } - } - (HeapCellValueTag::Cons, ptr) => { - let stream = cell_as_stream!(ptr); - let n2 = stream.options().get_alias().unwrap(); - if n1 != n2 { - self.pdl.clear(); - return Some(n1.cmp(&n2)); - } - } - _ => { - unreachable!(); - } - ) - } _ => { unreachable!() } @@ -743,9 +665,6 @@ impl MachineState { Some((2, atom!(".")).cmp(&(arity, name))) } } - (HeapCellValueTag::Cons, _s) => { - Some(Ordering::Greater) - } _ => { unreachable!() } @@ -849,10 +768,6 @@ impl MachineState { } } } - (HeapCellValueTag::Cons, _ptr) => { - self.pdl.clear(); - return Some(Ordering::Greater); - } _ => { unreachable!(); } @@ -954,68 +869,11 @@ impl MachineState { self.heap.pop(); self.heap.pop(); } - (HeapCellValueTag::Cons, s2) => { - let stream = cell_as_stream!(s2); - let ptr = stream.as_ptr() as u64; - - let (n1, a1) = cell_as_atom_cell!(self.heap[s1]) - .get_name_and_arity(); - - match (a1, n1).cmp(&(1, atom!("$stream"))) { - Ordering::Equal => { - self.pdl.push(HeapCellValue::from(ptr)); - self.pdl.push(self.heap[s1+1]); - } - ordering => { - self.pdl.clear(); - return Some(ordering); - } - } - } _ => { unreachable!() } ) } - (HeapCellValueTag::Cons, s1) => { - let stream = cell_as_stream!(s1); - let ptr = stream.as_ptr() as u64; - read_heap_cell!(v2, - (HeapCellValueTag::Str, s2) => { - let (n2, a2) = cell_as_atom_cell!(self.heap[s2]) - .get_name_and_arity(); - - match (1, atom!("$stream")).cmp(&(a2, n2)) { - Ordering::Equal => { - self.pdl.push(self.heap[s2+1]); - self.pdl.push(HeapCellValue::from(ptr)); - } - ordering => { - self.pdl.clear(); - return Some(ordering); - } - } - } - (HeapCellValueTag::Lis, _l2) => { - self.pdl.clear(); - return Some(Ordering::Less); - } - (HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc) => { - self.pdl.clear(); - return Some(Ordering::Less); - } - (HeapCellValueTag::Cons, s2) => { - let stream2 = cell_as_stream!(s2); - let ptr2 = stream2.as_ptr() as u64; - - self.pdl.clear(); - return Some(ptr.cmp(&ptr2)); - } - _ => { - unreachable!() - } - ) - } _ => { unreachable!() } diff --git a/src/types.rs b/src/types.rs index aa8384fc..12add4ef 100644 --- a/src/types.rs +++ b/src/types.rs @@ -616,18 +616,6 @@ impl HeapCellValue { Some(TermOrderCategory::Compound) } } - HeapCellValueTag::Cons => { - let ptr = cell_as_untyped_arena_ptr!(self); - match_untyped_arena_ptr!(ptr, - (ArenaHeaderTag::Stream, stream) => { - match stream.options().get_alias() { - Some(_) => Some(TermOrderCategory::Atom), - None => Some(TermOrderCategory::Compound) - } - }, - _ => None - ) - } _ => { None } From 3f5dbc16804ca349bf79a8d8f58d067c303f948f Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 12 Jul 2023 12:17:06 -0600 Subject: [PATCH 299/361] emit stream aliases as permission error culprits whenever possible --- src/machine/machine_errors.rs | 24 ++++++++++++++++++++- src/machine/streams.rs | 40 +++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/machine/machine_errors.rs b/src/machine/machine_errors.rs index 4422fdae..62b06ca2 100644 --- a/src/machine/machine_errors.rs +++ b/src/machine/machine_errors.rs @@ -1,3 +1,4 @@ +use crate::arena::*; use crate::atom_table::*; use crate::parser::ast::*; @@ -6,6 +7,7 @@ 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::*; @@ -158,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 { diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 80c51537..90f927c4 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -1491,21 +1491,21 @@ impl MachineState { } (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)); + (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)); + } + _ => { + } ); } _ => { @@ -1547,7 +1547,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) } @@ -1715,7 +1723,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); From 1791bd862645a3d0fa0bac00a847d43089beac79 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 12 Jul 2023 14:24:20 -0600 Subject: [PATCH 300/361] remove unsafe unwrap in put_char (#1881) --- src/machine/system_calls.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 88cdde16..111569ec 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -3102,9 +3102,10 @@ impl Machine { } else { read_heap_cell!(addr, (HeapCellValueTag::Atom, (name, _arity)) => { - let c = name.as_char().unwrap(); - write!(&mut stream, "{}", c).unwrap(); - return Ok(()); + if let Some(c) = name.as_char() { + write!(&mut stream, "{}", c).unwrap(); + return Ok(()); + } } (HeapCellValueTag::Char, c) => { write!(&mut stream, "{}", c).unwrap(); From 4fd37335f576240c25dfbfe6e70441aff166c60e Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 11 Jul 2023 17:59:53 -0600 Subject: [PATCH 301/361] use lookahead to skip inapplicable clauses (#1028, #1502) --- build/instructions_template.rs | 9 +- src/codegen.rs | 4 +- src/machine/dispatch.rs | 242 ++++++++++-------- src/machine/machine_state.rs | 59 ----- src/machine/mod.rs | 439 +++++++++++++++++++++++++++++---- src/machine/system_calls.rs | 2 +- src/targets.rs | 8 +- 7 files changed, 537 insertions(+), 226 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 2629f7b3..3b2bd971 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -598,7 +598,7 @@ enum InstructionTemplate { #[strum_discriminants(strum(props(Arity = "4", Name = "get_partial_string")))] GetPartialString(Level, Atom, RegType, bool), #[strum_discriminants(strum(props(Arity = "3", Name = "get_structure")))] - GetStructure(Atom, usize, RegType), + GetStructure(Level, Atom, usize, RegType), #[strum_discriminants(strum(props(Arity = "2", Name = "get_variable")))] GetVariable(RegType, usize), #[strum_discriminants(strum(props(Arity = "2", Name = "get_value")))] @@ -2105,13 +2105,14 @@ fn generate_instruction_preface() -> TokenStream { [lvl_stub, rt_stub] ) } - &Instruction::GetStructure(name, arity, r) => { + &Instruction::GetStructure(lvl, name, arity, r) => { + let lvl_stub = lvl.into_functor(); let rt_stub = reg_type_into_functor(r); functor!( atom!("get_structure"), - [atom(name), fixnum(arity), str(h, 0)], - [rt_stub] + [str(h, 0), atom(name), fixnum(arity), str(h, 1)], + [lvl_stub, rt_stub] ) } &Instruction::GetValue(r, arg) => { diff --git a/src/codegen.rs b/src/codegen.rs index 68374c58..7d4b92d3 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -309,7 +309,7 @@ impl DebrayAllocator { fn trim_structure_by_last_arg(instr: &mut Instruction, last_arg: &Term) { match instr { Instruction::PutStructure(_, ref mut arity, _) | - Instruction::GetStructure(_, ref mut arity, _) => { + Instruction::GetStructure(.., ref mut arity, _) => { if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg { // it is acceptable if arity == 0 is the result of // this decrement. call/N will have to read the index @@ -447,7 +447,7 @@ impl<'b> CodeGenerator<'b> { } TermRef::Clause(lvl, cell, name, terms) => { self.marker.mark_non_var::(lvl, term_loc, cell, &mut target); - target.push_back(Target::to_structure(name, terms.len(), cell.get())); + target.push_back(Target::to_structure(lvl, name, terms.len(), cell.get())); as AddToFreeList<'a, Target>>::add_term_to_free_list(self, cell.get()); diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index e1ba0c0f..3b30e75b 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -9,6 +9,8 @@ use crate::types::*; use crate::try_numeric_result; +use fxhash::FxBuildHasher; + macro_rules! step_or_fail { ($self:expr, $step_e:expr) => { if $self.machine_st.fail { @@ -182,6 +184,128 @@ impl MachineState { Ok(()) } + + #[inline(always)] + pub(crate) fn select_switch_on_term_index( + &self, + addr: HeapCellValue, + v: IndexingCodePtr, + c: IndexingCodePtr, + l: IndexingCodePtr, + s: IndexingCodePtr, + ) -> IndexingCodePtr { + read_heap_cell!(addr, + (HeapCellValueTag::Var | + HeapCellValueTag::StackVar | + HeapCellValueTag::AttrVar) => { + v + } + (HeapCellValueTag::PStrLoc | + HeapCellValueTag::Lis | + HeapCellValueTag::CStr) => { + l + } + (HeapCellValueTag::Fixnum | + HeapCellValueTag::Char | + HeapCellValueTag::F64) => { + c + } + (HeapCellValueTag::Atom, (_name, arity)) => { + // if arity == 0 { c } else { s } + debug_assert!(arity == 0); + c + } + (HeapCellValueTag::Str, st) => { + let (name, arity) = cell_as_atom_cell!(self.heap[st]) + .get_name_and_arity(); + + match (name, arity) { + (atom!("."), 2) => l, + (_, 0) => c, + _ => s, + } + } + (HeapCellValueTag::Cons, ptr) => { + match ptr.get_tag() { + ArenaHeaderTag::Rational | ArenaHeaderTag::Integer => { + c + } + _ => { + IndexingCodePtr::Fail + } + } + } + _ => { + unreachable!(); + } + ) + } + + #[inline(always)] + pub(crate) fn constant_to_literal(&self, addr: HeapCellValue) -> Literal { + read_heap_cell!(addr, + (HeapCellValueTag::Char, c) => { + Literal::Char(c) + } + (HeapCellValueTag::Fixnum, n) => { + Literal::Fixnum(n) + } + (HeapCellValueTag::F64, f) => { + Literal::Float(f.as_offset()) + } + (HeapCellValueTag::Atom, (atom, arity)) => { + debug_assert_eq!(arity, 0); + Literal::Atom(atom) + } + (HeapCellValueTag::Str, s) => { + Literal::Atom(cell_as_atom_cell!(self.heap[s]).get_name()) + } + (HeapCellValueTag::Cons, cons_ptr) => { + match_untyped_arena_ptr!(cons_ptr, + (ArenaHeaderTag::Rational, r) => { + Literal::Rational(r) + } + (ArenaHeaderTag::Integer, n) => { + Literal::Integer(n) + } + _ => { + unreachable!() + } + ) + } + _ => { + unreachable!() + } + ) + } + + #[inline(always)] + pub(crate) fn select_switch_on_structure_index( + &self, + addr: HeapCellValue, + hm: &IndexMap<(Atom, usize), IndexingCodePtr, FxBuildHasher>, + ) -> IndexingCodePtr { + read_heap_cell!(addr, + (HeapCellValueTag::Atom, (name, arity)) => { + match hm.get(&(name, arity)) { + Some(offset) => *offset, + None => IndexingCodePtr::Fail, + } + } + (HeapCellValueTag::Str, s) => { + let (name, arity) = cell_as_atom_cell!(self.heap[s]) + .get_name_and_arity(); + + match hm.get(&(name, arity)) { + Some(offset) => *offset, + None => IndexingCodePtr::Fail, + } + } + _ => { + IndexingCodePtr::Fail + } + ) + } } impl Machine { @@ -349,51 +473,7 @@ impl Machine { loop { match &indexing_lines[index] { &IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, v, c, l, s)) => { - let offset = read_heap_cell!(addr, - (HeapCellValueTag::Var | - HeapCellValueTag::StackVar | - HeapCellValueTag::AttrVar) => { - v - } - (HeapCellValueTag::PStrLoc | - HeapCellValueTag::Lis | - HeapCellValueTag::CStr) => { - l - } - (HeapCellValueTag::Fixnum | - HeapCellValueTag::Char | - HeapCellValueTag::F64) => { - c - } - (HeapCellValueTag::Atom, (_name, arity)) => { - // if arity == 0 { c } else { s } - debug_assert!(arity == 0); - c - } - (HeapCellValueTag::Str, st) => { - let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[st]) - .get_name_and_arity(); - - match (name, arity) { - (atom!("."), 2) => l, - (_, 0) => c, - _ => s, - } - } - (HeapCellValueTag::Cons, ptr) => { - match ptr.get_tag() { - ArenaHeaderTag::Rational | ArenaHeaderTag::Integer => { - c - } - _ => { - IndexingCodePtr::Fail - } - } - } - _ => { - unreachable!(); - } - ); + let offset = self.machine_st.select_switch_on_term_index(addr, v, c, l, s); match offset { IndexingCodePtr::Fail => { @@ -423,41 +503,8 @@ impl Machine { } } } - &IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref hm)) => { - let lit = read_heap_cell!(addr, - (HeapCellValueTag::Char, c) => { - Literal::Char(c) - } - (HeapCellValueTag::Fixnum, n) => { - Literal::Fixnum(n) - } - (HeapCellValueTag::F64, f) => { - Literal::Float(f.as_offset()) - } - (HeapCellValueTag::Atom, (atom, arity)) => { - debug_assert_eq!(arity, 0); - Literal::Atom(atom) - } - (HeapCellValueTag::Str, s) => { - Literal::Atom(cell_as_atom_cell!(self.machine_st.heap[s]).get_name()) - } - (HeapCellValueTag::Cons, cons_ptr) => { - match_untyped_arena_ptr!(cons_ptr, - (ArenaHeaderTag::Rational, r) => { - Literal::Rational(r) - } - (ArenaHeaderTag::Integer, n) => { - Literal::Integer(n) - } - _ => { - unreachable!() - } - ) - } - _ => { - unreachable!() - } - ); + IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(hm)) => { + let lit = self.machine_st.constant_to_literal(addr); let offset = match hm.get(&lit) { Some(offset) => *offset, @@ -492,27 +539,8 @@ impl Machine { } } } - &IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref hm)) => { - let offset = read_heap_cell!(addr, - (HeapCellValueTag::Atom, (name, arity)) => { - match hm.get(&(name, arity)) { - Some(offset) => *offset, - None => IndexingCodePtr::Fail, - } - } - (HeapCellValueTag::Str, s) => { - let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]) - .get_name_and_arity(); - - match hm.get(&(name, arity)) { - Some(offset) => *offset, - None => IndexingCodePtr::Fail, - } - } - _ => { - IndexingCodePtr::Fail - } - ); + IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(hm)) => { + let offset = self.machine_st.select_switch_on_structure_index(addr, hm); match offset { IndexingCodePtr::Fail => { @@ -997,7 +1025,7 @@ impl Machine { fixnum_as_cell!(Fixnum::build_with(self.machine_st.cc as i64)); self.machine_st.num_of_args += 1; - self.machine_st.try_me_else(next_i); + self.try_me_else(next_i); self.machine_st.num_of_args -= 1; } None => { @@ -1067,7 +1095,7 @@ impl Machine { fixnum_as_cell!(Fixnum::build_with(self.machine_st.cc as i64)); self.machine_st.num_of_args += 1; - self.machine_st.try_me_else(next_i); + self.try_me_else(next_i); self.machine_st.num_of_args -= 1; } None => { @@ -1118,7 +1146,7 @@ impl Machine { } } &Instruction::TryMeElse(offset) => { - self.machine_st.try_me_else(offset); + self.try_me_else(offset); } &Instruction::DefaultRetryMeElse(offset) => { self.retry_me_else(offset); @@ -2879,7 +2907,7 @@ impl Machine { step_or_fail!(self, self.machine_st.p += 1); } - &Instruction::GetStructure(name, arity, reg) => { + &Instruction::GetStructure(_lvl, name, arity, reg) => { let deref_v = self.machine_st.deref(self.machine_st[reg]); let store_v = self.machine_st.store(deref_v); @@ -3076,7 +3104,7 @@ impl Machine { IndexingLine::IndexedChoice(ref indexed_choice) => { match &indexed_choice[self.machine_st.iip as usize] { &IndexedChoiceInstruction::Try(offset) => { - self.machine_st.indexed_try(offset); + self.indexed_try(offset); } &IndexedChoiceInstruction::Retry(l) => { self.retry(l); @@ -3122,7 +3150,7 @@ impl Machine { fixnum_as_cell!(Fixnum::build_with(self.machine_st.cc as i64)); self.machine_st.num_of_args += 1; - self.machine_st.indexed_try(offset); + self.indexed_try(offset); self.machine_st.num_of_args -= 1; } None => { diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 7b074a64..5a8fc85d 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -858,65 +858,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.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; - or_frame.prelude.attr_var_queue_len = self.attr_var_init.attr_var_queue.len(); - - 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.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; - or_frame.prelude.attr_var_queue_len = self.attr_var_init.attr_var_queue.len(); - - 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)] diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 78c347f2..41cc4f39 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -547,38 +547,351 @@ 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 { + 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 { + 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.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; - 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.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)] @@ -587,34 +900,42 @@ impl Machine { let or_frame = self.machine_st.stack.index_or_frame_mut(b); 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; - 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.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)] @@ -623,19 +944,32 @@ impl Machine { let or_frame = self.machine_st.stack.index_or_frame(b); 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(or_frame.prelude.attr_var_queue_len); @@ -643,9 +977,6 @@ impl Machine { 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); @@ -663,12 +994,24 @@ impl Machine { 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; @@ -679,8 +1022,6 @@ impl Machine { 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); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 111569ec..532fb1e3 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1277,7 +1277,7 @@ impl Machine { } #[inline(always)] - pub(crate) fn deref_register(&mut self, i: usize) -> HeapCellValue { + pub(crate) fn deref_register(&self, i: usize) -> HeapCellValue { self.machine_st.store(self.machine_st.deref(self.machine_st.registers[i])) } diff --git a/src/targets.rs b/src/targets.rs index 56a4c127..1596aae2 100644 --- a/src/targets.rs +++ b/src/targets.rs @@ -16,7 +16,7 @@ pub(crate) trait CompilationTarget<'a> { fn to_constant(lvl: Level, literal: Literal, r: RegType) -> Instruction; fn to_list(lvl: Level, r: RegType) -> Instruction; - fn to_structure(name: Atom, arity: usize, r: RegType) -> Instruction; + fn to_structure(lvl: Level, name: Atom, arity: usize, r: RegType) -> Instruction; fn to_void(num_subterms: usize) -> Instruction; fn is_void_instr(instr: &Instruction) -> bool; @@ -51,8 +51,8 @@ impl<'a> CompilationTarget<'a> for FactInstruction { Instruction::GetConstant(lvl, HeapCellValue::from(constant), reg) } - fn to_structure(name: Atom, arity: usize, reg: RegType) -> Instruction { - Instruction::GetStructure(name, arity, reg) + fn to_structure(lvl: Level, name: Atom, arity: usize, reg: RegType) -> Instruction { + Instruction::GetStructure(lvl, name, arity, reg) } fn to_list(lvl: Level, reg: RegType) -> Instruction { @@ -125,7 +125,7 @@ impl<'a> CompilationTarget<'a> for QueryInstruction { post_order_iter(term) } - fn to_structure(name: Atom, arity: usize, r: RegType) -> Instruction { + fn to_structure(_lvl: Level, name: Atom, arity: usize, r: RegType) -> Instruction { Instruction::PutStructure(name, arity, r) } From 814ce2d672d95d0ec0d323fe072c28eddae3faa8 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Wed, 12 Jul 2023 07:26:52 +0200 Subject: [PATCH 302/361] ENHANCED: improved determinism of member/2 Example: ?- member(X, "abc"). %@ X = a %@ ; X = b %@ ; X = c. This addresses #750. --- src/lib/lists.pl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/lib/lists.pl b/src/lib/lists.pl index 815e2b2a..8a94ed6a 100644 --- a/src/lib/lists.pl +++ b/src/lib/lists.pl @@ -122,8 +122,13 @@ length_addendum([_|Xs], N, M) :- % X = h % ; ... . % ``` -member(X, [X|_]). -member(X, [_|Xs]) :- member(X, Xs). + +member(X, [L|Ls]) :- + member_(Ls, L, X). + +member_(_, X, X). +member_([L|Ls], _, X) :- + member_(Ls, L, X). %% select(X, Xs0, Xs1). % From 29430ec88b55b5c49896287e486d33cd25f7b0c6 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 12 Jul 2023 16:34:11 -0600 Subject: [PATCH 303/361] fix peek_byte/2 crash (#1882) --- src/machine/system_calls.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 532fb1e3..7045eff0 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -2447,7 +2447,7 @@ impl Machine { return Ok(()); } - match addr { + let addr = match addr { addr if addr.is_var() => addr, addr => match Number::try_from(addr) { Ok(Number::Integer(n)) => { @@ -2477,6 +2477,7 @@ impl Machine { match stream.peek_byte().map_err(|e| e.kind()) { Ok(b) => { self.machine_st.unify_fixnum(Fixnum::build_with(b as i64), addr); + break; } Err(ErrorKind::PermissionDenied) => { self.machine_st.fail = true; From cfc49243c8451e3f120a9b593ca81c56bdbcf34d Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 12 Jul 2023 18:11:56 -0600 Subject: [PATCH 304/361] improve ground/1 performance (#1389) --- src/heap_iter.rs | 5 +++++ src/machine/machine_state_impl.rs | 20 ++++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/heap_iter.rs b/src/heap_iter.rs index 96aef41d..c20840b7 100644 --- a/src/heap_iter.rs +++ b/src/heap_iter.rs @@ -218,6 +218,11 @@ impl<'a> StackfulPreOrderHeapIter<'a> { None } + #[inline] + pub fn stack_len(&self) -> usize { + self.stack.len() + } + fn push_if_unmarked(&mut self, loc: IterStackLoc) { let cell = self.read_cell_mut(loc); diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index 3bb118f9..6b08b93e 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -1612,6 +1612,8 @@ impl MachineState { // returns true on failure. pub fn ground_test(&mut self) -> bool { + use fxhash::FxBuildHasher; + if self.registers[1].is_constant() { return false; } @@ -1622,13 +1624,15 @@ impl MachineState { return true; } + let mut visited = IndexSet::with_hasher(FxBuildHasher::default()); let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, value); + let mut stack_len = 0; while let Some(value) = iter.next() { - let value = unmark_cell_bits!(value); + let mut value = unmark_cell_bits!(value); if value.is_var() { - let value = heap_bound_store( + value = heap_bound_store( iter.heap, heap_bound_deref(iter.heap, value), ); @@ -1637,6 +1641,18 @@ impl MachineState { return true; } } + + if value.is_compound(iter.heap) { + if visited.contains(&value) { + for _ in stack_len .. iter.stack_len() { + iter.pop_stack(); + } + } else { + visited.insert(value); + } + } + + stack_len = iter.stack_len(); } false From b051f391455ab10b4f3c29392a7478a0484f633c Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 13 Jul 2023 12:23:20 -0600 Subject: [PATCH 305/361] correct peek_byte/2 bugs (#1882) --- src/machine/streams.rs | 9 +++------ src/machine/system_calls.rs | 6 +++++- src/parser/char_reader.rs | 13 +++++++++++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/machine/streams.rs b/src/machine/streams.rs index 90f927c4..c1b4678e 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -1268,12 +1268,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, diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 7045eff0..ba67067d 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -2444,7 +2444,11 @@ impl Machine { addr, ); - return Ok(()); + if !self.machine_st.fail { + return Ok(()); + } else { + self.machine_st.fail = false; + } } let addr = match addr { diff --git a/src/parser/char_reader.rs b/src/parser/char_reader.rs index 32854b07..2a1db29f 100644 --- a/src/parser/char_reader.rs +++ b/src/parser/char_reader.rs @@ -128,6 +128,19 @@ impl CharReader { Ok(&self.buf[self.pos..]) } + + pub fn peek_byte(&mut self) -> Option> { + match self.refresh_buffer() { + Ok(_buf) => {} + Err(e) => return Some(Err(e)), + } + + return if let Some(b) = self.buf.get(0).cloned() { + Some(Ok(b)) + } else { + None + }; + } } impl CharRead for CharReader { From 4163cb038d83045b020ef036865fe11f3c282456 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 13 Jul 2023 13:37:34 -0600 Subject: [PATCH 306/361] correct peek_code/2, don't set stream position in peek functions --- src/machine/system_calls.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index ba67067d..4a4965a2 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -2437,17 +2437,15 @@ impl Machine { let addr = self.deref_register(2); if stream.at_end_of_stream() { - stream.set_past_end_of_stream(true); - self.machine_st.unify_fixnum( Fixnum::build_with(-1), addr, ); - if !self.machine_st.fail { - return Ok(()); - } else { + if self.machine_st.fail { self.machine_st.fail = false; + } else { + return Ok(()); } } @@ -2538,7 +2536,6 @@ impl Machine { if stream.at_end_of_stream() { let end_of_file = atom!("end_of_file"); - stream.set_past_end_of_stream(true); self.machine_st.unify_atom( end_of_file, @@ -2634,15 +2631,16 @@ impl Machine { let a2 = self.deref_register(2); if stream.at_end_of_stream() { - let end_of_file = atom!("end_of_file"); - stream.set_past_end_of_stream(true); - - self.machine_st.unify_atom( - end_of_file, + self.machine_st.unify_fixnum( + Fixnum::build_with(-1), a2, ); - return Ok(()); + if self.machine_st.fail { + self.machine_st.fail = false; + } else { + return Ok(()); + } } let addr = read_heap_cell!(a2, From a9cb826bf339f4f5c3362c6fa3ad3af13f16c260 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 13 Jul 2023 13:58:51 -0600 Subject: [PATCH 307/361] arith_eval_by_metacall may receive a stack variable --- src/machine/arithmetic_ops.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index f5aa982f..8eae327d 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -1123,7 +1123,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())); } From 12f890e4a23c4dd3d51379e6e89318e556ff5f84 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 13 Jul 2023 15:03:26 -0600 Subject: [PATCH 308/361] throw permission_error in compile_assert when attempting to assert a built-in (#1872) --- src/machine/dispatch.rs | 12 ++++++++++-- src/machine/loader.rs | 22 +++++----------------- src/machine/machine_errors.rs | 2 +- src/machine/machine_indices.rs | 15 +++++++++++++++ 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 3b30e75b..afe67fb9 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4919,11 +4919,19 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } &Instruction::CallBuiltInProperty => { - self.builtin_property(); + let key = self + .machine_st + .read_predicate_key(self.machine_st.registers[1], self.machine_st.registers[2]); + + self.machine_st.fail = !self.indices.builtin_property(key); step_or_fail!(self, self.machine_st.p += 1); } &Instruction::ExecuteBuiltInProperty => { - self.builtin_property(); + let key = self + .machine_st + .read_predicate_key(self.machine_st.registers[1], self.machine_st.registers[2]); + + self.machine_st.fail = !self.indices.builtin_property(key); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::CallMetaPredicateProperty => { diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 5a309197..9da054c7 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -2016,6 +2016,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 @@ -2026,7 +2027,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 @@ -2038,8 +2039,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 }; @@ -2466,20 +2468,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)); - } else { - self.machine_st.fail = true; - } - } - } } impl<'a> Loader<'a, LiveLoadAndMachineState<'a>> { diff --git a/src/machine/machine_errors.rs b/src/machine/machine_errors.rs index 62b06ca2..936200f0 100644 --- a/src/machine/machine_errors.rs +++ b/src/machine/machine_errors.rs @@ -442,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::(), ) } diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index df880447..b19ac3bf 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -3,6 +3,7 @@ use crate::parser::ast::*; use crate::arena::*; use crate::atom_table::*; use crate::forms::*; +use crate::machine::ClauseType; use crate::machine::loader::*; use crate::machine::machine_state::*; use crate::machine::streams::Stream; @@ -259,6 +260,20 @@ pub struct IndexStore { } impl IndexStore { + pub(crate) fn builtin_property(&self, key: PredicateKey) -> bool { + let (name, arity) = key; + + if !ClauseType::is_inbuilt(name, arity) { + return if let Some(module) = self.modules.get(&(atom!("builtins"))) { + module.code_dir.contains_key(&(name, arity)) + } else { + false + }; + } else { + true + } + } + #[inline(always)] pub(crate) fn goal_expansion_defined(&self, key: PredicateKey) -> bool { self.goal_expansion_indices.contains(&key) From 9590d5200c16f57b80cb71e9fd7118639c0a3f47 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 13 Jul 2023 23:08:13 +0200 Subject: [PATCH 309/361] ADDED: countall/2, for compatibility with GNU Prolog. Example: ?- countall(member(X, "abc"), N). N = 3. --- src/lib/iso_ext.pl | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/lib/iso_ext.pl b/src/lib/iso_ext.pl index 8b3e8dc3..afc28969 100644 --- a/src/lib/iso_ext.pl +++ b/src/lib/iso_ext.pl @@ -15,9 +15,10 @@ but they're not part of the ISO Prolog standard at the moment. partial_string_tail/2, setup_call_cleanup/3, call_nth/2, + countall/2, copy_term_nat/2, - asserta/2, - assertz/2]). + asserta/2, + assertz/2]). :- use_module(library(error), [can_be/2, domain_error/3, @@ -339,6 +340,36 @@ call_nth_nesting(C, ID) :- bb_put(ID, 0), bb_put(i_call_nth_counter, C). +%% countall(Goal, N). +% +% countall(Goal, N) counts all solutions of Goal and unifies N with +% this number of solutions. This predicate always succeeds once. + +:- meta_predicate(countall(0, ?)). + +countall(Goal, N) :- + can_be(integer, N), + ( integer(N) -> + ( N < 0 -> + domain_error(not_less_than_zero, N, countall/2) + ; N > 0 + ) + ; true + ), + setup_call_cleanup(call_nth_nesting(C, ID), + ( ( Goal, + bb_get(ID, N0), + N1 is N0 + 1, + bb_put(ID, N1), + false + ; bb_get(ID, N) + ) + ), + ( bb_get(i_call_nth_counter, C) -> + C1 is C - 1, + bb_put(i_call_nth_counter, C1) + ; true + )). %% copy_term_nat(Source, Dest) % From ba2cd4314434953611695e9c37588fc0b5d7cd86 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 13 Jul 2023 17:12:05 -0600 Subject: [PATCH 310/361] fix assert(a|z)/1 errors --- src/machine/disjuncts.rs | 16 ++++++++++++++++ src/machine/loader.rs | 27 ++++++++++++++++----------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index c1a71802..e8b59ed2 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -716,6 +716,22 @@ impl VariableClassifier { ), ); } + 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); diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 9da054c7..005d263f 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -466,7 +466,9 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { pub(crate) fn read_term_from_heap(&mut self, r: RegType) -> Result { let machine_st = LS::machine_st(&mut self.payload); - machine_st.read_term_from_heap(r) + let cell = machine_st[r]; + + machine_st.read_term_from_heap(cell) } pub(crate) fn load(mut self) -> Result { @@ -1048,8 +1050,9 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> { r: RegType, ) -> Result, SessionError> { let machine_st = LS::machine_st(&mut self.payload); + let cell = machine_st[r]; - let export_list = machine_st.read_term_from_heap(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)?; @@ -1400,9 +1403,7 @@ impl<'a> MachinePreludeView<'a> { } impl MachineState { - pub(super) fn read_term_from_heap(&mut self, r: RegType) -> Result { - let term_addr = self[r]; - + pub(super) fn read_term_from_heap(&mut self, term_addr: HeapCellValue) -> Result { let mut term_stack = vec![]; let mut iter = stackful_post_order_iter(&mut self.heap, &mut self.stack, term_addr); @@ -1983,11 +1984,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, @@ -2001,13 +1999,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 From b6a81c51ab3757d831808d0a2c285ffd774767b0 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 14 Jul 2023 12:44:25 -0600 Subject: [PATCH 311/361] add (:-)/1 and (:-)/2 to ClauseType::is_inbuilt (#1872) --- build/instructions_template.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 3b2bd971..5eaa542b 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -2261,6 +2261,10 @@ pub fn generate_instructions_rs() -> TokenStream { let mut is_inbuilt_arms = vec![]; let mut is_inlined_arms = vec![]; + is_inbuilt_arms.push(quote! { + (atom!(":-"), 1 | 2) => true + }); + for (name, arity, variant) in instr_data.compare_number_variants { let ident = variant.ident.clone(); From 101d0548db9d22b9ba09f05a0bdc59f5543d9be2 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 14 Jul 2023 17:01:46 -0600 Subject: [PATCH 312/361] fix group_by_variants/4 and keysort in setof/3 (#1440, #1856) --- build/instructions_template.rs | 4 ++++ src/forms.rs | 6 +++++ src/lib/builtins.pl | 39 +++++++++++++++++++++++++++--- src/machine/dispatch.rs | 40 ++++++++++++++++++++++++++----- src/machine/machine_state_impl.rs | 14 ++++++----- src/macros.rs | 8 ++++++- 6 files changed, 95 insertions(+), 16 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 5eaa542b..25fa6b1b 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -584,6 +584,8 @@ enum SystemClauseType { UnattributedVar, #[strum_discriminants(strum(props(Arity = "4", Name = "$get_db_refs")))] GetDBRefs, + #[strum_discriminants(strum(props(Arity = "2", Name = "$keysort_with_constant_var_ordering")))] + KeySortWithConstantVarOrdering, REPL(REPLCodePtr), } @@ -1653,6 +1655,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallDeleteAllAttributesFromVar | &Instruction::CallUnattributedVar | &Instruction::CallGetDBRefs | + &Instruction::CallKeySortWithConstantVarOrdering | &Instruction::CallFetchGlobalVar | &Instruction::CallFirstStream | &Instruction::CallFlushOutput | @@ -1877,6 +1880,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteDeleteAllAttributesFromVar | &Instruction::ExecuteUnattributedVar | &Instruction::ExecuteGetDBRefs | + &Instruction::ExecuteKeySortWithConstantVarOrdering | &Instruction::ExecuteFetchGlobalVar | &Instruction::ExecuteFirstStream | &Instruction::ExecuteFlushOutput | diff --git a/src/forms.rs b/src/forms.rs index 627fb4b2..ca5c7ffe 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -55,6 +55,12 @@ impl AppendOrPrepend { } } +#[derive(Debug)] +pub enum VarComparison { + Indistinct, + Distinct +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Level { Deep, diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index db5282f7..77695926 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -883,12 +883,45 @@ set_difference([X|Xs], [Y|Ys], Zs) :- set_difference([], _, []) :- !. set_difference(Xs, [], Xs). + +% variant/2 checks whether X is a variant of Y per the definition in +% 7.1.6.1 of the ISO standard. + +:- non_counted_backtracking variant/4. + +variant(X,Y,VPs,VPs0) :- + ( var(X) -> + var(Y), + VPs = [X-Y|VPs0] + ; var(Y) -> + false + ; X =.. [FX | XArgs], + Y =.. [FX | YArgs], + lists:foldl('$call'(builtins:variant), XArgs, YArgs, VPs, VPs0) + ). + +:- non_counted_backtracking variant/2. + +singleton([_]). + +variant(X, Y) :- + variant(X,Y, VPs, []), + keysort(VPs, SVPs), + pairs:group_pairs_by_key(SVPs, SVPKs), + pairs:pairs_values(SVPKs, Vals), + lists:maplist('$call'(builtins:term_variables), Vals, Vs), + lists:maplist('$call'(builtins:singleton), Vs), + term_variables(Vs, YVars), + lists:length(SVPKs, N), + lists:length(YVars, N). + + :- non_counted_backtracking group_by_variant/4. group_by_variant([V2-S2 | Pairs], V1-S1, [S2 | Solutions], Pairs0) :- - V1 = V2, % \+ \+ (V1 = V2), % (2) % iso_ext:variant(V1, V2), % (1) + variant(V1, V2), !, - % V1 = V2, % (3) + V1 = V2, group_by_variant(Pairs, V2-S2, Solutions, Pairs0). group_by_variant(Pairs, _, [], Pairs). @@ -1008,7 +1041,7 @@ setof(Template, Goal, Solution) :- term_variables(TemplateVars+GoalVars, TGVs), lists:append(TemplateVars, Witnesses0, TGVs), findall_with_existential(Template, Goal, PairedSolutions0, Witnesses0, Witnesses), - keysort(PairedSolutions0, PairedSolutions), + '$keysort_with_constant_var_ordering'(PairedSolutions0, PairedSolutions), % see 7.2.1 group_by_variants(PairedSolutions, GroupedSolutions), iterate_variants_and_sort(GroupedSolutions, Witnesses, Solution). diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index afe67fb9..a36bf29b 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -141,7 +141,7 @@ impl MachineState { Ok(()) } - fn keysort(&mut self) -> CallResult { + fn keysort(&mut self, var_comparison: VarComparison) -> CallResult { self.check_keysort_errors()?; let stub_gen = || functor_stub(atom!("keysort"), 2); @@ -155,7 +155,7 @@ impl MachineState { } key_pairs.sort_by(|a1, a2| { - compare_term_test!(self, a1.0, a2.0).unwrap_or(Ordering::Less) + compare_term_test!(self, a1.0, a2.0, var_comparison).unwrap_or(Ordering::Less) }); let key_pairs = key_pairs.into_iter().map(|kp| kp.1); @@ -1437,11 +1437,11 @@ impl Machine { step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } &Instruction::DefaultCallKeySort => { - try_or_throw!(self.machine_st, self.machine_st.keysort()); + try_or_throw!(self.machine_st, self.machine_st.keysort(VarComparison::Distinct)); step_or_fail!(self, self.machine_st.p += 1); } &Instruction::DefaultExecuteKeySort => { - try_or_throw!(self.machine_st, self.machine_st.keysort()); + try_or_throw!(self.machine_st, self.machine_st.keysort(VarComparison::Distinct)); if self.machine_st.fail { self.machine_st.backtrack(); @@ -1870,7 +1870,7 @@ impl Machine { } } &Instruction::CallKeySort => { - try_or_throw!(self.machine_st, self.machine_st.keysort()); + try_or_throw!(self.machine_st, self.machine_st.keysort(VarComparison::Distinct)); if self.machine_st.fail { self.machine_st.backtrack(); @@ -1884,7 +1884,35 @@ impl Machine { } } &Instruction::ExecuteKeySort => { - try_or_throw!(self.machine_st, self.machine_st.keysort()); + try_or_throw!(self.machine_st, self.machine_st.keysort(VarComparison::Distinct)); + + if self.machine_st.fail { + self.machine_st.backtrack(); + } else { + try_or_throw!( + self.machine_st, + (self.machine_st.increment_call_count_fn)(&mut self.machine_st) + ); + + self.machine_st.p = self.machine_st.cp; + } + } + &Instruction::CallKeySortWithConstantVarOrdering => { + try_or_throw!(self.machine_st, self.machine_st.keysort(VarComparison::Indistinct)); + + if self.machine_st.fail { + self.machine_st.backtrack(); + } else { + try_or_throw!( + self.machine_st, + (self.machine_st.increment_call_count_fn)(&mut self.machine_st) + ); + + self.machine_st.p += 1; + } + } + &Instruction::ExecuteKeySortWithConstantVarOrdering => { + try_or_throw!(self.machine_st, self.machine_st.keysort(VarComparison::Indistinct)); if self.machine_st.fail { self.machine_st.backtrack(); diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index 6b08b93e..a161481c 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -435,7 +435,7 @@ impl MachineState { } } - pub fn compare_term_test(&mut self) -> Option { + pub fn compare_term_test(&mut self, var_comparison: VarComparison) -> Option { let mut tabu_list = IndexSet::new(); while !self.pdl.is_empty() { @@ -462,12 +462,14 @@ impl MachineState { match order_cat_v1 { Some(TermOrderCategory::Variable) => { - let v1 = v1.as_var().unwrap(); - let v2 = v2.as_var().unwrap(); + if let VarComparison::Distinct = var_comparison { + let v1 = v1.as_var().unwrap(); + let v2 = v2.as_var().unwrap(); - if v1 != v2 { - self.pdl.clear(); - return Some(v1.cmp(&v2)); + if v1 != v2 { + self.pdl.clear(); + return Some(v1.cmp(&v2)); + } } } Some(TermOrderCategory::FloatingPoint) => { diff --git a/src/macros.rs b/src/macros.rs index 8a3ede55..9bd89ab7 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -625,6 +625,12 @@ macro_rules! compare_term_test { $machine_st.pdl.push($e2); $machine_st.pdl.push($e1); - $machine_st.compare_term_test() + $machine_st.compare_term_test(VarComparison::Distinct) + }}; + ($machine_st:expr, $e1:expr, $e2:expr, $var_comparison:expr) => {{ + $machine_st.pdl.push($e2); + $machine_st.pdl.push($e1); + + $machine_st.compare_term_test($var_comparison) }}; } From ff5e9a793b839c72250030286675487632ab174c Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 14 Jul 2023 19:10:10 -0600 Subject: [PATCH 313/361] add unknown flag to set_prolog_flag and current_prolog_flag --- build/instructions_template.rs | 8 ++++++++ src/forms.rs | 2 +- src/lib/builtins.pl | 12 +++++++++++- src/machine/dispatch.rs | 16 ++++++++++++++++ src/machine/mod.rs | 28 +++++++++++++++++++++++----- src/machine/system_calls.rs | 31 ++++++++++++++++++++++++++++++- src/parser/ast.rs | 30 ++++++++++++++++++++++++++++++ 7 files changed, 119 insertions(+), 8 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 25fa6b1b..a4c7294e 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -410,6 +410,8 @@ enum SystemClauseType { GetCutPoint, #[strum_discriminants(strum(props(Arity = "1", Name = "$get_double_quotes")))] GetDoubleQuotes, + #[strum_discriminants(strum(props(Arity = "1", Name = "$get_unknown")))] + GetUnknown, #[strum_discriminants(strum(props(Arity = "1", Name = "$install_new_block")))] InstallNewBlock, #[strum_discriminants(strum(props(Arity = "0", Name = "$maybe")))] @@ -438,6 +440,8 @@ enum SystemClauseType { SetCutPointByDefault(RegType), #[strum_discriminants(strum(props(Arity = "1", Name = "$set_double_quotes")))] SetDoubleQuotes, + #[strum_discriminants(strum(props(Arity = "1", Name = "$set_unknown")))] + SetUnknown, #[strum_discriminants(strum(props(Arity = "1", Name = "$set_seed")))] SetSeed, #[strum_discriminants(strum(props(Arity = "4", Name = "$skip_max_list")))] @@ -1723,6 +1727,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallGetCurrentSCCBlock | &Instruction::CallGetCutPoint | &Instruction::CallGetDoubleQuotes | + &Instruction::CallGetUnknown | &Instruction::CallInstallNewBlock | &Instruction::CallMaybe | &Instruction::CallCpuNow | @@ -1748,6 +1753,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallPopFromBallStack | &Instruction::CallSetCutPointByDefault(..) | &Instruction::CallSetDoubleQuotes | + &Instruction::CallSetUnknown | &Instruction::CallSetSeed | &Instruction::CallSkipMaxList | &Instruction::CallSleep | @@ -1948,6 +1954,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteGetCurrentSCCBlock | &Instruction::ExecuteGetCutPoint | &Instruction::ExecuteGetDoubleQuotes | + &Instruction::ExecuteGetUnknown | &Instruction::ExecuteInstallNewBlock | &Instruction::ExecuteMaybe | &Instruction::ExecuteCpuNow | @@ -1973,6 +1980,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecutePopFromBallStack | &Instruction::ExecuteSetCutPointByDefault(_) | &Instruction::ExecuteSetDoubleQuotes | + &Instruction::ExecuteSetUnknown | &Instruction::ExecuteSetSeed | &Instruction::ExecuteSkipMaxList | &Instruction::ExecuteSleep | diff --git a/src/forms.rs b/src/forms.rs index ca5c7ffe..2e04eb0e 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -55,7 +55,7 @@ impl AppendOrPrepend { } } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum VarComparison { Indistinct, Distinct diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 77695926..4d37710c 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -140,7 +140,9 @@ call(_, _, _, _, _, _, _, _, _). % * `occurs_check`: Returns if the occurs check is enabled. The occurs check prevents the creation cyclic terms. % Historically the Prolog unification algorithm didn't do that check so changing the value modifies how Prolog % operates in the low-level. Possible values are `false` (default), `true` (unification has this check -% enabled) and `error` which throws an exception when a cylic term is created. Read ans write. +% enabled) and `error` which throws an exception when a cylic term is created. Read and write. +% * `unknown`: How undefined predicates are handled when called. Possible values are `error` (the default, an error is thrown), +% `fail` (the call silently fails) and `warn` (the call fails and a warning about the undefined predicate is printed). % current_prolog_flag(Flag, Value) :- Flag == max_arity, !, Value = 1023. current_prolog_flag(max_arity, 1023). @@ -150,6 +152,8 @@ current_prolog_flag(Flag, Value) :- Flag == integer_rounding_function, !, Value current_prolog_flag(integer_rounding_function, toward_zero). current_prolog_flag(Flag, Value) :- Flag == double_quotes, !, '$get_double_quotes'(Value). current_prolog_flag(double_quotes, Value) :- '$get_double_quotes'(Value). +current_prolog_flag(Flag, Value) :- Flag == unknown, !, '$get_unknown'(Value). +current_prolog_flag(unknown, Value) :- '$get_unknown'(Value). current_prolog_flag(Flag, _) :- Flag == max_integer, !, '$fail'. current_prolog_flag(Flag, _) :- Flag == min_integer, !, '$fail'. current_prolog_flag(Flag, OccursCheckEnabled) :- @@ -190,6 +194,12 @@ set_prolog_flag(double_quotes, atom) :- !, '$set_double_quotes'(atom). % 7.11.2.5, list of char codes (UTF8). set_prolog_flag(double_quotes, codes) :- !, '$set_double_quotes'(codes). +set_prolog_flag(unknown, error) :- + !, '$set_unknown'(error). +set_prolog_flag(unknown, warning) :- + !, '$set_unknown'(warning). +set_prolog_flag(unknown, fail) :- + !, '$set_unknown'(fail). set_prolog_flag(occurs_check, true) :- !, '$set_sto_as_unify'. set_prolog_flag(occurs_check, false) :- diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index a36bf29b..d73be74f 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4194,6 +4194,14 @@ impl Machine { self.get_double_quotes(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallGetUnknown => { + self.get_unknown(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteGetUnknown => { + self.get_unknown(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } &Instruction::CallInstallNewBlock => { self.machine_st.install_new_block(self.machine_st.registers[1]); step_or_fail!(self, self.machine_st.p += 1); @@ -4374,6 +4382,14 @@ impl Machine { self.set_double_quotes(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallSetUnknown => { + self.set_unknown(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteSetUnknown => { + self.set_unknown(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } &Instruction::CallSetSeed => { self.set_seed(); step_or_fail!(self, self.machine_st.p += 1); diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 41cc4f39..0dd44a1e 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -1027,6 +1027,24 @@ impl Machine { 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; @@ -1036,7 +1054,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; @@ -1059,7 +1077,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; @@ -1088,7 +1106,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); @@ -1107,14 +1125,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); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 4a4965a2..26ac9e36 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5187,6 +5187,20 @@ impl Machine { ); } + #[inline(always)] + pub(crate) fn get_unknown(&mut self) { + let a1 = self.deref_register(1); + + self.machine_st.unify_atom( + match self.machine_st.flags.unknown { + Unknown::Error => atom!("error"), + Unknown::Fail => atom!("fail"), + Unknown::Warn => atom!("warning"), + }, + a1, + ); + } + #[inline(always)] pub(crate) fn get_scc_cleaner(&mut self) { let dest = self.machine_st.registers[1]; @@ -5521,7 +5535,7 @@ impl Machine { #[inline(always)] pub(crate) fn set_double_quotes(&mut self) { - let atom = cell_as_atom!(self.machine_st.registers[1]); + let atom = cell_as_atom!(self.deref_register(1)); self.machine_st.flags.double_quotes = match atom { atom!("atom") => DoubleQuotes::Atom, @@ -5534,6 +5548,21 @@ impl Machine { }; } + #[inline(always)] + pub(crate) fn set_unknown(&mut self) { + let atom = cell_as_atom!(self.deref_register(1)); + + self.machine_st.flags.unknown = match atom { + atom!("error") => Unknown::Error, + atom!("fail") => Unknown::Fail, + atom!("warning") => Unknown::Warn, + _ => { + self.machine_st.fail = true; + return; + } + }; + } + #[inline(always)] pub(crate) fn inference_level(&mut self) { let a1 = self.deref_register(1); diff --git a/src/parser/ast.rs b/src/parser/ast.rs index caed5915..68ebd0fa 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -303,12 +303,14 @@ pub type OpDir = IndexMap<(Atom, Fixity), OpDesc, FxBuildHasher>; #[derive(Debug, Clone, Copy)] pub struct MachineFlags { pub double_quotes: DoubleQuotes, + pub unknown: Unknown, } impl Default for MachineFlags { fn default() -> Self { MachineFlags { double_quotes: DoubleQuotes::default(), + unknown: Unknown::default(), } } } @@ -340,6 +342,34 @@ impl Default for DoubleQuotes { } } +#[derive(Debug, Clone, Copy)] +pub enum Unknown { + Error, + Fail, + Warn, +} + +impl Unknown { + pub fn is_error(self) -> bool { + matches!(self, Unknown::Error) + } + + pub fn is_fail(self) -> bool { + matches!(self, Unknown::Fail) + } + + pub fn is_warn(self) -> bool { + matches!(self, Unknown::Warn) + } +} + +impl Default for Unknown { + #[inline] + fn default() -> Self { + Unknown::Error + } +} + pub fn default_op_dir() -> OpDir { let mut op_dir = OpDir::with_hasher(FxBuildHasher::default()); From e95355e56e9336d3c064a9fd34ecfbf559e8dd35 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 15 Jul 2023 10:22:23 -0600 Subject: [PATCH 314/361] eliminate call_with_inference_limit/3 leaks (#1300) --- src/machine/system_calls.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 4a4965a2..d53ef79f 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5263,14 +5263,18 @@ impl Machine { }; let bp = cell_as_fixnum!(a1).get_num() as usize; + let a3 = self.deref_register(3); let count = self.machine_st.cwil.add_limit(n, bp); - let count = arena_alloc!(count.clone(), &mut self.machine_st.arena); + + if let Some(count) = count.to_i64() { + self.machine_st.unify_fixnum(Fixnum::build_with(count), a3); + } else { + let count = arena_alloc!(count.clone(), &mut self.machine_st.arena); + self.machine_st.unify_big_int(count, a3); + } self.machine_st.increment_call_count_fn = MachineState::increment_call_count; - let a3 = self.deref_register(3); - self.machine_st.unify_big_int(count, a3); - Ok(()) } @@ -5410,14 +5414,18 @@ impl Machine { #[inline(always)] pub(crate) fn remove_inference_counter(&mut self) { let a1 = self.deref_register(1); + let a2 = self.deref_register(2); + let bp = cell_as_fixnum!(a1).get_num() as usize; let count = self.machine_st.cwil.remove_limit(bp).clone(); - let count = arena_alloc!(count.clone(), &mut self.machine_st.arena); - let a2 = self.deref_register(2); - - self.machine_st.unify_big_int(count, a2); + if let Some(count) = count.to_i64() { + self.machine_st.unify_fixnum(Fixnum::build_with(count), a2); + } else { + let count = arena_alloc!(count.clone(), &mut self.machine_st.arena); + self.machine_st.unify_big_int(count, a2); + } } #[inline(always)] From 617c961f884099f4c102bba88c1e61f14c804bb0 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 15 Jul 2023 10:44:00 -0600 Subject: [PATCH 315/361] add is_inbuilt check to err_on_builtin_overwrite (#1872) --- src/machine/loader.rs | 4 ++++ src/machine/machine_indices.rs | 8 +++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 005d263f..0c39063c 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -328,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)); diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index b19ac3bf..3e358db9 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -264,11 +264,9 @@ impl IndexStore { let (name, arity) = key; if !ClauseType::is_inbuilt(name, arity) { - return if let Some(module) = self.modules.get(&(atom!("builtins"))) { - module.code_dir.contains_key(&(name, arity)) - } else { - false - }; + self.modules.get(&(atom!("builtins"))) + .map(|module| module.code_dir.contains_key(&(name, arity))) + .unwrap_or(false) } else { true } From b234ef7ea386786ec192d129053a24d128c2b176 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 15 Jul 2023 10:58:25 -0600 Subject: [PATCH 316/361] use double_quotes in write_error (#1886) --- src/loader.pl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/loader.pl b/src/loader.pl index f809cd1a..1eafedb0 100644 --- a/src/loader.pl +++ b/src/loader.pl @@ -24,10 +24,14 @@ write_error(Error) :- ; write(' ') % if '$first_answer' isn't defined yet or true, % print indentation. ), + ( current_prolog_flag(double_quotes, chars) -> + DQ = true + ; DQ = false + ), ( nonvar(Error), functor(Error, error, 2) -> - writeq(Error) - ; writeq(throw(Error)) + write_term(Error, [ignore_ops(false), numbervars(true), quoted(true), double_quotes(DQ)]) + ; write_term(throw(Error), [ignore_ops(false), numbervars(true), quoted(true), double_quotes(DQ)]) ), write('.'). From 5a7da721cd9f1ae02df9c515e4a9384ebb5d9f8c Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 15 Jul 2023 11:20:20 -0600 Subject: [PATCH 317/361] add read_term_from_chars/3 (#637) --- build/instructions_template.rs | 6 +++++- src/lib/charsio.pl | 18 +++++++++++++++++- src/machine/dispatch.rs | 8 ++++++++ src/machine/system_calls.rs | 12 +++++++++++- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 5eaa542b..62dc6146 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -418,7 +418,9 @@ enum SystemClauseType { CurrentTime, #[strum_discriminants(strum(props(Arity = "1", Name = "$quoted_token")))] QuotedToken, - #[strum_discriminants(strum(props(Arity = "2", Name = "$read_term_from_chars")))] + #[strum_discriminants(strum(props(Arity = "2", Name = "$read_from_chars")))] + ReadFromChars, + #[strum_discriminants(strum(props(Arity = "5", Name = "$read_term_from_chars")))] ReadTermFromChars, #[strum_discriminants(strum(props(Arity = "1", Name = "$reset_block")))] ResetBlock, @@ -1735,6 +1737,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallStripModule | &Instruction::CallCurrentTime | &Instruction::CallQuotedToken | + &Instruction::CallReadFromChars | &Instruction::CallReadTermFromChars | &Instruction::CallResetBlock | &Instruction::CallResetSCCBlock | @@ -1959,6 +1962,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteStripModule | &Instruction::ExecuteCurrentTime | &Instruction::ExecuteQuotedToken | + &Instruction::ExecuteReadFromChars | &Instruction::ExecuteReadTermFromChars | &Instruction::ExecuteResetBlock | &Instruction::ExecuteResetSCCBlock | diff --git a/src/lib/charsio.pl b/src/lib/charsio.pl index 128a6c50..618416f1 100644 --- a/src/lib/charsio.pl +++ b/src/lib/charsio.pl @@ -12,6 +12,7 @@ read and write chars. get_n_chars/3, get_line_to_chars/3, read_from_chars/2, + read_term_from_chars/3, write_term_to_chars/3, chars_base64/3]). @@ -194,7 +195,22 @@ get_single_char(C) :- read_from_chars(Chars, Term) :- must_be(chars, Chars), must_be(var, Term), - '$read_term_from_chars'(Chars, Term). + '$read_from_chars'(Chars, Term). + +%% read_term_from_chars(+Chars, -Term, +Options). +% +% Like `read_from_chars`, except the reader is configured according to +% `Options` which are those of `read_term`. +% +% ``` +% ?- read_term_from_chars("f(X,y).", T, [variable_names(['X'=X])]). +% T = f(X,y). +% ``` +read_term_from_chars(Chars, Term, Options) :- + must_be(chars, Chars), + must_be(var, Term), + builtins:parse_read_term_options(Options, [Singletons, VariableNames, Variables], read_term_from_chars/3), + '$read_term_from_chars'(Chars, Term, Singletons, Variables, VariableNames). %% write_term_to_chars(+Term, +Options, -Chars). % diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index afe67fb9..b57e77b9 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4270,6 +4270,14 @@ impl Machine { self.quoted_token(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallReadFromChars => { + try_or_throw!(self.machine_st, self.read_from_chars()); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteReadFromChars => { + try_or_throw!(self.machine_st, self.read_from_chars()); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } &Instruction::CallReadTermFromChars => { try_or_throw!(self.machine_st, self.read_term_from_chars()); step_or_fail!(self, self.machine_st.p += 1); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index d53ef79f..35e019ff 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5792,7 +5792,7 @@ impl Machine { } #[inline(always)] - pub(crate) fn read_term_from_chars(&mut self) -> CallResult { + pub(crate) fn read_from_chars(&mut self) -> CallResult { if let Some(atom_or_string) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { let chars = CharReader::new(ByteStream::from_string(atom_or_string.to_string())); let mut parser = Parser::new(chars, &mut self.machine_st); @@ -5829,6 +5829,16 @@ impl Machine { Ok(()) } + #[inline(always)] + pub(crate) fn read_term_from_chars(&mut self) -> CallResult { + if let Some(atom_or_string) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { + let stream = Stream::from_owned_string(atom_or_string.to_string(), &mut self.machine_st.arena); + self.machine_st.read_term(stream, &mut self.indices) + } else { + unreachable!() + } + } + #[inline(always)] pub(crate) fn reset_block(&mut self) { let addr = self.deref_register(1); From 65a8ce8e220e3002897c3fd43b346a4298418f8f Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 15 Jul 2023 19:29:06 -0600 Subject: [PATCH 318/361] generalize simple goal detection to fix call/N test failures in logtalk test suite --- src/machine/system_calls.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 35e019ff..926557d7 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1433,10 +1433,15 @@ impl Machine { post_supp_args .zip(supp_vars.iter()) .all(|(arg_term, supp_var)| { - let arg_term = self.machine_st.store(self.machine_st.deref(arg_term)); + let (module_loc, arg_term) = self.machine_st.strip_module( + arg_term, + heap_loc_as_cell!(0), + ); - if arg_term.is_var() && supp_var.is_var() { - return arg_term == *supp_var; + if module_loc.is_var() || module_loc == atom_as_cell!(atom!("user")) { + if arg_term.is_var() && supp_var.is_var() { + return arg_term == *supp_var; + } } false From de10ccfdeea33abb5d64d5a4bc33ee39b14ab002 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 15 Jul 2023 21:52:54 -0600 Subject: [PATCH 319/361] re-factor options handling of read_term into read_term_body (#1887) --- src/machine/machine_state.rs | 227 ++++++++++++++++++----------------- src/machine/system_calls.rs | 29 ++++- 2 files changed, 141 insertions(+), 115 deletions(-) diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 5a8fc85d..499dce55 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -12,6 +12,7 @@ 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; @@ -482,24 +483,7 @@ impl MachineState { } } - // 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 { - let atoms_ptr = (&self.atom_tbl.table) as *const indexmap::IndexSet; - - if let Stream::Readline(ptr) = stream { - 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 - } - } - - unreachable!("Stream must be a Stream::Readline(_)") - } - - pub fn read_term(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult { + pub fn read_term_body(&mut self, mut term_write_result: TermWriteResult) -> CallResult { fn push_var_eq_functors<'a>( heap: &mut Heap, iter: impl Iterator, @@ -521,6 +505,118 @@ impl MachineState { list_of_var_eqs } + 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 = IndexMap::new(); + + for cell in stackful_preorder_iter(&mut self.heap, &mut self.stack, term) { + 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); + } + } + } + + 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)); + } + + // 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 { + let atoms_ptr = (&self.atom_tbl.table) as *const indexmap::IndexSet; + + if let Stream::Readline(ptr) = stream { + 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 + } + } + + unreachable!("Stream must be a Stream::Readline(_)") + } + + pub fn read_term(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult { self.check_stream_properties( stream, StreamType::Text, @@ -539,100 +635,7 @@ 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 = IndexMap::new(); - - for cell in stackful_preorder_iter(&mut self.heap, &mut self.stack, term) { - 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); - } - } - } - - 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) => { match err { CompilationError::ParserError(e) if e.is_unexpected_eof() => { diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 926557d7..f18a5464 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5816,7 +5816,7 @@ impl Machine { let term_write_result = match term_write_result { Ok(term_write_result) => term_write_result, Err(e) => { - let stub = functor_stub(atom!("read_term_from_chars"), 2); + let stub = functor_stub(atom!("read_from_chars"), 2); let e = self.machine_st.session_error(SessionError::from(e)); return Err(self.machine_st.error_form(e, stub)); @@ -5837,8 +5837,31 @@ impl Machine { #[inline(always)] pub(crate) fn read_term_from_chars(&mut self) -> CallResult { if let Some(atom_or_string) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { - let stream = Stream::from_owned_string(atom_or_string.to_string(), &mut self.machine_st.arena); - self.machine_st.read_term(stream, &mut self.indices) + let chars = CharReader::new(ByteStream::from_string(atom_or_string.to_string())); + let mut parser = Parser::new(chars, &mut self.machine_st); + let op_dir = CompositeOpDir::new(&self.indices.op_dir, None); + + let term_write_result = parser.read_term(&op_dir, Tokens::Default) + .map_err(CompilationError::from) + .and_then(|term| { + write_term_to_heap( + &term, + &mut self.machine_st.heap, + &mut self.machine_st.atom_tbl, + ) + }); + + let term_write_result = match term_write_result { + Ok(term_write_result) => term_write_result, + Err(e) => { + let stub = functor_stub(atom!("read_term_from_chars"), 3); + let e = self.machine_st.session_error(SessionError::from(e)); + + return Err(self.machine_st.error_form(e, stub)); + } + }; + + self.machine_st.read_term_body(term_write_result) } else { unreachable!() } From 5d3295c40cceefeb2995576ffc6bbbb17df86482 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sun, 16 Jul 2023 09:14:02 +0200 Subject: [PATCH 320/361] ENHANCED: use newly available read_term_from_chars/3 for better errors Examples, previously: $ scryer-prolog -g "member(X,Ls" ?- $ scryer-prolog -g "member(X,Ls)" member(_542,_543) causes: error(existence_error(procedure,member/2),member/2) ?- Now: $ scryer-prolog -g "member(X,Ls" "member(X,Ls" cannot be read: error(syntax_error(incomplete_reduction),read_term_from_chars/3:0) $ scryer-prolog -g "member(X,Ls)" member(X,Ls) causes: error(existence_error(procedure,member/2),member/2) ?- This also addresses #1185. --- src/toplevel.pl | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/toplevel.pl b/src/toplevel.pl index 8318f43b..5431bad1 100644 --- a/src/toplevel.pl +++ b/src/toplevel.pl @@ -115,18 +115,27 @@ layout_and_dot([C|Cs]) :- layout_and_dot(Cs). run_goals([]). -run_goals([g(Gs0)|Goals]) :- +run_goals([g(Gs0)|Goals]) :- !, ( ends_with_dot(Gs0) -> Gs1 = Gs0 ; append(Gs0, ".", Gs1) ), - read_from_chars(Gs1, Goal), - ( catch( - user:Goal, - Exception, - (write(Goal), write(' causes: '), write(Exception), nl) % halt? - ) - ; write('Warning: initialization failed for '), - write(Gs0), nl + double_quotes_option(DQ), + catch(read_term_from_chars(Gs1, Goal, [variable_names(VNs)]), + E, + ( write_term(Gs0, [double_quotes(DQ)]), + write(' cannot be read: '), write(E), nl, + halt + ) + ), + ( catch(user:Goal, + Exception, + ( write_term(Goal, [variable_names(VNs),double_quotes(DQ)]), + write(' causes: '), + write_term(Exception, [double_quotes(DQ)]), nl % halt? + ) + ) -> true + ; write('Warning: initialization failed for: '), + write_term(Goal, [variable_names(VNs),double_quotes(DQ)]), nl ), run_goals(Goals). run_goals([Goal|_]) :- From cf345d817499caf989f41f4c1374b546199e09dc Mon Sep 17 00:00:00 2001 From: Fayeed Pawaskar Date: Mon, 17 Jul 2023 20:40:41 +0530 Subject: [PATCH 321/361] wip dashu move --- Cargo.lock | 104 ++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/arena.rs | 28 ++++++-- src/arithmetic.rs | 11 ++-- src/forms.rs | 14 ++-- src/heap_print.rs | 16 ++--- src/machine/arithmetic_ops.rs | 8 +-- src/machine/disjuncts.rs | 4 +- src/machine/heap.rs | 2 +- src/machine/loader.rs | 2 +- src/machine/machine_state.rs | 2 +- src/machine/machine_state_impl.rs | 2 +- src/machine/mod.rs | 2 +- src/machine/system_calls.rs | 2 +- src/parser/ast.rs | 2 +- src/parser/lexer.rs | 2 +- src/parser/mod.rs | 1 + 17 files changed, 165 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51a8fa49..46731842 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -313,6 +313,79 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "dashu" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51a1b5a00793e3ac2239993ef582603764bcb333a4d04c2a0944639a7e916c85" +dependencies = [ + "dashu-base", + "dashu-float", + "dashu-int", + "dashu-macros", + "dashu-ratio", +] + +[[package]] +name = "dashu-base" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2585452b8ecf7c874045dba02a7914b7e5b2e3cdd5e152573413aa290197aa" + +[[package]] +name = "dashu-float" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a168f338914fab603c31a371207c8b3245ab5ff5e9a0f4fd64a9b5f8a972d1f" +dependencies = [ + "dashu-base", + "dashu-int", + "num-traits", + "rand", + "static_assertions", +] + +[[package]] +name = "dashu-int" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a1009a3ce0c4c64e977c5e7dd8c475278750e145cbb8956d1e28832f557975" +dependencies = [ + "cfg-if", + "dashu-base", + "num-order", + "num-traits", + "rand", + "static_assertions", +] + +[[package]] +name = "dashu-macros" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6fc2425b6724a7d5bfc8e57044e231803b5fc3a7283d6efbff34bfab6ebe014" +dependencies = [ + "dashu-base", + "dashu-float", + "dashu-int", + "dashu-ratio", + "proc-macro2", + "quote", +] + +[[package]] +name = "dashu-ratio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b57c839e72af6be14e5c55630ffc9429c9970aed48f7db449b2399467336b4" +dependencies = [ + "dashu-base", + "dashu-float", + "dashu-int", + "num-traits", + "rand", +] + [[package]] name = "derive_deref" version = "1.1.1" @@ -1153,6 +1226,36 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-modular" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a5fe11d4135c3bcdf3a95b18b194afa9608a5f6ff034f5d857bc9a27fb0119" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-order" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e81e321057a0370997b13e6638bba6bd7f6f426e1f8e9a2562490a28eb23e1bc" +dependencies = [ + "num-modular", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -1669,6 +1772,7 @@ dependencies = [ "crossterm", "crrl", "ctrlc", + "dashu", "derive_deref", "dirs-next", "divrem", diff --git a/Cargo.toml b/Cargo.toml index 99218c71..ac9c98b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,7 @@ futures = "0.3" libffi = "3.1.0" libloading = "0.7" derive_deref = "1.1.1" +dashu = "0.3.0" [dev-dependencies] assert_cmd = "1.0.3" diff --git a/src/arena.rs b/src/arena.rs index b055c8db..fbbcd72a 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -6,7 +6,7 @@ use crate::raw_block::*; use crate::read::*; use ordered_float::OrderedFloat; -use crate::parser::rug::{Integer, Rational}; +use crate::parser::dashu::{Integer, Rational}; use std::alloc; use std::fmt; @@ -252,6 +252,26 @@ impl TypedArenaPtr { self.0.as_ptr() } + #[inline] + pub fn to_i64(&self) -> Option { + self.to_i64() + } + + #[inline] + pub fn to_u32(&self) -> Option { + self.to_u32() + } + + #[inline] + pub fn to_usize(&self) -> Option { + self.to_usize() + } + + #[inline] + pub fn to_isize(&self) -> Option { + self.to_isize() + } + #[inline] pub fn header_ptr(&self) -> *const ArenaHeader { let mut ptr = self.as_ptr() as *const u8 as usize; @@ -788,7 +808,7 @@ mod tests { use crate::machine::partial_string::*; use ordered_float::OrderedFloat; - use crate::parser::rug::{Integer, Rational}; + use crate::parser::dashu::{Integer, Rational}; #[test] fn float_ptr_cast() { @@ -889,7 +909,7 @@ mod tests { // rational - let big_rat = 2 * Rational::from(1u64 << 63); + let big_rat = Rational::from(2) * Rational::from(1u64 << 63); let big_rat_ptr: TypedArenaPtr = arena_alloc!(big_rat, &mut wam.machine_st.arena); assert!(!big_rat_ptr.as_ptr().is_null()); @@ -915,7 +935,7 @@ mod tests { (HeapCellValueTag::Cons, cons_ptr) => { match_untyped_arena_ptr!(cons_ptr, (ArenaHeaderTag::Rational, n) => { - assert_eq!(&*n, &(2 * Rational::from(1u64 << 63))); + assert_eq!(&*n, &(Rational::from(2) * Rational::from(1u64 << 63))); } _ => unreachable!() ) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 7f15a7f1..1a3bdf48 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -10,7 +10,8 @@ use crate::types::*; use crate::parser::ast::*; use crate::parser::rug::ops::PowAssign; -use crate::parser::rug::{Assign, Integer, Rational}; +use crate::parser::rug::{Assign}; +use crate::parser::dashu::{Integer, Rational}; use crate::machine::machine_errors::*; @@ -377,12 +378,12 @@ pub(crate) fn rnd_i<'a>(n: &'a Number, arena: &mut Arena) -> Number { if I64_MIN_TO_F <= f && f <= I64_MAX_TO_F { fixnum!(Number, f.into_inner() as i64, arena) } else { - Number::Integer(arena_alloc!(Integer::from_f64(f.into_inner()).unwrap(), arena)) + Number::Integer(arena_alloc!(Integer::from(f.into_inner()).unwrap(), arena)) } } &Number::Rational(ref r) => { let r_ref = r.fract_floor_ref(); - let (mut fract, mut floor) = (Rational::new(), Integer::new()); + let (mut fract, mut floor) = (Rational::from(0), Integer::from(0)); (&mut fract, &mut floor).assign(r_ref); if let Some(floor) = floor.to_i64() { @@ -405,9 +406,9 @@ impl From for Integer { pub(crate) fn rnd_f(n: &Number) -> f64 { match n { &Number::Fixnum(n) => n.get_num() as f64, - &Number::Integer(ref n) => n.to_f64(), + &Number::Integer(ref n) => n.to_f64().value(), &Number::Float(OrderedFloat(f)) => f, - &Number::Rational(ref r) => r.to_f64(), + &Number::Rational(ref r) => r.to_f64().value(), } } diff --git a/src/forms.rs b/src/forms.rs index 627fb4b2..6cc5315f 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -8,7 +8,7 @@ use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; use crate::parser::ast::*; use crate::parser::parser::CompositeOpDesc; -use crate::parser::rug::{Integer, Rational}; +use crate::parser::dashu::{Integer, Rational}; use crate::types::*; use fxhash::FxBuildHasher; @@ -765,9 +765,9 @@ impl Number { pub(crate) fn is_positive(&self) -> bool { match self { &Number::Fixnum(n) => n.get_num() > 0, - &Number::Integer(ref n) => &**n > &0, + &Number::Integer(ref n) => &**n > &Integer::from(0), &Number::Float(f) => f.is_sign_positive(), - &Number::Rational(ref r) => &**r > &0, + &Number::Rational(ref r) => &**r > &Rational::from(0), } } @@ -775,9 +775,9 @@ impl Number { pub(crate) fn is_negative(&self) -> bool { match self { &Number::Fixnum(n) => n.get_num() < 0, - &Number::Integer(ref n) => &**n < &0, + &Number::Integer(ref n) => &**n < &Integer::from(0), &Number::Float(OrderedFloat(f)) => f.is_sign_negative() && OrderedFloat(f) != -0f64, - &Number::Rational(ref r) => &**r < &0, + &Number::Rational(ref r) => &**r < &Rational::from(0), } } @@ -785,9 +785,9 @@ impl Number { pub(crate) fn is_zero(&self) -> bool { match self { &Number::Fixnum(n) => n.get_num() == 0, - &Number::Integer(ref n) => &**n == &0, + &Number::Integer(ref n) => &**n == &Integer::from(0), &Number::Float(f) => f == OrderedFloat(0f64) || f == OrderedFloat(-0f64), - &Number::Rational(ref r) => &**r == &0, + &Number::Rational(ref r) => &**r == &Rational::from(0), } } diff --git a/src/heap_print.rs b/src/heap_print.rs index 4e7781b7..cf2c293c 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -1,7 +1,7 @@ use crate::arena::*; use crate::atom_table::*; use crate::parser::ast::*; -use crate::parser::rug::{Integer, Rational}; +use crate::parser::dashu::{Integer, Rational}; use crate::{ alpha_numeric_char, capital_letter_char, cut_char, decimal_digit_char, graphic_token_char, is_fx, is_infix, is_postfix, is_prefix, is_xf, is_xfx, is_xfy, is_yfx, semicolon_char, @@ -196,7 +196,7 @@ impl NumberFocus { fn is_negative(&self) -> bool { match self { NumberFocus::Unfocused(n) => n.is_negative(), - NumberFocus::Denominator(r) | NumberFocus::Numerator(r) => **r < 0, + NumberFocus::Denominator(r) | NumberFocus::Numerator(r) => **r < Rational::from(0), } } } @@ -400,8 +400,8 @@ fn negated_op_needs_bracketing( && iter.leftmost_leaf_has_property(op_dir, |addr| match Number::try_from(addr) { Ok(Number::Fixnum(n)) => n.get_num() > 0, Ok(Number::Float(f)) => f > OrderedFloat(0f64), - Ok(Number::Integer(n)) => &*n > &0, - Ok(Number::Rational(n)) => &*n > &0, + Ok(Number::Integer(n)) => &*n > &Integer::from(0), + Ok(Number::Rational(n)) => &*n > &Rational::from(0), _ => false, }) } else { @@ -514,7 +514,7 @@ pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option let j = n.div_rem_floor(Integer::from(26)); let j = <(Integer, Integer)>::from(j).0; - if j == 0 { + if j == Integer::from(0) { CHAR_CODES[i].to_string() } else { format!("{}{}", CHAR_CODES[i], j) @@ -530,7 +530,7 @@ pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option } } Ok(Number::Integer(n)) => { - if &*n >= &0 { + if &*n >= &Integer::from(0) { Some(numbervar(Integer::from(offset + &*n))) } else { None @@ -1307,10 +1307,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { if self.numbervars && arity == 1 && name == atom!("$VAR") { !self.iter.immediate_leaf_has_property(|addr| { match Number::try_from(addr) { - Ok(Number::Integer(n)) => &*n >= &0, + Ok(Number::Integer(n)) => &*n >= &Integer::from(0), Ok(Number::Fixnum(n)) => n.get_num() >= 0, Ok(Number::Float(f)) => f >= OrderedFloat(0f64), - Ok(Number::Rational(r)) => &*r >= &0, + Ok(Number::Rational(r)) => &*r >= &Integer::from(0), _ => false, } }) && needs_bracketing(op_desc, op) diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index f5aa982f..0c948bf6 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -8,7 +8,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; @@ -338,7 +338,7 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result { 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 +349,7 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result { 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 +358,7 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result { - 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 { diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index f2a66851..33d24a06 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -6,7 +6,7 @@ use crate::machine::loader::*; use crate::machine::machine_errors::CompilationError; use crate::machine::preprocessor::*; use crate::parser::ast::*; -use crate::parser::rug::Rational; +use crate::parser::dashu::{Rational, Integer}; use crate::variable_records::*; use indexmap::{IndexMap, IndexSet}; @@ -236,7 +236,7 @@ fn merge_branch_seq>(branches: Iter) -> Branch branch_info.chunks.extend(branch.chunks.drain(..)); } - branch_info.branch_num.delta *= 2; + 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 diff --git a/src/machine/heap.rs b/src/machine/heap.rs index 59e63009..1cdfeb74 100644 --- a/src/machine/heap.rs +++ b/src/machine/heap.rs @@ -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; diff --git a/src/machine/loader.rs b/src/machine/loader.rs index 5a309197..d5b3a15f 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -1644,7 +1644,7 @@ impl Machine { .store(self.machine_st.deref(self.machine_st.registers[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()) } diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 78b1a7f5..768e4492 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -14,7 +14,7 @@ use crate::machine::streams::*; use crate::parser::ast::*; use crate::types::*; -use crate::parser::rug::Integer; +use crate::parser::dashu::Integer; use indexmap::IndexMap; diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index b4dc3fea..f0f27fcd 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -13,7 +13,7 @@ use crate::machine::partial_string::*; use crate::machine::stack::*; use crate::machine::unify::*; use crate::parser::ast::*; -use crate::parser::rug::{Integer, Rational}; +use crate::parser::dashu::{Integer, Rational}; use indexmap::IndexSet; diff --git a/src/machine/mod.rs b/src/machine/mod.rs index ddf64d34..a212b9ea 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -41,7 +41,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; diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 4c9c0f2f..84f86cec 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -24,7 +24,7 @@ use crate::machine::preprocessor::to_op_decl; use crate::machine::stack::*; use crate::machine::streams::*; use crate::parser::char_reader::*; -use crate::parser::rug::Integer; +use crate::parser::dashu::Integer; use crate::parser::rug::rand::RandState; use crate::read::*; use crate::types::*; diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 272d5b7e..73f5b4e5 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -12,7 +12,7 @@ use std::ops::{Deref, Neg}; use std::rc::Rc; use std::vec::Vec; -use crate::parser::rug::{Integer, Rational}; +use crate::parser::dashu::{Integer, Rational}; use fxhash::FxBuildHasher; use indexmap::IndexMap; diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs index 3fbddc1d..92b78050 100644 --- a/src/parser/lexer.rs +++ b/src/parser/lexer.rs @@ -5,7 +5,7 @@ use crate::atom_table::*; pub use crate::machine::machine_state::*; use crate::parser::ast::*; use crate::parser::char_reader::*; -use crate::parser::rug::Integer; +use crate::parser::dashu::Integer; use std::convert::TryFrom; use std::fmt; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index fa7b8859..dccaa49b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -3,6 +3,7 @@ pub use num_rug_adapter as rug; #[cfg(feature = "rug")] pub use rug; +pub use dashu; // #[macro_use] // extern crate lazy_static; From 4e1a4dae6c92b89d4ffd3a0edaa9a294fe9be673 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 17 Jul 2023 11:38:50 -0600 Subject: [PATCH 322/361] print strings in tails of lists (#1890) --- src/heap_print.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index e17eecd8..110f8942 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -1185,7 +1185,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let at_cdr = self.outputter.ends_with("|"); if self.double_quotes { - if !at_cdr && !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) { + if !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) { self.remove_list_children(focus.value() as usize); return self.print_proper_string(focus.value() as usize, max_depth); } From 86c90d77dd116694c1c8ec442ff227aa26c0e6ae Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 16 Jul 2023 20:42:40 -0600 Subject: [PATCH 323/361] do a better job handling EOF in read_term (#1887) --- src/machine/machine_state.rs | 31 ++++++----- src/machine/streams.rs | 102 ++++++++++++++++++++++++---------- src/machine/system_calls.rs | 105 +++++++++++++++++------------------ src/read.rs | 21 ++++++- 4 files changed, 162 insertions(+), 97 deletions(-) diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 499dce55..c8a9cbe0 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -616,7 +616,7 @@ impl MachineState { unreachable!("Stream must be a Stream::Readline(_)") } - pub fn read_term(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult { + pub fn read_term(&mut self, mut stream: Stream, indices: &mut IndexStore) -> CallResult { self.check_stream_properties( stream, StreamType::Text, @@ -637,22 +637,27 @@ impl MachineState { match self.read(stream, &indices.op_dir) { Ok(term_write_result) => return self.read_term_body(term_write_result), Err(err) => { - match err { + match &err { CompilationError::ParserError(e) if e.is_unexpected_eof() => { - self.eof_action( - self.registers[2], - stream, - atom!("read_term"), - 3, - )?; + if stream.at_end_of_stream() { + unify!(self, self.registers[2], atom_as_cell!(atom!("end_of_file"))); + return Ok(()); + } else if stream.past_end_of_stream() { + self.eof_action( + self.registers[2], + stream, + atom!("read_term"), + 3, + )?; - if stream.options().eof_action() == EOFAction::Reset { - if self.fail == false { - continue; + if stream.options().eof_action() == EOFAction::Reset { + if self.fail == false { + continue; + } } - } - return Ok(()); + return Ok(()); + } } _ => {} } diff --git a/src/machine/streams.rs b/src/machine/streams.rs index c1b4678e..0ea8591d 100644 --- a/src/machine/streams.rs +++ b/src/machine/streams.rs @@ -884,19 +884,38 @@ impl PartialEq for Stream { impl Eq for Stream {} +fn cursor_position(past_end_of_stream: &mut bool, cursor: &Cursor, 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, }; @@ -971,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 + } } } @@ -1306,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 { diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index f18a5464..86c4c1bd 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5796,72 +5796,71 @@ impl Machine { self.machine_st.read_term(stream, &mut self.indices) } + #[inline(always)] + fn read_term_and_write_to_heap( + &mut self, + atom_or_string: AtomOrString, + ) -> Result, MachineStub> { + let string = match atom_or_string { + AtomOrString::Atom(atom) if atom == atom!("[]") => "".to_owned(), + _ => atom_or_string.to_string(), + }; + + let chars = CharReader::new(ByteStream::from_string(string)); + let mut parser = Parser::new(chars, &mut self.machine_st); + let op_dir = CompositeOpDir::new(&self.indices.op_dir, None); + + let term_write_result = parser.read_term(&op_dir, Tokens::Default) + .map_err(|err| error_after_read_term(err, 0, &parser)) + .and_then(|term| { + write_term_to_heap( + &term, + &mut self.machine_st.heap, + &mut self.machine_st.atom_tbl, + ) + }); + + match term_write_result { + Ok(term_write_result) => Ok(Some(term_write_result)), + Err(CompilationError::ParserError(e)) if e.is_unexpected_eof() => { + let value = self.machine_st.registers[2]; + self.machine_st.unify_atom(atom!("end_of_file"), value); + + Ok(None) + } + Err(e) => { + let stub = functor_stub(atom!("read_term_from_chars"), 3); + let e = self.machine_st.session_error(SessionError::from(e)); + + Err(self.machine_st.error_form(e, stub)) + } + } + } + #[inline(always)] pub(crate) fn read_from_chars(&mut self) -> CallResult { if let Some(atom_or_string) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { - let chars = CharReader::new(ByteStream::from_string(atom_or_string.to_string())); - let mut parser = Parser::new(chars, &mut self.machine_st); - let op_dir = CompositeOpDir::new(&self.indices.op_dir, None); + if let Some(term_write_result) = self.read_term_and_write_to_heap(atom_or_string)? { + let result = heap_loc_as_cell!(term_write_result.heap_loc); + let var = self.deref_register(2).as_var().unwrap(); - let term_write_result = parser.read_term(&op_dir, Tokens::Default) - .map_err(CompilationError::from) - .and_then(|term| { - write_term_to_heap( - &term, - &mut self.machine_st.heap, - &mut self.machine_st.atom_tbl, - ) - }); + self.machine_st.bind(var, result); + } - let term_write_result = match term_write_result { - Ok(term_write_result) => term_write_result, - Err(e) => { - let stub = functor_stub(atom!("read_from_chars"), 2); - let e = self.machine_st.session_error(SessionError::from(e)); - - return Err(self.machine_st.error_form(e, stub)); - } - }; - - let result = heap_loc_as_cell!(term_write_result.heap_loc); - let var = self.deref_register(2).as_var().unwrap(); - - self.machine_st.bind(var, result); + Ok(()) } else { unreachable!() } - - Ok(()) } #[inline(always)] pub(crate) fn read_term_from_chars(&mut self) -> CallResult { if let Some(atom_or_string) = self.machine_st.value_to_str_like(self.machine_st.registers[1]) { - let chars = CharReader::new(ByteStream::from_string(atom_or_string.to_string())); - let mut parser = Parser::new(chars, &mut self.machine_st); - let op_dir = CompositeOpDir::new(&self.indices.op_dir, None); - - let term_write_result = parser.read_term(&op_dir, Tokens::Default) - .map_err(CompilationError::from) - .and_then(|term| { - write_term_to_heap( - &term, - &mut self.machine_st.heap, - &mut self.machine_st.atom_tbl, - ) - }); - - let term_write_result = match term_write_result { - Ok(term_write_result) => term_write_result, - Err(e) => { - let stub = functor_stub(atom!("read_term_from_chars"), 3); - let e = self.machine_st.session_error(SessionError::from(e)); - - return Err(self.machine_st.error_form(e, stub)); - } - }; - - self.machine_st.read_term_body(term_write_result) + if let Some(term_write_result) = self.read_term_and_write_to_heap(atom_or_string)? { + self.machine_st.read_term_body(term_write_result) + } else { + Ok(()) + } } else { unreachable!() } diff --git a/src/read.rs b/src/read.rs index 9a191dad..dfba5147 100644 --- a/src/read.rs +++ b/src/read.rs @@ -36,6 +36,25 @@ pub(crate) fn devour_whitespace<'a, R: CharRead>(parser: &mut Parser<'a, R>) -> } } +pub(crate) fn error_after_read_term( + err: ParserError, + prior_num_lines_read: usize, + parser: &Parser, +) -> CompilationError { + if err.is_unexpected_eof() { + let line_num = parser.lexer.line_num; + let col_num = parser.lexer.col_num; + + // rough overlap with errors 8.14.1.3 k) & l) of the ISO standard here + if !(line_num == prior_num_lines_read && col_num == 0) { + return CompilationError::from(ParserError::IncompleteReduction(line_num, col_num)); + } + } + + CompilationError::from(err) +} + + impl MachineState { pub(crate) fn read( &mut self, @@ -50,7 +69,7 @@ impl MachineState { parser.add_lines_read(prior_num_lines_read); let term = parser.read_term(&op_dir, Tokens::Default) - .map_err(CompilationError::from)?; + .map_err(|err| error_after_read_term(err, prior_num_lines_read, &parser))?; // CompilationError::from (term, parser.lines_read() - prior_num_lines_read) }; From a154a34f8746c4af0244a6d5ceb953ae39783fb1 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 16 Jul 2023 22:22:44 -0600 Subject: [PATCH 324/361] omit anonymous variables from read_term variable_names and singletons lists --- src/machine/machine_indices.rs | 27 ++++++++++++++++++++++++++- src/machine/machine_state.rs | 12 ++++++++---- src/machine/mock_wam.rs | 7 ++++++- src/read.rs | 24 ++++++++++++++---------- 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/machine/machine_indices.rs b/src/machine/machine_indices.rs index 3e358db9..7389caca 100644 --- a/src/machine/machine_indices.rs +++ b/src/machine/machine_indices.rs @@ -227,7 +227,32 @@ impl CodeIndex { } } -pub(crate) type HeapVarDict = IndexMap; +#[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; pub(crate) type GlobalVarDir = IndexMap), FxBuildHasher>; diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index c8a9cbe0..f7237836 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -486,13 +486,13 @@ impl MachineState { pub fn read_term_body(&mut self, mut term_write_result: TermWriteResult) -> CallResult { fn push_var_eq_functors<'a>( heap: &mut Heap, - iter: impl Iterator, + iter: impl Iterator, atom_tbl: &mut AtomTable, ) -> Vec { let mut list_of_var_eqs = vec![]; for (var, binding) in iter { - let var_atom = atom_tbl.build_with(&var.borrow().to_string()); + let var_atom = atom_tbl.build_with(&var.to_string()); let h = heap.len(); heap.push(atom_as_cell!(atom!("="), 2)); @@ -542,7 +542,11 @@ impl MachineState { let singleton_var_list = push_var_eq_functors( &mut self.heap, - term_write_result.var_dict.iter().filter(|(_, binding)| { + 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 { @@ -565,7 +569,7 @@ impl MachineState { let list_of_var_eqs = push_var_eq_functors( &mut self.heap, - var_list.iter().map(|(var_name, var,_)| (var_name,var)), + var_list.iter().filter_map(|(var_name, var,_)| if var_name.is_anon() { None } else { Some((var_name,var)) }), &mut self.atom_tbl, ); diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index 70264ac9..257735c6 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -71,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()) diff --git a/src/read.rs b/src/read.rs index dfba5147..fac2e4e8 100644 --- a/src/read.rs +++ b/src/read.rs @@ -259,9 +259,9 @@ impl CharRead for ReadlineStream { } #[inline] -pub(crate) fn write_term_to_heap( - term: &Term, - heap: &mut Heap, +pub(crate) fn write_term_to_heap<'a, 'b>( + term: &'a Term, + heap: &'b mut Heap, atom_tbl: &mut AtomTable, ) -> Result { let term_writer = TermWriter::new(heap, atom_tbl); @@ -294,7 +294,7 @@ impl<'a, 'b> TermWriter<'a, 'b> { } #[inline] - fn modify_head_of_queue(&mut self, term: &TermRef<'a>, h: usize) { + fn modify_head_of_queue(&mut self, term: &TermRef, h: usize) { if let Some((arity, site_h)) = self.queue.pop_front() { self.heap[site_h] = self.term_as_addr(term, h); @@ -310,7 +310,7 @@ impl<'a, 'b> TermWriter<'a, 'b> { self.heap.push(heap_loc_as_cell!(h)); } - fn term_as_addr(&mut self, term: &TermRef<'a>, h: usize) -> HeapCellValue { + fn term_as_addr(&mut self, term: &TermRef, h: usize) -> HeapCellValue { match term { &TermRef::Cons(..) => list_loc_as_cell!(h), &TermRef::AnonVar(_) | &TermRef::Var(..) => heap_loc_as_cell!(h), @@ -329,7 +329,7 @@ impl<'a, 'b> TermWriter<'a, 'b> { } } - fn write_term_to_heap(mut self, term: &'a Term) -> Result { + fn write_term_to_heap(mut self, term: &Term) -> Result { let heap_loc = self.heap.len(); for term in breadth_first_iter(term, RootIterationPolicy::Iterated) { @@ -383,17 +383,19 @@ impl<'a, 'b> TermWriter<'a, 'b> { self.push_stub_addr(); } } - &TermRef::AnonVar(Level::Root) | &TermRef::Literal(Level::Root, ..) => { + &TermRef::AnonVar(Level::Root) | TermRef::Literal(Level::Root, ..) => { let addr = self.term_as_addr(&term, h); self.heap.push(addr); } &TermRef::Var(Level::Root, _, ref var_ptr) => { let addr = self.term_as_addr(&term, h); - self.var_dict.insert(var_ptr.clone(), heap_loc_as_cell!(h)); + self.var_dict.insert(VarKey::VarPtr(var_ptr.clone()), addr); self.heap.push(addr); } &TermRef::AnonVar(_) => { if let Some((arity, site_h)) = self.queue.pop_front() { + self.var_dict.insert(VarKey::AnonVar(h), heap_loc_as_cell!(site_h)); + if arity > 1 { self.queue.push_front((arity - 1, site_h + 1)); } @@ -422,10 +424,12 @@ impl<'a, 'b> TermWriter<'a, 'b> { } &TermRef::Var(_, _, ref var) => { if let Some((arity, site_h)) = self.queue.pop_front() { - if let Some(addr) = self.var_dict.get(var).cloned() { + let var_key = VarKey::VarPtr(var.clone()); + + if let Some(addr) = self.var_dict.get(&var_key).cloned() { self.heap[site_h] = addr; } else { - self.var_dict.insert(var.clone(), heap_loc_as_cell!(site_h)); + self.var_dict.insert(var_key, heap_loc_as_cell!(site_h)); } if arity > 1 { From cf367024fd0da02c715f2816b1bc429757c196d6 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 17 Jul 2023 13:53:08 -0600 Subject: [PATCH 325/361] add specialized EOF handling for user input (#1892) --- src/machine/machine_state.rs | 80 ++++++++++++++++++++++++++---------- src/machine/system_calls.rs | 6 ++- 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index f7237836..d7b66f2e 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -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, @@ -603,6 +609,23 @@ impl MachineState { return Ok(unify_fn!(*self, var_names_offset, var_names_addr)); } + pub fn read_term_from_user_input_eof_handler(&mut self, stream: Stream) -> Result { + 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 { @@ -612,15 +635,45 @@ 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, + ); } } unreachable!("Stream must be a Stream::Readline(_)") } - pub fn read_term(&mut self, mut stream: Stream, indices: &mut IndexStore) -> CallResult { + pub fn read_term_eof_handler(&mut self, mut stream: Stream) -> Result { + if stream.at_end_of_stream() { + unify!(self, self.registers[2], atom_as_cell!(atom!("end_of_file"))); + return Ok(OnEOF::Return); + } else if stream.past_end_of_stream() { + 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) + } + + pub fn read_term( + &mut self, + stream: Stream, + indices: &mut IndexStore, + eof_handler: impl Fn(&mut Self, Stream) -> Result, + ) -> CallResult { self.check_stream_properties( stream, StreamType::Text, @@ -643,24 +696,9 @@ impl MachineState { Err(err) => { match &err { CompilationError::ParserError(e) if e.is_unexpected_eof() => { - if stream.at_end_of_stream() { - unify!(self, self.registers[2], atom_as_cell!(atom!("end_of_file"))); - return Ok(()); - } else if stream.past_end_of_stream() { - self.eof_action( - self.registers[2], - stream, - atom!("read_term"), - 3, - )?; - - if stream.options().eof_action() == EOFAction::Reset { - if self.fail == false { - continue; - } - } - - return Ok(()); + match eof_handler(self, stream)? { + OnEOF::Return => return Ok(()), + OnEOF::Continue => continue, } } _ => {} diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 86c4c1bd..259acc9e 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5793,7 +5793,11 @@ impl Machine { 3, )?; - self.machine_st.read_term(stream, &mut self.indices) + if let Stream::Readline(..) = stream { + self.machine_st.read_term(stream, &mut self.indices, MachineState::read_term_from_user_input_eof_handler) + } else { + self.machine_st.read_term(stream, &mut self.indices, MachineState::read_term_eof_handler) + } } #[inline(always)] From 42a50474daae81bc396f626faade2b77238417d3 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 17 Jul 2023 16:45:03 -0600 Subject: [PATCH 326/361] remove read/{1,2} as a builtin, write read options upon EOF, throw better domain errors in parse_read_term_options/2 --- build/instructions_template.rs | 6 -- src/lib/builtins.pl | 47 +++++++-- src/machine/dispatch.rs | 69 ------------- src/machine/machine_state.rs | 177 +++++++++++++++++---------------- src/machine/mod.rs | 1 - 5 files changed, 129 insertions(+), 171 deletions(-) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 62dc6146..d97410eb 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -101,8 +101,6 @@ enum BuiltInClauseType { Is(RegType, ArithmeticTerm), #[strum_discriminants(strum(props(Arity = "2", Name = "keysort")))] KeySort, - #[strum_discriminants(strum(props(Arity = "2", Name = "read")))] - Read, #[strum_discriminants(strum(props(Arity = "2", Name = "sort")))] Sort, } @@ -1504,7 +1502,6 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallFunctor | &Instruction::CallGround | &Instruction::CallKeySort | - &Instruction::CallRead | &Instruction::CallSort => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("call"), [atom(name), fixnum(arity)]) @@ -1530,7 +1527,6 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteGround | &Instruction::ExecuteIs(..) | &Instruction::ExecuteKeySort | - &Instruction::ExecuteRead | &Instruction::ExecuteSort => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("execute"), [atom(name), fixnum(arity)]) @@ -1556,7 +1552,6 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::DefaultCallGround | &Instruction::DefaultCallIs(..) | &Instruction::DefaultCallKeySort | - &Instruction::DefaultCallRead | &Instruction::DefaultCallSort => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("call_default"), [atom(name), fixnum(arity)]) @@ -1582,7 +1577,6 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::DefaultExecuteGround | &Instruction::DefaultExecuteIs(..) | &Instruction::DefaultExecuteKeySort | - &Instruction::DefaultExecuteRead | &Instruction::DefaultExecuteSort => { let (name, arity) = self.to_name_and_arity(); functor!(atom!("execute_default"), [atom(name), fixnum(arity)]) diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index db5282f7..dcc37eb0 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -17,13 +17,13 @@ peek_char/1, peek_char/2, peek_code/1, peek_code/2, put_byte/1, put_byte/2, put_code/1, put_code/2, put_char/1, put_char/2, read/1, - read_term/2, read_term/3, repeat/0, retract/1, - retractall/1, set_prolog_flag/2, set_input/1, - set_stream_position/2, set_output/1, setof/3, - stream_property/2, sub_atom/5, subsumes_term/2, - term_variables/2, throw/1, true/0, - unify_with_occurs_check/2, write/1, write/2, - write_canonical/1, write_canonical/2, + read/2, read_term/2, read_term/3, repeat/0, + retract/1, retractall/1, set_prolog_flag/2, + set_input/1, set_stream_position/2, set_output/1, + setof/3, stream_property/2, sub_atom/5, + subsumes_term/2, term_variables/2, throw/1, + true/0, unify_with_occurs_check/2, write/1, + write/2, write_canonical/1, write_canonical/2, write_term/2, write_term/3, writeq/1, writeq/2]). /** Builtin predicates @@ -668,9 +668,30 @@ parse_read_term_options(Options, OptionValues, Stub) :- parse_options_list(Options, builtins:parse_read_term_options_, DefaultOptions, OptionValues, Stub). -parse_read_term_options_(singletons(Vars), singletons-Vars) :- !. -parse_read_term_options_(variables(Vars), variables-Vars) :- !. -parse_read_term_options_(variable_names(Vars), variable_names-Vars) :- !. +parse_read_term_options_(singletons(Vars), singletons-Vars) :- + ( ( var(Vars) + ; '$skip_max_list'(_, _, Vars, Rs), + Rs == [] + ) -> + ! + ; throw(error(domain_error(read_option, singletons(Vars)), read_term/2)) + ). +parse_read_term_options_(variables(Vars), variables-Vars) :- + ( ( var(Vars) + ; '$skip_max_list'(_, _, Vars, Rs), + Rs == [] + ) -> + ! + ; throw(error(domain_error(read_option, variables(Vars)), read_term/2)) + ). +parse_read_term_options_(variable_names(Vars), variable_names-Vars) :- + ( ( var(Vars) + ; '$skip_max_list'(_, _, Vars, Rs), + Rs == [] + ) -> + ! + ; throw(error(domain_error(read_option, variable_names(Vars)), read_term/2)) + ). parse_read_term_options_(E,_) :- throw(error(domain_error(read_option, E), _)). @@ -698,7 +719,11 @@ read_term(Term, Options) :- % to read input from a file or the user. Use other predicates like `phrase_from_file/2` for that. read(Term) :- current_input(Stream), - read(Stream, Term). + read_term(Stream, Term, []). + % read(Stream, Term). + +read(Stream, Term) :- + read_term(Stream, Term, []). % ensures List is either a variable or a list. can_be_list(List, _) :- diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index b57e77b9..669b011b 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -309,34 +309,6 @@ impl MachineState { } impl Machine { - fn read(&mut self) -> CallResult { - let stream = self.machine_st.get_stream_or_alias( - self.machine_st.registers[1], - &self.indices.stream_aliases, - atom!("read"), - 2, - )?; - - match self.machine_st.read(stream, &self.indices.op_dir) { - Ok(offset) => { - let value = self.machine_st.registers[2]; - unify_fn!(&mut self.machine_st, value, heap_loc_as_cell!(offset.heap_loc)); - } - Err(CompilationError::ParserError(e)) if e.is_unexpected_eof() => { - let value = self.machine_st.registers[2]; - self.machine_st.unify_atom(atom!("end_of_file"), value); - } - Err(e) => { - let stub = functor_stub(atom!("read"), 2); - let err = self.machine_st.syntax_error(e); - - return Err(self.machine_st.error_form(err, stub)); - } - }; - - Ok(()) - } - pub(super) fn find_living_dynamic_else(&self, mut p: usize) -> Option<(usize, usize)> { loop { match &self.code[p] { @@ -1334,19 +1306,6 @@ impl Machine { } } } - &Instruction::DefaultCallRead => { - try_or_throw!(self.machine_st, self.read()); - step_or_fail!(self, self.machine_st.p += 1); - } - &Instruction::DefaultExecuteRead => { - try_or_throw!(self.machine_st, self.read()); - - if self.machine_st.fail { - self.machine_st.backtrack(); - } else { - self.machine_st.p = self.machine_st.cp; - } - } &Instruction::DefaultCallCopyTerm => { self.machine_st.copy_term(AttrVarPolicy::DeepCopy); step_or_fail!(self, self.machine_st.p += 1); @@ -1673,34 +1632,6 @@ impl Machine { } } } - &Instruction::CallRead => { - try_or_throw!(self.machine_st, self.read()); - - if self.machine_st.fail { - self.machine_st.backtrack(); - } else { - try_or_throw!( - self.machine_st, - (self.machine_st.increment_call_count_fn)(&mut self.machine_st) - ); - - self.machine_st.p += 1; - } - } - &Instruction::ExecuteRead => { - try_or_throw!(self.machine_st, self.read()); - - if self.machine_st.fail { - self.machine_st.backtrack(); - } else { - try_or_throw!( - self.machine_st, - (self.machine_st.increment_call_count_fn)(&mut self.machine_st) - ); - - self.machine_st.p = self.machine_st.cp; - } - } &Instruction::CallCopyTerm => { self.machine_st.copy_term(AttrVarPolicy::DeepCopy); diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index d7b66f2e..62663ee2 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -200,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, + atom_tbl: &mut AtomTable, +) -> Vec { + 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, @@ -489,88 +510,11 @@ impl MachineState { } } - pub fn read_term_body(&mut self, mut term_write_result: TermWriteResult) -> CallResult { - fn push_var_eq_functors<'a>( - heap: &mut Heap, - iter: impl Iterator, - atom_tbl: &mut AtomTable, - ) -> Vec { - 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 - } - - 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 = IndexMap::new(); - - for cell in stackful_preorder_iter(&mut self.heap, &mut self.stack, term) { - 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); - } - } - } - - 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(|(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, - ); - - 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)); - } - } - + fn write_read_term_options( + &mut self, + mut var_list: Vec<(VarKey, HeapCellValue, usize)>, + singleton_var_list: Vec, + ) -> CallResult { var_list.sort_by(|(_,_,idx_1),(_,_,idx_2)| idx_1.cmp(idx_2)); let list_of_var_eqs = push_var_eq_functors( @@ -606,7 +550,71 @@ impl MachineState { 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(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) + } + ); + + 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 = IndexMap::new(); + + for cell in stackful_preorder_iter(&mut self.heap, &mut self.stack, term) { + 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() { + let idx = singleton_var_set.get_index_of(&var).unwrap(); + 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 { @@ -649,6 +657,7 @@ impl MachineState { pub fn read_term_eof_handler(&mut self, mut stream: Stream) -> Result { 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( @@ -697,7 +706,7 @@ impl MachineState { match &err { CompilationError::ParserError(e) if e.is_unexpected_eof() => { match eof_handler(self, stream)? { - OnEOF::Return => return Ok(()), + OnEOF::Return => return self.write_read_term_options(vec![], vec![]), OnEOF::Continue => continue, } } diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 41cc4f39..402c83fb 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -385,7 +385,6 @@ impl Machine { Instruction::ExecuteFunctor, Instruction::ExecuteGround, Instruction::ExecuteKeySort, - Instruction::ExecuteRead, Instruction::ExecuteSort, Instruction::ExecuteN(1), Instruction::ExecuteN(2), From db43d461b9ec70b1868c824966dd8d50b886794e Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 18 Jul 2023 12:10:27 -0600 Subject: [PATCH 327/361] catch errors thrown from tabling Worker (#1526, #1888) --- src/lib/tabling.pl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/tabling.pl b/src/lib/tabling.pl index 606635fb..e9008085 100644 --- a/src/lib/tabling.pl +++ b/src/lib/tabling.pl @@ -164,7 +164,9 @@ activate(Wrapper,Worker,T) :- delim(Wrapper,Worker,Table) :- % debug(tabling, 'ACT: ~p on ~p', [Wrapper, Table]), - reset(Worker,SourceCall,Continuation), + catch(reset(Worker,SourceCall,Continuation), + _, + fail), ( Continuation = none -> ( add_answer(Table,Wrapper) -> true %debug(tabling, 'ADD: ~p', [Wrapper]) From 56f677242225e766aef0e925f3d57fac2b4369b6 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 18 Jul 2023 12:15:20 -0600 Subject: [PATCH 328/361] call write_read_term_options if read_term_from_chars/3 succeeds by unifying Term to end_of_file (#1892) --- src/machine/machine_state.rs | 2 +- src/machine/system_calls.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 62663ee2..142f5a98 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -510,7 +510,7 @@ impl MachineState { } } - fn write_read_term_options( + pub fn write_read_term_options( &mut self, mut var_list: Vec<(VarKey, HeapCellValue, usize)>, singleton_var_list: Vec, diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 259acc9e..9c76ee70 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5863,6 +5863,11 @@ impl Machine { if let Some(term_write_result) = self.read_term_and_write_to_heap(atom_or_string)? { self.machine_st.read_term_body(term_write_result) } else { + if !self.machine_st.fail { + // wrote end_of_file term in this case. + self.machine_st.write_read_term_options(vec![], vec![])?; + } + Ok(()) } } else { From 14646074bea92c5c2d51c1961e27ecbc7b29fe20 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 18 Jul 2023 14:44:02 -0600 Subject: [PATCH 329/361] remove failing append choicepoint in atom_concat/3 special case (#1893) --- src/lib/builtins.pl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 7f19d148..983503cb 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -1559,9 +1559,14 @@ atom_concat(Atom_1, Atom_2, Atom_12) :- ( var(Atom_12) -> throw(error(instantiation_error, atom_concat/3)) ; atom_chars(Atom_12, Atom_12_Chars), - lists:append(BeforeChars, AfterChars, Atom_12_Chars), - atom_chars(Atom_1, BeforeChars), - atom_chars(Atom_2, AfterChars) + ( var(Atom_2) -> + lists:append(BeforeChars, AfterChars, Atom_12_Chars), + atom_chars(Atom_2, AfterChars) + ; atom_chars(Atom_2, AfterChars), + lists:append(BeforeChars, AfterChars, Atom_12_Chars), + ! + ), + atom_chars(Atom_1, BeforeChars) ) ; var(Atom_2) -> ( var(Atom_12) -> throw(error(instantiation_error, atom_concat/3)) From 85bc544fb9a4b37bef9802596348431558f2985e Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 18 Jul 2023 15:39:51 -0600 Subject: [PATCH 330/361] dereference TermWriteResult variables sooner in read_term_body (#1894) --- src/machine/machine_state.rs | 15 +++++++++------ src/read.rs | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index 142f5a98..d76e1cf1 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -563,17 +563,19 @@ impl MachineState { } ); - let term = self.registers[2]; - unify_fn!(*self, heap_loc, term); - let term = 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 = IndexMap::new(); - for cell in stackful_preorder_iter(&mut self.heap, &mut self.stack, term) { + 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() { @@ -609,8 +611,9 @@ impl MachineState { 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)); + if let Some(idx) = singleton_var_set.get_index_of(&var) { + var_list.push((var_name, addr, idx)); + } } } diff --git a/src/read.rs b/src/read.rs index fac2e4e8..3afb4bf9 100644 --- a/src/read.rs +++ b/src/read.rs @@ -422,7 +422,7 @@ impl<'a, 'b> TermWriter<'a, 'b> { continue; } } - &TermRef::Var(_, _, ref var) => { + &TermRef::Var(.., ref var) => { if let Some((arity, site_h)) = self.queue.pop_front() { let var_key = VarKey::VarPtr(var.clone()); From 4fd247f881d7c6cd8dfc3e060f2a7cca74a24e5d Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 19 Jul 2023 17:13:47 -0600 Subject: [PATCH 331/361] check for unexpected EOF in get_to_eof (#1897) --- src/lib/charsio.pl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/charsio.pl b/src/lib/charsio.pl index 618416f1..d47d3642 100644 --- a/src/lib/charsio.pl +++ b/src/lib/charsio.pl @@ -323,8 +323,13 @@ get_n_chars(Stream, N, Cs) :- '$get_n_chars'(Stream, N, Cs) ). +get_n_chars_wrapper(Stream, N, Cs) :- + '$get_n_chars'(Stream, N, Cs). + get_to_eof(Stream, Cs) :- - '$get_n_chars'(Stream, 512, Cs0), + catch(get_n_chars_wrapper(Stream, 512, Cs0), + error(syntax_error(unexpected_end_of_file), _), + Cs0 = []), ( Cs0 == [] -> Cs = [] ; partial_string(Cs0, Cs, Rest), get_to_eof(Stream, Rest) From dcd7360b174fa79487594257e5e965d707cceb4c Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 20 Jul 2023 09:39:06 -0600 Subject: [PATCH 332/361] add EMIT_NEWLINE to add newlines to readline input only after query terms begin to be read (#1074, #1897) --- src/machine/system_calls.rs | 1 + src/read.rs | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 0d4b5e92..f9969669 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5797,6 +5797,7 @@ impl Machine { pub(crate) fn read_query_term(&mut self) -> CallResult { self.user_input.reset(); + set_emit_newline(true); set_prompt(true); // let result = self.machine_st.read_term(self.user_input, &mut self.indices); let result = self.machine_st.read_term_from_user_input(self.user_input, &mut self.indices); diff --git a/src/read.rs b/src/read.rs index 3afb4bf9..c21c3016 100644 --- a/src/read.rs +++ b/src/read.rs @@ -80,9 +80,16 @@ impl MachineState { } static mut PROMPT: bool = false; +static mut EMIT_NEWLINE: bool = false; const HISTORY_FILE: &'static str = ".scryer_history"; +pub(crate) fn set_emit_newline(value: bool) { + unsafe { + EMIT_NEWLINE = value; + } +} + pub(crate) fn set_prompt(value: bool) { unsafe { PROMPT = value; @@ -161,10 +168,12 @@ impl ReadlineStream { self.save_history(); PROMPT = false; } - } - if self.pending_input.get_ref().get_ref().chars().last() != Some('\n') { - *self.pending_input.get_mut().get_mut() += "\n"; + if EMIT_NEWLINE { + if self.pending_input.get_ref().get_ref().chars().last() != Some('\n') { + *self.pending_input.get_mut().get_mut() += "\n"; + } + } } Ok(self.pending_input.get_ref().get_ref().len()) From 1697cd5c7f915024cc8036230d62cffffb266c78 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 20 Jul 2023 12:33:23 -0600 Subject: [PATCH 333/361] add log10, hyperbolic tan and inverse hyperbolic tan functions (#1898) --- build/instructions_template.rs | 35 +++++++++++++++++ src/arithmetic.rs | 7 ++++ src/machine/arithmetic_ops.rs | 68 ++++++++++++++++++++++++++++++++++ src/machine/dispatch.rs | 63 +++++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+) diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 9b965750..8ec818e6 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -747,6 +747,20 @@ enum InstructionTemplate { Neg(ArithmeticTerm, usize), #[strum_discriminants(strum(props(Arity = "1", Name = "plus")))] Plus(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "acosh")))] + ACosh(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "asinh")))] + ASinh(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "atanh")))] + ATanh(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "cosh")))] + Cosh(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "sinh")))] + Sinh(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "tanh")))] + Tanh(ArithmeticTerm, usize), + #[strum_discriminants(strum(props(Arity = "1", Name = "log10")))] + Log10(ArithmeticTerm, usize), #[strum_discriminants(strum(props(Arity = "1", Name = "bitwise_complement")))] BitwiseComplement(ArithmeticTerm, usize), // control instructions @@ -1407,9 +1421,30 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ATan(ref at, t) => { arith_instr_unary_functor(h, atom!("atan"), arena, at, t) } + &Instruction::ACosh(ref at, t) => { + arith_instr_unary_functor(h, atom!("acosh"), arena, at, t) + } + &Instruction::ASinh(ref at, t) => { + arith_instr_unary_functor(h, atom!("asinh"), arena, at, t) + } + &Instruction::ATanh(ref at, t) => { + arith_instr_unary_functor(h, atom!("atanh"), arena, at, t) + } + &Instruction::Cosh(ref at, t) => { + arith_instr_unary_functor(h, atom!("cosh"), arena, at, t) + } + &Instruction::Sinh(ref at, t) => { + arith_instr_unary_functor(h, atom!("sinh"), arena, at, t) + } + &Instruction::Tanh(ref at, t) => { + arith_instr_unary_functor(h, atom!("tanh"), arena, at, t) + } &Instruction::Sqrt(ref at, t) => { arith_instr_unary_functor(h, atom!("sqrt"), arena, at, t) } + &Instruction::Log10(ref at, t) => { + arith_instr_unary_functor(h, atom!("log10"), arena, at, t) + } &Instruction::Abs(ref at, t) => { arith_instr_unary_functor(h, atom!("abs"), arena, at, t) } diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 7f15a7f1..bc58da50 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -195,6 +195,13 @@ impl<'a> ArithmeticEvaluator<'a> { atom!("sin") => Ok(Instruction::Sin(a1, t)), atom!("tan") => Ok(Instruction::Tan(a1, t)), atom!("log") => Ok(Instruction::Log(a1, t)), + atom!("asinh") => Ok(Instruction::ASinh(a1, t)), + atom!("acosh") => Ok(Instruction::ACosh(a1, t)), + atom!("atanh") => Ok(Instruction::ATanh(a1, t)), + atom!("sinh") => Ok(Instruction::Sinh(a1, t)), + atom!("cosh") => Ok(Instruction::Cosh(a1, t)), + atom!("tanh") => Ok(Instruction::Tanh(a1, t)), + atom!("log10") => Ok(Instruction::Log10(a1, t)), atom!("exp") => Ok(Instruction::Exp(a1, t)), atom!("sqrt") => Ok(Instruction::Sqrt(a1, t)), atom!("acos") => Ok(Instruction::ACos(a1, t)), diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index 8eae327d..6af111b2 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -1006,6 +1006,53 @@ pub(crate) fn atan(n1: Number) -> Result { unary_float_fn_template(n1, |f| f.atan()) } +#[inline] +pub(crate) fn asinh(n1: Number) -> Result { + unary_float_fn_template(n1, |f| f.asinh()) +} + +#[inline] +pub(crate) fn acosh(n1: Number) -> Result { + unary_float_fn_template(n1, |f| f.acosh()) +} + +#[inline] +pub(crate) fn atanh(n1: Number) -> Result { + 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 { + unary_float_fn_template(n1, |f| f.sinh()) +} + +#[inline] +pub(crate) fn cosh(n1: Number) -> Result { + unary_float_fn_template(n1, |f| f.cosh()) +} + +#[inline] +pub(crate) fn tanh(n1: Number) -> Result { + unary_float_fn_template(n1, |f| f.tanh()) +} + +#[inline] +pub(crate) fn log10(n1: Number) -> Result { + unary_float_fn_template(n1, |f| f.log(10f64)) +} + #[inline] pub(crate) fn sqrt(n1: Number) -> Result { if n1.is_negative() { @@ -1255,6 +1302,27 @@ 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!("sqrt") => self.interms.push(Number::Float(OrderedFloat( drop_iter_on_err!(self, iter, sqrt(a1)) ))), diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index f853dc7f..8f7e3e91 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -930,6 +930,69 @@ impl Machine { self.machine_st.p += 1; } + &Instruction::ACosh(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, acosh(n1)) + )); + + self.machine_st.p += 1; + } + &Instruction::ASinh(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, asinh(n1)) + )); + + self.machine_st.p += 1; + } + &Instruction::ATanh(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, atanh(n1)) + )); + + self.machine_st.p += 1; + } + &Instruction::Cosh(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, cosh(n1)) + )); + + self.machine_st.p += 1; + } + &Instruction::Sinh(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, sinh(n1)) + )); + + self.machine_st.p += 1; + } + &Instruction::Tanh(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, tanh(n1)) + )); + + self.machine_st.p += 1; + } + &Instruction::Log10(ref a1, t) => { + let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); + + self.machine_st.interms[t - 1] = Number::Float(OrderedFloat( + try_or_throw_gen!(&mut self.machine_st, log10(n1)) + )); + + self.machine_st.p += 1; + } &Instruction::Float(ref a1, t) => { let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1)); From e529e7ba2173aca9762909684b46a74b79c37651 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 20 Jul 2023 14:27:10 -0600 Subject: [PATCH 334/361] improve goal expansion and (',') interpretation error handling --- src/lib/builtins.pl | 3 +++ src/loader.pl | 25 +++++++++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/lib/builtins.pl b/src/lib/builtins.pl index 983503cb..81f63ddd 100644 --- a/src/lib/builtins.pl +++ b/src/lib/builtins.pl @@ -334,6 +334,9 @@ dispatch_prep(Gs, B, [Cont|Conts]) :- ; Gs0 == ! -> Cont = '$call'(builtins:set_cp(B)), Conts = [] + ; nonvar(Gs0), + \+ callable(Gs0) -> + throw(dispatch_prep_error) ; Cont = Gs, Conts = [] ) diff --git a/src/loader.pl b/src/loader.pl index 1eafedb0..441852ee 100644 --- a/src/loader.pl +++ b/src/loader.pl @@ -274,6 +274,13 @@ module_expanded_head_variables(Head, HeadVars) :- ). +print_goal_expansion_warning(Pred) :- + nl, + write('Warning: clause body goal expansion failed because '), + writeq(Pred), + write(' is not callable.'), + nl. + expand_term_goals(Terms0, Terms) :- ( Terms0 = (Head1 :- Body0) -> ( var(Head1) -> @@ -282,13 +289,21 @@ expand_term_goals(Terms0, Terms) :- ( atom(Module) -> prolog_load_context(module, Target), module_expanded_head_variables(Head2, HeadVars), - expand_goal(Body0, Target, Body1, HeadVars), + catch(expand_goal(Body0, Target, Body1, HeadVars), + error(type_error(callable, Pred), _), + ( loader:print_goal_expansion_warning(Pred), + builtins:(Body1 = Body0) + )), Terms = (Module:Head2 :- Body1) ; type_error(atom, Module, load/1) ) ; module_expanded_head_variables(Head1, HeadVars), prolog_load_context(module, Target), - expand_goal(Body0, Target, Body1, HeadVars), + catch(expand_goal(Body0, Target, Body1, HeadVars), + error(type_error(callable, Pred), _), + ( loader:print_goal_expansion_warning(Pred), + builtins:(Body1 = Body0) + )), Terms = (Head1 :- Body1) ) ; Terms = Terms0 @@ -688,7 +703,10 @@ expand_subgoal(UnexpandedGoals, MS, M, ExpandedGoals, HeadVars) :- expand_module_name(ESG0, MS, M, ESG) :- ( var(ESG0) -> - ESG = M:ESG0 + ( M == user -> + ESG = ESG0 + ; ESG = M:ESG0 + ) ; ESG0 = _:_ -> ESG = ESG0 ; functor(ESG0, F, A0), @@ -753,7 +771,6 @@ expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars) :- ). - :- non_counted_backtracking expand_goal/3. expand_goal(UnexpandedGoals, Module, ExpandedGoals) :- From b4e7000eb20212b4065cfb47da73a28949cb1c78 Mon Sep 17 00:00:00 2001 From: Fayeed Pawaskar Date: Fri, 21 Jul 2023 14:21:32 +0530 Subject: [PATCH 335/361] Fixed missing functionality in dashu with their methods still has some issue with move --- Cargo.lock | 241 ++++++++++++++++++++++++++---- Cargo.toml | 10 +- src/arithmetic.rs | 33 ++-- src/heap_print.rs | 10 +- src/machine/arithmetic_ops.rs | 43 +++--- src/machine/disjuncts.rs | 3 +- src/machine/dispatch.rs | 4 +- src/machine/machine_state_impl.rs | 4 +- src/machine/mod.rs | 2 +- src/machine/system_calls.rs | 54 +++++-- src/parser/mod.rs | 1 + src/parser/parser.rs | 20 ++- 12 files changed, 328 insertions(+), 97 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 46731842..a5faf337 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,6 +55,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + [[package]] name = "bit-set" version = "0.5.3" @@ -316,8 +322,6 @@ dependencies = [ [[package]] name = "dashu" version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51a1b5a00793e3ac2239993ef582603764bcb333a4d04c2a0944639a7e916c85" dependencies = [ "dashu-base", "dashu-float", @@ -329,41 +333,33 @@ dependencies = [ [[package]] name = "dashu-base" version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2585452b8ecf7c874045dba02a7914b7e5b2e3cdd5e152573413aa290197aa" [[package]] name = "dashu-float" version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a168f338914fab603c31a371207c8b3245ab5ff5e9a0f4fd64a9b5f8a972d1f" dependencies = [ "dashu-base", "dashu-int", + "num-order", "num-traits", - "rand", "static_assertions", ] [[package]] name = "dashu-int" version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a1009a3ce0c4c64e977c5e7dd8c475278750e145cbb8956d1e28832f557975" dependencies = [ "cfg-if", "dashu-base", + "num-modular 0.6.0", "num-order", "num-traits", - "rand", "static_assertions", ] [[package]] name = "dashu-macros" version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6fc2425b6724a7d5bfc8e57044e231803b5fc3a7283d6efbff34bfab6ebe014" dependencies = [ "dashu-base", "dashu-float", @@ -376,14 +372,12 @@ dependencies = [ [[package]] name = "dashu-ratio" version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b57c839e72af6be14e5c55630ffc9429c9970aed48f7db449b2399467336b4" dependencies = [ "dashu-base", "dashu-float", "dashu-int", + "num-order", "num-traits", - "rand", ] [[package]] @@ -471,6 +465,15 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + [[package]] name = "endian-type" version = "0.1.2" @@ -549,6 +552,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + [[package]] name = "funty" version = "2.0.0" @@ -718,8 +730,6 @@ dependencies = [ [[package]] name = "gmp-mpfr-sys" version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13eabc29d16e4a621b495e3919c71ebb7caaed24380955671e7d417370fea95d" dependencies = [ "libc", "windows-sys 0.42.0", @@ -821,6 +831,29 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-body" +version = "1.0.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951dfc2e32ac02d67c90c0d65bd27009a635dc9b381a2cc7d284ab01e3a0150d" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08ef12f041acdd397010e5fb6433270c147d3b8b2d0a840cd7fff8e531dca5c8" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body 1.0.0-rc.2", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.8.0" @@ -845,7 +878,7 @@ dependencies = [ "futures-util", "h2", "http", - "http-body", + "http-body 0.4.5", "httparse", "httpdate", "itoa", @@ -893,6 +926,16 @@ dependencies = [ "cc", ] +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -923,6 +966,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ipnet" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + [[package]] name = "itertools" version = "0.10.5" @@ -994,8 +1043,6 @@ checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" [[package]] name = "libffi" version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2" dependencies = [ "libc", "libffi-sys", @@ -1004,8 +1051,6 @@ dependencies = [ [[package]] name = "libffi-sys" version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36115160c57e8529781b4183c2bb51fdc1f6d6d1ed345591d84be7703befb3c" dependencies = [ "cc", ] @@ -1107,6 +1152,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "mio" version = "0.7.14" @@ -1246,13 +1297,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-modular" +version = "0.6.0" + [[package]] name = "num-order" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e81e321057a0370997b13e6638bba6bd7f6f426e1f8e9a2562490a28eb23e1bc" dependencies = [ - "num-modular", + "num-modular 0.5.1", "num-traits", ] @@ -1388,6 +1443,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + [[package]] name = "phf" version = "0.9.0" @@ -1638,6 +1699,43 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +[[package]] +name = "reqwest" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +dependencies = [ + "base64 0.21.2", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body 0.4.5", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "ring" version = "0.16.20" @@ -1676,8 +1774,6 @@ dependencies = [ [[package]] name = "rug" version = "1.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555e8b44763d034526db899c88cd56ccc4486cd38b444c8aa0e79d4e70ae5a34" dependencies = [ "az", "gmp-mpfr-sys", @@ -1763,10 +1859,11 @@ name = "scryer-prolog" version = "0.9.1" dependencies = [ "assert_cmd", - "base64", + "base64 0.12.3", "bit-set", "bitvec", "blake2 0.8.1", + "bytes", "chrono", "cpu-time", "crossterm", @@ -1780,6 +1877,7 @@ dependencies = [ "fxhash", "git-version", "hostname", + "http-body-util", "hyper", "hyper-tls", "indexmap", @@ -1795,7 +1893,9 @@ dependencies = [ "predicates-core", "proc-macro2", "quote", + "rand", "ref_thread_local", + "reqwest", "ring", "ripemd160", "roxmltree", @@ -1853,9 +1953,32 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.164" +version = "1.0.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "3b88756493a5bd5e5395d53baa70b194b05764ab85b59e43e4b8f4e1192fa9b1" + +[[package]] +name = "serde_json" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] [[package]] name = "serial_test" @@ -2147,6 +2270,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "to-syn-value" version = "0.1.0" @@ -2260,12 +2398,27 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + [[package]] name = "unicode-ident" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-segmentation" version = "1.10.1" @@ -2284,6 +2437,17 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "utf-8" version = "0.7.6" @@ -2373,6 +2537,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.87" @@ -2575,6 +2751,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index ac9c98b6..8a50c342 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ ctrlc = "3.2.2" ordered-float = "2.6.0" phf = { version = "0.9", features = ["macros"] } ref_thread_local = "0.0.0" -rug = { version = "1.15.0", optional = true } +rug = { path = "../rug", optional = true } rustyline = "9.0.0" ring = "0.16.13" ripemd160 = "0.8.0" @@ -65,10 +65,14 @@ hyper = { version = "0.14", features = ["full"] } hyper-tls = "0.5.0" tokio = { version = "1.24.2", features = ["full"] } futures = "0.3" -libffi = "3.1.0" libloading = "0.7" derive_deref = "1.1.1" -dashu = "0.3.0" +http-body-util = "0.1.0-rc.2" +bytes = "1" +reqwest = { version = "0.11.18", features = ["blocking"] } +dashu = { path = "../dashu" } +libffi = { path = "../libffi-rs/libffi-rs" } +rand = "0.8.5" [dev-dependencies] assert_cmd = "1.0.3" diff --git a/src/arithmetic.rs b/src/arithmetic.rs index 1a3bdf48..cfdc5b08 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -9,12 +9,11 @@ use crate::targets::QueryInstruction; use crate::types::*; use crate::parser::ast::*; -use crate::parser::rug::ops::PowAssign; -use crate::parser::rug::{Assign}; use crate::parser::dashu::{Integer, Rational}; use crate::machine::machine_errors::*; +use dashu::base::Abs; use ordered_float::*; use std::cell::Cell; @@ -378,13 +377,11 @@ pub(crate) fn rnd_i<'a>(n: &'a Number, arena: &mut Arena) -> Number { if I64_MIN_TO_F <= f && f <= I64_MAX_TO_F { fixnum!(Number, f.into_inner() as i64, arena) } else { - Number::Integer(arena_alloc!(Integer::from(f.into_inner()).unwrap(), arena)) + Number::Integer(arena_alloc!(Integer::from(f.0 as i64), arena)) } } &Number::Rational(ref r) => { - let r_ref = r.fract_floor_ref(); - let (mut fract, mut floor) = (Rational::from(0), Integer::from(0)); - (&mut fract, &mut floor).assign(r_ref); + let (mut fract, mut floor) = (r.fract(), r.floor()); if let Some(floor) = floor.to_i64() { fixnum!(Number, floor, arena) @@ -439,12 +436,12 @@ pub(crate) fn float_fn_to_f(n: i64) -> Result { #[inline] pub(crate) fn float_i_to_f(n: &Integer) -> Result { - classify_float(n.to_f64()) + classify_float(n.to_f64().value()) } #[inline] pub(crate) fn float_r_to_f(r: &Rational) -> Result { - classify_float(r.to_f64()) + classify_float(r.to_f64().value()) } #[inline] @@ -543,8 +540,8 @@ impl PartialEq for Number { (&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).eq(&n2), (&Number::Float(n1), &Number::Fixnum(n2)) => n1.eq(&OrderedFloat(n2.get_num() as f64)), (&Number::Integer(ref n1), &Number::Integer(ref n2)) => n1.eq(n2), - (&Number::Integer(ref n1), Number::Float(n2)) => OrderedFloat(n1.to_f64()).eq(n2), - (&Number::Float(n1), &Number::Integer(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64())), + (&Number::Integer(ref n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).eq(n2), + (&Number::Float(n1), &Number::Integer(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64().value())), (&Number::Integer(ref n1), &Number::Rational(ref n2)) => { #[cfg(feature = "num")] { @@ -565,8 +562,8 @@ impl PartialEq for Number { &**n1 == &**n2 } } - (&Number::Rational(ref n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64()).eq(&n2), - (&Number::Float(n1), &Number::Rational(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64())), + (&Number::Rational(ref n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).eq(&n2), + (&Number::Float(n1), &Number::Rational(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64().value())), (&Number::Float(f1), &Number::Float(f2)) => f1.eq(&f2), (&Number::Rational(ref r1), &Number::Rational(ref r2)) => r1.eq(&r2), } @@ -634,8 +631,8 @@ impl Ord for Number { (&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).cmp(&n2), (&Number::Float(n1), &Number::Fixnum(n2)) => n1.cmp(&OrderedFloat(n2.get_num() as f64)), (&Number::Integer(n1), &Number::Integer(n2)) => (*n1).cmp(&*n2), - (&Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64()).cmp(n2), - (&Number::Float(n1), &Number::Integer(ref n2)) => n1.cmp(&OrderedFloat(n2.to_f64())), + (&Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).cmp(n2), + (&Number::Float(n1), &Number::Integer(ref n2)) => n1.cmp(&OrderedFloat(n2.to_f64().value())), (&Number::Integer(n1), &Number::Rational(n2)) => { #[cfg(feature = "num")] { @@ -656,8 +653,8 @@ impl Ord for Number { (&*n1).partial_cmp(&*n2).unwrap_or(Ordering::Less) } } - (&Number::Rational(n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64()).cmp(&n2), - (&Number::Float(n1), &Number::Rational(n2)) => n1.cmp(&OrderedFloat(n2.to_f64())), + (&Number::Rational(n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).cmp(&n2), + (&Number::Float(n1), &Number::Rational(n2)) => n1.cmp(&OrderedFloat(n2.to_f64().value())), (&Number::Float(f1), &Number::Float(f2)) => f1.cmp(&f2), (&Number::Rational(r1), &Number::Rational(r2)) => (*r1).cmp(&*r2), } @@ -698,7 +695,7 @@ impl TryFrom for Number { // Computes n ^ power. Ignores the sign of power. pub(crate) fn binary_pow(mut n: Integer, power: &Integer) -> Integer { - let mut power = Integer::from(power.abs_ref()); + let mut power = Integer::from(power.abs()); if power == 0 { return Integer::from(1); @@ -711,7 +708,7 @@ pub(crate) fn binary_pow(mut n: Integer, power: &Integer) -> Integer { oddand *= &n; } - n.pow_assign(2); + n = n.pow(2); power >>= 1; } diff --git a/src/heap_print.rs b/src/heap_print.rs index cf2c293c..783b7f01 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -18,6 +18,8 @@ use crate::machine::stack::*; use crate::machine::streams::*; use crate::types::*; +use dashu::base::DivRem; +use dashu::base::DivRemEuclid; use ordered_float::OrderedFloat; use indexmap::IndexMap; @@ -510,8 +512,8 @@ pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ]; - let i = n.mod_u(26) as usize; - let j = n.div_rem_floor(Integer::from(26)); + let i = n.div_rem_euclid(Integer::from(26)).1.to_f32().value() as usize; + let j = n.div_rem(Integer::from(26)); let j = <(Integer, Integer)>::from(j).0; if j == Integer::from(0) { @@ -969,14 +971,14 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } }, NumberFocus::Denominator(r) => { - let output_str = format!("{}", r.denom()); + let output_str = format!("{}", r.denominator()); push_space_if_amb!(self, &output_str, { append_str!(self, &output_str); }); } NumberFocus::Numerator(r) => { - let output_str = format!("{}", r.numer()); + let output_str = format!("{}", r.numerator()); push_space_if_amb!(self, &output_str, { append_str!(self, &output_str); diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index 0c948bf6..153ca0c3 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -1,3 +1,6 @@ +use dashu::base::Abs; +use dashu::base::Gcd; +use dashu::integer::IBig; use divrem::*; use crate::arena::*; @@ -159,7 +162,7 @@ pub(crate) fn add(lhs: Number, rhs: Number, arena: &mut Arena) -> Result { - 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 { - 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 { - Ok(Number::arena_from(Rational::from(&*r1) + &*r2, arena)) + Ok(Number::arena_from(&*r1 + &*r2, arena)) } } } @@ -191,9 +194,9 @@ 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) => 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) => Number::arena_from(-Rational::from(*r), arena), } } @@ -203,12 +206,13 @@ 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) => Number::arena_from(Integer::from(n.abs()), arena), Number::Float(f) => Number::Float(f.abs()), - Number::Rational(r) => Number::arena_from(Rational::from(r.abs_ref()), arena), + Number::Rational(r) => Number::arena_from(Rational::from(r.abs()), arena), } } @@ -247,7 +251,7 @@ pub(crate) fn mul(lhs: Number, rhs: Number, arena: &mut Arena) -> Result { - Ok(Number::arena_from(Integer::from(&*n1) * &*n2, arena)) // mul_i + Ok(Number::arena_from(Integer::from(*n1) * &*n2, arena)) // mul_i } (Number::Integer(n1), Number::Float(OrderedFloat(n2))) | (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => { @@ -255,7 +259,7 @@ pub(crate) fn mul(lhs: Number, rhs: Number, arena: &mut Arena) -> Result { - Ok(Number::arena_from(Rational::from(&*n1) * &*n2, arena)) + Ok(Number::arena_from(Rational::from(*n1) * &*n2, arena)) } (Number::Rational(n1), Number::Float(OrderedFloat(n2))) | (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => { @@ -265,7 +269,7 @@ pub(crate) fn mul(lhs: Number, rhs: Number, arena: &mut Arena) -> Result { - Ok(Number::arena_from(Rational::from(&*r1) * &*r2, arena)) + Ok(Number::arena_from(Rational::from(*r1) * &*r2, arena)) } } } @@ -521,7 +525,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 +534,7 @@ 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) => Ok(arena_alloc!(Rational::from(*n), arena)), } } @@ -590,7 +594,7 @@ pub(crate) fn idiv(n1: Number, n2: Number, arena: &mut Arena) -> Result::from(n1.div_rem_ref(&*n2)).0, + <(Integer, Integer)>::from(n1.div_rem_floor_ref(&*n2)).0, arena, )) } @@ -696,7 +700,7 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result Ok(Number::arena_from(n1 << n2, arena)), + Some(n2) => Ok(Number::arena_from(n1.to_u64().unwrap() << n2, arena)), _ => { Ok(Number::arena_from(n1 << usize::max_value(), arena)) } @@ -709,7 +713,7 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result match n2.to_u32() { - Some(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), 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)) } @@ -926,18 +930,19 @@ pub(crate) fn gcd(n1: Number, n2: Number, arena: &mut Arena) -> Result { let n1 = Integer::from(n1.get_num()); - Ok(Number::arena_from(Integer::from(n2.gcd_ref(&n1)), arena)) + Ok(Number::arena_from(Integer::from(n2.gcd(&n1)), arena)) } (Number::Integer(n1), Number::Integer(n2)) => { - Ok(Number::arena_from(Integer::from(n1.gcd_ref(&n2)), arena)) + Ok(Number::arena_from(Integer::from(n1.gcd(&Integer::from(n2.to_isize().unwrap()))) as IBig, arena)) } (Number::Float(f), _) | (_, Number::Float(f)) => { let n = Number::Float(f); diff --git a/src/machine/disjuncts.rs b/src/machine/disjuncts.rs index 33d24a06..17eaaa47 100644 --- a/src/machine/disjuncts.rs +++ b/src/machine/disjuncts.rs @@ -6,9 +6,10 @@ use crate::machine::loader::*; use crate::machine::machine_errors::CompilationError; use crate::machine::preprocessor::*; use crate::parser::ast::*; -use crate::parser::dashu::{Rational, Integer}; +use crate::parser::dashu::Rational; use crate::variable_records::*; +use dashu::Integer; use indexmap::{IndexMap, IndexSet}; use std::cell::Cell; diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 8029bb70..b1f71422 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -2540,7 +2540,7 @@ impl Machine { self.machine_st.p += 1; } Ok(Number::Rational(n)) => { - if n.denom() == &1 { + if n.denominator().is_one() { self.machine_st.p += 1; } else { self.machine_st.backtrack(); @@ -2559,7 +2559,7 @@ impl Machine { self.machine_st.p = self.machine_st.cp; } Ok(Number::Rational(n)) => { - if n.denom() == &1 { + if n.denominator().is_one() { self.machine_st.p = self.machine_st.cp; } else { self.machine_st.backtrack(); diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index f0f27fcd..30f2c57e 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -1387,7 +1387,7 @@ impl MachineState { Ok(Number::Float(_)) => { return type_error(arity); } - Ok(Number::Rational(n)) if n.denom() != &1 => { + Ok(Number::Rational(n)) if !n.denominator().is_one() => { return type_error(arity); } Ok(n) if n > MAX_ARITY => { @@ -1400,7 +1400,7 @@ impl MachineState { let err = self.domain_error(DomainErrorType::NotLessThanZero, n); return Err(self.error_form(err, stub_gen())); } - Ok(Number::Rational(n)) => n.numer().to_i64().unwrap(), + Ok(Number::Rational(n)) => n.numerator().to_i64().unwrap(), Ok(Number::Fixnum(n)) => n.get_num(), Ok(Number::Integer(n)) => n.to_i64().unwrap(), Err(_) => { diff --git a/src/machine/mod.rs b/src/machine/mod.rs index a212b9ea..fb9cdf0e 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -905,4 +905,4 @@ impl Machine { } } } -} +} \ No newline at end of file diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 84f86cec..8f2523a7 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -1,6 +1,7 @@ use crate::parser::ast::*; use crate::parser::parser::*; +use dashu::integer::UBig; use lazy_static::lazy_static; use crate::arena::*; @@ -28,6 +29,8 @@ use crate::parser::dashu::Integer; use crate::parser::rug::rand::RandState; use crate::read::*; use crate::types::*; +use rand::{Rng, SeedableRng}; +use rand::rngs::StdRng; use ordered_float::OrderedFloat; @@ -36,10 +39,12 @@ use indexmap::IndexSet; use ref_thread_local::{RefThreadLocal, ref_thread_local}; +use std::borrow::BorrowMut; use std::cell::Cell; use std::cmp::Ordering; use std::collections::BTreeSet; -use std::convert::{TryFrom, Infallible}; +use std::convert::Infallible; +use std::convert::TryFrom; use std::env; use std::ffi::CString; use std::fs; @@ -2727,7 +2732,7 @@ impl Machine { // n has already been confirmed as an integer, and // internally, Rational is assumed reduced, so its denominator // must be 1. - r.numer().to_string() + r.numerator().to_string() } _ => { unreachable!() @@ -2756,7 +2761,7 @@ impl Machine { // n has already been confirmed as an integer, and // internally, Rational is assumed reduced, so its // denominator must be 1. - r.numer().to_string() + r.numerator().to_string() } _ => { unreachable!() @@ -4103,10 +4108,25 @@ impl Machine { } #[inline(always)] - pub(crate) fn maybe(&mut self) { + pub(crate) fn maybe(&mut self) { + fn generate_random_bits(num_bits: usize) -> u64 { + let mut rng = rand::thread_rng(); + let rand = rng.borrow_mut(); + let mut random_bits: u64 = 0; + + for _ in 0..num_bits { + random_bits <<= 1; + + if rand.gen_bool(0.5) { + random_bits |= 1; + } + } + + random_bits + } + let result = { - let mut rand = RANDOM_STATE.borrow_mut(); - rand.bits(1) == 0 + generate_random_bits(1) == 0 }; self.machine_st.fail = result; @@ -5189,7 +5209,7 @@ impl Machine { // n has already been confirmed as an integer, and // internally, Rational is assumed reduced, so its // denominator must be 1. - r.numer().to_i32().unwrap() + r.numerator().to_i32().unwrap() } _ => { unreachable!() @@ -5804,12 +5824,20 @@ impl Machine { #[inline(always)] pub(crate) fn set_seed(&mut self) { let seed = self.deref_register(1); - let mut rand = RANDOM_STATE.borrow_mut(); + match Number::try_from(seed) { - Ok(Number::Fixnum(n)) => rand.seed(&Integer::from(n)), - Ok(Number::Integer(n)) => rand.seed(&*n), - Ok(Number::Rational(n)) if n.denom() == &1 => rand.seed(n.numer()), + Ok(Number::Fixnum(n)) => { + let _: StdRng = SeedableRng::seed_from_u64(Integer::from(n).to_u64().unwrap()); + }, + Ok(Number::Integer(n)) => { + let _: StdRng = SeedableRng::seed_from_u64(n.to_u64().unwrap()); + }, + Ok(Number::Rational(n)) => { + if n.denominator() == &UBig::from(1 as u32) { + let _: StdRng = SeedableRng::seed_from_u64(n.numerator().to_u64().unwrap()); + } + }, _ => { self.machine_st.fail = true; } @@ -5823,7 +5851,7 @@ impl Machine { let time = match Number::try_from(time) { Ok(Number::Float(n)) => n.into_inner(), Ok(Number::Fixnum(n)) => n.get_num() as f64, - Ok(Number::Integer(n)) => n.to_f64(), + Ok(Number::Integer(n)) => n.to_f64().value(), _ => { unreachable!() } @@ -7469,7 +7497,7 @@ impl Machine { Number::Fixnum(Fixnum::build_with(n.get_num().count_ones() as i64)) } Ok(Number::Integer(n)) => { - Number::arena_from(n.count_ones().unwrap(), &mut self.machine_st.arena) + Number::arena_from(n.count_ones(), &mut self.machine_st.arena) } _ => { unreachable!() diff --git a/src/parser/mod.rs b/src/parser/mod.rs index dccaa49b..e7d0cec0 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -3,6 +3,7 @@ pub use num_rug_adapter as rug; #[cfg(feature = "rug")] pub use rug; + pub use dashu; // #[macro_use] diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 5bb600da..20f53e5b 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -1,13 +1,16 @@ +use dashu::Integer; +use dashu::Rational; + use crate::arena::*; use crate::atom_table::*; use crate::parser::ast::*; use crate::parser::char_reader::*; use crate::parser::lexer::*; -use crate::parser::rug::ops::NegAssign; use std::cell::Cell; use std::mem; +use std::ops::Neg; #[derive(Debug, Clone, Copy, PartialEq)] enum TokenType { @@ -955,9 +958,14 @@ impl<'a, R: CharRead> Parser<'a, R> { } fn shift_token(&mut self, token: Token, op_dir: &CompositeOpDir) -> Result<(), ParserError> { - fn negate_rc(mut t: TypedArenaPtr) -> TypedArenaPtr { - (&mut t).neg_assign(); - t + fn negate_int_rc(mut t: TypedArenaPtr) -> TypedArenaPtr { + let mut data = t.neg(); + TypedArenaPtr::new(&mut data) + } + + fn negate_rat_rc(mut t: TypedArenaPtr) -> TypedArenaPtr { + let mut data = t.neg(); + TypedArenaPtr::new(&mut data) } match token { @@ -965,10 +973,10 @@ impl<'a, R: CharRead> Parser<'a, R> { self.negate_number(n, |n| -n, |n, _| Literal::Fixnum(n)) } Token::Literal(Literal::Integer(n)) => { - self.negate_number(n, negate_rc, |n, _| Literal::Integer(n)) + self.negate_number(n, negate_int_rc, |n, _| Literal::Integer(n)) } Token::Literal(Literal::Rational(n)) => { - self.negate_number(n, negate_rc, |r, _| Literal::Rational(r)) + self.negate_number(n, negate_rat_rc, |r, _| Literal::Rational(r)) } Token::Literal(Literal::Float(n)) => self.negate_number( **n.as_ptr(), From b1963864d2d66f9245e90d6efd2af4d7a0ab3e50 Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Fri, 21 Jul 2023 17:07:40 +0200 Subject: [PATCH 336/361] Explicitly dereference pointer to avoid calling neg() on reference --- src/parser/parser.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 20f53e5b..5d637e02 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -959,12 +959,14 @@ impl<'a, R: CharRead> Parser<'a, R> { fn shift_token(&mut self, token: Token, op_dir: &CompositeOpDir) -> Result<(), ParserError> { fn negate_int_rc(mut t: TypedArenaPtr) -> TypedArenaPtr { - let mut data = t.neg(); + let i: Integer = (*t).clone(); + let mut data = i.neg(); TypedArenaPtr::new(&mut data) } - fn negate_rat_rc(mut t: TypedArenaPtr) -> TypedArenaPtr { - let mut data = t.neg(); + fn negate_rat_rc(t: TypedArenaPtr) -> TypedArenaPtr { + let r: Rational = (*t).clone(); + let mut data = r.neg(); TypedArenaPtr::new(&mut data) } From f310ff24a5dd9189ba5f1e685499d41cf4e5297d Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 21 Jul 2023 11:35:57 -0600 Subject: [PATCH 337/361] remove now unnecessary cut in lists.pl --- src/lib/lists.pl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/lists.pl b/src/lib/lists.pl index 8a94ed6a..f1b7cdd5 100644 --- a/src/lib/lists.pl +++ b/src/lib/lists.pl @@ -396,7 +396,6 @@ nth0(N, Es0, E) :- skipn(N0, Es0,Es) :- N0>0, - !, % should not be necessary #1028 N1 is N0-1, Es0 = [_|Es1], skipn(N1, Es1,Es). From cb79552dd09a4b5f4a69a91e0b224eb566ce8a36 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 21 Jul 2023 15:05:27 -0600 Subject: [PATCH 338/361] correct max_depth option (#1876) --- src/heap_print.rs | 75 +++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 110f8942..f1ac11ce 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -589,7 +589,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { fn enqueue_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) { if is_postfix!(spec.get_spec()) { - if self.check_max_depth(&mut max_depth) { + if self.max_depth_exhausted(max_depth) { + self.iter.pop_stack(); + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); + return; + } else if self.check_max_depth(&mut max_depth) { self.iter.pop_stack(); self.state_stack.push(TokenOrRedirect::Op(name, spec)); @@ -606,7 +610,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { right_directed_op, )); } else if is_prefix!(spec.get_spec()) { - if self.check_max_depth(&mut max_depth) { + if self.max_depth_exhausted(max_depth) { + self.iter.pop_stack(); + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); + return; + } else if self.check_max_depth(&mut max_depth) { self.iter.pop_stack(); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); @@ -618,13 +626,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let op = DirectedOp::Left(name, spec); self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, op)); - - /* - if fetch_op_spec(name, 2, self.op_dir).is_some() { - self.state_stack.push(TokenOrRedirect::Space); - } - */ - self.state_stack.push(TokenOrRedirect::Atom(name)); } else { match name.as_str() { @@ -635,31 +636,30 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { _ => {} }; - let ellipsis_atom = atom!("..."); + let left_directed_op = DirectedOp::Left(name, spec); + let right_directed_op = DirectedOp::Right(name, spec); - if self.check_max_depth(&mut max_depth) { + if self.max_depth_exhausted(max_depth) { self.iter.pop_stack(); self.iter.pop_stack(); - self.state_stack.push(TokenOrRedirect::Atom(ellipsis_atom)); + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); + + return; + } else if self.check_max_depth(&mut max_depth) { + self.iter.pop_stack(); + self.iter.pop_stack(); + + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); self.state_stack.push(TokenOrRedirect::Op(name, spec)); - self.state_stack.push(TokenOrRedirect::Atom(ellipsis_atom)); + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); return; } - let left_directed_op = DirectedOp::Left(name, spec); - let right_directed_op = DirectedOp::Right(name, spec); - - self.state_stack.push(TokenOrRedirect::CompositeRedirect( - max_depth, - left_directed_op, - )); + self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, left_directed_op)); self.state_stack.push(TokenOrRedirect::Op(name, spec)); - self.state_stack.push(TokenOrRedirect::CompositeRedirect( - max_depth, - right_directed_op, - )); + self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, right_directed_op)); } } @@ -1197,7 +1197,9 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { if !self.print_string_as_functor(focus.value() as usize, max_depth) { if end_cell == empty_list_as_cell!() { - append_str!(self, "[]"); + if !self.at_cdr("") { + append_str!(self, "[]"); + } } else { self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); self.iter.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap)); @@ -1269,6 +1271,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } } + #[inline] + fn max_depth_exhausted(&self, max_depth: usize) -> bool { + self.max_depth > 0 && max_depth == 0 + } + fn check_max_depth(&self, max_depth: &mut usize) -> bool { if self.max_depth > 0 && *max_depth == 0 { return true; @@ -1282,7 +1289,14 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } fn push_list(&mut self, mut max_depth: usize) { - if self.check_max_depth(&mut max_depth) { + if self.max_depth_exhausted(max_depth) { + self.iter.pop_stack(); + self.iter.pop_stack(); + + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); + + return; + } else if self.check_max_depth(&mut max_depth) { self.iter.pop_stack(); self.iter.pop_stack(); @@ -1301,7 +1315,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); self.state_stack.push(TokenOrRedirect::HeadTailSeparator); // bar - self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); + self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth+1)); self.state_stack.push(TokenOrRedirect::OpenList(cell)); } @@ -1504,6 +1518,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { None => return, }; + if !addr.is_compound(&self.iter.heap) && self.max_depth_exhausted(max_depth) { + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); + return; + } + read_heap_cell!(addr, (HeapCellValueTag::Atom, (name, arity)) => { print_struct(self, name, arity); @@ -1847,7 +1866,7 @@ mod tests { let output = printer.print(); - assert_eq!(output.result(), "[_1,_3,_5,_7,_9,...]"); + assert_eq!(output.result(), "[_1,_3,_5,_7,_9|...]"); } all_cells_unmarked(&wam.machine_st.heap); From 554e956ef50f34a6ee73124db8b7392c68e31325 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Sat, 22 Jul 2023 06:52:29 +0200 Subject: [PATCH 339/361] remove another !/0 which is now no longer necessary due to improved indexing --- src/lib/lists.pl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/lists.pl b/src/lib/lists.pl index f1b7cdd5..3d1cc6a2 100644 --- a/src/lib/lists.pl +++ b/src/lib/lists.pl @@ -426,7 +426,6 @@ nth1(N, Es0, E) :- skipn(N0, Es0,Es, Xs0,Xs) :- N0>0, - !, % should not be necessary #1028 N1 is N0-1, Es0 = [E|Es1], Xs0 = [E|Xs1], From 9a7862c3223149da81aff7eea5af61e6372279ad Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 21 Jul 2023 15:05:27 -0600 Subject: [PATCH 340/361] correct max_depth option (#1876) --- Cargo.lock | 44 +++++++++++++++----------------------------- Cargo.toml | 2 +- src/heap_print.rs | 4 +--- src/read.rs | 14 ++++++++------ 4 files changed, 25 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15599610..72725aac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -351,7 +351,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a011bbe2c35ce9c1f143b7af6f94f29a167beb4cd1d29e6740ce836f723120e" dependencies = [ - "nix 0.26.2", + "nix", "windows-sys 0.48.0", ] @@ -758,6 +758,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "hostname" version = "0.3.1" @@ -1149,15 +1158,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - [[package]] name = "mime" version = "0.3.17" @@ -1258,19 +1258,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "nix" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" -dependencies = [ - "bitflags 1.3.2", - "cc", - "cfg-if", - "libc", - "memoffset", -] - [[package]] name = "nix" version = "0.26.2" @@ -1813,22 +1800,21 @@ checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" [[package]] name = "rustyline" -version = "9.1.2" +version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7826789c0e25614b03e5a54a0717a86f9ff6e6e5247f92b369472869320039" +checksum = "994eca4bca05c87e86e15d90fc7a91d1be64b4482b38cb2d27474568fe7c9db9" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.3.3", "cfg-if", "clipboard-win", - "dirs-next", "fd-lock", + "home", "libc", "log", "memchr", - "nix 0.23.2", + "nix", "radix_trie", "scopeguard", - "smallvec", "unicode-segmentation", "unicode-width", "utf8parse", diff --git a/Cargo.toml b/Cargo.toml index c24d66f9..7db6c287 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ ordered-float = "2.6.0" phf = { version = "0.9", features = ["macros"] } ref_thread_local = "0.0.0" rug = { version = "1.15.0", optional = true } -rustyline = "9.0.0" +rustyline = "12.0.0" ring = "0.16.13" ripemd160 = "0.8.0" sha3 = "0.8.2" diff --git a/src/heap_print.rs b/src/heap_print.rs index f1ac11ce..3463b0e0 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -1465,9 +1465,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let print_struct = |printer: &mut Self, name: Atom, arity: usize| { if name == atom!("[]") && arity == 0 { - if !printer.at_cdr("") { - append_str!(printer, "[]"); - } + append_str!(printer, "[]"); } else if arity > 0 { if let Some(spec) = fetch_op_spec(name, arity, printer.op_dir) { printer.handle_op_as_struct( diff --git a/src/read.rs b/src/read.rs index c21c3016..e5b3e8ba 100644 --- a/src/read.rs +++ b/src/read.rs @@ -17,6 +17,7 @@ use fxhash::FxBuildHasher; use indexmap::IndexSet; use rustyline::error::ReadlineError; +use rustyline::history::DefaultHistory; use rustyline::{Config, Editor}; use std::collections::VecDeque; @@ -109,7 +110,7 @@ fn get_prompt() -> &'static str { #[derive(Debug)] pub struct ReadlineStream { - rl: Editor, + rl: Editor, pending_input: CharReader>, add_history: bool, } @@ -117,10 +118,13 @@ pub struct ReadlineStream { impl ReadlineStream { #[inline] pub fn new(pending_input: &str, add_history: bool) -> Self { - let config = Config::builder().check_cursor_position(true).build(); + let config = Config::builder() + .check_cursor_position(true) + .build(); + let helper = Helper::new(); - let mut rl = Editor::with_config(config); + let mut rl = Editor::with_config(config).unwrap(); rl.set_helper(Some(helper)); if let Some(mut path) = dirs_next::home_dir() { @@ -130,8 +134,6 @@ impl ReadlineStream { } } - // rl.bind_sequence(KeyEvent::from('\t'), Cmd::Insert(1, "\t".to_string())); - ReadlineStream { rl, pending_input: CharReader::new(Cursor::new(pending_input.to_owned())), @@ -164,7 +166,7 @@ impl ReadlineStream { unsafe { if PROMPT { - self.rl.history_mut().add(self.pending_input.get_ref().get_ref()); + self.rl.add_history_entry(self.pending_input.get_ref().get_ref()).unwrap(); self.save_history(); PROMPT = false; } From 3f819e2dfd1354158028163a75c753471a0e22b8 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 22 Jul 2023 11:53:09 -0600 Subject: [PATCH 341/361] additional write fixes, use rustyline 12.0.0 (#1876, #1901) --- src/heap_print.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 3463b0e0..9b0755d9 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -1465,6 +1465,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let print_struct = |printer: &mut Self, name: Atom, arity: usize| { if name == atom!("[]") && arity == 0 { + if let Some(TokenOrRedirect::CloseList(_)) = printer.state_stack.last() { + if printer.at_cdr("") { + return; + } + } + append_str!(printer, "[]"); } else if arity > 0 { if let Some(spec) = fetch_op_spec(name, arity, printer.op_dir) { @@ -1516,7 +1522,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { None => return, }; - if !addr.is_compound(&self.iter.heap) && self.max_depth_exhausted(max_depth) { + if !addr.is_var() && !addr.is_compound(&self.iter.heap) && self.max_depth_exhausted(max_depth) { self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); return; } From 24450a88272ff7468a33d9314103bf668820e1c3 Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 22 Jul 2023 14:12:38 -0600 Subject: [PATCH 342/361] use ExitCode when halting so Drop is called, close terminal stream in rustyline Drop --- src/bin/scryer-prolog.rs | 4 ++-- src/machine/dispatch.rs | 13 +++++-------- src/machine/mod.rs | 6 +++--- src/machine/system_calls.rs | 10 +++++----- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/bin/scryer-prolog.rs b/src/bin/scryer-prolog.rs index eae00fe2..e15bae14 100644 --- a/src/bin/scryer-prolog.rs +++ b/src/bin/scryer-prolog.rs @@ -1,4 +1,4 @@ -fn main() { +fn main() -> std::process::ExitCode { use std::sync::atomic::Ordering; use scryer_prolog::*; @@ -7,5 +7,5 @@ fn main() { }).unwrap(); let mut wam = machine::Machine::new(); - wam.run_top_level(); + wam.run_top_level() } diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index fbfa6580..31592d68 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -558,7 +558,7 @@ impl Machine { } #[inline(always)] - pub(super) fn dispatch_loop(&mut self) { + pub(super) fn dispatch_loop(&mut self) -> std::process::ExitCode { 'outer: loop { for _ in 0 .. INSTRUCTIONS_PER_INTERRUPT_POLL { match &self.code[self.machine_st.p] { @@ -3809,13 +3809,8 @@ impl Machine { self.is_partial_string(); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } - &Instruction::CallHalt => { - self.halt(); - self.machine_st.p += 1; - } - &Instruction::ExecuteHalt => { - self.halt(); - self.machine_st.p = self.machine_st.cp; + &Instruction::CallHalt | &Instruction::ExecuteHalt => { + return self.halt(); } &Instruction::CallGetLiftedHeapFromOffset => { self.get_lifted_heap_from_offset(); @@ -5356,5 +5351,7 @@ impl Machine { Err(_) => unreachable!(), } } + + std::process::ExitCode::SUCCESS } } diff --git a/src/machine/mod.rs b/src/machine/mod.rs index 9c5d498b..d52dba69 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -204,7 +204,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(); @@ -283,7 +283,7 @@ impl Machine { } } - pub fn run_top_level(&mut self) { + pub fn run_top_level(&mut self) -> std::process::ExitCode { let mut arg_pstrs = vec![]; for arg in env::args() { @@ -298,7 +298,7 @@ impl Machine { iter_to_heap_list(&mut self.machine_st.heap, arg_pstrs.into_iter()) ); - self.run_module_predicate(atom!("$toplevel"), (atom!("$repl"), 1)); + self.run_module_predicate(atom!("$toplevel"), (atom!("$repl"), 1)) } pub(crate) fn configure_modules(&mut self) { diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index f9969669..f6bc90d8 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5229,24 +5229,24 @@ impl Machine { } #[inline(always)] - pub(crate) fn halt(&mut self) { + pub(crate) fn halt(&mut self) -> std::process::ExitCode { let code = self.deref_register(1); let code = match Number::try_from(code) { - Ok(Number::Fixnum(n)) => i32::try_from(n.get_num()).unwrap(), - Ok(Number::Integer(n)) => n.to_i32().unwrap(), + Ok(Number::Fixnum(n)) => u8::try_from(n.get_num()).unwrap(), + Ok(Number::Integer(n)) => n.to_u8().unwrap(), Ok(Number::Rational(r)) => { // n has already been confirmed as an integer, and // internally, Rational is assumed reduced, so its // denominator must be 1. - r.numer().to_i32().unwrap() + r.numer().to_u8().unwrap() } _ => { unreachable!() } }; - std::process::exit(code); + std::process::ExitCode::from(code) } #[inline(always)] From fd7f24e26598ce04df13a08b0b6a7542c76c8dae Mon Sep 17 00:00:00 2001 From: Mark Date: Sat, 22 Jul 2023 21:42:40 -0600 Subject: [PATCH 343/361] remove EMIT_NEWLINE (#1900) --- src/machine/mod.rs | 2 +- src/machine/system_calls.rs | 1 - src/read.rs | 14 ++------------ 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/machine/mod.rs b/src/machine/mod.rs index d52dba69..afbdda67 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -437,7 +437,7 @@ impl Machine { user_error, load_contexts: vec![], runtime, - foreign_function_table: Default::default(), + foreign_function_table: Default::default(), }; let mut lib_path = current_dir(); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index f6bc90d8..85d26922 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -5797,7 +5797,6 @@ impl Machine { pub(crate) fn read_query_term(&mut self) -> CallResult { self.user_input.reset(); - set_emit_newline(true); set_prompt(true); // let result = self.machine_st.read_term(self.user_input, &mut self.indices); let result = self.machine_st.read_term_from_user_input(self.user_input, &mut self.indices); diff --git a/src/read.rs b/src/read.rs index e5b3e8ba..e23dc302 100644 --- a/src/read.rs +++ b/src/read.rs @@ -81,16 +81,8 @@ impl MachineState { } static mut PROMPT: bool = false; -static mut EMIT_NEWLINE: bool = false; - const HISTORY_FILE: &'static str = ".scryer_history"; -pub(crate) fn set_emit_newline(value: bool) { - unsafe { - EMIT_NEWLINE = value; - } -} - pub(crate) fn set_prompt(value: bool) { unsafe { PROMPT = value; @@ -171,10 +163,8 @@ impl ReadlineStream { PROMPT = false; } - if EMIT_NEWLINE { - if self.pending_input.get_ref().get_ref().chars().last() != Some('\n') { - *self.pending_input.get_mut().get_mut() += "\n"; - } + if self.pending_input.get_ref().get_ref().chars().last() != Some('\n') { + *self.pending_input.get_mut().get_mut() += "\n"; } } From e9ae80e25048c897ec3fbaefae70030f0addf491 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 23 Jul 2023 14:43:13 -0600 Subject: [PATCH 344/361] fix list abbreviation (#1901) --- src/heap_print.rs | 58 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 9b0755d9..832c4561 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -229,6 +229,8 @@ enum TokenOrRedirect { Space, LeftCurly, RightCurly, + ChildOpenList, + ChildCloseList, OpenList(Rc>), CloseList(Rc>), HeadTailSeparator, @@ -1217,7 +1219,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } _ => { let switch = Rc::new(Cell::new((!at_cdr, 0))); - self.state_stack.push(TokenOrRedirect::CloseList(switch.clone())); + let switch = self.close_list(switch); let (h, offset) = pstr_loc_and_offset(self.iter.heap, focus.value() as usize); let pstr = cell_as_string!(self.iter.heap[h]); @@ -1265,7 +1267,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.state_stack.pop(); } - self.state_stack.push(TokenOrRedirect::OpenList(switch)); + self.open_list(switch); } ); } @@ -1288,6 +1290,25 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { false } + fn close_list(&mut self, switch: Rc>) -> Option>> { + if let Some(TokenOrRedirect::Op(_, op_desc)) = self.state_stack.last() { + if is_postfix!(op_desc.get_spec()) || is_infix!(op_desc.get_spec()) { + self.state_stack.push(TokenOrRedirect::ChildCloseList); + return None; + } + } + + self.state_stack.push(TokenOrRedirect::CloseList(switch.clone())); + Some(switch) + } + + fn open_list(&mut self, switch: Option>>) { + self.state_stack.push(match switch { + Some(switch) => TokenOrRedirect::OpenList(switch), + None => TokenOrRedirect::ChildOpenList, + }); + } + fn push_list(&mut self, mut max_depth: usize) { if self.max_depth_exhausted(max_depth) { self.iter.pop_stack(); @@ -1302,22 +1323,23 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { let cell = Rc::new(Cell::new((true, 0))); - self.state_stack.push(TokenOrRedirect::CloseList(cell.clone())); + let switch = self.close_list(cell); + self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); - self.state_stack.push(TokenOrRedirect::OpenList(cell)); + self.open_list(switch); return; } let cell = Rc::new(Cell::new((true, max_depth))); - self.state_stack.push(TokenOrRedirect::CloseList(cell.clone())); + let switch = self.close_list(cell); self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); self.state_stack.push(TokenOrRedirect::HeadTailSeparator); // bar self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth+1)); - self.state_stack.push(TokenOrRedirect::OpenList(cell)); + self.open_list(switch); } fn handle_op_as_struct( @@ -1617,7 +1639,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { if self.outputter.ends_with("|") { self.outputter.truncate(len - "|".len()); append_str!(self, tr); - true } else { false @@ -1648,6 +1669,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { TokenOrRedirect::IpAddr(ip) => self.print_ip_addr(ip), TokenOrRedirect::RawPtr(ptr) => self.print_raw_ptr(ptr), TokenOrRedirect::Open => push_char!(self, '('), + TokenOrRedirect::ChildOpenList => { + push_char!(self, '['); + } + TokenOrRedirect::ChildCloseList => { + push_char!(self, ']'); + } TokenOrRedirect::OpenList(delimit) => { if !self.at_cdr(",") { push_char!(self, '['); @@ -1951,5 +1978,22 @@ mod tests { all_cells_unmarked(&wam.machine_st.heap); assert_eq!(&wam.parse_and_print_term("f((a,b)).").unwrap(), "f((a,b))"); + + all_cells_unmarked(&wam.machine_st.heap); + + wam.op_dir.insert( + (atom!("+"), Fixity::In), + OpDesc::build_with(500, YFX as u8), + ); + wam.op_dir.insert( + (atom!("*"), Fixity::In), + OpDesc::build_with(400, YFX as u8), + ); + + assert_eq!(&wam.parse_and_print_term("[a|[] + b].").unwrap(), "[a|[]+b]"); + + all_cells_unmarked(&wam.machine_st.heap); + + assert_eq!(&wam.parse_and_print_term("[a|[b|c]*d].").unwrap(), "[a|[b|c]*d]"); } } From 762e6d3ba4b23e9bf36f456ca8b210cec2f7fd4e Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 23 Jul 2023 15:02:27 -0600 Subject: [PATCH 345/361] pop both pending redirections in format_bar_separator_op when max depth exceeded (#1903) --- src/heap_print.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/heap_print.rs b/src/heap_print.rs index 832c4561..d1d87587 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -703,6 +703,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { fn format_bar_separator_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) { if self.check_max_depth(&mut max_depth) { self.iter.pop_stack(); + self.iter.pop_stack(); let ellipsis_atom = atom!("..."); From c89217903aff4aab72a8f2a5d7293685f73aab53 Mon Sep 17 00:00:00 2001 From: Fayeed Pawaskar Date: Mon, 24 Jul 2023 10:12:14 +0530 Subject: [PATCH 346/361] fix all the move errors --- src/heap_print.rs | 5 ++++- src/machine/arithmetic_ops.rs | 41 ++++++++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 783b7f01..45fb9bf6 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -23,6 +23,7 @@ use dashu::base::DivRemEuclid; use ordered_float::OrderedFloat; use indexmap::IndexMap; +use tokio::io::Interest; use std::cell::Cell; use std::convert::TryFrom; @@ -512,8 +513,10 @@ pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ]; + let n_clone: Integer = n.clone(); + let i = n.div_rem_euclid(Integer::from(26)).1.to_f32().value() as usize; - let j = n.div_rem(Integer::from(26)); + let j = n_clone.div_rem(Integer::from(26)); let j = <(Integer, Integer)>::from(j).0; if j == Integer::from(0) { diff --git a/src/machine/arithmetic_ops.rs b/src/machine/arithmetic_ops.rs index 153ca0c3..1859d1d7 100644 --- a/src/machine/arithmetic_ops.rs +++ b/src/machine/arithmetic_ops.rs @@ -194,9 +194,16 @@ 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.clone()), 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) + }, } } @@ -210,9 +217,15 @@ pub(crate) fn abs(n: Number, arena: &mut Arena) -> Number { Number::arena_from(arena_int.abs(), arena) } } - Number::Integer(n) => Number::arena_from(Integer::from(n.abs()), 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()), arena), + Number::Rational(r) => { + let r_clone: Rational = (*r).clone(); + Number::arena_from(Rational::from(r_clone.abs()), arena) + }, } } @@ -251,7 +264,8 @@ pub(crate) fn mul(lhs: Number, rhs: Number, arena: &mut Arena) -> Result { - 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)) => { @@ -259,7 +273,8 @@ pub(crate) fn mul(lhs: Number, rhs: Number, arena: &mut Arena) -> Result { - 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)) => { @@ -269,7 +284,8 @@ pub(crate) fn mul(lhs: Number, rhs: Number, arena: &mut Arena) -> Result { - 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)) } } } @@ -534,7 +550,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)) + }, } } @@ -939,10 +958,12 @@ pub(crate) fn gcd(n1: Number, n2: Number, arena: &mut Arena) -> Result { let n1 = Integer::from(n1.get_num()); - Ok(Number::arena_from(Integer::from(n2.gcd(&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(&Integer::from(n2.to_isize().unwrap()))) as IBig, 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); From 0e17d6acd7c075ecd7195eaa65825dfa00e76e97 Mon Sep 17 00:00:00 2001 From: Fayeed Pawaskar Date: Mon, 24 Jul 2023 10:12:22 +0530 Subject: [PATCH 347/361] remove rug completely --- Cargo.lock | 24 ------------------------ Cargo.toml | 2 -- src/machine/system_calls.rs | 5 ----- src/parser/mod.rs | 6 ------ 4 files changed, 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5faf337..ec3941ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,12 +43,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "az" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" - [[package]] name = "base64" version = "0.12.3" @@ -727,14 +721,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "gmp-mpfr-sys" -version = "1.5.3" -dependencies = [ - "libc", - "windows-sys 0.42.0", -] - [[package]] name = "h2" version = "0.3.19" @@ -1771,15 +1757,6 @@ dependencies = [ "xmlparser", ] -[[package]] -name = "rug" -version = "1.19.2" -dependencies = [ - "az", - "gmp-mpfr-sys", - "libc", -] - [[package]] name = "rustix" version = "0.37.20" @@ -1899,7 +1876,6 @@ dependencies = [ "ring", "ripemd160", "roxmltree", - "rug", "rustyline", "ryu", "select", diff --git a/Cargo.toml b/Cargo.toml index 8a50c342..76b0aa62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ build = "build/main.rs" rust-version = "1.63" [features] -default = ["rug"] [build-dependencies] indexmap = "1.0.2" @@ -45,7 +44,6 @@ ctrlc = "3.2.2" ordered-float = "2.6.0" phf = { version = "0.9", features = ["macros"] } ref_thread_local = "0.0.0" -rug = { path = "../rug", optional = true } rustyline = "9.0.0" ring = "0.16.13" ripemd160 = "0.8.0" diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 8f2523a7..7ee4c311 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -26,7 +26,6 @@ use crate::machine::stack::*; use crate::machine::streams::*; use crate::parser::char_reader::*; use crate::parser::dashu::Integer; -use crate::parser::rug::rand::RandState; use crate::read::*; use crate::types::*; use rand::{Rng, SeedableRng}; @@ -93,10 +92,6 @@ use hyper_tls::HttpsConnector; use tokio::sync::Mutex; use tokio::sync::mpsc::channel; -ref_thread_local! { - pub(crate) static managed RANDOM_STATE: RandState<'static> = RandState::new(); -} - pub(crate) fn get_key() -> KeyEvent { let key; enable_raw_mode().expect("failed to enable raw mode"); diff --git a/src/parser/mod.rs b/src/parser/mod.rs index e7d0cec0..ef6a8e0a 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,9 +1,3 @@ -#[cfg(feature = "num-rug-adapter")] -pub use num_rug_adapter as rug; - -#[cfg(feature = "rug")] -pub use rug; - pub use dashu; // #[macro_use] From 1dcc1ca5244fba27f8c86e81f2d3e7e280077c12 Mon Sep 17 00:00:00 2001 From: Fayeed Pawaskar Date: Mon, 24 Jul 2023 12:19:41 +0530 Subject: [PATCH 348/361] Fixed stackoverflow error --- src/arena.rs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/arena.rs b/src/arena.rs index fbbcd72a..3d321c5a 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -252,26 +252,6 @@ impl TypedArenaPtr { self.0.as_ptr() } - #[inline] - pub fn to_i64(&self) -> Option { - self.to_i64() - } - - #[inline] - pub fn to_u32(&self) -> Option { - self.to_u32() - } - - #[inline] - pub fn to_usize(&self) -> Option { - self.to_usize() - } - - #[inline] - pub fn to_isize(&self) -> Option { - self.to_isize() - } - #[inline] pub fn header_ptr(&self) -> *const ArenaHeader { let mut ptr = self.as_ptr() as *const u8 as usize; From 7248425a7631f4d7edc35237a38a79f451943738 Mon Sep 17 00:00:00 2001 From: Fayeed Pawaskar Date: Mon, 24 Jul 2023 12:41:56 +0530 Subject: [PATCH 349/361] Fixed warnings --- src/arithmetic.rs | 2 +- src/heap_print.rs | 1 - src/machine/system_calls.rs | 2 +- src/parser/ast.rs | 2 +- src/parser/parser.rs | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/arithmetic.rs b/src/arithmetic.rs index cfdc5b08..b48f43aa 100644 --- a/src/arithmetic.rs +++ b/src/arithmetic.rs @@ -381,7 +381,7 @@ pub(crate) fn rnd_i<'a>(n: &'a Number, arena: &mut Arena) -> Number { } } &Number::Rational(ref r) => { - let (mut fract, mut floor) = (r.fract(), r.floor()); + let (_, floor) = (r.fract(), r.floor()); if let Some(floor) = floor.to_i64() { fixnum!(Number, floor, arena) diff --git a/src/heap_print.rs b/src/heap_print.rs index 45fb9bf6..8d185e5f 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -23,7 +23,6 @@ use dashu::base::DivRemEuclid; use ordered_float::OrderedFloat; use indexmap::IndexMap; -use tokio::io::Interest; use std::cell::Cell; use std::convert::TryFrom; diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 7ee4c311..fb54d361 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -36,7 +36,7 @@ use ordered_float::OrderedFloat; use fxhash::{FxBuildHasher, FxHasher}; use indexmap::IndexSet; -use ref_thread_local::{RefThreadLocal, ref_thread_local}; +pub(crate) use ref_thread_local::RefThreadLocal; use std::borrow::BorrowMut; use std::cell::Cell; diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 73f5b4e5..6846aa72 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -7,7 +7,7 @@ use crate::types::HeapCellValueTag; use std::cell::{Cell, Ref, RefCell, RefMut}; use std::fmt; use std::hash::{Hash, Hasher}; -use std::io::{Error as IOError}; +use std::io::Error as IOError; use std::ops::{Deref, Neg}; use std::rc::Rc; use std::vec::Vec; diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 5d637e02..f427f18a 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -958,7 +958,7 @@ impl<'a, R: CharRead> Parser<'a, R> { } fn shift_token(&mut self, token: Token, op_dir: &CompositeOpDir) -> Result<(), ParserError> { - fn negate_int_rc(mut t: TypedArenaPtr) -> TypedArenaPtr { + fn negate_int_rc(t: TypedArenaPtr) -> TypedArenaPtr { let i: Integer = (*t).clone(); let mut data = i.neg(); TypedArenaPtr::new(&mut data) From 40b6890c5402fc79a10136c23024a42e11af3c66 Mon Sep 17 00:00:00 2001 From: Fayeed Pawaskar Date: Mon, 24 Jul 2023 19:11:41 +0530 Subject: [PATCH 350/361] updated cargo to use git --- Cargo.lock | 9 +++++++++ Cargo.toml | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec3941ae..66246593 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -316,6 +316,7 @@ dependencies = [ [[package]] name = "dashu" version = "0.3.1" +source = "git+https://github.com/coasys/dashu.git#ae7ee53fad213e09da5fe4b30e9e9e8bce96aedd" dependencies = [ "dashu-base", "dashu-float", @@ -327,10 +328,12 @@ dependencies = [ [[package]] name = "dashu-base" version = "0.3.1" +source = "git+https://github.com/coasys/dashu.git#ae7ee53fad213e09da5fe4b30e9e9e8bce96aedd" [[package]] name = "dashu-float" version = "0.3.2" +source = "git+https://github.com/coasys/dashu.git#ae7ee53fad213e09da5fe4b30e9e9e8bce96aedd" dependencies = [ "dashu-base", "dashu-int", @@ -342,6 +345,7 @@ dependencies = [ [[package]] name = "dashu-int" version = "0.3.1" +source = "git+https://github.com/coasys/dashu.git#ae7ee53fad213e09da5fe4b30e9e9e8bce96aedd" dependencies = [ "cfg-if", "dashu-base", @@ -354,6 +358,7 @@ dependencies = [ [[package]] name = "dashu-macros" version = "0.3.1" +source = "git+https://github.com/coasys/dashu.git#ae7ee53fad213e09da5fe4b30e9e9e8bce96aedd" dependencies = [ "dashu-base", "dashu-float", @@ -366,6 +371,7 @@ dependencies = [ [[package]] name = "dashu-ratio" version = "0.3.2" +source = "git+https://github.com/coasys/dashu.git#ae7ee53fad213e09da5fe4b30e9e9e8bce96aedd" dependencies = [ "dashu-base", "dashu-float", @@ -1029,6 +1035,7 @@ checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" [[package]] name = "libffi" version = "3.2.0" +source = "git+https://github.com/coasys/libffi-rs.git?branch=windows-space#f6e9e50efde0aa4e940dd6f709a59bb426875362" dependencies = [ "libc", "libffi-sys", @@ -1037,6 +1044,7 @@ dependencies = [ [[package]] name = "libffi-sys" version = "2.3.0" +source = "git+https://github.com/coasys/libffi-rs.git?branch=windows-space#f6e9e50efde0aa4e940dd6f709a59bb426875362" dependencies = [ "cc", ] @@ -1286,6 +1294,7 @@ dependencies = [ [[package]] name = "num-modular" version = "0.6.0" +source = "git+https://github.com/coasys/num-modular.git#87d6dc30600207445e07c2cc84e0a47ff58f0aca" [[package]] name = "num-order" diff --git a/Cargo.toml b/Cargo.toml index 76b0aa62..58d13d0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,8 +68,8 @@ derive_deref = "1.1.1" http-body-util = "0.1.0-rc.2" bytes = "1" reqwest = { version = "0.11.18", features = ["blocking"] } -dashu = { path = "../dashu" } -libffi = { path = "../libffi-rs/libffi-rs" } +dashu = { git = "https://github.com/coasys/dashu.git" } +libffi = { git = "https://github.com/coasys/libffi-rs.git", branch = "windows-space" } rand = "0.8.5" [dev-dependencies] From 287c308bc38f5e0734bb7221cd048e66474f6a1b Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 24 Jul 2023 20:05:28 -0600 Subject: [PATCH 351/361] track the parent operator of the current operator in heap_print to emit space if necessary (#1906) --- src/heap_print.rs | 142 ++++++++++++++++++------------- tests-pl/iso-conformity-tests.pl | 3 + 2 files changed, 84 insertions(+), 61 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index d1d87587..1f01b0de 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -26,7 +26,6 @@ use std::cell::Cell; use std::convert::TryFrom; use std::iter::once; use std::net::{IpAddr, TcpListener}; -use std::ops::{Range, RangeFrom}; use std::rc::Rc; /* contains the location, name, precision and Specifier of the parent op. */ @@ -64,11 +63,7 @@ impl DirectedOp { #[inline] fn is_left(&self) -> bool { - if let &DirectedOp::Left(..) = self { - true - } else { - false - } + matches!(self, DirectedOp::Left(..)) } } @@ -330,8 +325,7 @@ pub trait HCValueOutputter { fn ends_with(&self, s: &str) -> bool; fn len(&self) -> usize; fn truncate(&mut self, len: usize); - fn range(&self, range: Range) -> &str; - fn range_from(&self, range: RangeFrom) -> &str; + fn as_str(&self) -> &str; } #[derive(Debug)] @@ -386,12 +380,8 @@ impl HCValueOutputter for PrinterOutputter { self.contents.truncate(len); } - fn range(&self, index: Range) -> &str { - &self.contents.as_str()[index] - } - - fn range_from(&self, index: RangeFrom) -> &str { - &self.contents.as_str().get(index).unwrap_or("") + fn as_str(&self) -> &str { + &self.contents } } @@ -492,6 +482,8 @@ pub struct HCPrinter<'a, Outputter> { state_stack: Vec, toplevel_spec: Option, last_item_idx: usize, + num_ops_on_stack: usize, + parent_of_first_op: Option<(DirectedOp, usize)>, pub var_names: IndexMap, pub numbervars_offset: Integer, pub numbervars: bool, @@ -567,6 +559,8 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { state_stack: vec![], toplevel_spec: None, last_item_idx: 0, + num_ops_on_stack: 0, + parent_of_first_op: None, numbervars: false, numbervars_offset: Integer::from(0), quoted: false, @@ -580,7 +574,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { #[inline] fn ambiguity_check(&self, atom: &str) -> bool { - let tail = self.outputter.range_from(self.last_item_idx..); + let tail = &self.outputter.as_str()[self.last_item_idx..]; if !self.quoted || non_quoted_token(atom.chars()) { requires_space(tail, atom) @@ -589,7 +583,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } } - fn enqueue_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) { + fn set_parent_of_first_op(&mut self, parent_op: Option) { + self.parent_of_first_op = parent_op.map(|op| (op, self.last_item_idx)); + } + + fn enqueue_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc, parent_op: Option) { if is_postfix!(spec.get_spec()) { if self.max_depth_exhausted(max_depth) { self.iter.pop_stack(); @@ -600,17 +598,15 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.state_stack.push(TokenOrRedirect::Op(name, spec)); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); + } else { + let right_directed_op = DirectedOp::Right(name, spec); - return; + self.state_stack.push(TokenOrRedirect::Op(name, spec)); + self.state_stack.push(TokenOrRedirect::CompositeRedirect( + max_depth, + right_directed_op, + )); } - - let right_directed_op = DirectedOp::Right(name, spec); - - self.state_stack.push(TokenOrRedirect::Op(name, spec)); - self.state_stack.push(TokenOrRedirect::CompositeRedirect( - max_depth, - right_directed_op, - )); } else if is_prefix!(spec.get_spec()) { if self.max_depth_exhausted(max_depth) { self.iter.pop_stack(); @@ -620,15 +616,13 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.iter.pop_stack(); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); - self.state_stack.push(TokenOrRedirect::Atom(name)); + self.state_stack.push(TokenOrRedirect::Op(name, spec)); + } else { + let op = DirectedOp::Left(name, spec); - return; + self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, op)); + self.state_stack.push(TokenOrRedirect::Op(name, spec)); } - - let op = DirectedOp::Left(name, spec); - - self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, op)); - self.state_stack.push(TokenOrRedirect::Atom(name)); } else { match name.as_str() { "|" => { @@ -638,15 +632,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { _ => {} }; - let left_directed_op = DirectedOp::Left(name, spec); - let right_directed_op = DirectedOp::Right(name, spec); - if self.max_depth_exhausted(max_depth) { self.iter.pop_stack(); self.iter.pop_stack(); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); - return; } else if self.check_max_depth(&mut max_depth) { self.iter.pop_stack(); @@ -655,14 +645,21 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); self.state_stack.push(TokenOrRedirect::Op(name, spec)); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); + } else { + let left_directed_op = DirectedOp::Left(name, spec); + let right_directed_op = DirectedOp::Right(name, spec); - return; + self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, left_directed_op)); + self.state_stack.push(TokenOrRedirect::Op(name, spec)); + self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, right_directed_op)); } - - self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, left_directed_op)); - self.state_stack.push(TokenOrRedirect::Op(name, spec)); - self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, right_directed_op)); } + + if self.num_ops_on_stack == 0 { + self.set_parent_of_first_op(parent_op); + } + + self.num_ops_on_stack += 1; } fn format_struct(&mut self, mut max_depth: usize, arity: usize, name: Atom) -> bool { @@ -776,6 +773,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { arity: usize, name: Atom, op_desc: Option, + parent_op: Option, ) -> bool { if self.numbervars && is_numbered_var(name, arity) { if self.format_numbered_vars() { @@ -794,7 +792,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } if !self.ignore_ops && spec.get_prec() > 0 { - self.enqueue_op(max_depth, name, spec); + self.enqueue_op(max_depth, name, spec, parent_op); return true; } } @@ -976,7 +974,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { }); } Number::Rational(r) => { - self.print_rational(max_depth, r); + self.print_rational(max_depth, r, *op); } n => { let output_str = format!("{}", n); @@ -1007,7 +1005,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } } - fn print_rational(&mut self, mut max_depth: usize, r: TypedArenaPtr) { + fn print_rational( + &mut self, + mut max_depth: usize, + r: TypedArenaPtr, + parent_op: Option, + ) { if self.check_max_depth(&mut max_depth) { self.state_stack.push(TokenOrRedirect::Close); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); @@ -1050,15 +1053,15 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { NumberFocus::Denominator(r), left_directed_op, )); - - self.state_stack - .push(TokenOrRedirect::Op(rdiv_ct, *op_desc)); - + self.state_stack.push(TokenOrRedirect::Op(rdiv_ct, *op_desc)); self.state_stack.push(TokenOrRedirect::NumberFocus( max_depth, NumberFocus::Numerator(r), right_directed_op, )); + + self.num_ops_on_stack += 1; + self.set_parent_of_first_op(parent_op); } else { self.state_stack.push(TokenOrRedirect::Close); @@ -1347,7 +1350,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { &mut self, name: Atom, arity: usize, - op: &Option, + op: Option, is_functor_redirect: bool, op_desc: OpDesc, negated_operand: bool, @@ -1378,20 +1381,33 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { if add_brackets { self.state_stack.push(TokenOrRedirect::Close); - } - - if self.format_clause(max_depth, arity, name, Some(op_desc)) && add_brackets { + self.format_clause(max_depth, arity, name, Some(op_desc), op); self.state_stack.push(TokenOrRedirect::Open); - if let Some(ref op) = &op { - if !self.outputter.ends_with(" ") { - if op.is_left() { - if op.is_prefix() || requires_space(op.as_atom().as_str(), "(") { + let parent_op = self.parent_of_first_op + .and_then(|(parent_op, last_item_idx)| { + // if parent_op isn't printed to the output string + // already, then it doesn't border the present op + // and we should return None. + if self.last_item_idx == last_item_idx { + Some(parent_op) + } else { + None + } + }); + + for op in &[op, parent_op] { + if let Some(ref op) = &op { + if !self.outputter.ends_with(" ") { + if op.is_left() && (op.is_prefix() || requires_space(op.as_atom().as_str(), "(")) { self.state_stack.push(TokenOrRedirect::Space); + break; } } } } + } else { + self.format_clause(max_depth, arity, name, Some(op_desc), op); } } @@ -1500,7 +1516,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { printer.handle_op_as_struct( name, arity, - &op, + op, is_functor_redirect, spec, negated_operand, @@ -1508,7 +1524,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { ); } else { push_space_if_amb!(printer, name.as_str(), { - printer.format_clause(max_depth, arity, name, None); + printer.format_clause(max_depth, arity, name, None, op); }); } } else if fetch_op_spec(name, arity, printer.op_dir).is_some() { @@ -1566,7 +1582,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.handle_op_as_struct( name, arity, - &op, + op, is_functor_redirect, spec, negated_operand, @@ -1574,7 +1590,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { ); } else { push_space_if_amb!(self, name.as_str(), { - self.format_clause(max_depth, arity, name, None); + self.format_clause(max_depth, arity, name, None, op); }); } } @@ -1655,7 +1671,11 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { TokenOrRedirect::Atom(atom) => self.print_impromptu_atom(atom), TokenOrRedirect::BarAsOp => append_str!(self, " | "), TokenOrRedirect::Char(c) => print_char!(self, self.quoted, c), - TokenOrRedirect::Op(atom, _) => self.print_op(atom.as_str()), + TokenOrRedirect::Op(atom, ..) => { + self.num_ops_on_stack -= 1; + self.parent_of_first_op = None; + self.print_op(atom.as_str()); + } TokenOrRedirect::NumberedVar(num_var) => append_str!(self, &num_var), TokenOrRedirect::CompositeRedirect(max_depth, op) => { self.handle_heap_term(Some(op), false, max_depth) diff --git a/tests-pl/iso-conformity-tests.pl b/tests-pl/iso-conformity-tests.pl index c86620da..94185a07 100644 --- a/tests-pl/iso-conformity-tests.pl +++ b/tests-pl/iso-conformity-tests.pl @@ -998,6 +998,9 @@ test_310 :- test_syntax_error("writeq({\\+ (}).", syntax_error(incomplete_reduct test_311 :- test_syntax_error("Finis ().", syntax_error(incomplete_reduction)). +test_318 :- writeq_term_to_chars(+((1*2)^3), C), + C == "+ (1*2)^3". + run_tests([Test|Tests]) --> ( { call(Test) } -> [] From 3b67ffa814c1bd584ffe011b6d330734601f57bb Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 25 Jul 2023 13:48:34 -0600 Subject: [PATCH 352/361] overwrite code indices of dynamic_undefined predicates (dynamic, multifile, discontiguous) on export --- src/machine/load_state.rs | 6 +++++- src/machine/loader.rs | 13 +++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/machine/load_state.rs b/src/machine/load_state.rs index 56aa88eb..8273ac41 100644 --- a/src/machine/load_state.rs +++ b/src/machine/load_state.rs @@ -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, diff --git a/src/machine/loader.rs b/src/machine/loader.rs index b65c2351..9d66ce64 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -1631,23 +1631,16 @@ 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 >= &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 => { From bff48e7c7f9eb72fad3b059ed3b3d24444d3129f Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 25 Jul 2023 15:03:35 -0600 Subject: [PATCH 353/361] simplify and correct prefix-bracket spacing in heap_print.rs (#1914, #1918) --- src/heap_print.rs | 84 ++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/src/heap_print.rs b/src/heap_print.rs index 3ca68c4f..8be39076 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -484,7 +484,6 @@ pub struct HCPrinter<'a, Outputter> { state_stack: Vec, toplevel_spec: Option, last_item_idx: usize, - num_ops_on_stack: usize, parent_of_first_op: Option<(DirectedOp, usize)>, pub var_names: IndexMap, pub numbervars_offset: Integer, @@ -563,7 +562,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { state_stack: vec![], toplevel_spec: None, last_item_idx: 0, - num_ops_on_stack: 0, parent_of_first_op: None, numbervars: false, numbervars_offset: Integer::from(0), @@ -588,15 +586,18 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } fn set_parent_of_first_op(&mut self, parent_op: Option) { - self.parent_of_first_op = parent_op.map(|op| (op, self.last_item_idx)); + if let Some(op) = parent_op { + if op.is_left() && op.is_prefix() { + self.parent_of_first_op = Some((op, self.last_item_idx)); + } + } } - fn enqueue_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc, parent_op: Option) { + fn enqueue_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) { if is_postfix!(spec.get_spec()) { if self.max_depth_exhausted(max_depth) { self.iter.pop_stack(); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); - return; } else if self.check_max_depth(&mut max_depth) { self.iter.pop_stack(); @@ -641,7 +642,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.iter.pop_stack(); self.state_stack.push(TokenOrRedirect::Atom(atom!("..."))); - return; } else if self.check_max_depth(&mut max_depth) { self.iter.pop_stack(); self.iter.pop_stack(); @@ -658,12 +658,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { self.state_stack.push(TokenOrRedirect::CompositeRedirect(max_depth, right_directed_op)); } } - - if self.num_ops_on_stack == 0 { - self.set_parent_of_first_op(parent_op); - } - - self.num_ops_on_stack += 1; } fn format_struct(&mut self, mut max_depth: usize, arity: usize, name: Atom) -> bool { @@ -777,7 +771,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { arity: usize, name: Atom, op_desc: Option, - parent_op: Option, ) -> bool { if self.numbervars && is_numbered_var(name, arity) { if self.format_numbered_vars() { @@ -796,7 +789,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { } if !self.ignore_ops && spec.get_prec() > 0 { - self.enqueue_op(max_depth, name, spec, parent_op); + self.enqueue_op(max_depth, name, spec); return true; } } @@ -1064,7 +1057,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { right_directed_op, )); - self.num_ops_on_stack += 1; self.set_parent_of_first_op(parent_op); } else { self.state_stack.push(TokenOrRedirect::Close); @@ -1385,33 +1377,33 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { if add_brackets { self.state_stack.push(TokenOrRedirect::Close); - self.format_clause(max_depth, arity, name, Some(op_desc), op); + self.format_clause(max_depth, arity, name, Some(op_desc)); self.state_stack.push(TokenOrRedirect::Open); - let parent_op = self.parent_of_first_op - .and_then(|(parent_op, last_item_idx)| { - // if parent_op isn't printed to the output string - // already, then it doesn't border the present op - // and we should return None. - if self.last_item_idx == last_item_idx { - Some(parent_op) - } else { - None - } - }); + if !self.outputter.ends_with(" ") { + let parent_op = self.parent_of_first_op + .and_then(|(parent_op, last_item_idx)| { + // if parent_op isn't printed to the output string + // already, then it doesn't border the present op + // and we should return None. + if self.last_item_idx == last_item_idx { + Some(parent_op) + } else { + None + } + }); - for op in &[op, parent_op] { - if let Some(ref op) = &op { - if !self.outputter.ends_with(" ") { + for op in &[op, parent_op] { + if let Some(ref op) = &op { if op.is_left() && (op.is_prefix() || requires_space(op.as_atom().as_str(), "(")) { self.state_stack.push(TokenOrRedirect::Space); - break; + return; } } } } } else { - self.format_clause(max_depth, arity, name, Some(op_desc), op); + self.format_clause(max_depth, arity, name, Some(op_desc)); } } @@ -1528,7 +1520,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { ); } else { push_space_if_amb!(printer, name.as_str(), { - printer.format_clause(max_depth, arity, name, None, op); + printer.format_clause(max_depth, arity, name, None); }); } } else if fetch_op_spec(name, arity, printer.op_dir).is_some() { @@ -1594,7 +1586,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { ); } else { push_space_if_amb!(self, name.as_str(), { - self.format_clause(max_depth, arity, name, None, op); + self.format_clause(max_depth, arity, name, None); }); } } @@ -1675,10 +1667,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { TokenOrRedirect::Atom(atom) => self.print_impromptu_atom(atom), TokenOrRedirect::BarAsOp => append_str!(self, " | "), TokenOrRedirect::Char(c) => print_char!(self, self.quoted, c), - TokenOrRedirect::Op(atom, ..) => { - self.num_ops_on_stack -= 1; - self.parent_of_first_op = None; + TokenOrRedirect::Op(atom, op) => { self.print_op(atom.as_str()); + + if is_prefix!(op.get_spec()) { + self.set_parent_of_first_op(Some(DirectedOp::Left(atom, op))); + } } TokenOrRedirect::NumberedVar(num_var) => append_str!(self, &num_var), TokenOrRedirect::CompositeRedirect(max_depth, op) => { @@ -2020,5 +2014,21 @@ mod tests { all_cells_unmarked(&wam.machine_st.heap); assert_eq!(&wam.parse_and_print_term("[a|[b|c]*d].").unwrap(), "[a|[b|c]*d]"); + + all_cells_unmarked(&wam.machine_st.heap); + + wam.op_dir.insert( + (atom!("fy"), Fixity::Pre), + OpDesc::build_with(9, FY as u8), + ); + + wam.op_dir.insert( + (atom!("yf"), Fixity::Post), + OpDesc::build_with(9, YF as u8), + ); + + assert_eq!(&wam.parse_and_print_term("(fy (fy 1)yf)yf.").unwrap(), "(fy (fy 1)yf)yf"); + + assert_eq!(&wam.parse_and_print_term("fy(fy(yf(fy(1)))).").unwrap(), "fy fy (fy 1)yf"); } } From 03f7b01109627321c96d09daad8086808923ab6a Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 27 Jul 2023 20:22:05 +0200 Subject: [PATCH 354/361] FIXED: correct handling of ascii_punctuation in char_type/2 (#1926) --- src/machine/system_calls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 016a3c7d..3d18dc6b 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -2916,7 +2916,7 @@ impl Machine { method_check!(is_alphanumeric, atom!("alphanumeric")); macro_check!(alpha_numeric_char, atom!("alnum")); method_check!(is_ascii, atom!("ascii")); - method_check!(is_ascii_punctuation, atom!("ascii_ponctuaction")); + method_check!(is_ascii_punctuation, atom!("ascii_punctuation")); method_check!(is_ascii_graphic, atom!("ascii_graphic")); // macro_check!(backslash_char, atom!("backslash")); // macro_check!(back_quote_char, atom!("back_quote")); From 4ef8c5c47d61a8b5f5e4b9b32e419ecad737a041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= Date: Thu, 27 Jul 2023 23:35:57 +0200 Subject: [PATCH 355/361] =?UTF-8?q?detect=20and=20prevent=C2=B2=20concurre?= =?UTF-8?q?nt=20AtomTable=20use?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ²in the case of `#[cfg(not(test))]` there is still a toctou race as I am not sufficently familiar with Atomics --- src/atom_table.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/atom_table.rs b/src/atom_table.rs index b925273a..9f480139 100644 --- a/src/atom_table.rs +++ b/src/atom_table.rs @@ -54,7 +54,12 @@ static mut ATOM_TABLE_BUF_BASE: *const u8 = ptr::null_mut(); #[cfg(test)] fn set_atom_tbl_buf_base(ptr: *const u8) { ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| { - *atom_table_buf_base.borrow_mut() = ptr; + let mut borrow = atom_table_buf_base.borrow_mut(); + assert!( + borrow.is_null() || ptr.is_null(), + "Overwriting atom table base pointer!" + ); + *borrow = ptr; }); } @@ -66,6 +71,11 @@ pub(crate) fn get_atom_tbl_buf_base() -> *const u8 { #[cfg(not(test))] fn set_atom_tbl_buf_base(ptr: *const u8) { unsafe { + // FIXME: to prevent a toctou race-condition an atomic compare_exchange or a global lock should be used + assert!( + ATOM_TABLE_BUF_BASE.is_null() || ptr.is_null(), + "Overwriting atom table base pointer!" + ); ATOM_TABLE_BUF_BASE = ptr; } } @@ -75,6 +85,13 @@ pub(crate) fn get_atom_tbl_buf_base() -> *const u8 { unsafe { ATOM_TABLE_BUF_BASE } } +#[test] +#[should_panic(expected = "Overwriting atom table base pointer!")] +fn atomtable_is_not_concurrency_safe() { + let table_a = AtomTable::new(); + let table_b = AtomTable::new(); +} + impl RawBlockTraits for AtomTable { #[inline] fn init_size() -> usize { @@ -241,6 +258,7 @@ pub struct AtomTable { impl Drop for AtomTable { fn drop(&mut self) { + set_atom_tbl_buf_base(ptr::null()); self.block.deallocate(); } } From 6aa3c7d5d6ee84db5bbe63c201ed744e2452a170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= Date: Sat, 29 Jul 2023 11:08:32 +0200 Subject: [PATCH 356/361] handle atom table resize * bumping serial_test dev-dependency due to broken should_panic handling in old version --- Cargo.lock | 36 ++++++++++++---- Cargo.toml | 2 +- src/atom_table.rs | 93 +++++++++++++++++++++++++----------------- tests/scryer/issues.rs | 20 ++++++++- 4 files changed, 103 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 967181ad..d681658f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -349,6 +349,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "dashmap" +version = "5.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d" +dependencies = [ + "cfg-if", + "hashbrown 0.14.0", + "lock_api", + "once_cell", + "parking_lot_core 0.9.8", +] + [[package]] name = "dashu" version = "0.3.1" @@ -794,6 +807,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" version = "0.3.3" @@ -999,7 +1018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -2050,24 +2069,27 @@ dependencies = [ [[package]] name = "serial_test" -version = "0.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" +checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" dependencies = [ + "dashmap", + "futures", "lazy_static", - "parking_lot 0.11.2", + "log", + "parking_lot 0.12.1", "serial_test_derive", ] [[package]] name = "serial_test_derive" -version = "0.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" +checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.22", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 194d4482..b5f330f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,7 +74,7 @@ rand = "0.8.5" [dev-dependencies] assert_cmd = "1.0.3" predicates-core = "1.0.2" -serial_test = "0.5.1" +serial_test = "2.0.0" [patch.crates-io] modular-bitfield = { git = "https://github.com/mthom/modular-bitfield" } diff --git a/src/atom_table.rs b/src/atom_table.rs index 9f480139..df3e1e44 100644 --- a/src/atom_table.rs +++ b/src/atom_table.rs @@ -37,59 +37,61 @@ impl From for Atom { } } -#[cfg(test)] -use std::cell::RefCell; - const ATOM_TABLE_INIT_SIZE: usize = 1 << 16; const ATOM_TABLE_ALIGN: usize = 8; #[cfg(test)] thread_local! { - static ATOM_TABLE_BUF_BASE: RefCell<*const u8> = RefCell::new(ptr::null_mut()); + static ATOM_TABLE_BUF_BASE: std::cell::RefCell<*const u8> = std::cell::RefCell::new(ptr::null_mut()); } #[cfg(not(test))] -static mut ATOM_TABLE_BUF_BASE: *const u8 = ptr::null_mut(); +static ATOM_TABLE_BUF_BASE: std::sync::atomic::AtomicPtr = + std::sync::atomic::AtomicPtr::new(ptr::null_mut()); +fn set_atom_tbl_buf_base(old_ptr: *const u8, new_ptr: *const u8) -> Result<(), *const u8> { #[cfg(test)] -fn set_atom_tbl_buf_base(ptr: *const u8) { + { ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| { let mut borrow = atom_table_buf_base.borrow_mut(); - assert!( - borrow.is_null() || ptr.is_null(), - "Overwriting atom table base pointer!" - ); - *borrow = ptr; - }); + if *borrow != old_ptr { + Err(*borrow) + } else { + *borrow = new_ptr; + Ok(()) + } + })?; + }; + #[cfg(not(test))] + { + ATOM_TABLE_BUF_BASE + .compare_exchange( + old_ptr.cast_mut(), + new_ptr.cast_mut(), + std::sync::atomic::Ordering::Relaxed, + std::sync::atomic::Ordering::Relaxed, + ) + .map_err(|ptr| ptr.cast_const()) + }?; + Ok(()) } -#[cfg(test)] pub(crate) fn get_atom_tbl_buf_base() -> *const u8 { + #[cfg(test)] + { ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| *atom_table_buf_base.borrow()) } - #[cfg(not(test))] -fn set_atom_tbl_buf_base(ptr: *const u8) { - unsafe { - // FIXME: to prevent a toctou race-condition an atomic compare_exchange or a global lock should be used - assert!( - ATOM_TABLE_BUF_BASE.is_null() || ptr.is_null(), - "Overwriting atom table base pointer!" - ); - ATOM_TABLE_BUF_BASE = ptr; + { + ATOM_TABLE_BUF_BASE.load(std::sync::atomic::Ordering::Relaxed) } } -#[cfg(not(test))] -pub(crate) fn get_atom_tbl_buf_base() -> *const u8 { - unsafe { ATOM_TABLE_BUF_BASE } -} - #[test] -#[should_panic(expected = "Overwriting atom table base pointer!")] +#[should_panic(expected = "Overwriting atom table base pointer")] fn atomtable_is_not_concurrency_safe() { - let table_a = AtomTable::new(); - let table_b = AtomTable::new(); + let _table_a = AtomTable::new(); + let _table_b = AtomTable::new(); } impl RawBlockTraits for AtomTable { @@ -256,9 +258,17 @@ pub struct AtomTable { pub table: IndexSet, } +#[cold] +fn atom_table_base_pointer_missmatch(expected: *const u8, got: *const u8) -> ! { + assert_eq!(expected, got, "Overwriting atom table base pointer, expected old value to be {expected:p}, but found {got:p}"); + unreachable!("This should only be called in a case of a missmatch as such the assert_eq should have failed!") +} + impl Drop for AtomTable { fn drop(&mut self) { - set_atom_tbl_buf_base(ptr::null()); + if let Err(got) = set_atom_tbl_buf_base(self.block.base, ptr::null()) { + atom_table_base_pointer_missmatch(self.block.base, got); + } self.block.deallocate(); } } @@ -266,13 +276,17 @@ impl Drop for AtomTable { impl AtomTable { #[inline] pub fn new() -> Self { - let table = Self { - block: RawBlock::new(), - table: IndexSet::new(), - }; + let mut block = RawBlock::new(); - set_atom_tbl_buf_base(table.block.base); - table + if let Err(got) = set_atom_tbl_buf_base(ptr::null(), block.base) { + block.deallocate(); + atom_table_base_pointer_missmatch(ptr::null(), got); + } + + Self { + block, + table: IndexSet::new(), + } } #[inline] @@ -307,8 +321,11 @@ impl AtomTable { ptr = self.block.alloc(size); if ptr.is_null() { + let old_base = self.block.base; self.block.grow(); - set_atom_tbl_buf_base(self.block.base); + if let Err(got) = set_atom_tbl_buf_base(old_base, self.block.base) { + atom_table_base_pointer_missmatch(old_base, got); + } } else { break; } diff --git a/tests/scryer/issues.rs b/tests/scryer/issues.rs index f1735e78..ffa39148 100644 --- a/tests/scryer/issues.rs +++ b/tests/scryer/issues.rs @@ -1,4 +1,5 @@ use crate::helper::{load_module_test, run_top_level_test_no_args, run_top_level_test_with_args}; +use scryer_prolog::machine::Machine; use serial_test::serial; // issue #857 @@ -128,10 +129,12 @@ fn compound_goal() { // issue #815 #[test] fn no_stutter() { - run_top_level_test_no_args("write(a), write(b), false.\n\ + run_top_level_test_no_args( + "write(a), write(b), false.\n\ halt.\n\ ", - "ab false.\n") + "ab false.\n", + ) } /* @@ -168,3 +171,16 @@ fn call_0() { " error(existence_error(procedure,call/0),call/0).\n", ); } + +// issue #1206 +#[serial] +#[test] +#[should_panic(expected = "Overwriting atom table base pointer")] +fn atomtable_is_not_concurrency_safe() { + // this is basically the same test as scryer_prolog::atom_table::atomtable_is_not_concurrency_safe + // but for this integration test scryer_prolog is compiled with cfg!(not(test)) while for the unit test it is colpiled with cfg!(test) + // as the atom table implementation differ between cfg!(test) and cfg!(not(test)) both test serve a pourpose + // Note: this integration test itself is compiled with cfg!(test) independent of scryer_prolog itself + let _machine_a = Machine::with_test_streams(); + let _machine_b = Machine::with_test_streams(); +} From a70157003b60e744d31526ffeb84e6afdeff70a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bennet=20Ble=C3=9Fmann?= Date: Sat, 29 Jul 2023 13:11:00 +0200 Subject: [PATCH 357/361] fix spelling --- src/atom_table.rs | 10 +++++----- tests/scryer/issues.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/atom_table.rs b/src/atom_table.rs index df3e1e44..48f50ec9 100644 --- a/src/atom_table.rs +++ b/src/atom_table.rs @@ -259,15 +259,15 @@ pub struct AtomTable { } #[cold] -fn atom_table_base_pointer_missmatch(expected: *const u8, got: *const u8) -> ! { +fn atom_table_base_pointer_mismatch(expected: *const u8, got: *const u8) -> ! { assert_eq!(expected, got, "Overwriting atom table base pointer, expected old value to be {expected:p}, but found {got:p}"); - unreachable!("This should only be called in a case of a missmatch as such the assert_eq should have failed!") + unreachable!("This should only be called in a case of a mismatch as such the assert_eq should have failed!") } impl Drop for AtomTable { fn drop(&mut self) { if let Err(got) = set_atom_tbl_buf_base(self.block.base, ptr::null()) { - atom_table_base_pointer_missmatch(self.block.base, got); + atom_table_base_pointer_mismatch(self.block.base, got); } self.block.deallocate(); } @@ -280,7 +280,7 @@ impl AtomTable { if let Err(got) = set_atom_tbl_buf_base(ptr::null(), block.base) { block.deallocate(); - atom_table_base_pointer_missmatch(ptr::null(), got); + atom_table_base_pointer_mismatch(ptr::null(), got); } Self { @@ -324,7 +324,7 @@ impl AtomTable { let old_base = self.block.base; self.block.grow(); if let Err(got) = set_atom_tbl_buf_base(old_base, self.block.base) { - atom_table_base_pointer_missmatch(old_base, got); + atom_table_base_pointer_mismatch(old_base, got); } } else { break; diff --git a/tests/scryer/issues.rs b/tests/scryer/issues.rs index ffa39148..1f0e2737 100644 --- a/tests/scryer/issues.rs +++ b/tests/scryer/issues.rs @@ -178,7 +178,7 @@ fn call_0() { #[should_panic(expected = "Overwriting atom table base pointer")] fn atomtable_is_not_concurrency_safe() { // this is basically the same test as scryer_prolog::atom_table::atomtable_is_not_concurrency_safe - // but for this integration test scryer_prolog is compiled with cfg!(not(test)) while for the unit test it is colpiled with cfg!(test) + // but for this integration test scryer_prolog is compiled with cfg!(not(test)) while for the unit test it is compiled with cfg!(test) // as the atom table implementation differ between cfg!(test) and cfg!(not(test)) both test serve a pourpose // Note: this integration test itself is compiled with cfg!(test) independent of scryer_prolog itself let _machine_a = Machine::with_test_streams(); From 54a887cdc37982a18f363a56edfce9583db7ab95 Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Mon, 31 Jul 2023 21:57:25 +0200 Subject: [PATCH 358/361] ENHANCED: forget auxiliary constraints set up by the propagator for multiplication This addresses the issue raised in https://github.com/mthom/scryer-prolog/discussions/1937. --- src/lib/clpz.pl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 9b162a19..38e52657 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -5898,6 +5898,11 @@ in_(L, U, X) :- fd_put(X, NXD, XPs). min_max_factor(L1, U1, L2, U2, L3, U3, Min, Max) :- + % use findall/3 to forget auxiliary constraints that are only + % needed temporarily for reasoning about domain boundaries + findall(Min-Max, min_max_factor_(L1, U1, L2, U2, L3, U3, Min, Max), [Min-Max]). + +min_max_factor_(L1, U1, L2, U2, L3, U3, Min, Max) :- ( U1 cis_lt n(0), L2 cis_lt n(0), U2 cis_gt n(0), L3 cis_lt n(0), U3 cis_gt n(0) -> From 1c089a2bbbd398f897b63f1548f83a825ec2f57a Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Mon, 31 Jul 2023 22:03:43 +0200 Subject: [PATCH 359/361] better wording, applying the feedback from @dcnorris. Thank you a lot! --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0a78baa0..0f889c9b 100644 --- a/README.md +++ b/README.md @@ -694,7 +694,8 @@ Analysis of Grants](https://www.brz.gv.at/en/BRZ-Tech-Blog/Tech-Blog-7-Symbolic- by the Austrian Federal Computing Center, and parts of the [precautionary](https://github.com/dcnorris/precautionary/tree/main/exec/prolog) package for the analysis of dose-escalation trials in the -safety-critical and highly regulated domain of clinical oncology. +safety-critical and highly regulated domain of oncology +trial design. Scryer Prolog is also very well suited for teaching and learning Prolog, and for testing syntactic conformance and hence portability of From 0ddda0a864109f69273dc2d056986f9d240a83de Mon Sep 17 00:00:00 2001 From: Markus Triska Date: Thu, 3 Aug 2023 00:26:40 +0200 Subject: [PATCH 360/361] FIXED: do not attach constraint if the propagator is already entailed and killed Example: ?- A#=A//A#==>B,A-B=1-1. A = 1, B = 1. This addresses #1941. --- src/lib/clpz.pl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/clpz.pl b/src/lib/clpz.pl index 38e52657..ed01f079 100644 --- a/src/lib/clpz.pl +++ b/src/lib/clpz.pl @@ -3784,8 +3784,11 @@ var_eq(V, N, #V #= N). % Match variables to created skeleton. skeleton(Vs, Vs-Prop) :- - maplist(prop_init(Prop), Vs), - trigger_once(Prop). + ( propagator_state(Prop, State), State == dead -> + true + ; maplist(prop_init(Prop), Vs), + trigger_once(Prop) + ). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A drep is a user-accessible and visible domain representation. N, From ec450fc567b7da959b3930073226034711c7cbae Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 2 Aug 2023 19:50:27 -0600 Subject: [PATCH 361/361] allocate negator results in arena (#1898) --- src/arena.rs | 4 +++- src/parser/parser.rs | 22 ++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/arena.rs b/src/arena.rs index 3d321c5a..66b5c27b 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -242,9 +242,11 @@ impl fmt::Display for TypedArenaPtr { } impl TypedArenaPtr { + // data must be allocated in the arena already. #[inline] pub const fn new(data: *mut T) -> Self { - unsafe { TypedArenaPtr(ptr::NonNull::new_unchecked(data)) } + let result = unsafe { TypedArenaPtr(ptr::NonNull::new_unchecked(data)) }; + result } #[inline] diff --git a/src/parser/parser.rs b/src/parser/parser.rs index b14e0923..edf5d883 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -926,7 +926,7 @@ impl<'a, R: CharRead> Parser<'a, R> { fn negate_number(&mut self, n: N, negator: Negator, constr: ToLiteral) where - Negator: Fn(N) -> N, + Negator: Fn(N, &mut Arena) -> N, ToLiteral: Fn(N, &mut Arena) -> Literal, { if let Some(desc) = self.stack.last().cloned() { @@ -938,7 +938,9 @@ impl<'a, R: CharRead> Parser<'a, R> { self.stack.pop(); self.terms.pop(); - let literal = constr(negator(n), &mut self.lexer.machine_st.arena); + let arena = &mut self.lexer.machine_st.arena; + let literal = constr(negator(n, arena), arena); + self.shift(Token::Literal(literal), 0, TERM); return; @@ -953,21 +955,21 @@ impl<'a, R: CharRead> Parser<'a, R> { } fn shift_token(&mut self, token: Token, op_dir: &CompositeOpDir) -> Result<(), ParserError> { - fn negate_int_rc(t: TypedArenaPtr) -> TypedArenaPtr { + fn negate_int_rc(t: TypedArenaPtr, arena: &mut Arena) -> TypedArenaPtr { let i: Integer = (*t).clone(); - let mut data = i.neg(); - TypedArenaPtr::new(&mut data) + let data = i.neg(); + arena_alloc!(data, arena) } - fn negate_rat_rc(t: TypedArenaPtr) -> TypedArenaPtr { + fn negate_rat_rc(t: TypedArenaPtr, arena: &mut Arena) -> TypedArenaPtr { let r: Rational = (*t).clone(); - let mut data = r.neg(); - TypedArenaPtr::new(&mut data) + let data = r.neg(); + arena_alloc!(data, arena) } match token { Token::Literal(Literal::Fixnum(n)) => { - self.negate_number(n, |n| -n, |n, _| Literal::Fixnum(n)) + self.negate_number(n, |n, _| -n, |n, _| Literal::Fixnum(n)) } Token::Literal(Literal::Integer(n)) => { self.negate_number(n, negate_int_rc, |n, _| Literal::Integer(n)) @@ -977,7 +979,7 @@ impl<'a, R: CharRead> Parser<'a, R> { } Token::Literal(Literal::Float(n)) => self.negate_number( **n.as_ptr(), - |n| -n, + |n, _| -n, |n, arena| Literal::from(float_alloc!(n, arena)), ), Token::Literal(c) => {