scryer_prolog/machine/
mod.rs

1pub mod args;
2#[macro_use]
3pub mod arithmetic_ops;
4pub mod attributed_variables;
5pub mod code_walker;
6#[macro_use]
7pub mod loader;
8pub mod compile;
9pub mod config;
10pub mod copier;
11pub mod cycle_detection;
12pub mod disjuncts;
13pub mod dispatch;
14pub mod gc;
15pub mod heap;
16pub mod lib_machine;
17pub mod load_state;
18pub mod machine_errors;
19pub mod machine_indices;
20pub mod machine_state;
21pub mod machine_state_impl;
22pub mod mock_wam;
23pub mod partial_string;
24pub mod preprocessor;
25pub mod stack;
26pub mod streams;
27pub mod system_calls;
28pub mod term_stream;
29pub mod unify;
30
31use crate::arena::*;
32use crate::arithmetic::*;
33use crate::atom_table::*;
34#[cfg(feature = "ffi")]
35use crate::ffi::ForeignFunctionTable;
36use crate::forms::*;
37use crate::instructions::*;
38use crate::machine::args::*;
39use crate::machine::compile::*;
40use crate::machine::copier::*;
41use crate::machine::heap::*;
42use crate::machine::loader::*;
43use crate::machine::machine_errors::*;
44use crate::machine::machine_indices::*;
45use crate::machine::machine_state::*;
46use crate::machine::stack::*;
47use crate::machine::streams::*;
48use crate::offset_table::*;
49use crate::parser::ast::*;
50use crate::parser::dashu::{Integer, Rational};
51use crate::types::*;
52
53use indexmap::IndexMap;
54use lazy_static::lazy_static;
55use ordered_float::OrderedFloat;
56
57use rand::rngs::StdRng;
58use std::borrow::Cow;
59use std::cmp::Ordering;
60use std::env;
61use std::io::Read;
62use std::path::PathBuf;
63use std::sync::atomic::AtomicBool;
64use std::sync::OnceLock;
65
66lazy_static! {
67    pub static ref INTERRUPT: AtomicBool = AtomicBool::new(false);
68}
69
70/// An instance of Scryer Prolog.
71///
72/// Created with [`MachineBuilder::build`](crate::machine::config::MachineBuilder::build).
73#[derive(Debug)]
74pub struct Machine {
75    pub(super) machine_st: MachineState,
76    pub(super) indices: IndexStore,
77    pub(super) code: Code,
78    pub(super) user_input: Stream,
79    pub(super) user_output: Stream,
80    pub(super) user_error: Stream,
81    pub(super) load_contexts: Vec<LoadContext>,
82    #[cfg(feature = "ffi")]
83    pub(super) foreign_function_table: ForeignFunctionTable,
84    pub(super) rng: StdRng,
85}
86
87#[derive(Debug)]
88pub struct LoadContext {
89    pub(super) path: PathBuf,
90    pub(super) stream: Stream,
91    pub(super) module: Atom,
92}
93
94impl LoadContext {
95    #[inline]
96    fn new(path: &str, stream: Stream) -> Self {
97        let mut path_buf = PathBuf::from(path);
98
99        if path_buf.is_relative() {
100            let mut current_dir = current_dir();
101            current_dir.push(path_buf);
102            path_buf = current_dir;
103        }
104
105        LoadContext {
106            path: path_buf,
107            stream,
108            module: atom!("user"),
109        }
110    }
111}
112
113#[inline]
114fn current_dir() -> PathBuf {
115    if !cfg!(miri) {
116        env::current_dir().unwrap_or(PathBuf::from("./"))
117    } else {
118        PathBuf::from("./")
119    }
120}
121
122mod libraries {
123    use indexmap::IndexMap;
124    use std::sync::LazyLock;
125
126    static LIBRARIES: LazyLock<IndexMap<&'static str, &'static str>> = LazyLock::new(|| {
127        let mut m = IndexMap::new();
128
129        include!(concat!(env!("OUT_DIR"), "/libraries.rs"));
130
131        m
132    });
133
134    pub(crate) fn contains(name: &str) -> bool {
135        LIBRARIES.contains_key(name)
136    }
137
138    pub(crate) fn get(name: &str) -> Option<&'static str> {
139        LIBRARIES.get(name).copied()
140    }
141}
142
143pub static BREAK_FROM_DISPATCH_LOOP_LOC: usize = 0;
144pub static INSTALL_VERIFY_ATTR_INTERRUPT: usize = 1;
145pub static VERIFY_ATTR_INTERRUPT_LOC: usize = 2;
146pub static LIB_QUERY_SUCCESS: usize = 3;
147
148pub struct MachinePreludeView<'a> {
149    pub indices: &'a mut IndexStore,
150    pub code: &'a mut Code,
151    pub load_contexts: &'a mut Vec<LoadContext>,
152}
153
154pub(crate) fn import_builtin_impls(code_dir: &CodeDir, builtins: &mut Module) {
155    let keys = [
156        (atom!("@>"), 2),
157        (atom!("@<"), 2),
158        (atom!("@>="), 2),
159        (atom!("@=<"), 2),
160        (atom!("=="), 2),
161        (atom!("\\=="), 2),
162        (atom!(">"), 2),
163        (atom!("<"), 2),
164        (atom!(">="), 2),
165        (atom!("=<"), 2),
166        (atom!("=:="), 2),
167        (atom!("=\\="), 2),
168        (atom!("is"), 2),
169        (atom!("acyclic_term"), 1),
170        (atom!("arg"), 3),
171        (atom!("compare"), 3),
172        (atom!("copy_term"), 2),
173        (atom!("functor"), 3),
174        (atom!("ground"), 1),
175        (atom!("keysort"), 2),
176        (atom!("read"), 1),
177        (atom!("sort"), 2),
178        (atom!("$call"), 1),
179        (atom!("$call"), 2),
180        (atom!("$call"), 3),
181        (atom!("$call"), 4),
182        (atom!("$call"), 5),
183        (atom!("$call"), 6),
184        (atom!("$call"), 7),
185        (atom!("$call"), 8),
186        (atom!("$call"), 9),
187        (atom!("atom"), 1),
188        (atom!("atomic"), 1),
189        (atom!("compound"), 1),
190        (atom!("integer"), 1),
191        (atom!("number"), 1),
192        (atom!("rational"), 1),
193        (atom!("float"), 1),
194        (atom!("nonvar"), 1),
195        (atom!("var"), 1),
196    ];
197
198    for key in keys {
199        let idx = code_dir.get(&key).unwrap();
200        builtins.code_dir.insert(key, *idx);
201        builtins
202            .module_decl
203            .exports
204            .push(ModuleExport::PredicateKey(key));
205    }
206}
207
208#[inline]
209pub(crate) fn get_structure_index(value: HeapCellValue) -> Option<CodeIndex> {
210    read_heap_cell!(value,
211        (HeapCellValueTag::CodeIndexOffset, offset) => {
212            return Some(CodeIndex::from(offset));
213        }
214        _ => {
215        }
216    );
217
218    None
219}
220
221impl Machine {
222    #[inline]
223    fn prelude_view_and_machine_st(&mut self) -> (MachinePreludeView<'_>, &mut MachineState) {
224        (
225            MachinePreludeView {
226                indices: &mut self.indices,
227                code: &mut self.code,
228                load_contexts: &mut self.load_contexts,
229            },
230            &mut self.machine_st,
231        )
232    }
233
234    /// Gets the current inference count.
235    pub fn get_inference_count(&mut self) -> u64 {
236        self.machine_st
237            .cwil
238            .global_count
239            .clone()
240            .try_into()
241            .unwrap()
242    }
243
244    /// Runs the predicate `key` in `module_name` until completion.
245    /// Silently ignores failure, thrown errors and choice points.
246    ///
247    /// Consider using [`Machine::run_query`] if you wish to handle
248    /// predicates that may fail, leave a choice point or throw.
249    pub(crate) fn run_module_predicate(
250        &mut self,
251        module_name: Atom,
252        key: PredicateKey,
253    ) -> std::process::ExitCode {
254        if let Some(module) = self.indices.modules.get(&module_name) {
255            if let Some(code_idx) = module.code_dir.get(&key) {
256                let index_ptr = self
257                    .machine_st
258                    .arena
259                    .code_index_tbl
260                    .get_entry(code_idx.into());
261                let p = index_ptr.local().unwrap();
262
263                // Leave a halting choice point to backtrack to in case the predicate fails or throws.
264                self.allocate_stub_choice_point();
265
266                self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC;
267                self.machine_st.p = p;
268
269                return self.dispatch_loop();
270            }
271        }
272
273        unreachable!();
274    }
275
276    fn load_file(&mut self, path: &str, stream: Stream) {
277        self.machine_st.registers[1] = stream.into();
278        self.machine_st.registers[2] =
279            atom_as_cell!(AtomTable::build_with(&self.machine_st.atom_tbl, path));
280
281        self.run_module_predicate(atom!("loader"), (atom!("file_load"), 2));
282    }
283
284    fn load_top_level(&mut self, program: Cow<'static, str>) {
285        let mut path_buf = current_dir();
286
287        path_buf.push("src/toplevel.pl");
288
289        let path = path_buf.to_str().unwrap();
290        let toplevel_stream = match program {
291            Cow::Borrowed(s) => Stream::from_static_string(s, &mut self.machine_st.arena),
292            Cow::Owned(s) => Stream::from_owned_string(s, &mut self.machine_st.arena),
293        };
294
295        self.load_file(path, toplevel_stream);
296
297        if let Some(toplevel) = self.indices.modules.get(&atom!("$toplevel")) {
298            load_module(
299                &mut self.machine_st,
300                &mut self.indices.code_dir,
301                &mut self.indices.op_dir,
302                &mut self.indices.meta_predicates,
303                &CompilationTarget::User,
304                toplevel,
305            );
306        } else {
307            unreachable!()
308        }
309    }
310
311    fn load_special_forms(&mut self) {
312        let mut path_buf = current_dir();
313        path_buf.push("machine/attributed_variables.pl");
314
315        let stream = Stream::from_static_string(
316            include_str!("attributed_variables.pl"),
317            &mut self.machine_st.arena,
318        );
319
320        self.load_file(path_buf.to_str().unwrap(), stream);
321
322        let mut path_buf = current_dir();
323        path_buf.push("machine/project_attributes.pl");
324
325        let stream = Stream::from_static_string(
326            include_str!("project_attributes.pl"),
327            &mut self.machine_st.arena,
328        );
329
330        self.load_file(path_buf.to_str().unwrap(), stream);
331
332        if let Some(module) = self.indices.modules.get(&atom!("$atts")) {
333            if let Some(code_idx) = module.code_dir.get(&(atom!("driver"), 2)) {
334                let index_ptr = self
335                    .machine_st
336                    .arena
337                    .code_index_tbl
338                    .get_entry(code_idx.into());
339                self.machine_st.attr_var_init.verify_attrs_loc = index_ptr.local().unwrap();
340            }
341        }
342    }
343
344    pub(crate) fn configure_modules(&mut self) {
345        fn update_call_n_indices(
346            loader: &Module,
347            target_code_dir: &mut CodeDir,
348            arena: &mut Arena,
349        ) {
350            for arity in 1..66 {
351                let key = (atom!("call"), arity);
352
353                match loader.code_dir.get(&key).cloned() {
354                    Some(src_code_index) => {
355                        let code_index_tbl = &mut arena.code_index_tbl;
356
357                        let target_code_index = target_code_dir.entry(key).or_insert_with(|| {
358                            CodeIndex::new(IndexPtr::undefined(), code_index_tbl)
359                        });
360
361                        let src_code_ptr = code_index_tbl.get_entry(src_code_index.into());
362                        target_code_index.set(code_index_tbl, src_code_ptr);
363                    }
364                    None => {
365                        unreachable!();
366                    }
367                }
368            }
369        }
370
371        if let Some(loader) = self.indices.modules.swap_remove(&atom!("loader")) {
372            if let Some(builtins) = self.indices.modules.get_mut(&atom!("builtins")) {
373                // Import loader's exports into the builtins module so they will be
374                // implicitly included in every further module.
375                load_module(
376                    &mut self.machine_st,
377                    &mut builtins.code_dir,
378                    &mut builtins.op_dir,
379                    &mut builtins.meta_predicates,
380                    &CompilationTarget::Module(atom!("builtins")),
381                    &loader,
382                );
383
384                for export in &loader.module_decl.exports {
385                    builtins.module_decl.exports.push(export.clone());
386                }
387
388                for arity in 10..66 {
389                    builtins
390                        .module_decl
391                        .exports
392                        .push(ModuleExport::PredicateKey((atom!("call"), arity)));
393                }
394            }
395
396            for (_, target_module) in self.indices.modules.iter_mut() {
397                update_call_n_indices(
398                    &loader,
399                    &mut target_module.code_dir,
400                    &mut self.machine_st.arena,
401                );
402            }
403
404            update_call_n_indices(
405                &loader,
406                &mut self.indices.code_dir,
407                &mut self.machine_st.arena,
408            );
409
410            self.indices.modules.insert(atom!("loader"), loader);
411        } else {
412            unreachable!()
413        }
414    }
415
416    pub(crate) fn add_impls_to_indices(&mut self) {
417        let impls_offset = self.code.len() + 4;
418
419        self.code.extend(vec![
420            Instruction::BreakFromDispatchLoop,
421            Instruction::InstallVerifyAttr,
422            Instruction::VerifyAttrInterrupt(0),
423            Instruction::BreakFromDispatchLoop, // the location of LIB_QUERY_SUCCESS
424            Instruction::ExecuteTermGreaterThan,
425            Instruction::ExecuteTermLessThan,
426            Instruction::ExecuteTermGreaterThanOrEqual,
427            Instruction::ExecuteTermLessThanOrEqual,
428            Instruction::ExecuteTermEqual,
429            Instruction::ExecuteTermNotEqual,
430            Instruction::ExecuteNumberGreaterThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
431            Instruction::ExecuteNumberLessThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
432            Instruction::ExecuteNumberGreaterThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
433            Instruction::ExecuteNumberLessThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
434            Instruction::ExecuteNumberEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
435            Instruction::ExecuteNumberNotEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
436            Instruction::ExecuteIs(temp_v!(1), ar_reg!(temp_v!(2))),
437            Instruction::ExecuteAcyclicTerm,
438            Instruction::ExecuteArg,
439            Instruction::ExecuteCompare,
440            Instruction::ExecuteCopyTerm,
441            Instruction::ExecuteFunctor,
442            Instruction::ExecuteGround,
443            Instruction::ExecuteKeySort,
444            Instruction::ExecuteSort,
445            Instruction::ExecuteN(1),
446            Instruction::ExecuteN(2),
447            Instruction::ExecuteN(3),
448            Instruction::ExecuteN(4),
449            Instruction::ExecuteN(5),
450            Instruction::ExecuteN(6),
451            Instruction::ExecuteN(7),
452            Instruction::ExecuteN(8),
453            Instruction::ExecuteN(9),
454            Instruction::ExecuteIsAtom(temp_v!(1)),
455            Instruction::ExecuteIsAtomic(temp_v!(1)),
456            Instruction::ExecuteIsCompound(temp_v!(1)),
457            Instruction::ExecuteIsInteger(temp_v!(1)),
458            Instruction::ExecuteIsNumber(temp_v!(1)),
459            Instruction::ExecuteIsRational(temp_v!(1)),
460            Instruction::ExecuteIsFloat(temp_v!(1)),
461            Instruction::ExecuteIsNonVar(temp_v!(1)),
462            Instruction::ExecuteIsVar(temp_v!(1)),
463        ]);
464
465        for (p, instr) in self.code[impls_offset..].iter().enumerate() {
466            let key = instr.to_name_and_arity();
467            self.indices.code_dir.insert(
468                key,
469                CodeIndex::new(
470                    IndexPtr::index(p + impls_offset),
471                    &mut self.machine_st.arena.code_index_tbl,
472                ),
473            );
474        }
475    }
476
477    /// Ensures that [`Machine::indices`] properly reflects
478    /// the streams stored in [`Machine::user_input`], [`Machine::user_output`]
479    /// and [`Machine::user_error`].
480    pub(crate) fn configure_streams(&mut self) {
481        self.indices
482            .set_stream(atom!("user_input"), self.user_input);
483        self.indices
484            .set_stream(atom!("user_output"), self.user_output);
485        self.indices
486            .set_stream(atom!("user_error"), self.user_error);
487
488        let mut null_options = StreamOptions::default();
489        null_options.set_alias_to_atom_opt(Some(atom!("null_stream")));
490
491        self.indices
492            .set_stream(atom!("null_stream"), Stream::Null(null_options));
493    }
494
495    #[inline(always)]
496    pub(crate) fn run_verify_attr_interrupt(&mut self, arity: usize) {
497        let p = self.machine_st.attr_var_init.verify_attrs_loc;
498        step_or_resource_error!(
499            self.machine_st,
500            self.machine_st.verify_attr_interrupt(p, arity)
501        );
502    }
503
504    fn next_clause_applicable(&mut self, mut offset: usize) -> bool {
505        loop {
506            match &self.code[offset] {
507                Instruction::IndexingCode(indexing_lines) => {
508                    let mut oip = 0;
509                    let mut cell = empty_list_as_cell!();
510
511                    loop {
512                        let indexing_code_ptr = match &indexing_lines[oip] {
513                            &IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
514                                arg,
515                                v,
516                                c,
517                                l,
518                                s,
519                            )) => {
520                                cell = self.deref_register(arg);
521                                self.machine_st
522                                    .select_switch_on_term_index(cell, v, c, l, s)
523                            }
524                            IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(hm)) => {
525                                // let lit = self.machine_st.constant_to_literal(cell);
526                                hm.get(&cell).cloned().unwrap_or(IndexingCodePtr::Fail)
527                            }
528                            IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(hm)) => {
529                                self.machine_st.select_switch_on_structure_index(cell, hm)
530                            }
531                            _ => {
532                                offset += 1;
533                                break;
534                            }
535                        };
536
537                        match indexing_code_ptr {
538                            IndexingCodePtr::External(_) | IndexingCodePtr::DynamicExternal(_) => {
539                                offset += 1;
540                                break;
541                            }
542                            IndexingCodePtr::Internal(i) => oip += i,
543                            IndexingCodePtr::Fail => return false,
544                        }
545                    }
546                }
547                &Instruction::GetConstant(Level::Shallow, lit, RegType::Temp(t)) => {
548                    let cell = self.deref_register(t);
549
550                    if cell.is_var() {
551                        offset += 1;
552                    } else {
553                        unify!(self.machine_st, cell, lit);
554
555                        if self.machine_st.fail {
556                            self.machine_st.fail = false;
557                            return false;
558                        } else {
559                            offset += 1;
560                        }
561                    }
562                }
563                &Instruction::GetList(Level::Shallow, RegType::Temp(t)) => {
564                    let cell = self.deref_register(t);
565
566                    read_heap_cell!(cell,
567                        (HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => {// | HeapCellValueTag::CStr) => {
568                            offset += 1;
569                        }
570                        (HeapCellValueTag::Str, s) => {
571                            let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity();
572
573                            if name == atom!(".") && arity == 2 {
574                                offset += 1;
575                            } else {
576                                return false;
577                            }
578                        }
579                        (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
580                            offset += 1;
581                        }
582                        _ => {
583                            return false;
584                        }
585                    );
586                }
587                &Instruction::GetStructure(Level::Shallow, name, arity, RegType::Temp(t)) => {
588                    let cell = self.deref_register(t);
589
590                    read_heap_cell!(cell,
591                        (HeapCellValueTag::Str, s) => {
592                            if (name, arity) == cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity() {
593                                offset += 1;
594                            } else {
595                                return false;
596                            }
597                        }
598                        (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
599                            offset += 1;
600                        }
601                        _ => {
602                            return false;
603                        }
604                    );
605                }
606                &Instruction::GetPartialString(Level::Shallow, ref string, RegType::Temp(t)) => {
607                    let cell = self.deref_register(t);
608
609                    read_heap_cell!(cell,
610                        (HeapCellValueTag::PStrLoc, pstr_loc) => {
611                            let heap_slice = &self.machine_st.heap.as_slice()[pstr_loc ..];
612
613                            match compare_pstr_slices(heap_slice, string.as_bytes()) {
614                                PStrSegmentCmpResult::Continue(..) => offset += 1,
615                                _ => return false,
616                            }
617                        }
618                        (HeapCellValueTag::Lis) => {
619                            offset += 1;
620                        }
621                        (HeapCellValueTag::Str, s) => {
622                            let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s])
623                                .get_name_and_arity();
624
625                            if name == atom!(".") && arity == 2 {
626                                offset += 1;
627                            } else {
628                                return false;
629                            }
630                        }
631                        (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
632                            offset += 1;
633                        }
634                        _ => {
635                            return false;
636                        }
637                    );
638                }
639                Instruction::GetConstant(..)
640                | Instruction::GetList(..)
641                | Instruction::GetStructure(..)
642                | Instruction::GetPartialString(..)
643                | &Instruction::UnifyVoid(..)
644                | &Instruction::UnifyConstant(..)
645                | &Instruction::GetVariable(..)
646                | &Instruction::GetValue(..)
647                | &Instruction::UnifyVariable(..)
648                | &Instruction::UnifyValue(..)
649                | &Instruction::UnifyLocalValue(..) => {
650                    offset += 1;
651                }
652                _ => {
653                    break;
654                }
655            }
656        }
657
658        true
659    }
660
661    fn next_applicable_clause(&mut self, mut offset: usize) -> Option<usize> {
662        while !self.next_clause_applicable(self.machine_st.p + offset + 1) {
663            match &self.code[self.machine_st.p + offset] {
664                &Instruction::DefaultRetryMeElse(o)
665                | &Instruction::RetryMeElse(o)
666                | &Instruction::DynamicElse(.., NextOrFail::Next(o))
667                | &Instruction::DynamicInternalElse(.., NextOrFail::Next(o)) => offset += o,
668                _ => {
669                    return None;
670                }
671            }
672        }
673
674        Some(offset)
675    }
676
677    fn next_inner_applicable_clause(&mut self) -> Option<u32> {
678        let mut inner_offset = 1u32;
679
680        loop {
681            match &self.code[self.machine_st.p] {
682                Instruction::IndexingCode(indexing_lines) => {
683                    match &indexing_lines[self.machine_st.oip as usize] {
684                        IndexingLine::IndexedChoice(indexed_choice) => {
685                            match &indexed_choice[(self.machine_st.iip + inner_offset) as usize] {
686                                &IndexedChoiceInstruction::Retry(o)
687                                | &IndexedChoiceInstruction::DefaultRetry(o) => {
688                                    if self.next_clause_applicable(self.machine_st.p + o) {
689                                        return Some(inner_offset);
690                                    }
691
692                                    inner_offset += 1;
693                                }
694                                &IndexedChoiceInstruction::Trust(o)
695                                | &IndexedChoiceInstruction::DefaultTrust(o) => {
696                                    return if self.next_clause_applicable(self.machine_st.p + o) {
697                                        Some(inner_offset)
698                                    } else {
699                                        None
700                                    };
701                                }
702                                _ => unreachable!(),
703                            }
704                        }
705                        IndexingLine::DynamicIndexedChoice(indexed_choice) => {
706                            let idx = (self.machine_st.iip + inner_offset) as usize;
707                            let o = indexed_choice[idx];
708
709                            if idx + 1 == indexed_choice.len() {
710                                return if self.next_clause_applicable(self.machine_st.p + o) {
711                                    Some(inner_offset)
712                                } else {
713                                    None
714                                };
715                            } else {
716                                if self.next_clause_applicable(self.machine_st.p + o) {
717                                    return Some(inner_offset);
718                                }
719
720                                inner_offset += 1;
721                            }
722                        }
723                        _ => unreachable!(),
724                    }
725                }
726                _ => unreachable!(),
727            }
728        }
729    }
730
731    #[inline(always)]
732    pub(super) fn try_me_else(&mut self, offset: usize) {
733        if let Some(offset) = self.next_applicable_clause(offset) {
734            let n = self.machine_st.num_of_args;
735            let b = self.machine_st.stack.allocate_or_frame(n);
736            let or_frame = self.machine_st.stack.index_or_frame_mut(b);
737
738            or_frame.prelude.num_cells = n;
739            or_frame.prelude.e = self.machine_st.e;
740            or_frame.prelude.cp = self.machine_st.cp;
741            or_frame.prelude.b = self.machine_st.b;
742            or_frame.prelude.bp = self.machine_st.p + offset;
743            or_frame.prelude.boip = 0;
744            or_frame.prelude.biip = 0;
745            or_frame.prelude.tr = self.machine_st.tr;
746            or_frame.prelude.h = self.machine_st.heap.cell_len();
747            or_frame.prelude.b0 = self.machine_st.b0;
748            or_frame.prelude.attr_var_queue_len =
749                self.machine_st.attr_var_init.attr_var_queue.len();
750
751            self.machine_st.b = b;
752
753            for i in 0..n {
754                or_frame[i] = self.machine_st.registers[i + 1];
755            }
756
757            self.machine_st.hb = self.machine_st.heap.cell_len();
758        }
759
760        self.machine_st.p += 1;
761    }
762
763    #[inline(always)]
764    pub(super) fn indexed_try(&mut self, offset: usize) {
765        if let Some(iip_offset) = self.next_inner_applicable_clause() {
766            let n = self.machine_st.num_of_args;
767            let b = self.machine_st.stack.allocate_or_frame(n);
768            let or_frame = self.machine_st.stack.index_or_frame_mut(b);
769
770            or_frame.prelude.num_cells = n;
771            or_frame.prelude.e = self.machine_st.e;
772            or_frame.prelude.cp = self.machine_st.cp;
773            or_frame.prelude.b = self.machine_st.b;
774            or_frame.prelude.bp = self.machine_st.p;
775            or_frame.prelude.boip = self.machine_st.oip;
776            or_frame.prelude.biip = self.machine_st.iip + iip_offset; // 1
777            or_frame.prelude.tr = self.machine_st.tr;
778            or_frame.prelude.h = self.machine_st.heap.cell_len();
779            or_frame.prelude.b0 = self.machine_st.b0;
780            or_frame.prelude.attr_var_queue_len =
781                self.machine_st.attr_var_init.attr_var_queue.len();
782
783            self.machine_st.b = b;
784
785            for i in 0..n {
786                or_frame[i] = self.machine_st.registers[i + 1];
787            }
788
789            self.machine_st.hb = self.machine_st.heap.cell_len();
790
791            // self.machine_st.oip = 0;
792            // self.machine_st.iip = 0;
793        }
794
795        self.machine_st.p += offset;
796    }
797
798    #[inline(always)]
799    fn retry_me_else(&mut self, offset: usize) {
800        let b = self.machine_st.b;
801        let or_frame = self.machine_st.stack.index_or_frame_mut(b);
802        let n = or_frame.prelude.num_cells;
803
804        let old_tr = or_frame.prelude.tr;
805        let curr_tr = self.machine_st.tr;
806
807        for i in 0..n {
808            self.machine_st.registers[i + 1] = or_frame[i];
809        }
810
811        self.unwind_trail(old_tr, curr_tr);
812
813        if let Some(offset) = self.next_applicable_clause(offset) {
814            let or_frame = self.machine_st.stack.index_or_frame_mut(b);
815
816            self.machine_st.num_of_args = n;
817            self.machine_st.e = or_frame.prelude.e;
818            self.machine_st.cp = or_frame.prelude.cp;
819
820            or_frame.prelude.bp = self.machine_st.p + offset;
821
822            let target_h = or_frame.prelude.h;
823            let attr_var_queue_len = or_frame.prelude.attr_var_queue_len;
824
825            self.machine_st.tr = or_frame.prelude.tr;
826            self.reset_attr_var_state(attr_var_queue_len);
827
828            self.machine_st.hb = target_h;
829
830            self.machine_st.trail.truncate(self.machine_st.tr);
831            self.machine_st.heap.truncate(target_h);
832
833            self.machine_st.p += 1;
834        } else {
835            self.trust_me_epilogue();
836        }
837    }
838
839    #[inline(always)]
840    fn retry(&mut self, offset: usize) {
841        let b = self.machine_st.b;
842        let or_frame = self.machine_st.stack.index_or_frame_mut(b);
843        let n = or_frame.prelude.num_cells;
844
845        let old_tr = or_frame.prelude.tr;
846        let curr_tr = self.machine_st.tr;
847
848        for i in 0..n {
849            self.machine_st.registers[i + 1] = or_frame[i];
850        }
851
852        self.unwind_trail(old_tr, curr_tr);
853
854        if let Some(iip_offset) = self.next_inner_applicable_clause() {
855            let or_frame = self.machine_st.stack.index_or_frame_mut(b);
856
857            self.machine_st.num_of_args = n;
858            self.machine_st.e = or_frame.prelude.e;
859            self.machine_st.cp = or_frame.prelude.cp;
860
861            or_frame.prelude.biip += iip_offset;
862
863            let target_h = or_frame.prelude.h;
864            let attr_var_queue_len = or_frame.prelude.attr_var_queue_len;
865
866            self.machine_st.tr = or_frame.prelude.tr;
867            self.machine_st.trail.truncate(self.machine_st.tr);
868
869            self.reset_attr_var_state(attr_var_queue_len);
870
871            self.machine_st.hb = target_h;
872            self.machine_st.p += offset;
873
874            self.machine_st.heap.truncate(target_h);
875
876            // these registers don't need to be reset here and MUST
877            // NOT be (nor in indexed_try! trust_epilogue is an
878            // exception, see next paragraph)! oip could be reset
879            // without any adverse effects but iip is needed by
880            // get_clause_p to find the last executed clause/2 clause.
881
882            // trust_epilogue must reset these for the sake of
883            // subsequent predicates beginning with
884            // switch_to_term. get_clause_p copes by checking
885            // self.machine_st.b > self.machine.e: if true, it is safe
886            // to use self.machine_st.iip; if false, use the choice
887            // point left at the top of the stack by '$clause'
888            // (specifically its biip value).
889
890            // self.machine_st.oip = 0;
891            // self.machine_st.iip = 0;
892        } else {
893            self.trust_epilogue(offset);
894        }
895    }
896
897    #[inline(always)]
898    fn trust(&mut self, offset: usize) {
899        let b = self.machine_st.b;
900        let or_frame = self.machine_st.stack.index_or_frame(b);
901        let n = or_frame.prelude.num_cells;
902
903        let old_tr = or_frame.prelude.tr;
904        let curr_tr = self.machine_st.tr;
905
906        for i in 0..n {
907            self.machine_st.registers[i + 1] = or_frame[i];
908        }
909
910        self.unwind_trail(old_tr, curr_tr);
911        self.trust_epilogue(offset);
912    }
913
914    #[inline(always)]
915    fn trust_epilogue(&mut self, offset: usize) {
916        let b = self.machine_st.b;
917        let or_frame = self.machine_st.stack.index_or_frame(b);
918        let n = or_frame.prelude.num_cells;
919
920        self.machine_st.num_of_args = n;
921        self.machine_st.e = or_frame.prelude.e;
922        self.machine_st.cp = or_frame.prelude.cp;
923
924        let target_h = or_frame.prelude.h;
925
926        self.machine_st.tr = or_frame.prelude.tr;
927        self.machine_st.trail.truncate(self.machine_st.tr);
928
929        self.machine_st.b = or_frame.prelude.b;
930
931        self.reset_attr_var_state(or_frame.prelude.attr_var_queue_len);
932
933        self.machine_st.hb = target_h;
934        self.machine_st.p += offset;
935
936        self.machine_st.stack.truncate(b);
937        self.machine_st.heap.truncate(target_h);
938
939        self.machine_st.oip = 0;
940        self.machine_st.iip = 0;
941    }
942
943    #[inline(always)]
944    fn trust_me(&mut self) {
945        let b = self.machine_st.b;
946        let or_frame = self.machine_st.stack.index_or_frame(b);
947        let n = or_frame.prelude.num_cells;
948
949        for i in 0..n {
950            self.machine_st.registers[i + 1] = or_frame[i];
951        }
952
953        let old_tr = or_frame.prelude.tr;
954        let curr_tr = self.machine_st.tr;
955
956        self.unwind_trail(old_tr, curr_tr);
957
958        self.trust_me_epilogue();
959    }
960
961    #[inline(always)]
962    fn trust_me_epilogue(&mut self) {
963        let b = self.machine_st.b;
964        let or_frame = self.machine_st.stack.index_or_frame(b);
965        let n = or_frame.prelude.num_cells;
966
967        self.machine_st.num_of_args = n;
968        self.machine_st.e = or_frame.prelude.e;
969        self.machine_st.cp = or_frame.prelude.cp;
970
971        let target_h = or_frame.prelude.h;
972
973        self.machine_st.tr = or_frame.prelude.tr;
974        self.machine_st.b = or_frame.prelude.b;
975
976        self.reset_attr_var_state(or_frame.prelude.attr_var_queue_len);
977
978        self.machine_st.hb = target_h;
979        self.machine_st.p += 1;
980
981        self.machine_st.trail.truncate(self.machine_st.tr);
982        self.machine_st.stack.truncate(b);
983        self.machine_st.heap.truncate(target_h);
984    }
985
986    #[inline(always)]
987    fn undefined_procedure(&mut self, name: Atom, arity: usize) -> CallResult {
988        match self.machine_st.flags.unknown {
989            Unknown::Error => Err(self.machine_st.throw_undefined_error(name, arity)),
990            Unknown::Fail => {
991                self.machine_st.fail = true;
992                Ok(())
993            }
994            Unknown::Warn => {
995                println!(
996                    "% Warning: predicate {}/{} is undefined",
997                    name.as_str(),
998                    arity
999                );
1000                self.machine_st.fail = true;
1001                Ok(())
1002            }
1003        }
1004    }
1005
1006    #[inline(always)]
1007    fn try_call(&mut self, name: Atom, arity: usize, idx: IndexPtr) -> CallResult {
1008        let compiled_tl_index = idx.p() as usize;
1009
1010        match idx.tag() {
1011            IndexPtrTag::DynamicUndefined => {
1012                self.machine_st.fail = true;
1013            }
1014            IndexPtrTag::Undefined => {
1015                return self.undefined_procedure(name, arity);
1016            }
1017            IndexPtrTag::DynamicIndex => {
1018                self.machine_st.dynamic_mode = FirstOrNext::First;
1019                self.machine_st.call_at_index(arity, compiled_tl_index);
1020            }
1021            IndexPtrTag::Index => {
1022                self.machine_st.call_at_index(arity, compiled_tl_index);
1023            }
1024        }
1025
1026        Ok(())
1027    }
1028
1029    #[inline(always)]
1030    fn try_execute(&mut self, name: Atom, arity: usize, idx: IndexPtr) -> CallResult {
1031        let compiled_tl_index = idx.p() as usize;
1032
1033        match idx.tag() {
1034            IndexPtrTag::DynamicUndefined => {
1035                self.machine_st.fail = true;
1036            }
1037            IndexPtrTag::Undefined => {
1038                return self.undefined_procedure(name, arity);
1039            }
1040            IndexPtrTag::DynamicIndex => {
1041                self.machine_st.dynamic_mode = FirstOrNext::First;
1042                self.machine_st.execute_at_index(arity, compiled_tl_index);
1043            }
1044            IndexPtrTag::Index => self.machine_st.execute_at_index(arity, compiled_tl_index),
1045        }
1046
1047        Ok(())
1048    }
1049
1050    #[inline(always)]
1051    fn call_clause(&mut self, module_name: Atom, key: PredicateKey) -> CallResult {
1052        let (name, arity) = key;
1053
1054        if module_name == atom!("user") {
1055            if let Some(idx) = self.indices.code_dir.get(&(name, arity)).cloned() {
1056                let index_ptr = self.machine_st.arena.code_index_tbl.get_entry(idx.into());
1057                self.try_call(name, arity, index_ptr)
1058            } else {
1059                Err(self.machine_st.throw_undefined_error(name, arity))
1060            }
1061        } else if let Some(module) = self.indices.modules.get(&module_name) {
1062            if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() {
1063                let index_ptr = self.machine_st.arena.code_index_tbl.get_entry(idx.into());
1064                self.try_call(name, arity, index_ptr)
1065            } else {
1066                self.undefined_procedure(name, arity)
1067            }
1068        } else {
1069            let stub = functor_stub(name, arity);
1070            let err = self
1071                .machine_st
1072                .existence_error(ExistenceError::QualifiedProcedure {
1073                    module_name,
1074                    name,
1075                    arity,
1076                });
1077
1078            Err(self.machine_st.error_form(err, stub))
1079        }
1080    }
1081
1082    #[inline(always)]
1083    fn execute_clause(&mut self, module_name: Atom, key: PredicateKey) -> CallResult {
1084        let (name, arity) = key;
1085
1086        if module_name == atom!("user") {
1087            if let Some(offset) = self.indices.code_dir.get(&(name, arity)).cloned() {
1088                let index_ptr = self
1089                    .machine_st
1090                    .arena
1091                    .code_index_tbl
1092                    .get_entry(offset.into());
1093                self.try_execute(name, arity, index_ptr)
1094            } else {
1095                self.undefined_procedure(name, arity)
1096            }
1097        } else if let Some(module) = self.indices.modules.get(&module_name) {
1098            if let Some(offset) = module.code_dir.get(&(name, arity)).cloned() {
1099                let index_ptr = self
1100                    .machine_st
1101                    .arena
1102                    .code_index_tbl
1103                    .get_entry(offset.into());
1104                self.try_execute(name, arity, index_ptr)
1105            } else {
1106                self.undefined_procedure(name, arity)
1107            }
1108        } else {
1109            let stub = functor_stub(name, arity);
1110            let err = self
1111                .machine_st
1112                .existence_error(ExistenceError::QualifiedProcedure {
1113                    module_name,
1114                    name,
1115                    arity,
1116                });
1117
1118            Err(self.machine_st.error_form(err, stub))
1119        }
1120    }
1121
1122    #[inline(always)]
1123    fn call_n(&mut self, module_name: Atom, arity: usize) -> CallResult {
1124        let key = self.machine_st.setup_call_n(arity)?;
1125        self.call_clause(module_name, key)
1126    }
1127
1128    #[inline(always)]
1129    fn execute_n(&mut self, module_name: Atom, arity: usize) -> CallResult {
1130        let key = self.machine_st.setup_call_n(arity)?;
1131        self.execute_clause(module_name, key)
1132    }
1133
1134    #[inline(always)]
1135    fn run_cleaners(&mut self) -> bool {
1136        static CLEANER_INIT: OnceLock<(usize, usize)> = OnceLock::new();
1137
1138        let (r_c_w_h, r_c_wo_h) = *CLEANER_INIT.get_or_init(|| {
1139            let r_c_w_h_atom = atom!("run_cleaners_with_handling");
1140            let r_c_wo_h_atom = atom!("run_cleaners_without_handling");
1141            let iso_ext = atom!("iso_ext");
1142
1143            let r_c_w_h = self
1144                .indices
1145                .get_predicate_code_index(r_c_w_h_atom, 0, iso_ext)
1146                .and_then(|code_idx| {
1147                    self.machine_st
1148                        .arena
1149                        .code_index_tbl
1150                        .get_entry(code_idx.into())
1151                        .local()
1152                })
1153                .unwrap();
1154            let r_c_wo_h = self
1155                .indices
1156                .get_predicate_code_index(r_c_wo_h_atom, 1, iso_ext)
1157                .and_then(|code_idx| {
1158                    self.machine_st
1159                        .arena
1160                        .code_index_tbl
1161                        .get_entry(code_idx.into())
1162                        .local()
1163                })
1164                .unwrap();
1165            (r_c_w_h, r_c_wo_h)
1166        });
1167
1168        if let Some(&(_, b_cutoff, prev_block)) = self.machine_st.cont_pts.last() {
1169            if self.machine_st.b < b_cutoff {
1170                let (idx, arity) = if self.machine_st.effective_block() > prev_block {
1171                    (r_c_w_h, 0)
1172                } else {
1173                    self.machine_st.registers[1] = fixnum_as_cell!(
1174                        /* FIXME this is not safe */
1175                        unsafe { Fixnum::build_with_unchecked(b_cutoff as i64) }
1176                    );
1177
1178                    (r_c_wo_h, 1)
1179                };
1180
1181                self.machine_st.call_at_index(arity, idx);
1182                return true;
1183            }
1184        }
1185
1186        false
1187    }
1188
1189    pub(super) fn unwind_trail(&mut self, a1: usize, a2: usize) {
1190        // the sequence is reversed to respect the chronology of trail
1191        // additions now that deleted attributes can be undeleted by
1192        // backtracking.
1193        for i in (a1..a2).rev() {
1194            let h = self.machine_st.trail[i].get_value() as usize;
1195
1196            match self.machine_st.trail[i].get_tag() {
1197                TrailEntryTag::TrailedHeapVar => {
1198                    self.machine_st.heap[h] = heap_loc_as_cell!(h);
1199                }
1200                TrailEntryTag::TrailedStackVar => {
1201                    self.machine_st.stack[h] = stack_loc_as_cell!(h);
1202                }
1203                TrailEntryTag::TrailedAttrVar => {
1204                    self.machine_st.heap[h] = attr_var_as_cell!(h);
1205                }
1206                TrailEntryTag::TrailedAttrVarListLink => {
1207                    let l = self.machine_st.trail[i + 1].get_value() as usize;
1208
1209                    if l < self.machine_st.hb {
1210                        if h == l {
1211                            self.machine_st.heap[h] = heap_loc_as_cell!(h);
1212                        } else {
1213                            read_heap_cell!(self.machine_st.heap[l],
1214                                (HeapCellValueTag::Var) => {
1215                                    self.machine_st.heap[h] = list_loc_as_cell!(l);
1216                                }
1217                                _ => {
1218                                    self.machine_st.heap[h] = heap_loc_as_cell!(l);
1219                                }
1220                            );
1221                        }
1222                    } else {
1223                        self.machine_st.heap[h] = heap_loc_as_cell!(h);
1224                    }
1225                }
1226                TrailEntryTag::TrailedBlackboardEntry => {
1227                    let key = Atom::from(h as u64);
1228
1229                    match self.indices.global_variables.get_mut(&key) {
1230                        Some((_, ref mut loc)) => *loc = None,
1231                        None => unreachable!(),
1232                    }
1233                }
1234                TrailEntryTag::TrailedBlackboardOffset => {
1235                    let key = Atom::from(h as u64);
1236                    let value_cell = HeapCellValue::from(u64::from(self.machine_st.trail[i + 1]));
1237
1238                    match self.indices.global_variables.get_mut(&key) {
1239                        Some((_, ref mut loc)) => *loc = Some(value_cell),
1240                        None => unreachable!(),
1241                    }
1242                }
1243                TrailEntryTag::TrailedAttachedValue => {}
1244            }
1245        }
1246    }
1247}
1248
1249#[cfg(test)]
1250mod tests {
1251    use super::config::*;
1252    use super::*;
1253
1254    #[test]
1255    #[cfg_attr(miri, ignore)]
1256    fn test_run_module_predicate_throw() {
1257        let mut machine = MachineBuilder::default()
1258            .with_toplevel(
1259                r#"
1260                    :- module('$toplevel', []).
1261                    repl :- throw(kaboom).
1262                "#,
1263            )
1264            .build();
1265
1266        machine.run_module_predicate(atom!("$toplevel"), (atom!("repl"), 0));
1267    }
1268}