]> Repositorios git - scryer-prolog.git/commitdiff
Merge branch 'add_float_integer_part_and_float_fractional_part_standard_functions... pmoura-add_float_integer_part_and_float_fractional_part_standard_functions origin/pmoura-add_float_integer_part_and_float_fractional_part_standard_functions
authorMark <[email protected]>
Fri, 21 Jul 2023 21:13:29 +0000 (15:13 -0600)
committerMark <[email protected]>
Fri, 21 Jul 2023 21:35:28 +0000 (15:35 -0600)
1  2 
build/instructions_template.rs
src/arithmetic.rs
src/machine/arithmetic_ops.rs
src/machine/dispatch.rs

index 8ec818e649e4390ed748882af11d71b2f19a31e4,0000000000000000000000000000000000000000..3ac76eff89071432bc607e9480edd0dfcb9f681b
mode 100644,000000..100644
--- /dev/null
@@@ -1,3480 -1,0 +1,3490 @@@
 +// use crate::atom_table::*;
 +// use crate::machine::machine_indices::*;
 +// use crate::types::*;
 +//
 +
 +use proc_macro2::TokenStream;
 +use quote::{format_ident, quote, ToTokens, TokenStreamExt};
 +use strum_macros::{EnumDiscriminants, EnumProperty, EnumString};
 +use syn::*;
 +use to_syn_value_derive::ToDeriveInput;
 +
 +/*
 + * This crate exists to generate the Instruction enum in
 + * src/instructions.rs and its adjoining impl functions. The types
 + * defined in it are empty and serve only as schema for the generation
 + * of Instruction. They mimick most of the structure of the previous
 + * Line instruction type. The strum crate is used to provide reflection
 + * on each of the node types to the tree walker.
 + */
 +
 +use std::any::*;
 +use std::str::FromStr;
 +
 +struct ArithmeticTerm;
 +struct Atom;
 +struct CodeIndex;
 +struct Death;
 +struct HeapCellValue;
 +struct IndexingLine;
 +struct Level;
 +struct NextOrFail;
 +struct RegType;
 +
 +#[allow(dead_code)]
 +#[derive(ToDeriveInput, EnumDiscriminants)]
 +#[strum_discriminants(derive(EnumProperty, EnumString))]
 +enum CompareNumber {
 +    #[strum_discriminants(strum(props(Arity = "2", Name = ">")))]
 +    NumberGreaterThan(ArithmeticTerm, ArithmeticTerm),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "<")))]
 +    NumberLessThan(ArithmeticTerm, ArithmeticTerm),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = ">=")))]
 +    NumberGreaterThanOrEqual(ArithmeticTerm, ArithmeticTerm),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "=<")))]
 +    NumberLessThanOrEqual(ArithmeticTerm, ArithmeticTerm),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "=\\=")))]
 +    NumberNotEqual(ArithmeticTerm, ArithmeticTerm),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "=:=")))]
 +    NumberEqual(ArithmeticTerm, ArithmeticTerm),
 +}
 +
 +#[allow(dead_code)]
 +#[derive(ToDeriveInput, EnumDiscriminants)]
 +#[strum_discriminants(derive(EnumProperty, EnumString))]
 +enum CompareTerm {
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "@<")))]
 +    TermLessThan,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "@=<")))]
 +    TermLessThanOrEqual,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "@>=")))]
 +    TermGreaterThanOrEqual,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "@>")))]
 +    TermGreaterThan,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "==")))]
 +    TermEqual,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "\\==")))]
 +    TermNotEqual,
 +}
 +
 +#[allow(dead_code)]
 +#[derive(ToDeriveInput, EnumDiscriminants)]
 +#[strum_discriminants(derive(EnumProperty, EnumString))]
 +enum ClauseType {
 +    BuiltIn(BuiltInClauseType),
 +    #[strum_discriminants(strum(props(Arity = "arity", Name = "$call")))]
 +    CallN(usize),
 +    Inlined(InlinedClauseType),
 +    #[strum_discriminants(strum(props(Arity = "arity", Name = "call_named")))]
 +    Named(usize, Atom, CodeIndex), // name, arity, index.
 +    System(SystemClauseType),
 +}
 +
 +#[allow(dead_code)]
 +#[derive(ToDeriveInput, EnumDiscriminants)]
 +#[strum_discriminants(derive(EnumProperty, EnumString))]
 +enum BuiltInClauseType {
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "acyclic_term")))]
 +    AcyclicTerm,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "arg")))]
 +    Arg,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "compare")))]
 +    Compare,
 +    CompareTerm(CompareTerm),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "copy_term")))]
 +    CopyTerm,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "functor")))]
 +    Functor,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "ground")))]
 +    Ground,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "is")))]
 +    Is(RegType, ArithmeticTerm),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "keysort")))]
 +    KeySort,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "sort")))]
 +    Sort,
 +}
 +
 +#[allow(dead_code)]
 +#[derive(ToDeriveInput, EnumDiscriminants)]
 +#[strum_discriminants(derive(EnumProperty, EnumString))]
 +enum InlinedClauseType {
 +    CompareNumber(CompareNumber),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "atom")))]
 +    IsAtom(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "atomic")))]
 +    IsAtomic(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "compound")))]
 +    IsCompound(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "integer")))]
 +    IsInteger(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "number")))]
 +    IsNumber(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "rational")))]
 +    IsRational(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "float")))]
 +    IsFloat(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "nonvar")))]
 +    IsNonVar(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "var")))]
 +    IsVar(RegType),
 +}
 +
 +#[allow(dead_code)]
 +#[derive(ToDeriveInput, EnumDiscriminants)]
 +#[strum_discriminants(derive(EnumProperty, EnumString))]
 +enum REPLCodePtr {
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "$add_discontiguous_predicate")))]
 +    AddDiscontiguousPredicate,
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "$add_dynamic_predicate")))]
 +    AddDynamicPredicate,
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "$add_multifile_predicate")))]
 +    AddMultifilePredicate,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$add_goal_expansion_clause")))]
 +    AddGoalExpansionClause,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$add_term_expansion_clause")))]
 +    AddTermExpansionClause,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$add_in_situ_filename_module")))]
 +    AddInSituFilenameModule,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$clause_to_evacuable")))]
 +    ClauseToEvacuable,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$scoped_clause_to_evacuable")))]
 +    ScopedClauseToEvacuable,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$conclude_load")))]
 +    ConcludeLoad,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$declare_module")))]
 +    DeclareModule,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$load_compiled_library")))]
 +    LoadCompiledLibrary,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$prolog_lc_source")))]
 +    LoadContextSource,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$prolog_lc_file")))]
 +    LoadContextFile,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$prolog_lc_dir")))]
 +    LoadContextDirectory,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$prolog_lc_module")))]
 +    LoadContextModule,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$prolog_lc_stream")))]
 +    LoadContextStream,
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "$pop_load_context")))]
 +    PopLoadContext,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$pop_load_state_payload")))]
 +    PopLoadStatePayload,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$push_load_state_payload")))]
 +    PushLoadStatePayload,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$push_load_context")))]
 +    PushLoadContext,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$use_module")))]
 +    UseModule,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$built_in_property")))]
 +    BuiltInProperty,
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "$meta_predicate_property")))]
 +    MetaPredicateProperty,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$multifile_property")))]
 +    MultifileProperty,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$discontiguous_property")))]
 +    DiscontiguousProperty,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$dynamic_property")))]
 +    DynamicProperty,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$abolish_clause")))]
 +    AbolishClause,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$asserta")))]
 +    Asserta,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$assertz")))]
 +    Assertz,
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "$retract_clause")))]
 +    Retract,
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "$is_consistent_with_term_queue")))]
 +    IsConsistentWithTermQueue,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$flush_term_queue")))]
 +    FlushTermQueue,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$remove_module_exports")))]
 +    RemoveModuleExports,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$add_non_counted_backtracking")))]
 +    AddNonCountedBacktracking,
 +}
 +
 +#[allow(dead_code)]
 +#[derive(ToDeriveInput, EnumDiscriminants)]
 +#[strum_discriminants(derive(EnumProperty, EnumString))]
 +enum SystemClauseType {
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$atom_chars")))]
 +    AtomChars,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$atom_codes")))]
 +    AtomCodes,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$atom_length")))]
 +    AtomLength,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$bind_from_register")))]
 +    BindFromRegister,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$call_continuation")))]
 +    CallContinuation,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$char_code")))]
 +    CharCode,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$char_type")))]
 +    CharType,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$chars_to_number")))]
 +    CharsToNumber,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$codes_to_number")))]
 +    CodesToNumber,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$copy_term_without_attr_vars")))]
 +    CopyTermWithoutAttrVars,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$check_cp")))]
 +    CheckCutPoint,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$close")))]
 +    Close,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$copy_to_lh")))]
 +    CopyToLiftedHeap,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$create_partial_string")))]
 +    CreatePartialString,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$current_hostname")))]
 +    CurrentHostname,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$current_input")))]
 +    CurrentInput,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$current_output")))]
 +    CurrentOutput,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$directory_files")))]
 +    DirectoryFiles,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$file_size")))]
 +    FileSize,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$file_exists")))]
 +    FileExists,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$directory_exists")))]
 +    DirectoryExists,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$directory_separator")))]
 +    DirectorySeparator,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$make_directory")))]
 +    MakeDirectory,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$make_directory_path")))]
 +    MakeDirectoryPath,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$delete_file")))]
 +    DeleteFile,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$rename_file")))]
 +    RenameFile,
 +    #[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")))]
 +    DeleteDirectory,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$path_canonical")))]
 +    PathCanonical,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$file_time")))]
 +    FileTime,
 +    #[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 = "2", Name = "$fetch_global_var")))]
 +    FetchGlobalVar,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$first_stream")))]
 +    FirstStream,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$flush_output")))]
 +    FlushOutput,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$get_byte")))]
 +    GetByte,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$get_char")))]
 +    GetChar,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$get_n_chars")))]
 +    GetNChars,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$get_code")))]
 +    GetCode,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$get_single_char")))]
 +    GetSingleChar,
 +    #[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")))]
 +    TruncateIfNoLiftedHeapGrowth,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$get_attr_list")))]
 +    GetAttributedVariableList,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$get_attr_var_queue_delim")))]
 +    GetAttrVarQueueDelimiter,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$get_attr_var_queue_beyond")))]
 +    GetAttrVarQueueBeyond,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$get_b_value")))]
 +    GetBValue,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$get_cont_chunk")))]
 +    GetContinuationChunk,
 +    #[strum_discriminants(strum(props(Arity = "7", Name = "$get_next_op_db_ref")))]
 +    GetNextOpDBRef,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$lookup_db_ref")))]
 +    LookupDBRef,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$is_partial_string")))]
 +    IsPartialString,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$halt")))]
 +    Halt,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$get_lh_from_offset")))]
 +    GetLiftedHeapFromOffset,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$get_lh_from_offset_diff")))]
 +    GetLiftedHeapFromOffsetDiff,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$get_scc_cleaner")))]
 +    GetSCCCleaner,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$head_is_dynamic")))]
 +    HeadIsDynamic,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$install_scc_cleaner")))]
 +    InstallSCCCleaner,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$install_inference_counter")))]
 +    InstallInferenceCounter,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$lh_length")))]
 +    LiftedHeapLength,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$load_library_as_stream")))]
 +    LoadLibraryAsStream,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$module_exists")))]
 +    ModuleExists,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$nextEP")))]
 +    NextEP,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$no_such_predicate")))]
 +    NoSuchPredicate,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$number_to_chars")))]
 +    NumberToChars,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$number_to_codes")))]
 +    NumberToCodes,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$op")))]
 +    OpDeclaration,
 +    #[strum_discriminants(strum(props(Arity = "7", Name = "$open")))]
 +    Open,
 +    #[strum_discriminants(strum(props(Arity = "5", Name = "$set_stream_options")))]
 +    SetStreamOptions,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$next_stream")))]
 +    NextStream,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$partial_string_tail")))]
 +    PartialStringTail,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$peek_byte")))]
 +    PeekByte,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$peek_char")))]
 +    PeekChar,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$peek_code")))]
 +    PeekCode,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$points_to_cont_reset_marker")))]
 +    PointsToContinuationResetMarker,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$put_byte")))]
 +    PutByte,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$put_char")))]
 +    PutChar,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$put_chars")))]
 +    PutChars,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$put_code")))]
 +    PutCode,
 +    #[strum_discriminants(strum(props(Arity = "5", Name = "$read_query_term")))]
 +    ReadQueryTerm,
 +    #[strum_discriminants(strum(props(Arity = "5", Name = "$read_term")))]
 +    ReadTerm,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$redo_attr_var_binding")))]
 +    RedoAttrVarBinding,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$remove_call_policy_check")))]
 +    RemoveCallPolicyCheck,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$remove_inference_counter")))]
 +    RemoveInferenceCounter,
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "$reset_cont_marker")))]
 +    ResetContinuationMarker,
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "$restore_cut_policy")))]
 +    RestoreCutPolicy,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$set_cp")))]
 +    SetCutPoint(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$set_input")))]
 +    SetInput,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$set_output")))]
 +    SetOutput,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$store_backtrackable_global_var")))]
 +    StoreBacktrackableGlobalVar,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$store_global_var")))]
 +    StoreGlobalVar,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$stream_property")))]
 +    StreamProperty,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$set_stream_position")))]
 +    SetStreamPosition,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$inference_level")))]
 +    InferenceLevel,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$clean_up_block")))]
 +    CleanUpBlock,
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "$fail")))]
 +    Fail,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$get_ball")))]
 +    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")))]
 +    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")))]
 +    Maybe,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$current_time")))]
 +    CurrentTime,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$quoted_token")))]
 +    QuotedToken,
 +    #[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,
 +    #[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")))]
 +    SetBall,
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "$push_ball_stack")))]
 +    PushBallStack,
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "$pop_ball_stack")))]
 +    PopBallStack,
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "$pop_from_ball_stack")))]
 +    PopFromBallStack,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$set_cp_by_default")))]
 +    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")))]
 +    SkipMaxList,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$sleep")))]
 +    Sleep,
 +    #[strum_discriminants(strum(props(Arity = "7", Name = "$socket_client_open")))]
 +    SocketClientOpen,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$socket_server_open")))]
 +    SocketServerOpen,
 +    #[strum_discriminants(strum(props(Arity = "7", Name = "$socket_server_accept")))]
 +    SocketServerAccept,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$socket_server_close")))]
 +    SocketServerClose,
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "$tls_accept_client")))]
 +    TLSAcceptClient,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$tls_client_connect")))]
 +    TLSClientConnect,
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "$succeed")))]
 +    Succeed,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$term_attributed_variables")))]
 +    TermAttributedVariables,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$term_variables")))]
 +    TermVariables,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$term_variables_under_max_depth")))]
 +    TermVariablesUnderMaxDepth,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$truncate_lh_to")))]
 +    TruncateLiftedHeapTo,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$unify_with_occurs_check")))]
 +    UnifyWithOccursCheck,
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "$unwind_environments")))]
 +    UnwindEnvironments,
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "$unwind_stack")))]
 +    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 = "8", Name = "$write_term")))]
 +    WriteTerm,
 +    #[strum_discriminants(strum(props(Arity = "8", Name = "$write_term_to_chars")))]
 +    WriteTermToChars,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$scryer_prolog_version")))]
 +    ScryerPrologVersion,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$crypto_random_byte")))]
 +    CryptoRandomByte,
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "$crypto_data_hash")))]
 +    CryptoDataHash,
 +    #[strum_discriminants(strum(props(Arity = "7", Name = "$crypto_data_hkdf")))]
 +    CryptoDataHKDF,
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "$crypto_password_hash")))]
 +    CryptoPasswordHash,
 +    #[strum_discriminants(strum(props(Arity = "7", Name = "$crypto_data_encrypt")))]
 +    CryptoDataEncrypt,
 +    #[strum_discriminants(strum(props(Arity = "6", Name = "$crypto_data_decrypt")))]
 +    CryptoDataDecrypt,
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "$crypto_curve_scalar_mult")))]
 +    CryptoCurveScalarMult,
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "$ed25519_sign")))]
 +    Ed25519Sign,
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "$ed25519_verify")))]
 +    Ed25519Verify,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$ed25519_new_keypair")))]
 +    Ed25519NewKeyPair,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$ed25519_keypair_public_key")))]
 +    Ed25519KeyPairPublicKey,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$curve25519_scalar_mult")))]
 +    Curve25519ScalarMult,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$first_non_octet")))]
 +    FirstNonOctet,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$load_html")))]
 +    LoadHTML,
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "$load_xml")))]
 +    LoadXML,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$getenv")))]
 +    GetEnv,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$setenv")))]
 +    SetEnv,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$unsetenv")))]
 +    UnsetEnv,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$shell")))]
 +    Shell,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$pid")))]
 +    PID,
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "$chars_base64")))]
 +    CharsBase64,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$devour_whitespace")))]
 +    DevourWhitespace,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$is_sto_enabled")))]
 +    IsSTOEnabled,
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "$set_sto_as_unify")))]
 +    SetSTOAsUnify,
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "$set_nsto_as_unify")))]
 +    SetNSTOAsUnify,
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "$set_sto_with_error_as_unify")))]
 +    SetSTOWithErrorAsUnify,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$home_directory")))]
 +    HomeDirectory,
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "$debug_hook")))]
 +    DebugHook,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$popcount")))]
 +    PopCount,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$cpu_now")))]
 +    CpuNow,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$det_length_rundown")))]
 +    DeterministicLengthRundown,
 +    #[strum_discriminants(strum(props(Arity = "7", Name = "$http_open")))]
 +    HttpOpen,
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "$http_listen")))]
 +    HttpListen,
 +    #[strum_discriminants(strum(props(Arity = "7", Name = "$http_accept")))]
 +    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")))]
 +    StripModule,
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "$compile_inline_or_expanded_goal")))]
 +    CompileInlineOrExpandedGoal,
 +    #[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")))]
 +    GetClauseP,
 +    #[strum_discriminants(strum(props(Arity = "6", Name = "$invoke_clause_at_p")))]
 +    InvokeClauseAtP,
 +    #[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,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$delete_all_attributes_from_var")))]
 +    DeleteAllAttributesFromVar,
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "$unattributed_var")))]
 +    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),
 +}
 +
 +#[allow(dead_code)]
 +#[derive(ToDeriveInput, EnumDiscriminants)]
 +#[strum_discriminants(derive(EnumProperty, EnumString))]
 +enum InstructionTemplate {
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "get_constant")))]
 +    GetConstant(Level, HeapCellValue, RegType),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "get_list")))]
 +    GetList(Level, RegType),
 +    #[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(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")))]
 +    GetValue(RegType, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "unify_constant")))]
 +    UnifyConstant(HeapCellValue),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "unify_local_value")))]
 +    UnifyLocalValue(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "unify_variable")))]
 +    UnifyVariable(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "unify_value")))]
 +    UnifyValue(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "unify_void")))]
 +    UnifyVoid(usize),
 +    // query instruction
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "put_constant")))]
 +    PutConstant(Level, HeapCellValue, RegType),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "put_list")))]
 +    PutList(Level, RegType),
 +    #[strum_discriminants(strum(props(Arity = "4", Name = "put_partial_string")))]
 +    PutPartialString(Level, Atom, RegType, bool),
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "put_structure")))]
 +    PutStructure(Atom, usize, RegType),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "put_unsafe_value")))]
 +    PutUnsafeValue(usize, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "put_value")))]
 +    PutValue(RegType, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "put_variable")))]
 +    PutVariable(RegType, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "set_constant")))]
 +    SetConstant(HeapCellValue),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "set_local_value")))]
 +    SetLocalValue(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "set_variable")))]
 +    SetVariable(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "set_value")))]
 +    SetValue(RegType),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "set_void")))]
 +    SetVoid(usize),
 +    // cut instruction
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "cut")))]
 +    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
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "dynamic_else")))]
 +    DynamicElse(usize, Death, NextOrFail),
 +    #[strum_discriminants(strum(props(Arity = "3", Name = "dynamic_internal_else")))]
 +    DynamicInternalElse(usize, Death, NextOrFail),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "default_retry_me_else")))]
 +    DefaultRetryMeElse(usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "default_trust_me")))]
 +    DefaultTrustMe(usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "retry_me_else")))]
 +    RetryMeElse(usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "trust_me")))]
 +    TrustMe(usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "default_trust_me")))]
 +    TryMeElse(usize),
 +    // arithmetic instruction
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "add")))]
 +    Add(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "sub")))]
 +    Sub(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "mul")))]
 +    Mul(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "pow")))]
 +    Pow(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "int_pow")))]
 +    IntPow(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "i_div")))]
 +    IDiv(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "max")))]
 +    Max(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "min")))]
 +    Min(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "int_floor_div")))]
 +    IntFloorDiv(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "r_div")))]
 +    RDiv(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "div")))]
 +    Div(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "shl")))]
 +    Shl(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "shr")))]
 +    Shr(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "xor")))]
 +    Xor(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "and")))]
 +    And(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "or")))]
 +    Or(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "mod")))]
 +    Mod(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "rem")))]
 +    Rem(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "2", Name = "gcd")))]
 +    Gcd(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "sign")))]
 +    Sign(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "cos")))]
 +    Cos(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "sin")))]
 +    Sin(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "tan")))]
 +    Tan(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "log")))]
 +    Log(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "exp")))]
 +    Exp(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "acos")))]
 +    ACos(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "asin")))]
 +    ASin(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "atan")))]
 +    ATan(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "atan2")))]
 +    ATan2(ArithmeticTerm, ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "sqrt")))]
 +    Sqrt(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "abs")))]
 +    Abs(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "float")))]
 +    Float(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "truncate")))]
 +    Truncate(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "round")))]
 +    Round(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "ceiling")))]
 +    Ceiling(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "floor")))]
 +    Floor(ArithmeticTerm, usize),
++    #[strum_discriminants(strum(props(Arity = "1", Name = "float_fractional_part")))]
++    FloatFractionalPart(ArithmeticTerm, usize),
++    #[strum_discriminants(strum(props(Arity = "1", Name = "float_integer_part")))]
++    FloatIntegerPart(ArithmeticTerm, usize),
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "neg")))]
 +    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
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "allocate")))]
 +    Allocate(usize), // num_frames.
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "deallocate")))]
 +    Deallocate,
 +    #[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")))]
 +    Proceed,
 +    // indexing.
 +    #[strum_discriminants(strum(props(Arity = "1", Name = "indexing_code")))]
 +    IndexingCode(Vec<IndexingLine>),
 +    // break from loop instruction.
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "break_from_dispatch")))]
 +    BreakFromDispatchLoop,
 +    // swap the verify attr interrupt instruction with the next control instruction.
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "install_verify_attr")))]
 +    InstallVerifyAttr,
 +    // call verify_attrs.
 +    #[strum_discriminants(strum(props(Arity = "0", Name = "verify_attr_interrupt")))]
 +    VerifyAttrInterrupt,
 +    // procedures
 +    CallClause(ClauseType, usize, usize, bool, bool), // ClauseType,
 +                                                      // arity,
 +                                                      // perm_vars,
 +                                                      // last_call,
 +                                                      // use_default_call_policy.
 +}
 +
 +fn derive_input(ty: &Type) -> Option<DeriveInput> {
 +    let clause_type: Type = parse_quote!{ ClauseType };
 +    let built_in_clause_type: Type = parse_quote! { BuiltInClauseType };
 +    let inlined_clause_type: Type = parse_quote! { InlinedClauseType };
 +    let system_clause_type: Type = parse_quote! { SystemClauseType };
 +    let compare_term_type: Type = parse_quote! { CompareTerm };
 +    let compare_number_type: Type = parse_quote! { CompareNumber };
 +    let repl_code_ptr_type: Type = parse_quote! { REPLCodePtr };
 +
 +    if ty == &clause_type {
 +        Some(ClauseType::to_derive_input())
 +    } else if ty == &built_in_clause_type {
 +        Some(BuiltInClauseType::to_derive_input())
 +    } else if ty == &inlined_clause_type {
 +        Some(InlinedClauseType::to_derive_input())
 +    } else if ty == &system_clause_type {
 +        Some(SystemClauseType::to_derive_input())
 +    } else if ty == &compare_number_type {
 +        Some(CompareNumber::to_derive_input())
 +    } else if ty == &compare_term_type {
 +        Some(CompareTerm::to_derive_input())
 +    } else if ty == &repl_code_ptr_type {
 +        Some(REPLCodePtr::to_derive_input())
 +    } else {
 +        None
 +    }
 +}
 +
 +impl ToTokens for Arity {
 +    fn to_tokens(&self, tokens: &mut TokenStream) {
 +        match self {
 +            Arity::Static(arity) => {
 +                arity.to_tokens(tokens);
 +            }
 +            Arity::Ident(arity) => {
 +                let ident = format_ident!("{}", arity);
 +                tokens.append(ident);
 +            }
 +        }
 +    }
 +}
 +
 +fn add_discriminant_data<DiscriminantT>(
 +    variant: &Variant,
 +    prefix: &'static str,
 +    variant_data: &mut Vec<(&'static str, Arity, Variant)>,
 +) -> (&'static str, Arity)
 +    where DiscriminantT: FromStr + strum::EnumProperty + std::fmt::Debug
 +{
 +    let name = prop_from_ident::<DiscriminantT>(&variant.ident, "Name");
 +    let arity = Arity::from(prop_from_ident::<DiscriminantT>(&variant.ident, "Arity"));
 +
 +    if prefix == "Call" {
 +        let mut variant = variant.clone();
 +        variant.attrs.clear();
 +
 +        variant_data.push((name, arity, variant));
 +    }
 +
 +    (name, arity)
 +}
 +
 +fn generate_instruction_preface() -> TokenStream {
 +    quote! {
 +        use crate::arena::*;
 +        use crate::arithmetic::*;
 +        use crate::atom_table::*;
 +        use crate::forms::*;
 +        use crate::machine::heap::*;
 +        use crate::machine::machine_errors::MachineStub;
 +        use crate::machine::machine_indices::CodeIndex;
 +        use crate::parser::ast::*;
 +        use crate::types::*;
 +
 +        use fxhash::FxBuildHasher;
 +        use indexmap::IndexMap;
 +
 +        use std::collections::VecDeque;
 +
 +        fn reg_type_into_functor(r: RegType) -> MachineStub {
 +            match r {
 +                RegType::Temp(r) => functor!(atom!("x"), [fixnum(r)]),
 +                RegType::Perm(r) => functor!(atom!("y"), [fixnum(r)]),
 +            }
 +        }
 +
 +        impl Level {
 +            fn into_functor(self) -> MachineStub {
 +                match self {
 +                    Level::Root => functor!(atom!("level"), [atom(atom!("root"))]),
 +                    Level::Shallow => functor!(atom!("level"), [atom(atom!("shallow"))]),
 +                    Level::Deep => functor!(atom!("level"), [atom(atom!("deep"))]),
 +                }
 +            }
 +        }
 +
 +        impl ArithmeticTerm {
 +            fn into_functor(&self, arena: &mut Arena) -> MachineStub {
 +                match self {
 +                    &ArithmeticTerm::Reg(r) => reg_type_into_functor(r),
 +                    &ArithmeticTerm::Interm(i) => {
 +                        functor!(atom!("intermediate"), [fixnum(i)])
 +                    }
 +                    &ArithmeticTerm::Number(n) => {
 +                        vec![HeapCellValue::from((n, arena))]
 +                    }
 +                }
 +            }
 +        }
 +
 +        #[derive(Debug, Clone, Copy)]
 +        pub enum NextOrFail {
 +            Next(usize),
 +            Fail(usize),
 +        }
 +
 +        impl Default for NextOrFail {
 +            fn default() -> Self {
 +                NextOrFail::Fail(0)
 +            }
 +        }
 +
 +        impl NextOrFail {
 +            #[inline]
 +            pub fn is_next(&self) -> bool {
 +                if let NextOrFail::Next(_) = self {
 +                    true
 +                } else {
 +                    false
 +                }
 +            }
 +        }
 +
 +        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 +        pub enum Death {
 +            Finite(usize),
 +            Infinity,
 +        }
 +
 +        impl Default for Death {
 +            fn default() -> Self {
 +                Death::Infinity
 +            }
 +        }
 +
 +        #[derive(Clone, Copy, Debug)]
 +        pub enum IndexedChoiceInstruction {
 +            Retry(usize),
 +            Trust(usize),
 +            Try(usize),
 +        }
 +
 +        impl IndexedChoiceInstruction {
 +            pub(crate) fn offset(&self) -> usize {
 +                match self {
 +                    &IndexedChoiceInstruction::Retry(offset) => offset,
 +                    &IndexedChoiceInstruction::Trust(offset) => offset,
 +                    &IndexedChoiceInstruction::Try(offset) => offset,
 +                }
 +            }
 +
 +            pub(crate) fn to_functor(&self) -> MachineStub {
 +                match self {
 +                    &IndexedChoiceInstruction::Try(offset) => {
 +                        functor!(atom!("try"), [fixnum(offset)])
 +                    }
 +                    &IndexedChoiceInstruction::Trust(offset) => {
 +                        functor!(atom!("trust"), [fixnum(offset)])
 +                    }
 +                    &IndexedChoiceInstruction::Retry(offset) => {
 +                        functor!(atom!("retry"), [fixnum(offset)])
 +                    }
 +                }
 +            }
 +        }
 +
 +        /// `IndexingInstruction` cf. page 110 of wambook.
 +        #[derive(Clone, Debug)]
 +        pub enum IndexingInstruction {
 +            // The first index is the optimal argument being indexed.
 +            SwitchOnTerm(
 +                usize,
 +                IndexingCodePtr,
 +                IndexingCodePtr,
 +                IndexingCodePtr,
 +                IndexingCodePtr,
 +            ),
 +            SwitchOnConstant(IndexMap<Literal, IndexingCodePtr, FxBuildHasher>),
 +            SwitchOnStructure(IndexMap<(Atom, usize), IndexingCodePtr, FxBuildHasher>),
 +        }
 +
 +        #[derive(Debug, Clone, Copy)]
 +        pub enum IndexingCodePtr {
 +            External(usize),        // the index points past the indexing instruction prelude.
 +            DynamicExternal(usize), // an External index of a dynamic predicate, potentially invalidated by retraction.
 +            Fail,
 +            Internal(usize), // the index points into the indexing instruction prelude.
 +        }
 +
 +        impl IndexingCodePtr {
 +            #[allow(dead_code)]
 +            pub fn to_functor(self) -> MachineStub {
 +                match self {
 +                    IndexingCodePtr::DynamicExternal(o) => functor!(atom!("dynamic_external"), [fixnum(o)]),
 +                    IndexingCodePtr::External(o) => functor!(atom!("external"), [fixnum(o)]),
 +                    IndexingCodePtr::Internal(o) => functor!(atom!("internal"), [fixnum(o)]),
 +                    IndexingCodePtr::Fail => {
 +                        vec![atom_as_cell!(atom!("fail"))]
 +                    },
 +                }
 +            }
 +        }
 +
 +        impl IndexingInstruction {
 +            pub fn to_functor(&self, mut h: usize) -> MachineStub {
 +                match self {
 +                    &IndexingInstruction::SwitchOnTerm(arg, vars, constants, lists, structures) => {
 +                        functor!(
 +                            atom!("switch_on_term"),
 +                            [
 +                                fixnum(arg),
 +                                indexing_code_ptr(h, vars),
 +                                indexing_code_ptr(h, constants),
 +                                indexing_code_ptr(h, lists),
 +                                indexing_code_ptr(h, structures)
 +                            ]
 +                        )
 +                    }
 +                    &IndexingInstruction::SwitchOnConstant(ref constants) => {
 +                        let mut key_value_list_stub = vec![];
 +                        let orig_h = h;
 +
 +                        h += 2; // skip the 2-cell "switch_on_constant" functor.
 +
 +                        for (c, ptr) in constants.iter() {
 +                            let key_value_pair = functor!(
 +                                atom!(":"),
 +                                [literal(*c), indexing_code_ptr(h + 3, *ptr)]
 +                            );
 +
 +                            key_value_list_stub.push(list_loc_as_cell!(h + 1));
 +                            key_value_list_stub.push(str_loc_as_cell!(h + 3));
 +                            key_value_list_stub.push(heap_loc_as_cell!(h + 3 + key_value_pair.len()));
 +
 +                            h += key_value_pair.len() + 3;
 +                            key_value_list_stub.extend(key_value_pair.into_iter());
 +                        }
 +
 +                        key_value_list_stub.push(empty_list_as_cell!());
 +
 +                        functor!(
 +                            atom!("switch_on_constant"),
 +                            [str(orig_h, 0)],
 +                            [key_value_list_stub]
 +                        )
 +                    }
 +                    &IndexingInstruction::SwitchOnStructure(ref structures) => {
 +                        let mut key_value_list_stub = vec![];
 +                        let orig_h = h;
 +
 +                        h += 2; // skip the 2-cell "switch_on_constant" functor.
 +
 +                        for ((name, arity), ptr) in structures.iter() {
 +                            let predicate_indicator_stub = functor!(
 +                                atom!("/"),
 +                                [atom(name), fixnum(*arity)]
 +                            );
 +
 +                            let key_value_pair = functor!(
 +                                atom!(":"),
 +                                [str(h + 3, 0), indexing_code_ptr(h + 3, *ptr)],
 +                                [predicate_indicator_stub]
 +                            );
 +
 +                            key_value_list_stub.push(list_loc_as_cell!(h + 1));
 +                            key_value_list_stub.push(str_loc_as_cell!(h + 3));
 +                            key_value_list_stub.push(heap_loc_as_cell!(h + 3 + key_value_pair.len()));
 +
 +                            h += key_value_pair.len() + 3;
 +                            key_value_list_stub.extend(key_value_pair.into_iter());
 +                        }
 +
 +                        key_value_list_stub.push(empty_list_as_cell!());
 +
 +                        functor!(
 +                            atom!("switch_on_structure"),
 +                            [str(orig_h, 0)],
 +                            [key_value_list_stub]
 +                        )
 +                    }
 +                }
 +            }
 +        }
 +
 +        /// A `Line` is an instruction (cf. page 98 of wambook).
 +        #[derive(Clone, Debug)]
 +        pub enum IndexingLine {
 +            Indexing(IndexingInstruction),
 +            IndexedChoice(VecDeque<IndexedChoiceInstruction>),
 +            DynamicIndexedChoice(VecDeque<usize>),
 +        }
 +
 +        impl From<IndexingInstruction> for IndexingLine {
 +            #[inline]
 +            fn from(instr: IndexingInstruction) -> Self {
 +                IndexingLine::Indexing(instr)
 +            }
 +        }
 +
 +        impl From<VecDeque<IndexedChoiceInstruction>> for IndexingLine {
 +            #[inline]
 +            fn from(instrs: VecDeque<IndexedChoiceInstruction>) -> Self {
 +                IndexingLine::IndexedChoice(instrs.into_iter().collect())
 +            }
 +        }
 +
 +        fn arith_instr_unary_functor(
 +            h: usize,
 +            name: Atom,
 +            arena: &mut Arena,
 +            at: &ArithmeticTerm,
 +            t: usize,
 +        ) -> MachineStub {
 +            let at_stub = at.into_functor(arena);
 +            functor!(name, [str(h, 0), fixnum(t)], [at_stub])
 +        }
 +
 +        fn arith_instr_bin_functor(
 +            h: usize,
 +            name: Atom,
 +            arena: &mut Arena,
 +            at_1: &ArithmeticTerm,
 +            at_2: &ArithmeticTerm,
 +            t: usize,
 +        ) -> MachineStub {
 +            let at_1_stub = at_1.into_functor(arena);
 +            let at_2_stub = at_2.into_functor(arena);
 +
 +            functor!(
 +                name,
 +                [str(h, 0), str(h, 1), fixnum(t)],
 +                [at_1_stub, at_2_stub]
 +            )
 +        }
 +
 +        pub type Code = Vec<Instruction>;
 +        pub type CodeDeque = VecDeque<Instruction>;
 +
 +        impl Instruction {
 +            #[inline]
 +            pub fn to_indexing_line_mut(&mut self) -> Option<&mut Vec<IndexingLine>> {
 +                match self {
 +                    Instruction::IndexingCode(ref mut indexing_code) => Some(indexing_code),
 +                    _ => None,
 +                }
 +            }
 +
 +            #[inline]
 +            pub fn to_indexing_line(&self) -> Option<&Vec<IndexingLine>> {
 +                match self {
 +                    Instruction::IndexingCode(ref indexing_code) => Some(indexing_code),
 +                    _ => None,
 +                }
 +            }
 +
 +            #[inline]
 +            pub fn is_head_instr(&self) -> bool {
 +                match self {
 +                    Instruction::Deallocate |
 +                    Instruction::GetConstant(..) |
 +                    Instruction::GetList(..) |
 +                    Instruction::GetPartialString(..) |
 +                    Instruction::GetStructure(..) |
 +                    Instruction::GetValue(..) |
 +                    Instruction::UnifyConstant(..) |
 +                    Instruction::UnifyLocalValue(..) |
 +                    Instruction::UnifyVariable(..) |
 +                    Instruction::UnifyValue(..) |
 +                    Instruction::UnifyVoid(..) |
 +                    Instruction::GetVariable(..) |
 +                    Instruction::PutConstant(..) |
 +                    Instruction::PutList(..) |
 +                    Instruction::PutPartialString(..) |
 +                    Instruction::PutStructure(..) |
 +                    Instruction::PutUnsafeValue(..) |
 +                    Instruction::PutValue(..) |
 +                    Instruction::PutVariable(..) |
 +                    Instruction::SetConstant(..) |
 +                    Instruction::SetLocalValue(..) |
 +                    Instruction::SetVariable(..) |
 +                    Instruction::SetValue(..) |
 +                    Instruction::SetVoid(..) => true,
 +                    _ => false,
 +                }
 +            }
 +
 +            pub fn enqueue_functors(
 +                &self,
 +                mut h: usize,
 +                arena: &mut Arena,
 +                functors: &mut Vec<MachineStub>,
 +            ) {
 +                match self {
 +                    &Instruction::IndexingCode(ref indexing_instrs) => {
 +                        for indexing_instr in indexing_instrs {
 +                            match indexing_instr {
 +                                IndexingLine::Indexing(indexing_instr) => {
 +                                    let section = indexing_instr.to_functor(h);
 +                                    h += section.len();
 +                                    functors.push(section);
 +                                }
 +                                IndexingLine::IndexedChoice(indexed_choice_instrs) => {
 +                                    for indexed_choice_instr in indexed_choice_instrs {
 +                                        let section = indexed_choice_instr.to_functor();
 +                                        h += section.len();
 +                                        functors.push(section);
 +                                    }
 +                                }
 +                                IndexingLine::DynamicIndexedChoice(indexed_choice_instrs) => {
 +                                    for indexed_choice_instr in indexed_choice_instrs {
 +                                        let section = functor!(atom!("dynamic"), [fixnum(*indexed_choice_instr)]);
 +
 +                                        h += section.len();
 +                                        functors.push(section);
 +                                    }
 +                                }
 +                            }
 +                        }
 +                    }
 +                    instr => functors.push(instr.to_functor(h, arena)),
 +                }
 +            }
 +
 +            fn to_functor(&self, h: usize, arena: &mut Arena) -> MachineStub {
 +                match self {
 +                    &Instruction::InstallVerifyAttr => {
 +                        functor!(atom!("install_verify_attr"))
 +                    }
 +                    &Instruction::VerifyAttrInterrupt => {
 +                        functor!(atom!("verify_attr_interrupt"))
 +                    }
 +                    &Instruction::DynamicElse(birth, death, next_or_fail) => {
 +                        match (death, next_or_fail) {
 +                            (Death::Infinity, NextOrFail::Next(i)) => {
 +                                functor!(
 +                                    atom!("dynamic_else"),
 +                                    [fixnum(birth), atom(atom!("inf")), fixnum(i)]
 +                                )
 +                            }
 +                            (Death::Infinity, NextOrFail::Fail(i)) => {
 +                                let next_functor = functor!(atom!("fail"), [fixnum(i)]);
 +
 +                                functor!(
 +                                    atom!("dynamic_else"),
 +                                    [fixnum(birth), atom(atom!("inf")), str(h, 0)],
 +                                    [next_functor]
 +                                )
 +                            }
 +                            (Death::Finite(d), NextOrFail::Fail(i)) => {
 +                                let next_functor = functor!(atom!("fail"), [fixnum(i)]);
 +
 +                                functor!(
 +                                    atom!("dynamic_else"),
 +                                    [fixnum(birth), fixnum(d), str(h, 0)],
 +                                    [next_functor]
 +                                )
 +                            }
 +                            (Death::Finite(d), NextOrFail::Next(i)) => {
 +                                functor!(atom!("dynamic_else"), [fixnum(birth), fixnum(d), fixnum(i)])
 +                            }
 +                        }
 +                    }
 +                    &Instruction::DynamicInternalElse(birth, death, next_or_fail) => {
 +                        match (death, next_or_fail) {
 +                            (Death::Infinity, NextOrFail::Next(i)) => {
 +                                functor!(
 +                                    atom!("dynamic_internal_else"),
 +                                    [fixnum(birth), atom(atom!("inf")), fixnum(i)]
 +                                )
 +                            }
 +                            (Death::Infinity, NextOrFail::Fail(i)) => {
 +                                let next_functor = functor!(atom!("fail"), [fixnum(i)]);
 +
 +                                functor!(
 +                                    atom!("dynamic_internal_else"),
 +                                    [fixnum(birth), atom(atom!("inf")), str(h, 0)],
 +                                    [next_functor]
 +                                )
 +                            }
 +                            (Death::Finite(d), NextOrFail::Fail(i)) => {
 +                                let next_functor = functor!(atom!("fail"), [fixnum(i)]);
 +
 +                                functor!(
 +                                    atom!("dynamic_internal_else"),
 +                                    [fixnum(birth), fixnum(d), str(h, 0)],
 +                                    [next_functor]
 +                                )
 +                            }
 +                            (Death::Finite(d), NextOrFail::Next(i)) => {
 +                                functor!(
 +                                    atom!("dynamic_internal_else"),
 +                                    [fixnum(birth), fixnum(d), fixnum(i)]
 +                                )
 +                            }
 +                        }
 +                    }
 +                    &Instruction::TryMeElse(offset) => {
 +                        functor!(atom!("try_me_else"), [fixnum(offset)])
 +                    }
 +                    &Instruction::RetryMeElse(offset) => {
 +                        functor!(atom!("retry_me_else"), [fixnum(offset)])
 +                    }
 +                    &Instruction::TrustMe(offset) => {
 +                        functor!(atom!("trust_me"), [fixnum(offset)])
 +                    }
 +                    &Instruction::DefaultRetryMeElse(offset) => {
 +                        functor!(atom!("default_retry_me_else"), [fixnum(offset)])
 +                    }
 +                    &Instruction::DefaultTrustMe(offset) => {
 +                        functor!(atom!("default_trust_me"), [fixnum(offset)])
 +                    }
 +                    &Instruction::Cut(r) => {
 +                        let rt_stub = reg_type_into_functor(r);
 +                        functor!(atom!("cut"), [str(h, 0)], [rt_stub])
 +                    }
 +                    &Instruction::GetLevel(r) => {
 +                        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"))
 +                    }
 +                    &Instruction::Add(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("add"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::Sub(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("sub"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::Mul(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("mul"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::IntPow(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("int_pow"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::Pow(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("pow"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::IDiv(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("idiv"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::Max(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("max"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::Min(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("min"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::IntFloorDiv(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("int_floor_div"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::RDiv(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("rdiv"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::Div(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("div"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::Shl(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("shl"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::Shr(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("shr"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::Xor(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("xor"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::And(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("and"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::Or(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("or"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::Mod(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("mod"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::Rem(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("rem"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::ATan2(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("rem"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::Gcd(ref at_1, ref at_2, t) => {
 +                        arith_instr_bin_functor(h, atom!("gcd"), arena, at_1, at_2, t)
 +                    }
 +                    &Instruction::Sign(ref at, t) => {
 +                        arith_instr_unary_functor(h, atom!("sign"), arena, at, t)
 +                    }
 +                    &Instruction::Cos(ref at, t) => {
 +                        arith_instr_unary_functor(h, atom!("cos"), arena, at, t)
 +                    }
 +                    &Instruction::Sin(ref at, t) => {
 +                        arith_instr_unary_functor(h, atom!("sin"), arena, at, t)
 +                    }
 +                    &Instruction::Tan(ref at, t) => {
 +                        arith_instr_unary_functor(h, atom!("tan"), arena, at, t)
 +                    }
 +                    &Instruction::Log(ref at, t) => {
 +                        arith_instr_unary_functor(h, atom!("log"), arena, at, t)
 +                    }
 +                    &Instruction::Exp(ref at, t) => {
 +                        arith_instr_unary_functor(h, atom!("exp"), arena, at, t)
 +                    }
 +                    &Instruction::ACos(ref at, t) => {
 +                        arith_instr_unary_functor(h, atom!("acos"), arena, at, t)
 +                    }
 +                    &Instruction::ASin(ref at, t) => {
 +                        arith_instr_unary_functor(h, atom!("asin"), arena, at, t)
 +                    }
 +                    &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)
 +                    }
 +                    &Instruction::Float(ref at, t) => {
 +                        arith_instr_unary_functor(h, atom!("float"), arena, at, t)
 +                    }
 +                    &Instruction::Truncate(ref at, t) => {
 +                        arith_instr_unary_functor(h, atom!("truncate"), arena, at, t)
 +                    }
 +                    &Instruction::Round(ref at, t) => {
 +                        arith_instr_unary_functor(h, atom!("round"), arena, at, t)
 +                    }
 +                    &Instruction::Ceiling(ref at, t) => {
 +                        arith_instr_unary_functor(h, atom!("ceiling"), arena, at, t)
 +                    }
 +                    &Instruction::Floor(ref at, t) => {
 +                        arith_instr_unary_functor(h, atom!("floor"), arena, at, t)
 +                    }
++                    &Instruction::FloatFractionalPart(ref at, t) => {
++                        arith_instr_unary_functor(h, atom!("float_fractional_part"), arena, at, t)
++                    }
++                    &Instruction::FloatIntegerPart(ref at, t) => {
++                        arith_instr_unary_functor(h, atom!("float_integer_part"), arena, at, t)
++                    }
 +                    &Instruction::Neg(ref at, t) => arith_instr_unary_functor(
 +                        h,
 +                        atom!("-"),
 +                        arena,
 +                        at,
 +                        t,
 +                    ),
 +                    &Instruction::Plus(ref at, t) => arith_instr_unary_functor(
 +                        h,
 +                        atom!("+"),
 +                        arena,
 +                        at,
 +                        t,
 +                    ),
 +                    &Instruction::BitwiseComplement(ref at, t) => arith_instr_unary_functor(
 +                        h,
 +                        atom!("\\"),
 +                        arena,
 +                        at,
 +                        t,
 +                    ),
 +                    &Instruction::IndexingCode(_) => {
 +                        // this case is covered in enqueue_functors, which
 +                        // should be called instead (to_functor is a private
 +                        // function for this reason).
 +                        vec![]
 +                    }
 +                    &Instruction::Allocate(num_frames) => {
 +                        functor!(atom!("allocate"), [fixnum(num_frames)])
 +                    }
 +                    &Instruction::CallNamed(arity, name, ..) => {
 +                        functor!(atom!("call"), [atom(name), fixnum(arity)])
 +                    }
 +                    &Instruction::ExecuteNamed(arity, name, ..) => {
 +                        functor!(atom!("execute"), [atom(name), fixnum(arity)])
 +                    }
 +                    &Instruction::DefaultCallNamed(arity, name, ..) => {
 +                        functor!(atom!("call_default"), [atom(name), fixnum(arity)])
 +                    }
 +                    &Instruction::DefaultExecuteNamed(arity, name, ..) => {
 +                        functor!(atom!("execute_default"), [atom(name), fixnum(arity)])
 +                    }
 +                    &Instruction::CallN(arity) => {
 +                        functor!(atom!("call_n"), [fixnum(arity)])
 +                    }
 +                    &Instruction::ExecuteN(arity) => {
 +                        functor!(atom!("execute_n"), [fixnum(arity)])
 +                    }
 +                    &Instruction::DefaultCallN(arity) => {
 +                        functor!(atom!("call_default_n"), [fixnum(arity)])
 +                    }
 +                    &Instruction::DefaultExecuteN(arity) => {
 +                        functor!(atom!("execute_default_n"), [fixnum(arity)])
 +                    }
 +                    &Instruction::CallFastCallN(arity) => {
 +                        functor!(atom!("call_fast_call_n"), [fixnum(arity)])
 +                    }
 +                    &Instruction::ExecuteFastCallN(arity) => {
 +                        functor!(atom!("execute_fast_call_n"), [fixnum(arity)])
 +                    }
 +                    &Instruction::CallTermGreaterThan |
 +                    &Instruction::CallTermLessThan |
 +                    &Instruction::CallTermGreaterThanOrEqual |
 +                    &Instruction::CallTermLessThanOrEqual |
 +                    &Instruction::CallTermEqual |
 +                    &Instruction::CallTermNotEqual |
 +                    &Instruction::CallNumberGreaterThan(..) |
 +                    &Instruction::CallNumberLessThan(..) |
 +                    &Instruction::CallNumberGreaterThanOrEqual(..) |
 +                    &Instruction::CallNumberLessThanOrEqual(..) |
 +                    &Instruction::CallNumberEqual(..) |
 +                    &Instruction::CallNumberNotEqual(..) |
 +                    &Instruction::CallIs(..) |
 +                    &Instruction::CallAcyclicTerm |
 +                    &Instruction::CallArg |
 +                    &Instruction::CallCompare |
 +                    &Instruction::CallCopyTerm |
 +                    &Instruction::CallFunctor |
 +                    &Instruction::CallGround |
 +                    &Instruction::CallKeySort |
 +                    &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::ExecuteNumberGreaterThan(..) |
 +                    &Instruction::ExecuteNumberLessThan(..) |
 +                    &Instruction::ExecuteNumberGreaterThanOrEqual(..) |
 +                    &Instruction::ExecuteNumberLessThanOrEqual(..) |
 +                    &Instruction::ExecuteNumberEqual(..) |
 +                    &Instruction::ExecuteNumberNotEqual(..) |
 +                    &Instruction::ExecuteAcyclicTerm |
 +                    &Instruction::ExecuteArg |
 +                    &Instruction::ExecuteCompare |
 +                    &Instruction::ExecuteCopyTerm |
 +                    &Instruction::ExecuteFunctor |
 +                    &Instruction::ExecuteGround |
 +                    &Instruction::ExecuteIs(..) |
 +                    &Instruction::ExecuteKeySort |
 +                    &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::DefaultCallNumberGreaterThan(..) |
 +                    &Instruction::DefaultCallNumberLessThan(..) |
 +                    &Instruction::DefaultCallNumberGreaterThanOrEqual(..) |
 +                    &Instruction::DefaultCallNumberLessThanOrEqual(..) |
 +                    &Instruction::DefaultCallNumberEqual(..) |
 +                    &Instruction::DefaultCallNumberNotEqual(..) |
 +                    &Instruction::DefaultCallAcyclicTerm |
 +                    &Instruction::DefaultCallArg |
 +                    &Instruction::DefaultCallCompare |
 +                    &Instruction::DefaultCallCopyTerm |
 +                    &Instruction::DefaultCallFunctor |
 +                    &Instruction::DefaultCallGround |
 +                    &Instruction::DefaultCallIs(..) |
 +                    &Instruction::DefaultCallKeySort |
 +                    &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::DefaultExecuteNumberGreaterThan(..) |
 +                    &Instruction::DefaultExecuteNumberLessThan(..) |
 +                    &Instruction::DefaultExecuteNumberGreaterThanOrEqual(..) |
 +                    &Instruction::DefaultExecuteNumberLessThanOrEqual(..) |
 +                    &Instruction::DefaultExecuteNumberEqual(..) |
 +                    &Instruction::DefaultExecuteNumberNotEqual(..) |
 +                    &Instruction::DefaultExecuteAcyclicTerm |
 +                    &Instruction::DefaultExecuteArg |
 +                    &Instruction::DefaultExecuteCompare |
 +                    &Instruction::DefaultExecuteCopyTerm |
 +                    &Instruction::DefaultExecuteFunctor |
 +                    &Instruction::DefaultExecuteGround |
 +                    &Instruction::DefaultExecuteIs(..) |
 +                    &Instruction::DefaultExecuteKeySort |
 +                    &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(_) => {
 +                        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(_) => {
 +                        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::CallDynamicModuleResolution(..) |
 +                    &Instruction::CallPrepareCallClause(..) |
 +                    &Instruction::CallCompileInlineOrExpandedGoal |
 +                    &Instruction::CallIsExpandedOrInlined |
 +                    &Instruction::CallGetClauseP |
 +                    &Instruction::CallInvokeClauseAtP |
 +                    &Instruction::CallGetFromAttributedVarList |
 +                    &Instruction::CallPutToAttributedVarList |
 +                    &Instruction::CallDeleteFromAttributedVarList |
 +                    &Instruction::CallDeleteAllAttributesFromVar |
 +                    &Instruction::CallUnattributedVar |
 +                    &Instruction::CallGetDBRefs |
 +                    &Instruction::CallKeySortWithConstantVarOrdering |
 +                    &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::CallGetCurrentSCCBlock |
 +                    &Instruction::CallGetCutPoint |
 +                    &Instruction::CallGetDoubleQuotes |
 +                    &Instruction::CallGetUnknown |
 +                    &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::CallReadFromChars |
 +                    &Instruction::CallReadTermFromChars |
 +                    &Instruction::CallResetBlock |
 +                    &Instruction::CallResetSCCBlock |
 +                    &Instruction::CallReturnFromVerifyAttr |
 +                    &Instruction::CallSetBall |
 +                    &Instruction::CallPushBallStack |
 +                    &Instruction::CallPopBallStack |
 +                    &Instruction::CallPopFromBallStack |
 +                    &Instruction::CallSetCutPointByDefault(..) |
 +                    &Instruction::CallSetDoubleQuotes |
 +                    &Instruction::CallSetUnknown |
 +                    &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::ExecuteDynamicModuleResolution(..) |
 +                    &Instruction::ExecutePrepareCallClause(..) |
 +                    &Instruction::ExecuteCompileInlineOrExpandedGoal |
 +                    &Instruction::ExecuteIsExpandedOrInlined |
 +                    &Instruction::ExecuteGetClauseP |
 +                    &Instruction::ExecuteInvokeClauseAtP |
 +                    &Instruction::ExecuteGetFromAttributedVarList |
 +                    &Instruction::ExecutePutToAttributedVarList |
 +                    &Instruction::ExecuteDeleteFromAttributedVarList |
 +                    &Instruction::ExecuteDeleteAllAttributesFromVar |
 +                    &Instruction::ExecuteUnattributedVar |
 +                    &Instruction::ExecuteGetDBRefs |
 +                    &Instruction::ExecuteKeySortWithConstantVarOrdering |
 +                    &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::ExecuteGetCurrentSCCBlock |
 +                    &Instruction::ExecuteGetCutPoint |
 +                    &Instruction::ExecuteGetDoubleQuotes |
 +                    &Instruction::ExecuteGetUnknown |
 +                    &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::ExecuteReadFromChars |
 +                    &Instruction::ExecuteReadTermFromChars |
 +                    &Instruction::ExecuteResetBlock |
 +                    &Instruction::ExecuteResetSCCBlock |
 +                    &Instruction::ExecuteReturnFromVerifyAttr |
 +                    &Instruction::ExecuteSetBall |
 +                    &Instruction::ExecutePushBallStack |
 +                    &Instruction::ExecutePopBallStack |
 +                    &Instruction::ExecutePopFromBallStack |
 +                    &Instruction::ExecuteSetCutPointByDefault(_) |
 +                    &Instruction::ExecuteSetDoubleQuotes |
 +                    &Instruction::ExecuteSetUnknown |
 +                    &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)])
 +                    }
 +                    //
 +                    &Instruction::Deallocate => {
 +                        functor!(atom!("deallocate"))
 +                    }
 +                    &Instruction::JmpByCall(offset) => {
 +                        functor!(atom!("jmp_by_call"), [fixnum(offset)])
 +                    }
 +                    &Instruction::RevJmpBy(offset) => {
 +                        functor!(atom!("rev_jmp_by"), [fixnum(offset)])
 +                    }
 +                    &Instruction::Proceed => {
 +                        functor!(atom!("proceed"))
 +                    }
 +                    &Instruction::GetConstant(lvl, c, r) => {
 +                        let lvl_stub = lvl.into_functor();
 +                        let rt_stub = reg_type_into_functor(r);
 +
 +                        functor!(
 +                            atom!("get_constant"),
 +                            [str(h, 0), cell(c), str(h, 1)],
 +                            [lvl_stub, rt_stub]
 +                        )
 +                    }
 +                    &Instruction::GetList(lvl, r) => {
 +                        let lvl_stub = lvl.into_functor();
 +                        let rt_stub = reg_type_into_functor(r);
 +
 +                        functor!(
 +                            atom!("get_list"),
 +                            [str(h, 0), str(h, 1)],
 +                            [lvl_stub, rt_stub]
 +                        )
 +                    }
 +                    &Instruction::GetPartialString(lvl, s, r, has_tail) => {
 +                        let lvl_stub = lvl.into_functor();
 +                        let rt_stub = reg_type_into_functor(r);
 +
 +                        functor!(
 +                            atom!("get_partial_string"),
 +                            [
 +                                str(h, 0),
 +                                string(h, s),
 +                                str(h, 1),
 +                                boolean(has_tail)
 +                            ],
 +                            [lvl_stub, rt_stub]
 +                        )
 +                    }
 +                    &Instruction::GetStructure(lvl, name, arity, r) => {
 +                        let lvl_stub = lvl.into_functor();
 +                        let rt_stub = reg_type_into_functor(r);
 +
 +                        functor!(
 +                            atom!("get_structure"),
 +                            [str(h, 0), atom(name), fixnum(arity), str(h, 1)],
 +                            [lvl_stub, rt_stub]
 +                        )
 +                    }
 +                    &Instruction::GetValue(r, arg) => {
 +                        let rt_stub = reg_type_into_functor(r);
 +                        functor!(atom!("get_value"), [str(h, 0), fixnum(arg)], [rt_stub])
 +                    }
 +                    &Instruction::GetVariable(r, arg) => {
 +                        let rt_stub = reg_type_into_functor(r);
 +                        functor!(atom!("get_variable"), [str(h, 0), fixnum(arg)], [rt_stub])
 +                    }
 +                    &Instruction::UnifyConstant(c) => {
 +                        functor!(atom!("unify_constant"), [cell(c)])
 +                    }
 +                    &Instruction::UnifyLocalValue(r) => {
 +                        let rt_stub = reg_type_into_functor(r);
 +                        functor!(atom!("unify_local_value"), [str(h, 0)], [rt_stub])
 +                    }
 +                    &Instruction::UnifyVariable(r) => {
 +                        let rt_stub = reg_type_into_functor(r);
 +                        functor!(atom!("unify_variable"), [str(h, 0)], [rt_stub])
 +                    }
 +                    &Instruction::UnifyValue(r) => {
 +                        let rt_stub = reg_type_into_functor(r);
 +                        functor!(atom!("unify_value"), [str(h, 0)], [rt_stub])
 +                    }
 +                    &Instruction::UnifyVoid(vars) => {
 +                        functor!(atom!("unify_void"), [fixnum(vars)])
 +                    }
 +                    &Instruction::PutUnsafeValue(norm, arg) => {
 +                        functor!(atom!("put_unsafe_value"), [fixnum(norm), fixnum(arg)])
 +                    }
 +                    &Instruction::PutConstant(lvl, c, r) => {
 +                        let lvl_stub = lvl.into_functor();
 +                        let rt_stub = reg_type_into_functor(r);
 +
 +                        functor!(
 +                            atom!("put_constant"),
 +                            [str(h, 0), cell(c), str(h, 1)],
 +                            [lvl_stub, rt_stub]
 +                        )
 +                    }
 +                    &Instruction::PutList(lvl, r) => {
 +                        let lvl_stub = lvl.into_functor();
 +                        let rt_stub = reg_type_into_functor(r);
 +
 +                        functor!(
 +                            atom!("put_list"),
 +                            [str(h, 0), str(h, 1)],
 +                            [lvl_stub, rt_stub]
 +                        )
 +                    }
 +                    &Instruction::PutPartialString(lvl, s, r, has_tail) => {
 +                        let lvl_stub = lvl.into_functor();
 +                        let rt_stub = reg_type_into_functor(r);
 +
 +                        functor!(
 +                            atom!("put_partial_string"),
 +                            [
 +                                str(h, 0),
 +                                string(h, s),
 +                                str(h, 1),
 +                                boolean(has_tail)
 +                            ],
 +                            [lvl_stub, rt_stub]
 +                        )
 +                    }
 +                    &Instruction::PutStructure(name, arity, r) => {
 +                        let rt_stub = reg_type_into_functor(r);
 +
 +                        functor!(
 +                            atom!("put_structure"),
 +                            [atom(name), fixnum(arity), str(h, 0)],
 +                            [rt_stub]
 +                        )
 +                    }
 +                    &Instruction::PutValue(r, arg) => {
 +                        let rt_stub = reg_type_into_functor(r);
 +                        functor!(atom!("put_value"), [str(h, 0), fixnum(arg)], [rt_stub])
 +                    }
 +                    &Instruction::PutVariable(r, arg) => {
 +                        let rt_stub = reg_type_into_functor(r);
 +                        functor!(atom!("put_variable"), [str(h, 0), fixnum(arg)], [rt_stub])
 +                    }
 +                    &Instruction::SetConstant(c) => {
 +                        functor!(atom!("set_constant"), [cell(c)])
 +                    }
 +                    &Instruction::SetLocalValue(r) => {
 +                        let rt_stub = reg_type_into_functor(r);
 +                        functor!(atom!("set_local_value"), [str(h, 0)], [rt_stub])
 +                    }
 +                    &Instruction::SetVariable(r) => {
 +                        let rt_stub = reg_type_into_functor(r);
 +                        functor!(atom!("set_variable"), [str(h, 0)], [rt_stub])
 +                    }
 +                    &Instruction::SetValue(r) => {
 +                        let rt_stub = reg_type_into_functor(r);
 +                        functor!(atom!("set_value"), [str(h, 0)], [rt_stub])
 +                    }
 +                    &Instruction::SetVoid(vars) => {
 +                        functor!(atom!("set_void"), [fixnum(vars)])
 +                    }
 +                    &Instruction::BreakFromDispatchLoop => {
 +                        functor!(atom!("$break_from_dispatch_loop"))
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +pub fn generate_instructions_rs() -> TokenStream {
 +    let input = InstructionTemplate::to_derive_input();
 +    let mut instr_data = InstructionData::new();
 +
 +    instr_data.generate_instruction_enum_loop(input);
 +
 +    let instr_variants: Vec<_> = instr_data.instr_variants
 +        .iter()
 +        .cloned()
 +        .map(|(_, _, _, variant)| variant)
 +        .collect();
 +
 +    fn attributeless_enum<T: ToDeriveInput>() -> Vec<Variant> {
 +        if let Data::Enum(DataEnum { mut variants, .. }) = T::to_derive_input().data {
 +            for variant in &mut variants {
 +                variant.attrs.clear();
 +            }
 +
 +            variants.into_iter().collect()
 +        } else {
 +            unreachable!()
 +        }
 +    }
 +
 +    let clause_type_variants = attributeless_enum::<ClauseType>();
 +    let builtin_type_variants = attributeless_enum::<BuiltInClauseType>();
 +    let inlined_type_variants = attributeless_enum::<InlinedClauseType>();
 +    let system_clause_type_variants = attributeless_enum::<SystemClauseType>();
 +    let repl_code_ptr_variants = attributeless_enum::<REPLCodePtr>();
 +    let compare_number_variants = attributeless_enum::<CompareNumber>();
 +    let compare_term_variants = attributeless_enum::<CompareTerm>();
 +
 +    let mut clause_type_from_name_and_arity_arms = vec![];
 +    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![];
 +
 +    is_inbuilt_arms.push(quote! {
 +        (atom!(":-"), 1 | 2) => true
 +    });
 +
 +    for (name, arity, variant) in instr_data.compare_number_variants {
 +        let ident = variant.ident.clone();
 +
 +        let variant_fields: Vec<_> = variant.fields
 +            .into_iter()
 +            .map(|field| {
 +                let ty = field.ty;
 +                quote! { #ty::default() }
 +            })
 +            .collect();
 +
 +        clause_type_from_name_and_arity_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                (atom!(#name), #arity) => ClauseType::Inlined(
 +                    InlinedClauseType::CompareNumber(CompareNumber::#ident(#(#variant_fields),*))
 +                )
 +            }
 +        } else {
 +            quote! {
 +                (atom!(#name), #arity) => ClauseType::Inlined(
 +                    InlinedClauseType::CompareNumber(CompareNumber::#ident)
 +                )
 +            }
 +        });
 +
 +        clause_type_name_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                ClauseType::Inlined(
 +                    InlinedClauseType::CompareNumber(CompareNumber::#ident(..))
 +                ) => atom!(#name)
 +            }
 +        } else {
 +            quote! {
 +                ClauseType::Inlined(
 +                    InlinedClauseType::CompareNumber(CompareNumber::#ident)
 +                ) => (atom!(#name), #arity)
 +            }
 +        });
 +
 +        let ident = variant.ident;
 +        let instr_ident = format_ident!("Call{}", ident);
 +
 +        let placeholder_ids: Vec<_> = (0 .. variant_fields.len())
 +            .map(|n| format_ident!("f_{}", n))
 +            .collect();
 +
 +        clause_type_to_instr_arms.push(
 +            quote! {
 +                ClauseType::Inlined(
 +                    InlinedClauseType::CompareNumber(CompareNumber::#ident(#(#placeholder_ids),*))
 +                ) => Instruction::#instr_ident(#(*#placeholder_ids),*)
 +            }
 +        );
 +
 +        is_inbuilt_arms.push(
 +            quote! {
 +                (atom!(#name), #arity) => true
 +            }
 +        );
 +
 +        is_inlined_arms.push(
 +            quote! {
 +                (atom!(#name), #arity) => true
 +            }
 +        );
 +    }
 +
 +    for (name, arity, variant) in instr_data.compare_term_variants {
 +        let ident = variant.ident.clone();
 +
 +        clause_type_from_name_and_arity_arms.push(quote! {
 +            (atom!(#name), #arity) => ClauseType::BuiltIn(
 +                BuiltInClauseType::CompareTerm(CompareTerm::#ident)
 +            )
 +        });
 +
 +        clause_type_name_arms.push(
 +            quote! {
 +                ClauseType::BuiltIn(
 +                    BuiltInClauseType::CompareTerm(CompareTerm::#ident)
 +                ) => atom!(#name)
 +            }
 +        );
 +
 +        let ident = variant.ident;
 +        let instr_ident = format_ident!("Call{}", ident);
 +
 +        clause_type_to_instr_arms.push(
 +            quote! {
 +                ClauseType::BuiltIn(
 +                    BuiltInClauseType::CompareTerm(CompareTerm::#ident)
 +                ) => Instruction::#instr_ident
 +            }
 +        );
 +
 +        is_inbuilt_arms.push(
 +            quote! {
 +                (atom!(#name), #arity) => true
 +            }
 +        );
 +    }
 +
 +    for (name, arity, variant) in instr_data.builtin_type_variants {
 +        let ident = variant.ident.clone();
 +
 +        let variant_fields: Vec<_> = variant.fields
 +            .into_iter()
 +            .map(|field| {
 +                let ty = field.ty;
 +                quote! { #ty::default() }
 +            })
 +            .collect();
 +
 +        clause_type_from_name_and_arity_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                (atom!(#name), #arity) => ClauseType::BuiltIn(
 +                    BuiltInClauseType::#ident(#(#variant_fields),*)
 +                )
 +            }
 +        } else {
 +            quote! {
 +                (atom!(#name), #arity) => ClauseType::BuiltIn(
 +                    BuiltInClauseType::#ident
 +                )
 +            }
 +        });
 +
 +        clause_type_name_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                ClauseType::BuiltIn(
 +                    BuiltInClauseType::#ident(..)
 +                ) => atom!(#name)
 +            }
 +        } else {
 +            quote! {
 +                ClauseType::BuiltIn(
 +                    BuiltInClauseType::#ident
 +                ) => atom!(#name)
 +            }
 +        });
 +
 +        let ident = variant.ident;
 +        let instr_ident = format_ident!("Call{}", ident);
 +
 +        let placeholder_ids: Vec<_> = (0 .. variant_fields.len())
 +            .map(|n| format_ident!("f_{}", n))
 +            .collect();
 +
 +        clause_type_to_instr_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                ClauseType::BuiltIn(
 +                    BuiltInClauseType::#ident(#(#placeholder_ids),*)
 +                ) => Instruction::#instr_ident(#(*#placeholder_ids),*)
 +            }
 +        } else {
 +            quote! {
 +                ClauseType::BuiltIn(
 +                    BuiltInClauseType::#ident
 +                ) => Instruction::#instr_ident
 +            }
 +        });
 +
 +        is_inbuilt_arms.push(
 +            quote! {
 +                (atom!(#name), #arity) => true
 +            }
 +        );
 +    }
 +
 +    for (name, arity, variant) in instr_data.inlined_type_variants {
 +        let ident = variant.ident.clone();
 +
 +        let variant_fields: Vec<_> = variant.fields
 +            .into_iter()
 +            .map(|field| {
 +                if field.ty.type_id() == TypeId::of::<usize>() {
 +                    quote! { #arity }
 +                } else {
 +                    let ty = field.ty;
 +                    quote! { #ty::default() }
 +                }
 +            })
 +            .collect();
 +
 +        clause_type_from_name_and_arity_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                (atom!(#name), #arity) => ClauseType::Inlined(
 +                    InlinedClauseType::#ident(#(#variant_fields),*)
 +                )
 +            }
 +        } else {
 +            quote! {
 +                (atom!(#name), #arity) => ClauseType::Inlined(
 +                    InlinedClauseType::#ident
 +                )
 +            }
 +        });
 +
 +        clause_type_name_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                ClauseType::Inlined(
 +                    InlinedClauseType::#ident(..)
 +                ) => atom!(#name)
 +            }
 +        } else {
 +            quote! {
 +                ClauseType::Inlined(
 +                    InlinedClauseType::#ident
 +                ) => atom!(#name)
 +            }
 +        });
 +
 +        let ident = variant.ident;
 +        let instr_ident = format_ident!("Call{}", ident);
 +
 +        let placeholder_ids: Vec<_> = (0 .. variant_fields.len())
 +            .map(|n| format_ident!("f_{}", n))
 +            .collect();
 +
 +        clause_type_to_instr_arms.push(
 +            quote! {
 +                ClauseType::Inlined(
 +                    InlinedClauseType::#ident(#(#placeholder_ids),*)
 +                ) => Instruction::#instr_ident(*#(#placeholder_ids),*)
 +            }
 +        );
 +
 +        is_inbuilt_arms.push(
 +            quote! {
 +                (atom!(#name), #arity) => true
 +            }
 +        );
 +
 +        is_inlined_arms.push(
 +            quote! {
 +                (atom!(#name), #arity) => true
 +            }
 +        );
 +    }
 +
 +    for (name, arity, variant) in instr_data.system_clause_type_variants {
 +        let ident = variant.ident.clone();
 +
 +        let variant_fields: Vec<_> = variant.fields
 +            .into_iter()
 +            .map(|field| {
 +                if field.ty == parse_quote! { usize } {
 +                    quote! { #arity }
 +                } else {
 +                    let ty = field.ty;
 +                    quote! { #ty::default() }
 +                }
 +            })
 +            .collect();
 +
 +        clause_type_from_name_and_arity_arms.push(if !variant_fields.is_empty() {
 +            if ident.to_string() == "SetCutPoint" {
 +                quote! {
 +                    (atom!(#name), #arity) => ClauseType::System(
 +                        SystemClauseType::#ident(temp_v!(1))
 +                    )
 +                }
 +            } else if ident.to_string() == "SetCutPointByDefault" {
 +                quote! {
 +                    (atom!(#name), #arity) => ClauseType::System(
 +                        SystemClauseType::#ident(temp_v!(1))
 +                    )
 +                }
 +            } else if ident.to_string() == "InlineCallN" {
 +                quote! {
 +                    (atom!(#name), arity) => ClauseType::System(
 +                        SystemClauseType::#ident(arity)
 +                    )
 +                }
 +            } else {
 +                quote! {
 +                    (atom!(#name), #arity) => ClauseType::System(
 +                        SystemClauseType::#ident(#(#variant_fields),*)
 +                    )
 +                }
 +            }
 +        } else {
 +            quote! {
 +                (atom!(#name), #arity) => ClauseType::System(
 +                    SystemClauseType::#ident
 +                )
 +            }
 +        });
 +
 +        clause_type_name_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                ClauseType::System(
 +                    SystemClauseType::#ident(..)
 +                ) => atom!(#name)
 +            }
 +        } else {
 +            quote! {
 +                ClauseType::System(
 +                    SystemClauseType::#ident
 +                ) => atom!(#name)
 +            }
 +        });
 +
 +        let ident = variant.ident;
 +
 +        let instr_ident = if ident != "CallContinuation" {
 +            format_ident!("Call{}", ident)
 +        } else {
 +            ident.clone()
 +        };
 +
 +        let placeholder_ids: Vec<_> = (0 .. variant_fields.len())
 +            .map(|n| format_ident!("f_{}", n))
 +            .collect();
 +
 +        clause_type_to_instr_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                ClauseType::System(
 +                    SystemClauseType::#ident(#(#placeholder_ids),*)
 +                ) => Instruction::#instr_ident(#(*#placeholder_ids),*)
 +            }
 +        } else {
 +            quote! {
 +                ClauseType::System(
 +                    SystemClauseType::#ident
 +                ) => Instruction::#instr_ident
 +            }
 +        });
 +
 +        is_inbuilt_arms.push(
 +            if let Arity::Ident("arity") = &arity {
 +                quote! {
 +                    (atom!(#name), _arity) => true
 +                }
 +            } else {
 +                quote! {
 +                    (atom!(#name), #arity) => true
 +                }
 +            }
 +        );
 +    }
 +
 +    for (name, arity, variant) in instr_data.repl_code_ptr_variants {
 +        let ident = variant.ident.clone();
 +
 +        let variant_fields: Vec<_> = variant.fields
 +            .into_iter()
 +            .map(|field| {
 +                if field.ty.type_id() == TypeId::of::<usize>() {
 +                    quote! { #arity }
 +                } else {
 +                    let ty = field.ty;
 +                    quote! { #ty::default() }
 +                }
 +            })
 +            .collect();
 +
 +        clause_type_from_name_and_arity_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                (atom!(#name), #arity) => ClauseType::System(SystemClauseType::REPL(
 +                    REPLCodePtr::#ident(#(#variant_fields),*)
 +                ))
 +            }
 +        } else {
 +            quote! {
 +                (atom!(#name), #arity) => ClauseType::System(SystemClauseType::REPL(
 +                    REPLCodePtr::#ident
 +                ))
 +            }
 +        });
 +
 +        clause_type_name_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                ClauseType::System(
 +                    SystemClauseType::REPL(REPLCodePtr::#ident(..))
 +                ) => atom!(#name)
 +            }
 +        } else {
 +            quote! {
 +                ClauseType::System(
 +                    SystemClauseType::REPL(REPLCodePtr::#ident)
 +                ) => atom!(#name)
 +            }
 +        });
 +
 +        let ident = variant.ident;
 +        let instr_ident = format_ident!("Call{}", ident);
 +
 +        let placeholder_ids: Vec<_> = (0 .. variant_fields.len())
 +            .map(|n| format_ident!("f_{}", n))
 +            .collect();
 +
 +        clause_type_to_instr_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                ClauseType::System(SystemClauseType::REPL(
 +                    REPLCodePtr::#ident(#(#placeholder_ids),*)
 +                )) => Instruction::#instr_ident(#(*#placeholder_ids),*)
 +            }
 +        } else {
 +            quote! {
 +                ClauseType::System(SystemClauseType::REPL(
 +                    REPLCodePtr::#ident
 +                )) => Instruction::#instr_ident
 +            }
 +        });
 +
 +        is_inbuilt_arms.push(
 +            quote! {
 +                (atom!(#name), #arity) => true
 +            }
 +        );
 +    }
 +
 +    for (name, arity, variant) in instr_data.clause_type_variants {
 +        let ident = variant.ident.clone();
 +
 +        if ident == "Named" {
 +            clause_type_from_name_and_arity_arms.push(quote! {
 +                (name, arity) => ClauseType::Named(arity, name, CodeIndex::default(arena))
 +            });
 +
 +            clause_type_to_instr_arms.push(quote! {
 +                ClauseType::Named(arity, name, idx) => Instruction::CallNamed(*arity, *name, *idx)
 +            });
 +
 +            clause_type_name_arms.push(quote! {
 +                ClauseType::Named(_, name, _) => *name
 +            });
 +
 +            continue;
 +        }
 +
 +        let variant_fields: Vec<_> = variant.fields
 +            .into_iter()
 +            .map(|field| {
 +                if field.ty == parse_quote! { usize } {
 +                    quote! { #arity }
 +                } else {
 +                    let ty = field.ty;
 +                    quote! { #ty::default() }
 +                }
 +            })
 +            .collect();
 +
 +        clause_type_from_name_and_arity_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                (atom!(#name), #arity) => ClauseType::#ident(#(#variant_fields),*)
 +            }
 +        } else {
 +            quote! {
 +                (atom!(#name), #arity) => ClauseType::#ident
 +            }
 +        });
 +
 +        clause_type_name_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                ClauseType::#ident(..) => atom!(#name)
 +            }
 +        } else {
 +            quote! {
 +                ClauseType::#ident => atom!(#name)
 +            }
 +        });
 +
 +        let ident = variant.ident;
 +
 +        let placeholder_ids: Vec<_> = (0 .. variant_fields.len())
 +            .map(|n| format_ident!("f_{}", n))
 +            .collect();
 +
 +        clause_type_to_instr_arms.push(if !variant_fields.is_empty() {
 +            quote! {
 +                ClauseType::#ident(#(#placeholder_ids),*) =>
 +                    Instruction::#ident(#(*#placeholder_ids),*)
 +            }
 +        } else {
 +            quote! {
 +                ClauseType::#ident => Instruction::#ident
 +            }
 +        });
 +
 +        is_inbuilt_arms.push(
 +            quote! {
 +                (atom!(#name), _arity) => true
 +            }
 +        );
 +    }
 +
 +    let to_execute_arms: Vec<_> = instr_data.instr_variants
 +        .iter()
 +        .cloned()
 +        .filter_map(|(_, _, _, variant)| {
 +            let variant_ident = variant.ident.clone();
 +            let variant_string = variant.ident.to_string();
 +
 +            let enum_arity = if let Fields::Unnamed(fields) = &variant.fields {
 +                fields.unnamed.len()
 +            } else {
 +                0
 +            };
 +
 +            let placeholder_ids: Vec<_> = (0 .. enum_arity)
 +                .map(|n| format_ident!("f_{}", n))
 +                .collect();
 +
 +            if variant_string.starts_with("Call") {
 +                let execute_ident = format_ident!("Execute{}", variant_string["Call".len() ..]);
 +
 +                Some(if enum_arity == 0 {
 +                    quote! {
 +                        Instruction::#variant_ident =>
 +                            Instruction::#execute_ident
 +                    }
 +                } else {
 +                    quote! {
 +                        Instruction::#variant_ident(#(#placeholder_ids),*) =>
 +                            Instruction::#execute_ident(#(#placeholder_ids),*)
 +                    }
 +                })
 +            } else if variant_string.starts_with("DefaultCall") {
 +                let execute_ident =
 +                    format_ident!("DefaultExecute{}", variant_string["DefaultCall".len() ..]);
 +
 +                Some(if enum_arity == 0 {
 +                    quote! {
 +                        Instruction::#variant_ident =>
 +                            Instruction::#execute_ident
 +                    }
 +                } else {
 +                    quote! {
 +                        Instruction::#variant_ident(#(#placeholder_ids),*) =>
 +                            Instruction::#execute_ident(#(#placeholder_ids),*)
 +                    }
 +                })
 +            } else {
 +                None
 +            }
 +        })
 +        .collect();
 +
 +    let is_execute_arms: Vec<_> = instr_data.instr_variants
 +        .iter()
 +        .cloned()
 +        .filter_map(|(_, _, _, variant)| {
 +            let variant_ident = variant.ident.clone();
 +            let variant_string = variant.ident.to_string();
 +
 +            let enum_arity = if let Fields::Unnamed(fields) = &variant.fields {
 +                fields.unnamed.len()
 +            } else {
 +                0
 +            };
 +
 +            if variant_string.starts_with("Execute") {
 +                Some(if enum_arity == 0 {
 +                    quote! {
 +                        Instruction::#variant_ident => true
 +                    }
 +                } else {
 +                    quote! {
 +                        Instruction::#variant_ident(..) => true
 +                    }
 +                })
 +            } else if variant_string.starts_with("DefaultExecute") {
 +                Some(if enum_arity == 0 {
 +                    quote! {
 +                        Instruction::#variant_ident => true
 +                    }
 +                } else {
 +                    quote! {
 +                        Instruction::#variant_ident(..) => true
 +                    }
 +                })
 +            } else if variant_string == "JmpByExecute" {
 +                Some(quote! {
 +                    Instruction::#variant_ident(..) => true
 +                })
 +            } else {
 +                None
 +            }
 +        })
 +        .collect();
 +
 +    let to_default_arms: Vec<_> = instr_data.instr_variants
 +        .iter()
 +        .cloned()
 +        .filter_map(|(_, _, countable_inference, variant)| {
 +            if !is_non_default_callable(&variant.ident) {
 +                return None;
 +            }
 +
 +            if let CountableInference::HasDefault = countable_inference {
 +                let variant_ident = variant.ident.clone();
 +                let def_variant_ident = format_ident!("Default{}", variant.ident);
 +                let enum_arity = if let Fields::Unnamed(fields) = &variant.fields {
 +                    fields.unnamed.len()
 +                } else {
 +                    0
 +                };
 +
 +                let placeholder_ids: Vec<_> = (0 .. enum_arity)
 +                    .map(|n| format_ident!("f_{}", n))
 +                    .collect();
 +
 +                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
 +            }
 +        })
 +        .collect();
 +
 +    let control_flow_arms: Vec<_> = instr_data.instr_variants
 +        .iter()
 +        .cloned()
 +        .filter_map(|(_, _, _, variant)| {
 +            if !is_callable(&variant.ident) && !is_jmp(&variant.ident) {
 +                return None;
 +            }
 +
 +            let enum_arity = if let Fields::Unnamed(fields) = &variant.fields {
 +                fields.unnamed.len()
 +            } else {
 +                0
 +            };
 +
 +            let variant_ident = variant.ident.clone();
 +
 +            Some(if enum_arity == 0 {
 +                quote! {
 +                    Instruction::#variant_ident => true
 +                }
 +            } else {
 +                quote! {
 +                    Instruction::#variant_ident(..) => true
 +                }
 +            })
 +        })
 +        .collect();
 +
 +    let instr_macro_arms: Vec<_> = instr_data.instr_variants
 +        .iter()
 +        .rev() // produce default, execute & default & execute cases first.
 +        .cloned()
 +        .filter_map(|(name, arity, _, variant)| {
 +            let variant_ident = variant.ident.clone();
 +            let variant_string = variant.ident.to_string();
 +            let arity = match arity {
 +                Arity::Static(arity) => arity,
 +                _ => 1
 +            };
 +
 +            Some(if variant_string.starts_with("Execute") {
 +                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") {
 +                if arity == 0 {
 +                    quote! {
 +                        (#name) => {
 +                            Instruction::#variant_ident
 +                        }
 +                    }
 +                } else {
 +                    quote! {
 +                        (#name, $($args:expr),*) => {
 +                            Instruction::#variant_ident($($args),*)
 +                        }
 +                    }
 +                }
 +            } else if variant_string.starts_with("DefaultExecute") {
 +                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") {
 +                if arity == 0 {
 +                    quote! {
 +                        (#name, default) => {
 +                            Instruction::#variant_ident
 +                        }
 +                    }
 +                } else {
 +                    quote! {
 +                        (#name, default, $($args:expr),*) => {
 +                            Instruction::#variant_ident($($args),*)
 +                        }
 +                    }
 +                }
 +            } else {
 +                if arity == 0 {
 +                    quote! {
 +                        (#name) => {
 +                            Instruction::#variant_ident
 +                        }
 +                    }
 +                } else {
 +                    quote! {
 +                        (#name, $($args:expr),*) => {
 +                            Instruction::#variant_ident($($args),*)
 +                        }
 +                    }
 +                }
 +            })
 +        })
 +        .collect();
 +
 +    let name_and_arity_arms: Vec<_> = instr_data.instr_variants
 +        .into_iter()
 +        .map(|(name,arity,_,variant)| {
 +            let ident = &variant.ident;
 +
 +            let enum_arity = if let Fields::Unnamed(fields) = &variant.fields {
 +                fields.unnamed.len()
 +            } else {
 +                0
 +            };
 +
 +            match arity {
 +                Arity::Static(_) if enum_arity == 0 => {
 +                    quote! { &Instruction::#ident => (atom!(#name), #arity) }
 +                }
 +                Arity::Static(_) => {
 +                    quote! { &Instruction::#ident(..) => (atom!(#name), #arity) }
 +                }
 +                Arity::Ident(_) if enum_arity == 0 => {
 +                    quote! { &Instruction::#ident(#arity) => (atom!(#name), #arity) }
 +                }
 +                Arity::Ident(_) => {
 +                    quote! { &Instruction::#ident(#arity, ..) => (atom!(#name), #arity) }
 +                }
 +            }
 +        })
 +        .collect();
 +
 +    let preface_tokens = generate_instruction_preface();
 +
 +    quote! {
 +        #preface_tokens
 +
 +        #[derive(Clone, Debug)]
 +        pub enum CompareTerm {
 +            #(
 +                #compare_term_variants,
 +            )*
 +        }
 +
 +        #[derive(Clone, Copy, Debug)]
 +        pub enum CompareNumber {
 +            #(
 +                #compare_number_variants,
 +            )*
 +        }
 +
 +        impl CompareNumber {
 +            pub fn set_terms(&mut self, l_at_1: ArithmeticTerm, l_at_2: ArithmeticTerm) {
 +                match self {
 +                    CompareNumber::NumberGreaterThan(ref mut at_1, ref mut at_2) |
 +                    CompareNumber::NumberLessThan(ref mut at_1, ref mut at_2) |
 +                    CompareNumber::NumberGreaterThanOrEqual(ref mut at_1, ref mut at_2) |
 +                    CompareNumber::NumberLessThanOrEqual(ref mut at_1, ref mut at_2) |
 +                    CompareNumber::NumberNotEqual(ref mut at_1, ref mut at_2) |
 +                    CompareNumber::NumberEqual(ref mut at_1, ref mut at_2) => {
 +                        *at_1 = l_at_1;
 +                        *at_2 = l_at_2;
 +                    }
 +                }
 +            }
 +        }
 +
 +        #[derive(Clone, Debug)]
 +        pub enum BuiltInClauseType {
 +            #(
 +                #builtin_type_variants,
 +            )*
 +        }
 +
 +        #[derive(Clone, Debug)]
 +        pub enum InlinedClauseType {
 +            #(
 +                #inlined_type_variants,
 +            )*
 +        }
 +
 +        #[derive(Clone, Debug)]
 +        pub enum SystemClauseType {
 +            #(
 +                #system_clause_type_variants,
 +            )*
 +        }
 +
 +        #[derive(Clone, Debug)]
 +        pub enum REPLCodePtr {
 +            #(
 +                #repl_code_ptr_variants,
 +            )*
 +        }
 +
 +        #[derive(Clone, Debug)]
 +        pub enum ClauseType {
 +            #(
 +                #clause_type_variants,
 +            )*
 +        }
 +
 +        impl ClauseType {
 +            pub fn from(name: Atom, arity: usize, arena: &mut Arena) -> ClauseType {
 +                match (name, arity) {
 +                    #(
 +                        #clause_type_from_name_and_arity_arms,
 +                    )*
 +                }
 +            }
 +
 +            pub fn to_instr(&self) -> Instruction {
 +                match self {
 +                    #(
 +                        #clause_type_to_instr_arms,
 +                    )*
 +                }
 +            }
 +
 +            pub fn is_inbuilt(name: Atom, arity: usize) -> bool {
 +                match (name, arity) {
 +                    #(
 +                        #is_inbuilt_arms,
 +                    )*
 +                    _ => false,
 +                }
 +            }
 +
 +            pub fn name(&self) -> Atom {
 +                match self {
 +                    #(
 +                        #clause_type_name_arms,
 +                    )*
 +                }
 +            }
 +
 +            pub fn is_inlined(name: Atom, arity: usize) -> bool {
 +                match (name, arity) {
 +                    #(
 +                        #is_inlined_arms,
 +                    )*
 +                    _ => false,
 +                }
 +            }
 +        }
 +
 +        #[derive(Clone, Debug)]
 +        pub enum Instruction {
 +            #(
 +                #instr_variants,
 +            )*
 +        }
 +
 +        impl Instruction {
 +            pub fn to_name_and_arity(&self) -> (Atom, usize) {
 +                match self {
 +                    #(
 +                        #name_and_arity_arms,
 +                    )*
 +                }
 +            }
 +
 +            pub fn to_default(self) -> Instruction {
 +                match self {
 +                    #(
 +                        #to_default_arms,
 +                    )*
 +                    _ => self,
 +                }
 +            }
 +
 +            pub fn to_execute(self) -> Instruction {
 +                match self {
 +                    #(
 +                        #to_execute_arms,
 +                    )*
 +                    _ => self
 +                }
 +            }
 +
 +            pub fn is_execute(&self) -> bool {
 +                match self {
 +                    #(
 +                        #is_execute_arms,
 +                    )*
 +                    _ => false,
 +                }
 +            }
 +
 +            pub fn is_ctrl_instr(&self) -> bool {
 +                match self {
 +                    &Instruction::Allocate(_) |
 +                    &Instruction::Deallocate |
 +                    &Instruction::Proceed |
 +                    &Instruction::RevJmpBy(_) => true,
 +                    #(
 +                        #control_flow_arms,
 +                    )*
 +                    _ => false,
 +                }
 +            }
 +
 +            pub fn is_query_instr(&self) -> bool {
 +                match self {
 +                    &Instruction::GetVariable(..) |
 +                    &Instruction::PutConstant(..) |
 +                    &Instruction::PutList(..) |
 +                    &Instruction::PutPartialString(..) |
 +                    &Instruction::PutStructure(..) |
 +                    &Instruction::PutUnsafeValue(..) |
 +                    &Instruction::PutValue(..) |
 +                    &Instruction::PutVariable(..) |
 +                    &Instruction::SetConstant(..) |
 +                    &Instruction::SetLocalValue(..) |
 +                    &Instruction::SetVariable(..) |
 +                    &Instruction::SetValue(..) |
 +                    &Instruction::SetVoid(..) => true,
 +                    _ => false,
 +                }
 +            }
 +        }
 +
 +        #[macro_export]
 +        macro_rules! _instr {
 +            #(
 +                #instr_macro_arms
 +            );*
 +        }
 +
 +        pub use _instr as instr; // https://github.com/rust-lang/rust/pull/52234#issuecomment-976702997
 +    }
 +}
 +
 +fn is_callable(id: &Ident) -> bool {
 +    let id = id.to_string();
 +
 +    id.starts_with("Call") || id.starts_with("Execute") || id.starts_with("DefaultCall") ||
 +        id.starts_with("DefaultExecute")
 +}
 +
 +fn is_non_default_callable(id: &Ident) -> bool {
 +    let id = id.to_string();
 +    id.starts_with("Call") || id.starts_with("Execute")
 +}
 +
 +fn is_jmp(id: &Ident) -> bool {
 +    let id = id.to_string();
 +    id.starts_with("JmpByCall") || id.starts_with("JmpByExecute")
 +}
 +
 +fn create_instr_variant(id: Ident, mut variant: Variant) -> Variant {
 +    variant.ident = id;
 +    variant.attrs.clear();
 +
 +    variant
 +}
 +
 +fn prop_from_ident<DiscriminantT>(id: &Ident, key: &'static str) -> &'static str
 +   where DiscriminantT: FromStr + strum::EnumProperty + std::fmt::Debug
 +{
 +    let disc = match DiscriminantT::from_str(id.to_string().as_str()) {
 +        Ok(disc) => disc,
 +        Err(_) => {
 +            panic!("can't generate discriminant {}", id);
 +        }
 +    };
 +
 +    match disc.get_str(key) {
 +        Some(prop) => prop,
 +        None => {
 +            panic!("can't find property {} of discriminant {:?}", key, disc);
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +enum Arity {
 +    Static(usize),
 +    Ident(&'static str)
 +}
 +
 +impl From<&'static str> for Arity {
 +    fn from(arity: &'static str) -> Self {
 +        usize::from_str_radix(&arity, 10)
 +            .map(Arity::Static)
 +            .unwrap_or_else(|_| Arity::Ident(arity))
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +enum CountableInference {
 +    HasDefault,
 +    NotCounted,
 +}
 +
 +struct InstructionData {
 +    instr_variants: Vec<(&'static str, Arity, CountableInference, Variant)>,
 +    clause_type_variants: Vec<(&'static str, Arity, Variant)>,
 +    builtin_type_variants: Vec<(&'static str, Arity, Variant)>,
 +    inlined_type_variants: Vec<(&'static str, Arity, Variant)>,
 +    system_clause_type_variants: Vec<(&'static str, Arity, Variant)>,
 +    compare_number_variants: Vec<(&'static str, Arity, Variant)>,
 +    compare_term_variants: Vec<(&'static str, Arity, Variant)>,
 +    repl_code_ptr_variants: Vec<(&'static str, Arity, Variant)>,
 +}
 +
 +impl InstructionData {
 +    fn new() -> Self {
 +        Self {
 +            instr_variants: vec![],
 +            clause_type_variants: vec![],
 +            builtin_type_variants: vec![],
 +            inlined_type_variants: vec![],
 +            system_clause_type_variants: vec![],
 +            compare_number_variants: vec![],
 +            compare_term_variants: vec![],
 +            repl_code_ptr_variants: vec![],
 +        }
 +    }
 +
 +    fn label_variant(&mut self, id: &Ident, prefix: &'static str, variant: Variant) {
 +        let (name, arity, countable_inference) = if id == "CompareNumber" {
 +            let (name, arity) = add_discriminant_data::<CompareNumberDiscriminants>(
 +                &variant,
 +                prefix,
 +                &mut self.compare_number_variants,
 +            );
 +
 +            (name, arity, CountableInference::HasDefault)
 +        } else if id == "CompareTerm" {
 +            let (name, arity) = add_discriminant_data::<CompareTermDiscriminants>(
 +                &variant,
 +                prefix,
 +                &mut self.compare_term_variants,
 +            );
 +
 +            (name, arity, CountableInference::HasDefault)
 +        } else if id == "BuiltInClauseType" {
 +            let (name, arity) = add_discriminant_data::<BuiltInClauseTypeDiscriminants>(
 +                &variant,
 +                prefix,
 +                &mut self.builtin_type_variants,
 +            );
 +
 +            (name, arity, CountableInference::HasDefault)
 +        } else if id == "InlinedClauseType" {
 +            let (name, arity) = add_discriminant_data::<InlinedClauseTypeDiscriminants>(
 +                &variant,
 +                prefix,
 +                &mut self.inlined_type_variants,
 +            );
 +
 +            (name, arity, CountableInference::NotCounted)
 +        } else if id == "REPLCodePtr" {
 +            let (name, arity) = add_discriminant_data::<REPLCodePtrDiscriminants>(
 +                &variant,
 +                prefix,
 +                &mut self.repl_code_ptr_variants,
 +            );
 +
 +            (name, arity, CountableInference::NotCounted)
 +        } else if id == "SystemClauseType" {
 +            let (name, arity) = add_discriminant_data::<SystemClauseTypeDiscriminants>(
 +                &variant,
 +                prefix,
 +                &mut self.system_clause_type_variants,
 +            );
 +
 +            (name, arity, CountableInference::NotCounted)
 +        } else if id == "InstructionTemplate" {
 +            ( prop_from_ident::<InstructionTemplateDiscriminants>(&variant.ident, "Name"),
 +              Arity::from(prop_from_ident::<InstructionTemplateDiscriminants>(&variant.ident, "Arity")),
 +              CountableInference::NotCounted
 +            )
 +        } else if id == "ClauseType" {
 +            let (name, arity) = add_discriminant_data::<ClauseTypeDiscriminants>(
 +                &variant,
 +                prefix,
 +                &mut self.clause_type_variants,
 +            );
 +
 +            (name, arity, CountableInference::HasDefault)
 +        } else {
 +            panic!("type ID is: {}", id);
 +        };
 +
 +        let v_string = variant.ident.to_string();
 +
 +        let v_ident = if v_string.starts_with("Call") {
 +            format_ident!("{}", v_string["Call".len() ..])
 +        } else {
 +            variant.ident.clone()
 +        };
 +
 +        let generated_variant = create_instr_variant(
 +            format_ident!("{}{}", prefix, v_ident),
 +            variant.clone(),
 +        );
 +
 +        self.instr_variants.push(
 +            (name, arity, countable_inference, generated_variant)
 +        );
 +    }
 +
 +    fn generate_instruction_enum_loop(&mut self, input: syn::DeriveInput) {
 +        if let Data::Enum(DataEnum { variants, .. }) = input.data {
 +            for mut variant in variants {
 +                if let Some(field) = variant.fields.iter().next() {
 +                    if let Some(input) = derive_input(&field.ty) {
 +                        self.generate_instruction_enum_loop(input);
 +                        continue;
 +                    }
 +                }
 +
 +                if input.ident == "InstructionTemplate" {
 +                    variant.attrs.clear();
 +                    self.label_variant(&input.ident, "", variant);
 +                    continue;
 +                }
 +
 +                self.label_variant(&input.ident, "Call", variant.clone());
 +                self.label_variant(&input.ident, "Execute", variant.clone());
 +
 +                if input.ident == "BuiltInClauseType" ||
 +                    input.ident == "CompareNumber" ||
 +                    input.ident == "CompareTerm" ||
 +                    input.ident == "ClauseType"
 +                {
 +                    self.label_variant(&input.ident, "DefaultCall", variant.clone());
 +                    self.label_variant(&input.ident, "DefaultExecute", variant);
 +                }
 +            }
 +        } else {
 +            panic!("{} must be an enum!", input.ident);
 +        }
 +    }
 +}
index bc58da507746972a298c3dd5d57e445c65831553,2edd90fdfb03e2dc2ef8f003c3747f34c9984d00..a75f2efee06141b9b4a8f5f4bba747590a49a860
@@@ -182,39 -143,36 +182,41 @@@ impl<'a> ArithmeticEvaluator<'a> 
      }
  
      fn get_unary_instr(
 -        name: ClauseName,
 +        &self,
 +        name: Atom,
          a1: ArithmeticTerm,
          t: usize,
 -    ) -> Result<ArithmeticInstruction, ArithmeticError> {
 -        match name.as_str() {
 -            "abs" => Ok(ArithmeticInstruction::Abs(a1, t)),
 -            "-" => Ok(ArithmeticInstruction::Neg(a1, t)),
 -            "+" => Ok(ArithmeticInstruction::Plus(a1, t)),
 -            "cos" => Ok(ArithmeticInstruction::Cos(a1, t)),
 -            "sin" => Ok(ArithmeticInstruction::Sin(a1, t)),
 -            "tan" => Ok(ArithmeticInstruction::Tan(a1, t)),
 -            "log" => Ok(ArithmeticInstruction::Log(a1, t)),
 -            "exp" => Ok(ArithmeticInstruction::Exp(a1, t)),
 -            "sqrt" => Ok(ArithmeticInstruction::Sqrt(a1, t)),
 -            "acos" => Ok(ArithmeticInstruction::ACos(a1, t)),
 -            "asin" => Ok(ArithmeticInstruction::ASin(a1, t)),
 -            "atan" => Ok(ArithmeticInstruction::ATan(a1, t)),
 -            "float" => Ok(ArithmeticInstruction::Float(a1, t)),
 -            "truncate" => Ok(ArithmeticInstruction::Truncate(a1, t)),
 -            "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(
 -                Constant::Atom(name, None),
 -                1,
 -            )),
 +    ) -> Result<Instruction, ArithmeticError> {
 +        match name {
 +            atom!("abs") => Ok(Instruction::Abs(a1, t)),
 +            atom!("-") => Ok(Instruction::Neg(a1, t)),
 +            atom!("+") => Ok(Instruction::Plus(a1, t)),
 +            atom!("cos") => Ok(Instruction::Cos(a1, t)),
 +            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)),
 +            atom!("asin") => Ok(Instruction::ASin(a1, t)),
 +            atom!("atan") => Ok(Instruction::ATan(a1, t)),
 +            atom!("float") => Ok(Instruction::Float(a1, t)),
 +            atom!("truncate") => Ok(Instruction::Truncate(a1, t)),
 +            atom!("round") => Ok(Instruction::Round(a1, t)),
 +            atom!("ceiling") => Ok(Instruction::Ceiling(a1, t)),
 +            atom!("floor") => Ok(Instruction::Floor(a1, t)),
++            atom!("float_integer_part") => Ok(Instruction::FloatIntegerPart(a1, t)),
++            atom!("float_fractional_part") => Ok(Instruction::FloatFractionalPart(a1, t)),
 +            atom!("sign") => Ok(Instruction::Sign(a1, t)),
 +            atom!("\\") => Ok(Instruction::BitwiseComplement(a1, t)),
 +            _ => Err(ArithmeticError::NonEvaluableFunctor(Literal::Atom(name), 1)),
          }
      }
  
index 6af111b2724609d71f794d396a20bc89e00a38a6,4e3f832af794771e89736ebe027a0182940211e3..6be49e8deff4843c394d323df8870db9f08c5640
@@@ -595,546 -510,364 +595,557 @@@ pub(crate) fn idiv(n1: Number, n2: Numb
                  ))
              }
          }
 +        (Number::Fixnum(_), n2) | (Number::Integer(_), n2) => {
 +            Err(numerical_type_error(ValidType::Integer, n2, stub_gen))
 +        }
 +        (n1, _) => Err(numerical_type_error(ValidType::Integer, n1, stub_gen)),
      }
 +}
  
 -    pub(crate) fn float_pow(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
 -        let f1 = result_f(&n1, rnd_f);
 -        let f2 = result_f(&n2, rnd_f);
 +pub(crate) fn int_floor_div(
 +    n1: Number,
 +    n2: Number,
 +    arena: &mut Arena,
 +) -> Result<Number, MachineStubGen> {
 +    let stub_gen = || {
 +        let div_atom = atom!("div");
 +        functor_stub(div_atom, 2)
 +    };
  
 -        let stub = MachineError::functor_stub(clause_name!("(**)"), 2);
 +    let modulus = modulus(n1, n2, arena)?;
 +    let n1 = try_numeric_result!(sub(n1, modulus, arena), stub_gen)?;
  
 -        let f1 = try_numeric_result!(self, f1, stub)?;
 -        let f2 = try_numeric_result!(self, f2, stub)?;
 +    idiv(n1, n2, arena)
 +}
  
 -        let result = result_f(&Number::Float(OrderedFloat(f1.powf(f2))), rnd_f);
 +pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
 +    let stub_gen = || {
 +        let shr_atom = atom!(">>");
 +        functor_stub(shr_atom, 2)
 +    };
  
 -        Ok(Number::Float(OrderedFloat(try_numeric_result!(
 -            self, result, stub
 -        )?)))
 +    if n2.is_integer() && n2.is_negative() {
 +        return shl(n1, neg(n2, arena), arena);
      }
  
 -    pub(crate) fn pow(
 -        &self,
 -        n1: Number,
 -        n2: Number,
 -        culprit: &'static str,
 -    ) -> Result<Number, MachineStub> {
 -        if n2.is_negative() && n1.is_zero() {
 -            let stub = MachineError::functor_stub(clause_name!(culprit), 2);
 -            return Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub));
 +    match (n1, n2) {
 +        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
 +            let n1_i = n1.get_num();
 +            let n2_i = n2.get_num();
 +
 +            let n1 = Integer::from(n1_i);
 +
 +            if let Ok(n2) = usize::try_from(n2_i) {
 +                return Ok(Number::arena_from(n1 >> n2, arena));
 +              } else {
 +                return Ok(Number::arena_from(n1 >> usize::max_value(), arena));
 +            }
 +        }
 +        (Number::Fixnum(n1), Number::Integer(n2)) => {
 +            let n1 = Integer::from(n1.get_num());
 +
 +            match n2.to_usize() {
 +                Some(n2) => Ok(Number::arena_from(n1 >> n2, arena)),
 +                _ => {
 +                              Ok(Number::arena_from(n1 >> usize::max_value(), arena))
 +                      },
 +            }
          }
 +        (Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) {
 +            Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
 +            _ => {
 +                      Ok(Number::arena_from(Integer::from(&*n1 >> usize::max_value()),arena))
 +              },
 +        },
 +        (Number::Integer(n1), Number::Integer(n2)) => match n2.to_usize() {
 +            Some(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
 +            _ => {
 +                      Ok(Number::arena_from(Integer::from(&*n1 >> 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)),
 +        (n1, _) => Err(numerical_type_error(ValidType::Integer, n1, stub_gen)),
 +    }
 +}
  
 -        self.float_pow(n1, n2)
 +pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
 +    let stub_gen = || {
 +        let shl_atom = atom!("<<");
 +        functor_stub(shl_atom, 2)
 +    };
 +
 +    if n2.is_integer() && n2.is_negative() {
 +        return shr(n1, neg(n2, arena), arena);
      }
  
 -    #[inline]
 -    pub(crate) fn unary_float_fn_template<FloatFn>(
 -        &self,
 -        n1: Number,
 -        f: FloatFn,
 -    ) -> Result<f64, MachineStub>
 -    where
 -        FloatFn: Fn(f64) -> f64,
 -    {
 -        let stub = MachineError::functor_stub(clause_name!("is"), 2);
 +    match (n1, n2) {
 +        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
 +            let n1_i = n1.get_num();
 +            let n2_i = n2.get_num();
  
 -        let f1 = try_numeric_result!(self, result_f(&n1, rnd_f), stub)?;
 -        let f1 = result_f(&Number::Float(OrderedFloat(f(f1))), rnd_f);
 +            let n1 = Integer::from(n1_i);
  
 -        try_numeric_result!(self, f1, stub)
 +            if let Ok(n2) = usize::try_from(n2_i) {
 +                return Ok(Number::arena_from(n1 << n2, arena));
 +              } else {
 +                return Ok(Number::arena_from(n1 << usize::max_value(), arena));
 +            }
 +        }
 +        (Number::Fixnum(n1), Number::Integer(n2)) => {
 +            let n1 = Integer::from(n1.get_num());
 +
 +            match n2.to_u32() {
 +                Some(n2) => Ok(Number::arena_from(n1 << n2, arena)),
 +                _ => {
 +                              Ok(Number::arena_from(n1 << usize::max_value(), arena))
 +                      }
 +            }
 +        }
 +        (Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) {
 +            Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
 +            _ => {
 +                      Ok(Number::arena_from(Integer::from(&*n1 << usize::max_value()),arena))
 +                  }
 +        },
 +        (Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() {
 +            Some(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
 +            _ => {
 +                      Ok(Number::arena_from(Integer::from(&*n1 << 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)),
 +        (n1, _) => Err(numerical_type_error(ValidType::Integer, n1, stub_gen)),
      }
 +}
  
 -    #[inline]
 -    pub(crate) fn sin(&self, n1: Number) -> Result<f64, MachineStub> {
 -        self.unary_float_fn_template(n1, |f| f.sin())
 -    }
 +pub(crate) fn and(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
 +    let stub_gen = || {
 +        let and_atom = atom!("/\\");
 +        functor_stub(and_atom, 2)
 +    };
  
 -    #[inline]
 -    pub(crate) fn cos(&self, n1: Number) -> Result<f64, MachineStub> {
 -        self.unary_float_fn_template(n1, |f| f.cos())
 +    match (n1, n2) {
 +        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
 +            Ok(Number::arena_from(n1.get_num() & n2.get_num(), arena))
 +        }
 +        (Number::Fixnum(n1), Number::Integer(n2)) => {
 +            let n1 = Integer::from(n1.get_num());
 +            Ok(Number::arena_from(n1 & &*n2, arena))
 +        }
 +        (Number::Integer(n1), Number::Fixnum(n2)) => Ok(Number::arena_from(
 +            &*n1 & Integer::from(n2.get_num()),
 +            arena,
 +        )),
 +        (Number::Integer(n1), Number::Integer(n2)) => {
 +            Ok(Number::arena_from(Integer::from(&*n1 & &*n2), arena))
 +        }
 +        (Number::Integer(_), n2) | (Number::Fixnum(_), n2) => {
 +            Err(numerical_type_error(ValidType::Integer, n2, stub_gen))
 +        }
 +        (n1, _) => Err(numerical_type_error(ValidType::Integer, n1, stub_gen)),
      }
 +}
  
 -    #[inline]
 -    pub(crate) fn tan(&self, n1: Number) -> Result<f64, MachineStub> {
 -        self.unary_float_fn_template(n1, |f| f.tan())
 -    }
 +pub(crate) fn or(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
 +    let stub_gen = || {
 +        let or_atom = atom!("\\/");
 +        functor_stub(or_atom, 2)
 +    };
  
 -    #[inline]
 -    pub(crate) fn log(&self, n1: Number) -> Result<f64, MachineStub> {
 -        self.unary_float_fn_template(n1, |f| f.log(f64::consts::E))
 +    match (n1, n2) {
 +        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
 +            Ok(Number::arena_from(n1.get_num() | n2.get_num(), arena))
 +        }
 +        (Number::Fixnum(n1), Number::Integer(n2)) => {
 +            let n1 = Integer::from(n1.get_num());
 +            Ok(Number::arena_from(n1 | &*n2, arena))
 +        }
 +        (Number::Integer(n1), Number::Fixnum(n2)) => Ok(Number::arena_from(
 +            &*n1 | Integer::from(n2.get_num()),
 +            arena,
 +        )),
 +        (Number::Integer(n1), Number::Integer(n2)) => {
 +            Ok(Number::arena_from(Integer::from(&*n1 | &*n2), arena))
 +        }
 +        (Number::Integer(_), n2) | (Number::Fixnum(_), n2) => {
 +            Err(numerical_type_error(ValidType::Integer, n2, stub_gen))
 +        }
 +        (n1, _) => Err(numerical_type_error(ValidType::Integer, n1, stub_gen)),
      }
 +}
  
 -    #[inline]
 -    pub(crate) fn exp(&self, n1: Number) -> Result<f64, MachineStub> {
 -        self.unary_float_fn_template(n1, |f| f.exp())
 -    }
 +pub(crate) fn xor(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
 +    let stub_gen = || {
 +        let xor_atom = atom!("xor");
 +        functor_stub(xor_atom, 2)
 +    };
  
 -    #[inline]
 -    pub(crate) fn asin(&self, n1: Number) -> Result<f64, MachineStub> {
 -        self.unary_float_fn_template(n1, |f| f.asin())
 +    match (n1, n2) {
 +        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
 +            Ok(Number::arena_from(n1.get_num() ^ n2.get_num(), arena))
 +        }
 +        (Number::Fixnum(n1), Number::Integer(n2)) => {
 +            let n1 = Integer::from(n1.get_num());
 +            Ok(Number::arena_from(n1 ^ &*n2, arena))
 +        }
 +        (Number::Integer(n1), Number::Fixnum(n2)) => Ok(Number::arena_from(
 +            &*n1 ^ Integer::from(n2.get_num()),
 +            arena,
 +        )),
 +        (Number::Integer(n1), Number::Integer(n2)) => {
 +            Ok(Number::arena_from(Integer::from(&*n1 ^ &*n2), arena))
 +        }
 +        (Number::Integer(_), n2) | (Number::Fixnum(_), n2) => {
 +            Err(numerical_type_error(ValidType::Integer, n2, stub_gen))
 +        }
 +        (n1, Number::Integer(_)) => Err(numerical_type_error(ValidType::Integer, n1, stub_gen)),
 +        _ => Err(numerical_type_error(ValidType::Integer, n1, stub_gen)),
      }
 +}
  
 -    #[inline]
 -    pub(crate) fn acos(&self, n1: Number) -> Result<f64, MachineStub> {
 -        self.unary_float_fn_template(n1, |f| f.acos())
 -    }
 +pub(crate) fn modulus(x: Number, y: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
 +    let stub_gen = || {
 +        let mod_atom = atom!("mod");
 +        functor_stub(mod_atom, 2)
 +    };
  
 -    #[inline]
 -    pub(crate) fn atan(&self, n1: Number) -> Result<f64, MachineStub> {
 -        self.unary_float_fn_template(n1, |f| f.atan())
 -    }
 +    match (x, y) {
 +        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
 +            let n2_i = n2.get_num();
  
 -    #[inline]
 -    pub(crate) fn sqrt(&self, n1: Number) -> Result<f64, MachineStub> {
 -        if n1.is_negative() {
 -            let stub = MachineError::functor_stub(clause_name!("is"), 2);
 -            return Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub));
 +            if n2_i == 0 {
 +                Err(zero_divisor_eval_error(stub_gen))
 +            } else {
 +                let n1_i = n1.get_num();
 +                Ok(Number::arena_from(n1_i.rem_floor(n2_i), arena))
 +            }
          }
 -
 -        self.unary_float_fn_template(n1, |f| f.sqrt())
 +        (Number::Fixnum(n1), Number::Integer(n2)) => {
 +            if &*n2 == &0 {
 +                Err(zero_divisor_eval_error(stub_gen))
 +            } else {
 +                let n1 = Integer::from(n1.get_num());
 +                Ok(Number::arena_from(
 +                    <(Integer, Integer)>::from(n1.div_rem_floor_ref(&*n2)).1,
 +                    arena,
 +                ))
 +            }
 +        }
 +        (Number::Integer(n1), Number::Fixnum(n2)) => {
 +            let n2_i = n2.get_num();
 +
 +            if n2_i == 0 {
 +                Err(zero_divisor_eval_error(stub_gen))
 +            } else {
 +                let n2 = Integer::from(n2_i);
 +                Ok(Number::arena_from(
 +                    <(Integer, Integer)>::from(n1.div_rem_floor_ref(&n2)).1,
 +                    arena,
 +                ))
 +            }
 +        }
 +        (Number::Integer(x), Number::Integer(y)) => {
 +            if &*y == &0 {
 +                Err(zero_divisor_eval_error(stub_gen))
 +            } else {
 +                Ok(Number::arena_from(
 +                    <(Integer, Integer)>::from(x.div_rem_floor_ref(&*y)).1,
 +                    arena,
 +                ))
 +            }
 +        }
 +        (Number::Integer(_), n2) | (Number::Fixnum(_), n2) => {
 +            Err(numerical_type_error(ValidType::Integer, n2, stub_gen))
 +        }
 +        (n1, _) => Err(numerical_type_error(ValidType::Integer, n1, stub_gen)),
      }
 +}
  
 -    #[inline]
 -    pub(crate) fn float(&self, n: Number) -> Result<f64, MachineStub> {
 -        let stub = MachineError::functor_stub(clause_name!("is"), 2);
 -        try_numeric_result!(self, result_f(&n, rnd_f), stub)
 +pub(crate) fn remainder(x: Number, y: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
 +    let stub_gen = || {
 +        let rem_atom = atom!("rem");
 +        functor_stub(rem_atom, 2)
 +    };
 +
 +    match (x, y) {
 +        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
 +            let n2_i = n2.get_num();
 +
 +            if n2_i == 0 {
 +                Err(zero_divisor_eval_error(stub_gen))
 +            } else {
 +                let n1_i = n1.get_num();
 +                Ok(Number::arena_from(n1_i % n2_i, arena))
 +            }
 +        }
 +        (Number::Fixnum(n1), Number::Integer(n2)) => {
 +            if &*n2 == &0 {
 +                Err(zero_divisor_eval_error(stub_gen))
 +            } else {
 +                let n1 = Integer::from(n1.get_num());
 +                Ok(Number::arena_from(n1 % &*n2, arena))
 +            }
 +        }
 +        (Number::Integer(n1), Number::Fixnum(n2)) => {
 +            let n2_i = n2.get_num();
 +
 +            if n2_i == 0 {
 +                Err(zero_divisor_eval_error(stub_gen))
 +            } else {
 +                let n2 = Integer::from(n2_i);
 +                Ok(Number::arena_from(&*n1 % n2, arena))
 +            }
 +        }
 +        (Number::Integer(n1), Number::Integer(n2)) => {
 +            if &*n2 == &0 {
 +                Err(zero_divisor_eval_error(stub_gen))
 +            } else {
 +                Ok(Number::arena_from(Integer::from(&*n1 % &*n2), arena))
 +            }
 +        }
 +        (Number::Integer(_), n2) | (Number::Fixnum(_), n2) => {
 +            Err(numerical_type_error(ValidType::Integer, n2, stub_gen))
 +        }
 +        (n1, _) => Err(numerical_type_error(ValidType::Integer, n1, stub_gen)),
      }
 +}
  
 -    #[inline]
 -    pub(crate) fn floor(&self, n1: Number) -> Number {
 -        rnd_i(&n1).to_owned()
 +pub(crate) fn gcd(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
 +    let stub_gen = || {
 +        let gcd_atom = atom!("gcd");
 +        functor_stub(gcd_atom, 2)
 +    };
 +
 +    match (n1, n2) {
 +        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
 +            let n1_i = n1.get_num() as isize;
 +            let n2_i = n2.get_num() as isize;
 +
 +            if let Some(result) = isize_gcd(n1_i, n2_i) {
 +                Ok(Number::arena_from(result, arena))
 +            } else {
 +                Ok(Number::arena_from(
 +                    Integer::from(n1_i).gcd(&Integer::from(n2_i)),
 +                    arena,
 +                ))
 +            }
 +        }
 +        (Number::Fixnum(n1), Number::Integer(n2)) | (Number::Integer(n2), Number::Fixnum(n1)) => {
 +            let n1 = Integer::from(n1.get_num());
 +            Ok(Number::arena_from(Integer::from(n2.gcd_ref(&n1)), arena))
 +        }
 +        (Number::Integer(n1), Number::Integer(n2)) => {
 +            Ok(Number::arena_from(Integer::from(n1.gcd_ref(&n2)), arena))
 +        }
 +        (Number::Float(f), _) | (_, Number::Float(f)) => {
 +            let n = Number::Float(f);
 +            Err(numerical_type_error(ValidType::Integer, n, stub_gen))
 +        }
 +        (Number::Rational(r), _) | (_, Number::Rational(r)) => {
 +            let n = Number::Rational(r);
 +            Err(numerical_type_error(ValidType::Integer, n, stub_gen))
 +        }
      }
 +}
  
 -    #[inline]
 -    pub(crate) fn ceiling(&self, n1: Number) -> Number {
 -        -self.floor(-n1)
 +pub(crate) fn atan2(n1: Number, n2: Number) -> Result<f64, MachineStubGen> {
 +    if n1.is_zero() && n2.is_zero() {
 +        let stub_gen = || {
 +            let is_atom = atom!("is");
 +            functor_stub(is_atom, 2)
 +        };
 +
 +        Err(undefined_eval_error(stub_gen))
 +    } else {
 +        let f1 = float(n1)?;
 +        let f2 = float(n2)?;
 +
 +        unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.atan2(f2))
      }
 +}
  
 -    #[inline]
 -    pub(crate) fn truncate(&self, n: Number) -> Number {
 -        if n.is_negative() {
 -            -self.floor(n.abs())
 -        } else {
 -            self.floor(n)
 -        }
 +#[inline]
 +pub(crate) fn sin(n1: Number) -> Result<f64, MachineStubGen> {
 +    unary_float_fn_template(n1, |f| f.sin())
 +}
 +
 +#[inline]
 +pub(crate) fn cos(n1: Number) -> Result<f64, MachineStubGen> {
 +    unary_float_fn_template(n1, |f| f.cos())
 +}
 +
 +#[inline]
 +pub(crate) fn tan(n1: Number) -> Result<f64, MachineStubGen> {
 +    unary_float_fn_template(n1, |f| f.tan())
 +}
 +
 +#[inline]
 +pub(crate) fn log(n1: Number) -> Result<f64, MachineStubGen> {
 +    unary_float_fn_template(n1, |f| f.log(f64::consts::E))
 +}
 +
 +#[inline]
 +pub(crate) fn exp(n1: Number) -> Result<f64, MachineStubGen> {
 +    unary_float_fn_template(n1, |f| f.exp())
 +}
 +
 +#[inline]
 +pub(crate) fn asin(n1: Number) -> Result<f64, MachineStubGen> {
 +    unary_float_fn_template(n1, |f| f.asin())
 +}
 +
 +#[inline]
 +pub(crate) fn acos(n1: Number) -> Result<f64, MachineStubGen> {
 +    unary_float_fn_template(n1, |f| f.acos())
 +}
 +
 +#[inline]
 +pub(crate) fn atan(n1: Number) -> Result<f64, MachineStubGen> {
 +    unary_float_fn_template(n1, |f| f.atan())
 +}
 +
 +#[inline]
 +pub(crate) fn asinh(n1: Number) -> Result<f64, MachineStubGen> {
 +    unary_float_fn_template(n1, |f| f.asinh())
 +}
 +
 +#[inline]
 +pub(crate) fn acosh(n1: Number) -> Result<f64, MachineStubGen> {
 +    unary_float_fn_template(n1, |f| f.acosh())
 +}
 +
 +#[inline]
 +pub(crate) fn atanh(n1: Number) -> Result<f64, MachineStubGen> {
 +    let stub_gen = || {
 +        let is_atom = atom!("is");
 +        functor_stub(is_atom, 2)
 +    };
 +
 +    let f1 = try_numeric_result!(result_f(&n1), stub_gen)?;
 +
 +    try_numeric_result!(if f1 == 1.0 || f1 == -1.0 {
 +        Err(EvalError::Undefined)
 +    } else {
 +        result_f(&Number::Float(OrderedFloat(f1.atanh())))
 +    },
 +    stub_gen)
 +}
 +
 +#[inline]
 +pub(crate) fn sinh(n1: Number) -> Result<f64, MachineStubGen> {
 +    unary_float_fn_template(n1, |f| f.sinh())
 +}
 +
 +#[inline]
 +pub(crate) fn cosh(n1: Number) -> Result<f64, MachineStubGen> {
 +    unary_float_fn_template(n1, |f| f.cosh())
 +}
 +
 +#[inline]
 +pub(crate) fn tanh(n1: Number) -> Result<f64, MachineStubGen> {
 +    unary_float_fn_template(n1, |f| f.tanh())
 +}
 +
 +#[inline]
 +pub(crate) fn log10(n1: Number) -> Result<f64, MachineStubGen> {
 +    unary_float_fn_template(n1, |f| f.log(10f64))
 +}
 +
++#[inline]
++pub(crate) fn float_fractional_part(n1: Number) -> Result<f64, MachineStubGen> {
++    unary_float_fn_template(n1, |f| f.fract())
++}
++
++#[inline]
++pub(crate) fn float_integer_part(n1: Number) -> Result<f64, MachineStubGen> {
++    unary_float_fn_template(n1, |f| f.trunc())
++}
++
 +#[inline]
 +pub(crate) fn sqrt(n1: Number) -> Result<f64, MachineStubGen> {
 +    if n1.is_negative() {
 +        let stub_gen = || {
 +            let is_atom = atom!("is");
 +            functor_stub(is_atom, 2)
 +        };
 +
 +        return Err(undefined_eval_error(stub_gen));
      }
  
 -    pub(crate) fn round(&self, n: Number) -> Result<Number, MachineStub> {
 -        let stub = MachineError::functor_stub(clause_name!("is"), 2);
 +    unary_float_fn_template(n1, |f| f.sqrt())
 +}
 +
 +#[inline]
 +pub(crate) fn floor(n1: Number, arena: &mut Arena) -> Number {
 +    rnd_i(&n1, arena)
 +}
 +
 -        let result = n + Number::Float(OrderedFloat(0.5f64));
 -        let result = try_numeric_result!(self, result, stub)?;
 +#[inline]
 +pub(crate) fn ceiling(n1: Number, arena: &mut Arena) -> Number {
 +    let n1 = neg(n1, arena);
 +    let n1 = floor(n1, arena);
 +
 +    neg(n1, arena)
 +}
  
 -        Ok(self.floor(result))
 +#[inline]
 +pub(crate) fn truncate(n: Number, arena: &mut Arena) -> Number {
 +    if n.is_negative() {
 +        let n = abs(n, arena);
 +        let n = floor(n, arena);
 +
 +        neg(n, arena)
 +    } else {
 +        floor(n, arena)
      }
 +}
  
 -    pub(crate) fn shr(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
 -        let stub = MachineError::functor_stub(clause_name!("(>>)"), 2);
 +pub(crate) fn round(n: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
 +    let stub_gen = || {
 +        let is_atom = atom!("is");
 +        functor_stub(is_atom, 2)
 +    };
  
 -        match (n1, n2) {
 -            (Number::Fixnum(n1), Number::Fixnum(n2)) => {
 -                let n1 = Integer::from(n1);
 +    let result = add(n, Number::Float(OrderedFloat(0.5f64)), arena);
 +    let result = try_numeric_result!(result, stub_gen)?;
  
 -                if let Ok(n2) = u32::try_from(n2) {
 -                    return Ok(Number::from(n1 >> n2));
 -                } else {
 -                    return Ok(Number::from(n1 >> u32::max_value()));
 -                }
 -            }
 -            (Number::Fixnum(n1), Number::Integer(n2)) => {
 -                let n1 = Integer::from(n1);
 +    Ok(floor(result, arena))
 +}
  
 -                match n2.to_u32() {
 -                    Some(n2) => Ok(Number::from(n1 >> n2)),
 -                    _ => Ok(Number::from(n1 >> u32::max_value())),
 -                }
 -            }
 -            (Number::Integer(n1), Number::Fixnum(n2)) => match u32::try_from(n2) {
 -                Ok(n2) => Ok(Number::from(Integer::from(&*n1 >> n2))),
 -                _ => Ok(Number::from(Integer::from(&*n1 >> u32::max_value()))),
 -            },
 -            (Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() {
 -                Some(n2) => Ok(Number::from(Integer::from(&*n1 >> n2))),
 -                _ => Ok(Number::from(Integer::from(&*n1 >> u32::max_value()))),
 -            },
 -            (Number::Integer(_), n2) => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n2),
 -                stub,
 -            )),
 -            (Number::Fixnum(_), n2) => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n2),
 -                stub,
 -            )),
 -            (n1, _) => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n1),
 -                stub,
 -            )),
 -        }
 -    }
 -
 -    pub(crate) fn shl(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
 -        let stub = MachineError::functor_stub(clause_name!("(<<)"), 2);
 -
 -        match (n1, n2) {
 -            (Number::Fixnum(n1), Number::Fixnum(n2)) => {
 -                let n1 = Integer::from(n1);
 -
 -                if let Ok(n2) = u32::try_from(n2) {
 -                    return Ok(Number::from(n1 << n2));
 -                } else {
 -                    return Ok(Number::from(n1 << u32::max_value()));
 -                }
 -            }
 -            (Number::Fixnum(n1), Number::Integer(n2)) => {
 -                let n1 = Integer::from(n1);
 +pub(crate) fn bitwise_complement(n1: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
 +    match n1 {
 +        Number::Fixnum(n) => Ok(Number::Fixnum(Fixnum::build_with(!n.get_num()))),
 +        Number::Integer(n1) => Ok(Number::arena_from(Integer::from(!&*n1), arena)),
 +        _ => {
 +            let stub_gen = || {
 +                let bitwise_atom = atom!("\\");
 +                functor_stub(bitwise_atom, 2)
 +            };
 +
 +            Err(numerical_type_error(ValidType::Integer, n1, stub_gen))
 +        }
 +    }
 +}
  
 -                match n2.to_u32() {
 -                    Some(n2) => Ok(Number::from(n1 << n2)),
 -                    _ => Ok(Number::from(n1 << u32::max_value())),
 -                }
 -            }
 -            (Number::Integer(n1), Number::Fixnum(n2)) => match u32::try_from(n2) {
 -                Ok(n2) => Ok(Number::from(Integer::from(&*n1 << n2))),
 -                _ => Ok(Number::from(Integer::from(&*n1 << u32::max_value()))),
 -            },
 -            (Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() {
 -                Some(n2) => Ok(Number::from(Integer::from(&*n1 << n2))),
 -                _ => Ok(Number::from(Integer::from(&*n1 << u32::max_value()))),
 -            },
 -            (Number::Integer(_), n2) => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n2),
 -                stub,
 -            )),
 -            (Number::Fixnum(_), n2) => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n2),
 -                stub,
 -            )),
 -            (n1, _) => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n1),
 -                stub,
 -            )),
 -        }
 -    }
 -
 -    pub(crate) fn bitwise_complement(&self, n1: Number) -> Result<Number, MachineStub> {
 -        let stub = MachineError::functor_stub(clause_name!("(\\)"), 2);
 -
 -        match n1 {
 -            Number::Fixnum(n) => Ok(Number::Fixnum(!n)),
 -            Number::Integer(n1) => Ok(Number::from(Integer::from(!&*n1))),
 -            _ => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n1),
 -                stub,
 -            )),
 -        }
 -    }
 -
 -    pub(crate) fn xor(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
 -        let stub = MachineError::functor_stub(clause_name!("(xor)"), 2);
 -
 -        match (n1, n2) {
 -            (Number::Fixnum(n1), Number::Fixnum(n2)) => Ok(Number::from(n1 ^ n2)),
 -            (Number::Fixnum(n1), Number::Integer(n2)) => {
 -                let n1 = Integer::from(n1);
 -                Ok(Number::from(n1 ^ &*n2))
 -            }
 -            (Number::Integer(n1), Number::Fixnum(n2)) => Ok(Number::from(&*n1 ^ Integer::from(n2))),
 -            (Number::Integer(n1), Number::Integer(n2)) => {
 -                Ok(Number::from(Integer::from(&*n1 ^ &*n2)))
 -            }
 -            (Number::Integer(_), n2) | (Number::Fixnum(_), n2) => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n2),
 -                stub,
 -            )),
 -            (n1, _) => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n1),
 -                stub,
 -            )),
 -        }
 -    }
 -
 -    pub(crate) fn and(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
 -        let stub = MachineError::functor_stub(clause_name!("(/\\)"), 2);
 -
 -        match (n1, n2) {
 -            (Number::Fixnum(n1), Number::Fixnum(n2)) => Ok(Number::from(n1 & n2)),
 -            (Number::Fixnum(n1), Number::Integer(n2)) => {
 -                let n1 = Integer::from(n1);
 -                Ok(Number::from(n1 & &*n2))
 -            }
 -            (Number::Integer(n1), Number::Fixnum(n2)) => Ok(Number::from(&*n1 & Integer::from(n2))),
 -            (Number::Integer(n1), Number::Integer(n2)) => {
 -                Ok(Number::from(Integer::from(&*n1 & &*n2)))
 -            }
 -            (Number::Integer(_), n2) | (Number::Fixnum(_), n2) => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n2),
 -                stub,
 -            )),
 -            (n1, _) => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n1),
 -                stub,
 -            )),
 -        }
 -    }
 -
 -    pub(crate) fn or(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
 -        let stub = MachineError::functor_stub(clause_name!("(\\/)"), 2);
 -
 -        match (n1, n2) {
 -            (Number::Fixnum(n1), Number::Fixnum(n2)) => Ok(Number::from(n1 | n2)),
 -            (Number::Fixnum(n1), Number::Integer(n2)) => {
 -                let n1 = Integer::from(n1);
 -                Ok(Number::from(n1 | &*n2))
 -            }
 -            (Number::Integer(n1), Number::Fixnum(n2)) => Ok(Number::from(&*n1 | Integer::from(n2))),
 -            (Number::Integer(n1), Number::Integer(n2)) => {
 -                Ok(Number::from(Integer::from(&*n1 | &*n2)))
 -            }
 -            (Number::Integer(_), n2) | (Number::Fixnum(_), n2) => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n2),
 -                stub,
 -            )),
 -            (n1, _) => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n1),
 -                stub,
 -            )),
 -        }
 -    }
 -
 -    pub(crate) fn modulus(&self, x: Number, y: Number) -> Result<Number, MachineStub> {
 -        let stub = MachineError::functor_stub(clause_name!("(mod)"), 2);
 +impl MachineState {
 +    #[inline]
 +    pub fn get_number(&mut self, at: &ArithmeticTerm) -> Result<Number, MachineStub> {
 +        match at {
 +            &ArithmeticTerm::Reg(r) => {
 +                      let value = self.store(self.deref(self[r]));
  
 -        match (x, y) {
 -            (Number::Fixnum(n1), Number::Fixnum(n2)) => {
 -                if n2 == 0 {
 -                    Err(self
 -                        .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
 -                } else {
 -                    Ok(Number::from(n1.rem_floor(n2)))
 -                }
 -            }
 -            (Number::Fixnum(n1), Number::Integer(n2)) => {
 -                if &*n2 == &0 {
 -                    Err(self
 -                        .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
 -                } else {
 -                    let n1 = Integer::from(n1);
 -                    Ok(Number::from(
 -                        <(Integer, Integer)>::from(n1.div_rem_floor_ref(&*n2)).1,
 -                    ))
 +                match Number::try_from(value) {
 +                    Ok(n) => Ok(n),
 +                    Err(_) => self.arith_eval_by_metacall(value),
                  }
              }
 -            (Number::Integer(n1), Number::Fixnum(n2)) => {
 -                if n2 == 0 {
 -                    Err(self
 -                        .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
 -                } else {
 -                    let n2 = Integer::from(n2);
 -                    Ok(Number::from(
 -                        <(Integer, Integer)>::from(n1.div_rem_floor_ref(&n2)).1,
 -                    ))
 -                }
 -            }
 -            (Number::Integer(x), Number::Integer(y)) => {
 -                if &*y == &0 {
 -                    Err(self
 -                        .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
 -                } else {
 -                    Ok(Number::from(
 -                        <(Integer, Integer)>::from(x.div_rem_floor_ref(&*y)).1,
 -                    ))
 -                }
 +            &ArithmeticTerm::Interm(i) => {
 +                Ok(mem::replace(&mut self.interms[i - 1], Number::Fixnum(Fixnum::build_with(0))))
              }
 -            (Number::Integer(_), n2) | (Number::Fixnum(_), n2) => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n2),
 -                stub,
 -            )),
 -            (n1, _) => Err(self.error_form(
 -                MachineError::type_error(self.heap.h(), ValidType::Integer, n1),
 -                stub,
 -            )),
 +            &ArithmeticTerm::Number(n) => Ok(n),
          }
      }
  
          }
      }
  
 -    pub(crate) fn max(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
 -        match (n1, n2) {
 -            (Number::Fixnum(n1), Number::Fixnum(n2)) => {
 -                if n1 > n2 {
 -                    Ok(Number::Fixnum(n1))
 -                } else {
 -                    Ok(Number::Fixnum(n2))
 -                }
 +    pub(crate) fn arith_eval_by_metacall(&mut self, value: HeapCellValue) -> Result<Number, MachineStub> {
 +        let stub_gen = || functor_stub(atom!("is"), 2);
 +        let mut iter = stackful_post_order_iter(&mut self.heap, &mut self.stack, value);
 +
 +        while let Some(value) = iter.next() {
 +            if value.get_forwarding_bit() {
 +                std::mem::drop(iter);
 +
 +                let (name, arity) = read_heap_cell!(value,
 +                     (HeapCellValueTag::Atom, (name, arity)) => {
 +                         (name, arity)
 +                     }
 +                     (HeapCellValueTag::Str, s) => {
 +                         cell_as_atom_cell!(self.heap[s]).get_name_and_arity()
 +                     }
 +                     (HeapCellValueTag::Lis | HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset |
 +                      HeapCellValueTag::PStrLoc) => {
 +                         (atom!("."), 2)
 +                     }
 +                     (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
 +                         let err = self.instantiation_error();
 +                         return Err(self.error_form(err, stub_gen()));
 +                     }
 +                     _ => {
 +                         unreachable!()
 +                     }
 +                );
 +
 +                let evaluable_error = self.evaluable_error(name, arity);
 +                return Err(self.error_form(evaluable_error, stub_gen()));
              }
 -            (Number::Fixnum(n1), Number::Integer(n2)) => {
 -                if &*n2 > &n1 {
 -                    Ok(Number::Integer(n2))
 -                } else {
 -                    Ok(Number::Fixnum(n1))
 -                }
 -            }
 -            (Number::Integer(n1), Number::Fixnum(n2)) => {
 -                if &*n1 > &n2 {
 -                    Ok(Number::Integer(n1))
 -                } else {
 -                    Ok(Number::Fixnum(n2))
 -                }
 -            }
 -            (Number::Integer(n1), Number::Integer(n2)) => {
 -                if n1 > n2 {
 -                    Ok(Number::Integer(n1))
 -                } else {
 -                    Ok(Number::Integer(n2))
 -                }
 -            }
 -            (n1, n2) => {
 -                let stub = MachineError::functor_stub(clause_name!("max"), 2);
  
 -                let f1 = try_numeric_result!(self, result_f(&n1, rnd_f), stub)?;
 -                let f2 = try_numeric_result!(self, result_f(&n2, rnd_f), stub)?;
 +            let value = unmark_cell_bits!(value);
 +
 +            read_heap_cell!(value,
 +                (HeapCellValueTag::Atom, (name, arity)) => {
 +                    if arity == 2 {
 +                        let a2 = self.interms.pop().unwrap();
 +                        let a1 = self.interms.pop().unwrap();
 +
 +                        match name {
 +                            atom!("+") => self.interms.push(drop_iter_on_err!(
 +                                self,
 +                                iter,
 +                                try_numeric_result!(add(a1, a2, &mut self.arena), stub_gen)
 +                            )),
 +                            atom!("-") => self.interms.push(drop_iter_on_err!(
 +                                self,
 +                                iter,
 +                                try_numeric_result!(sub(a1, a2, &mut self.arena), stub_gen)
 +                            )),
 +                            atom!("*") => self.interms.push(drop_iter_on_err!(
 +                                self,
 +                                iter,
 +                                try_numeric_result!(mul(a1, a2, &mut self.arena), stub_gen)
 +                            )),
 +                            atom!("/") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, div(a1, a2))
 +                            ),
 +                            atom!("**") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, pow(a1, a2, atom!("is")))
 +                            ),
 +                            atom!("^") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, int_pow(a1, a2, &mut self.arena))
 +                            ),
 +                            atom!("max") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, max(a1, a2))
 +                            ),
 +                            atom!("min") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, min(a1, a2))
 +                            ),
 +                            atom!("rdiv") => {
 +                                let r1 = drop_iter_on_err!(
 +                                    self,
 +                                    iter,
 +                                    rational_from_number(a1, stub_gen, &mut self.arena)
 +                                );
 +
 +                                let r2 = drop_iter_on_err!(
 +                                    self,
 +                                    iter,
 +                                    rational_from_number(a2, stub_gen, &mut self.arena)
 +                                );
 +
 +                                let result = arena_alloc!(
 +                                    drop_iter_on_err!(self, iter, rdiv(r1, r2)),
 +                                    &mut self.arena
 +                                );
 +
 +                                self.interms.push(Number::Rational(result));
 +                            }
 +                            atom!("//") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, idiv(a1, a2, &mut self.arena))
 +                            ),
 +                            atom!("div") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, int_floor_div(a1, a2, &mut self.arena))
 +                            ),
 +                            atom!(">>") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, shr(a1, a2, &mut self.arena))
 +                            ),
 +                            atom!("<<") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, shl(a1, a2, &mut self.arena))
 +                            ),
 +                            atom!("/\\") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, and(a1, a2, &mut self.arena))
 +                            ),
 +                            atom!("\\/") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, or(a1, a2, &mut self.arena))
 +                            ),
 +                            atom!("xor") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, xor(a1, a2, &mut self.arena))
 +                            ),
 +                            atom!("mod") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, modulus(a1, a2, &mut self.arena))
 +                            ),
 +                            atom!("rem") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, remainder(a1, a2, &mut self.arena))
 +                            ),
 +                            atom!("atan2") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, atan2(a1, a2))
 +                            ))),
 +                            atom!("gcd") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, gcd(a1, a2, &mut self.arena))
 +                            ),
 +                            _ => {
 +                                let evaluable_stub = functor_stub(name, 2);
 +                                let stub = stub_gen();
 +
 +                                std::mem::drop(iter);
 +
 +                                let type_error = self.type_error(ValidType::Evaluable, evaluable_stub);
 +                                return Err(self.error_form(type_error, stub));
 +                            }
 +                        }
  
 -                Ok(Number::Float(cmp::max(OrderedFloat(f1), OrderedFloat(f2))))
 -            }
 -        }
 -    }
 +                        continue;
 +                    } else if arity == 1 {
 +                        let a1 = self.interms.pop().unwrap();
 +
 +                        match name {
 +                            atom!("-") => self.interms.push(neg(a1, &mut self.arena)),
 +                            atom!("+") => self.interms.push(a1),
 +                            atom!("cos") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, cos(a1))
 +                            ))),
 +                            atom!("sin") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, sin(a1))
 +                            ))),
 +                            atom!("tan") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, tan(a1))
 +                            ))),
 +                            atom!("cosh") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, cosh(a1))
 +                            ))),
 +                            atom!("sinh") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, sinh(a1))
 +                            ))),
 +                            atom!("tanh") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, tanh(a1))
 +                            ))),
 +                            atom!("acosh") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, acosh(a1))
 +                            ))),
 +                            atom!("asinh") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, asinh(a1))
 +                            ))),
 +                            atom!("atanh") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, atanh(a1))
 +                            ))),
 +                            atom!("log10") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, log10(a1))
 +                            ))),
++                            atom!("float_fractional_part") => self.interms.push(Number::Float(OrderedFloat(
++                                drop_iter_on_err!(self, iter, float_fractional_part(a1))
++                            ))),
++                            atom!("float_integer_part") => self.interms.push(Number::Float(OrderedFloat(
++                                drop_iter_on_err!(self, iter, float_integer_part(a1))
++                            ))),
 +                            atom!("sqrt") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, sqrt(a1))
 +                            ))),
 +                            atom!("log") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, log(a1))
 +                            ))),
 +                            atom!("exp") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, exp(a1))
 +                            ))),
 +                            atom!("acos") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, acos(a1))
 +                            ))),
 +                            atom!("asin") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, asin(a1))
 +                            ))),
 +                            atom!("atan") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, atan(a1))
 +                            ))),
 +                            atom!("abs") => self.interms.push(abs(a1, &mut self.arena)),
 +                            atom!("float") => self.interms.push(Number::Float(OrderedFloat(
 +                                drop_iter_on_err!(self, iter, float(a1))
 +                            ))),
 +                            atom!("truncate") => self.interms.push(truncate(a1, &mut self.arena)),
 +                            atom!("round") => self.interms.push(drop_iter_on_err!(self, iter, round(a1, &mut self.arena))),
 +                            atom!("ceiling") => self.interms.push(ceiling(a1, &mut self.arena)),
 +                            atom!("floor") => self.interms.push(floor(a1, &mut self.arena)),
 +                            atom!("\\") => self.interms.push(
 +                                drop_iter_on_err!(self, iter, bitwise_complement(a1, &mut self.arena))
 +                            ),
 +                            atom!("sign") => self.interms.push(a1.sign()),
 +                            _ => {
 +                                let evaluable_stub = functor_stub(name, 1);
 +                                std::mem::drop(iter);
 +
 +                                let type_error = self.type_error(
 +                                    ValidType::Evaluable,
 +                                    evaluable_stub,
 +                                );
  
 -    pub(crate) fn min(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
 -        match (n1, n2) {
 -            (Number::Fixnum(n1), Number::Fixnum(n2)) => {
 -                if n1 < n2 {
 -                    Ok(Number::Fixnum(n1))
 -                } else {
 -                    Ok(Number::Fixnum(n2))
 +                                let stub = stub_gen();
 +                                return Err(self.error_form(type_error, stub));
 +                            }
 +                        }
 +
 +                        continue;
 +                    } else if arity == 0 {
 +                        match name {
 +                            atom!("pi") => {
 +                                self.interms.push(Number::Float(OrderedFloat(f64::consts::PI)));
 +                                continue;
 +                            }
 +                            atom!("e") => {
 +                                self.interms.push(Number::Float(OrderedFloat(f64::consts::E)));
 +                                continue;
 +                            }
 +                            atom!("epsilon") => {
 +                                self.interms.push(Number::Float(OrderedFloat(f64::EPSILON)));
 +                                continue;
 +                            }
 +                            _ => {
 +                            }
 +                        }
 +                    }
 +
 +                    std::mem::drop(iter);
 +
 +                    let evaluable_error = self.evaluable_error(name, arity);
 +                    let stub = stub_gen();
 +
 +                    return Err(self.error_form(evaluable_error, stub));
                  }
 -            }
 -            (Number::Fixnum(n1), Number::Integer(n2)) => {
 -                if &*n2 < &n1 {
 -                    Ok(Number::Integer(n2))
 -                } else {
 -                    Ok(Number::Fixnum(n1))
 +                (HeapCellValueTag::Fixnum, n) => {
 +                    self.interms.push(Number::Fixnum(n));
                  }
 -            }
 -            (Number::Integer(n1), Number::Fixnum(n2)) => {
 -                if &*n1 < &n2 {
 -                    Ok(Number::Integer(n1))
 -                } else {
 -                    Ok(Number::Fixnum(n2))
 +                (HeapCellValueTag::F64, fl) => {
 +                    self.interms.push(Number::Float(*fl));
                  }
 -            }
 -            (Number::Integer(n1), Number::Integer(n2)) => {
 -                if n1 < n2 {
 -                    Ok(Number::Integer(n1))
 -                } else {
 -                    Ok(Number::Integer(n2))
 +                (HeapCellValueTag::Cons, ptr) => {
 +                    match_untyped_arena_ptr!(ptr,
 +                         (ArenaHeaderTag::Integer, n) => {
 +                             self.interms.push(Number::Integer(n));
 +                         }
 +                         (ArenaHeaderTag::Rational, r) => {
 +                             self.interms.push(Number::Rational(r));
 +                         }
 +                         _ => {
 +                             std::mem::drop(iter);
 +
 +                             let type_error = self.type_error(ValidType::Evaluable, value);
 +                             let stub = stub_gen();
 +
 +                             return Err(self.error_form(type_error, stub));
 +                         }
 +                    )
                  }
 -            }
 -            (n1, n2) => {
 -                let stub = MachineError::functor_stub(clause_name!("max"), 2);
 +                (HeapCellValueTag::Var | HeapCellValueTag::AttrVar) => {
 +                    std::mem::drop(iter);
  
 -                let f1 = try_numeric_result!(self, result_f(&n1, rnd_f), stub)?;
 -                let f2 = try_numeric_result!(self, result_f(&n2, rnd_f), stub)?;
 +                    let instantiation_error = self.instantiation_error();
 +                    let stub = stub_gen();
  
 -                Ok(Number::Float(cmp::min(OrderedFloat(f1), OrderedFloat(f2))))
 -            }
 +                    return Err(self.error_form(instantiation_error, stub));
 +                }
 +                _ => {
 +                    std::mem::drop(iter);
 +
 +                    let type_error = self.type_error(ValidType::Evaluable, value);
 +                    let stub = stub_gen();
 +
 +                    return Err(self.error_form(type_error, stub));
 +                }
 +            )
          }
 +
 +        Ok(self.interms.pop().unwrap())
      }
 +}
  
 -    pub(crate) fn sign(&self, n: Number) -> Number {
 -        if n.is_positive() {
 -            Number::from(1)
 -        } else if n.is_negative() {
 -            Number::from(-1)
 -        } else {
 -            Number::from(0)
 -        }
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +    use crate::machine::mock_wam::*;
 +
 +    #[test]
 +    fn arith_eval_by_metacall_tests() {
 +        let mut wam = MachineState::new();
 +        let mut op_dir = default_op_dir();
 +
 +        op_dir.insert(
 +            (atom!("+"), Fixity::In),
 +            OpDesc::build_with(500, YFX as u8),
 +        );
 +        op_dir.insert(
 +            (atom!("-"), Fixity::In),
 +            OpDesc::build_with(500, YFX as u8),
 +        );
 +        op_dir.insert(
 +            (atom!("-"), Fixity::Pre),
 +            OpDesc::build_with(200, FY as u8),
 +        );
 +        op_dir.insert(
 +            (atom!("*"), Fixity::In),
 +            OpDesc::build_with(400, YFX as u8),
 +        );
 +        op_dir.insert(
 +            (atom!("/"), Fixity::In),
 +            OpDesc::build_with(400, YFX as u8),
 +        );
 +
 +        let term_write_result =
 +            parse_and_write_parsed_term_to_heap(&mut wam, "3 + 4 - 1 + 2.", &op_dir).unwrap();
 +
 +        assert_eq!(
 +            wam.arith_eval_by_metacall(heap_loc_as_cell!(term_write_result.heap_loc)),
 +            Ok(Number::Fixnum(Fixnum::build_with(8))),
 +        );
 +
 +        wam.heap.clear();
 +
 +        let term_write_result =
 +            parse_and_write_parsed_term_to_heap(&mut wam, "5 * 4 - 1.", &op_dir).unwrap();
 +
 +        assert_eq!(
 +            wam.arith_eval_by_metacall(heap_loc_as_cell!(term_write_result.heap_loc)),
 +            Ok(Number::Fixnum(Fixnum::build_with(19))),
 +        );
 +
 +        wam.heap.clear();
 +
 +        let term_write_result =
 +            parse_and_write_parsed_term_to_heap(&mut wam, "sign(-1).", &op_dir).unwrap();
 +
 +        assert_eq!(
 +            wam.arith_eval_by_metacall(heap_loc_as_cell!(term_write_result.heap_loc)),
 +            Ok(Number::Fixnum(Fixnum::build_with(-1)))
 +        );
      }
  }
index 8f7e3e919c6dc31c2bcf0d0370024a2b697fa4e9,0000000000000000000000000000000000000000..fbfa6580ff6443440b294adcbe5dd2fe79428d06
mode 100644,000000..100644
--- /dev/null
@@@ -1,5342 -1,0 +1,5360 @@@
 +use crate::arena::*;
 +use crate::atom_table::*;
 +use crate::instructions::*;
 +use crate::machine::*;
 +use crate::machine::arithmetic_ops::*;
 +use crate::machine::machine_errors::*;
 +use crate::machine::machine_state::*;
 +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 {
 +            $self.machine_st.backtrack();
 +        } else {
 +            $step_e;
 +        }
 +    };
 +}
 +
 +macro_rules! try_or_throw {
 +    ($s:expr, $e:expr) => {{
 +        match $e {
 +            Ok(val) => val,
 +            Err(msg) => {
 +                $s.throw_exception(msg);
 +                $s.backtrack();
 +                continue;
 +            }
 +        }
 +    }};
 +}
 +
 +macro_rules! try_or_throw_gen {
 +    ($s:expr, $e:expr) => {{
 +        match $e {
 +            Ok(val) => val,
 +            Err(msg_fn) => {
 +                let e = msg_fn($s);
 +                $s.throw_exception(e);
 +                $s.backtrack();
 +                continue;
 +            }
 +        }
 +    }};
 +}
 +
 +static INSTRUCTIONS_PER_INTERRUPT_POLL: usize = 256;
 +
 +impl MachineState {
 +    #[inline(always)]
 +    fn compare(&mut self) -> CallResult {
 +        let stub_gen = || functor_stub(atom!("compare"), 3);
 +
 +        let a1 = self.store(self.deref(self.registers[1]));
 +        let a2 = self.registers[2];
 +        let a3 = self.registers[3];
 +
 +        let check_atom = |machine_st: &mut MachineState, name: Atom, arity: usize| -> Result<(), MachineStub> {
 +            match name {
 +                atom!(">") | atom!("<") | atom!("=") if arity == 0 => {
 +                    Ok(())
 +                }
 +                _ => {
 +                    let err = machine_st.domain_error(DomainErrorType::Order, a1);
 +                    Err(machine_st.error_form(err, stub_gen()))
 +                }
 +            }
 +        };
 +
 +        read_heap_cell!(a1,
 +            (HeapCellValueTag::Atom, (name, arity)) => {
 +                debug_assert_eq!(arity, 0);
 +                check_atom(self, name, arity)?;
 +            }
 +            (HeapCellValueTag::Str, s) => {
 +                let (name, arity) = cell_as_atom_cell!(self.heap[s])
 +                    .get_name_and_arity();
 +
 +                check_atom(self, name, arity)?;
 +            }
 +            (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
 +            }
 +            _ => {
 +                let err = self.type_error(ValidType::Atom, a1);
 +                return Err(self.error_form(err, stub_gen()));
 +            }
 +        );
 +
 +        let atom = match compare_term_test!(self, a2, a3) {
 +            Some(Ordering::Greater) => {
 +                atom!(">")
 +            }
 +            Some(Ordering::Equal) => {
 +                atom!("=")
 +            }
 +            None | Some(Ordering::Less) => {
 +                atom!("<")
 +            }
 +        };
 +
 +        self.unify_atom(atom, a1);
 +        Ok(())
 +    }
 +
 +    pub fn copy_term(&mut self, attr_var_policy: AttrVarPolicy) {
 +        let old_h = self.heap.len();
 +
 +        let a1 = self.registers[1];
 +        let a2 = self.registers[2];
 +
 +        copy_term(CopyTerm::new(self), a1, attr_var_policy);
 +
 +        unify_fn!(*self, heap_loc_as_cell!(old_h), a2);
 +    }
 +
 +    fn sort(&mut self) -> CallResult {
 +        self.check_sort_errors()?;
 +
 +        let stub_gen = || functor_stub(atom!("sort"), 2);
 +        let mut list = self.try_from_list(self.registers[1], stub_gen)?;
 +
 +        list.sort_unstable_by(|v1, v2| {
 +            compare_term_test!(self, *v1, *v2).unwrap_or(Ordering::Less)
 +        });
 +
 +        list.dedup_by(|v1, v2| {
 +            compare_term_test!(self, *v1, *v2) == Some(Ordering::Equal)
 +        });
 +
 +        let heap_addr = heap_loc_as_cell!(
 +            iter_to_heap_list(&mut self.heap, list.into_iter())
 +        );
 +
 +        let target_addr = self.registers[2];
 +
 +        unify_fn!(*self, target_addr, heap_addr);
 +        Ok(())
 +    }
 +
 +    fn keysort(&mut self, var_comparison: VarComparison) -> CallResult {
 +        self.check_keysort_errors()?;
 +
 +        let stub_gen = || functor_stub(atom!("keysort"), 2);
 +        let list = self.try_from_list(self.registers[1], stub_gen)?;
 +
 +        let mut key_pairs = Vec::with_capacity(list.len());
 +
 +        for val in list {
 +            let key = self.project_onto_key(val)?;
 +            key_pairs.push((key, val));
 +        }
 +
 +        key_pairs.sort_by(|a1, a2| {
 +            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);
 +        let heap_addr = heap_loc_as_cell!(
 +            iter_to_heap_list(&mut self.heap, key_pairs)
 +        );
 +
 +        let target_addr = self.registers[2];
 +
 +        unify_fn!(*self, target_addr, heap_addr);
 +        Ok(())
 +    }
 +
 +    fn is(&mut self, r: RegType, at: ArithmeticTerm) -> CallResult {
 +        let n1 = self.store(self.deref(self[r]));
 +
 +        match self.get_number(&at)? {
 +            Number::Fixnum(n) => self.unify_fixnum(n, n1),
 +            Number::Float(n) => {
 +                let n = float_alloc!(n.into_inner(), self.arena);
 +                self.unify_f64(n, n1)
 +            }
 +            Number::Integer(n) => self.unify_big_int(n, n1),
 +            Number::Rational(n) => self.unify_rational(n, n1),
 +        }
 +
 +        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 {
 +    pub(super) fn find_living_dynamic_else(&self, mut p: usize) -> Option<(usize, usize)> {
 +        loop {
 +            match &self.code[p] {
 +                &Instruction::DynamicElse(
 +                    birth,
 +                    death,
 +                    NextOrFail::Next(i),
 +                ) => {
 +                    if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death {
 +                        return Some((p, i));
 +                    } else if i > 0 {
 +                        p += i;
 +                    } else {
 +                        return None;
 +                    }
 +                }
 +                &Instruction::DynamicElse(
 +                    birth,
 +                    death,
 +                    NextOrFail::Fail(_),
 +                ) => {
 +                    if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death {
 +                        return Some((p, 0));
 +                    } else {
 +                        return None;
 +                    }
 +                }
 +                &Instruction::DynamicInternalElse(
 +                    birth,
 +                    death,
 +                    NextOrFail::Next(i),
 +                ) => {
 +                    if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death {
 +                        return Some((p, i));
 +                    } else if i > 0 {
 +                        p += i;
 +                    } else {
 +                        return None;
 +                    }
 +                }
 +                &Instruction::DynamicInternalElse(
 +                    birth,
 +                    death,
 +                    NextOrFail::Fail(_),
 +                ) => {
 +                    if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death {
 +                        return Some((p, 0));
 +                    } else {
 +                        return None;
 +                    }
 +                }
 +                &Instruction::RevJmpBy(i) => {
 +                    p -= i;
 +                }
 +                _ => {
 +                    unreachable!();
 +                }
 +            }
 +        }
 +    }
 +
 +    pub(super) fn find_living_dynamic(&self, oi: u32, mut ii: u32) -> Option<(usize, u32, u32, bool)> {
 +        let p = self.machine_st.p;
 +
 +        let indexed_choice_instrs = match &self.code[p] {
 +            Instruction::IndexingCode(ref indexing_code) => match &indexing_code[oi as usize] {
 +                IndexingLine::DynamicIndexedChoice(ref indexed_choice_instrs) => {
 +                    indexed_choice_instrs
 +                }
 +                _ => unreachable!(),
 +            },
 +            _ => unreachable!(),
 +        };
 +
 +        loop {
 +            match &indexed_choice_instrs.get(ii as usize) {
 +                Some(&offset) => match &self.code[p + offset - 1] {
 +                    &Instruction::DynamicInternalElse(
 +                        birth,
 +                        death,
 +                        next_or_fail,
 +                    ) => {
 +                        if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death {
 +                            return Some((offset, oi, ii, next_or_fail.is_next()));
 +                        } else {
 +                            ii += 1;
 +                        }
 +                    }
 +                    _ => unreachable!(),
 +                },
 +                None => return None,
 +            }
 +        }
 +    }
 +
 +    #[inline(always)]
 +    fn execute_switch_on_term(&mut self) {
 +        #[inline(always)]
 +        fn dynamic_external_of_clause_is_valid(machine: &mut Machine, p: usize) -> bool {
 +            match &machine.code[p] {
 +                Instruction::DynamicInternalElse(..) => {
 +                    machine.machine_st.dynamic_mode = FirstOrNext::First;
 +                    return true;
 +                }
 +                _ => {}
 +            }
 +
 +            match &machine.code[p - 1] {
 +                &Instruction::DynamicInternalElse(birth, death, _) => {
 +                    if birth < machine.machine_st.cc && Death::Finite(machine.machine_st.cc) <= death {
 +                        return true;
 +                    } else {
 +                        return false;
 +                    }
 +                }
 +                _ => {}
 +            }
 +
 +            true
 +        }
 +
 +        let indexing_lines = self.code[self.machine_st.p].to_indexing_line_mut().unwrap();
 +
 +        let mut index = 0;
 +        let addr = match &indexing_lines[0] {
 +            &IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(arg, ..)) => {
 +                self.machine_st.store(self.machine_st.deref(self.machine_st.registers[arg]))
 +            }
 +            _ => {
 +                unreachable!()
 +            }
 +        };
 +
 +        loop {
 +            match &indexing_lines[index] {
 +                &IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, v, c, l, s)) => {
 +                    let offset = self.machine_st.select_switch_on_term_index(addr, v, c, l, s);
 +
 +                    match offset {
 +                        IndexingCodePtr::Fail => {
 +                            self.machine_st.fail = true;
 +                            break;
 +                        }
 +                        IndexingCodePtr::DynamicExternal(o) => {
 +                            // either points directly to a
 +                            // DynamicInternalElse, or just ahead of
 +                            // one. Or neither!
 +                            let p = self.machine_st.p;
 +
 +                            if !dynamic_external_of_clause_is_valid(self, p + o) {
 +                                self.machine_st.fail = true;
 +                            } else {
 +                                self.machine_st.p += o;
 +                            }
 +
 +                            break;
 +                        }
 +                        IndexingCodePtr::External(o) => {
 +                            self.machine_st.p += o;
 +                            break;
 +                        }
 +                        IndexingCodePtr::Internal(o) => {
 +                            index += o;
 +                        }
 +                    }
 +                }
 +                IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(hm)) => {
 +                    let lit = self.machine_st.constant_to_literal(addr);
 +
 +                    let offset = match hm.get(&lit) {
 +                        Some(offset) => *offset,
 +                        _ => IndexingCodePtr::Fail,
 +                    };
 +
 +                    match offset {
 +                        IndexingCodePtr::Fail => {
 +                            self.machine_st.fail = true;
 +                            break;
 +                        }
 +                        IndexingCodePtr::DynamicExternal(o) => {
 +                            // either points directly to a
 +                            // DynamicInternalElse, or just ahead of
 +                            // one. Or neither!
 +                            let p = self.machine_st.p;
 +
 +                            if !dynamic_external_of_clause_is_valid(self, p + o) {
 +                                self.machine_st.fail = true;
 +                            } else {
 +                                self.machine_st.p += o;
 +                            }
 +
 +                            break;
 +                        }
 +                        IndexingCodePtr::External(o) => {
 +                            self.machine_st.p += o;
 +                            break;
 +                        }
 +                        IndexingCodePtr::Internal(o) => {
 +                            index += o;
 +                        }
 +                    }
 +                }
 +                IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(hm)) => {
 +                    let offset = self.machine_st.select_switch_on_structure_index(addr, hm);
 +
 +                    match offset {
 +                        IndexingCodePtr::Fail => {
 +                            self.machine_st.fail = true;
 +                            break;
 +                        }
 +                        IndexingCodePtr::DynamicExternal(o) => {
 +                            let p = self.machine_st.p;
 +
 +                            if !dynamic_external_of_clause_is_valid(self, p + o) {
 +                                self.machine_st.fail = true;
 +                            } else {
 +                                self.machine_st.p += o;
 +                            }
 +
 +                            break;
 +                        }
 +                        IndexingCodePtr::External(o) => {
 +                            self.machine_st.p += o;
 +                            break;
 +                        }
 +                        IndexingCodePtr::Internal(o) => {
 +                            index += o;
 +                        }
 +                    }
 +                }
 +                &IndexingLine::IndexedChoice(_) => {
 +                    self.machine_st.oip = index as u32;
 +                    self.machine_st.iip = 0;
 +
 +                    break;
 +                }
 +                &IndexingLine::DynamicIndexedChoice(_) => {
 +                    self.machine_st.dynamic_mode = FirstOrNext::First;
 +
 +                    self.machine_st.oip = index as u32;
 +                    self.machine_st.iip = 0;
 +
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +
 +    #[inline(always)]
 +    pub(super) fn dispatch_loop(&mut self) {
 +        'outer: loop {
 +        for _ in 0 .. INSTRUCTIONS_PER_INTERRUPT_POLL {
 +            match &self.code[self.machine_st.p] {
 +                &Instruction::BreakFromDispatchLoop => {
 +                    break 'outer;
 +                }
 +                &Instruction::InstallVerifyAttr => {
 +                    self.machine_st.p = self.machine_st.attr_var_init.p;
 +
 +                    if self.code[self.machine_st.p].is_execute() {
 +                        self.machine_st.p = self.machine_st.attr_var_init.cp;
 +                    } else {
 +                        self.machine_st.p += 1;
 +                        self.machine_st.cp = self.machine_st.attr_var_init.cp;
 +                    }
 +
 +                    let mut p = self.machine_st.p;
 +
 +                    while self.code[p].is_head_instr() {
 +                        p += 1;
 +                    }
 +
 +                    let instr = std::mem::replace(
 +                        &mut self.code[p],
 +                        Instruction::VerifyAttrInterrupt,
 +                    );
 +
 +                    self.code[VERIFY_ATTR_INTERRUPT_LOC] = instr;
 +                    self.machine_st.attr_var_init.cp = p;
 +                }
 +                &Instruction::VerifyAttrInterrupt => {
 +                    let (_, arity) = self.code[VERIFY_ATTR_INTERRUPT_LOC].to_name_and_arity();
 +                    let arity = std::cmp::max(arity, self.machine_st.num_of_args);
 +                    self.run_verify_attr_interrupt(arity);
 +                }
 +                &Instruction::Add(ref a1, ref a2, t) => {
 +                    let stub_gen = || functor_stub(atom!("is"), 2);
 +
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        try_numeric_result!(add(n1, n2, &mut self.machine_st.arena), stub_gen)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Sub(ref a1, ref a2, t) => {
 +                    let stub_gen = || functor_stub(atom!("is"), 2);
 +
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        try_numeric_result!(sub(n1, n2, &mut self.machine_st.arena), stub_gen)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Mul(ref a1, ref a2, t) => {
 +                    let stub_gen = || functor_stub(atom!("is"), 2);
 +
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        try_numeric_result!(mul(n1, n2, &mut self.machine_st.arena), stub_gen)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Max(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        max(n1, n2)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Min(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        min(n1, n2)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::IntPow(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        int_pow(n1, n2, &mut self.machine_st.arena)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Gcd(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        gcd(n1, n2, &mut self.machine_st.arena)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Pow(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        pow(n1, n2, atom!("**"))
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::RDiv(ref a1, ref a2, t) => {
 +                    let stub_gen = || functor_stub(atom!("(rdiv)"), 2);
 +
 +                    let r1 = try_or_throw!(self.machine_st, self.machine_st.get_rational(a1, stub_gen));
 +                    let r2 = try_or_throw!(self.machine_st, self.machine_st.get_rational(a2, stub_gen));
 +
 +                    self.machine_st.interms[t - 1] = Number::Rational(arena_alloc!(
 +                        try_or_throw_gen!(&mut self.machine_st, rdiv(r1, r2)),
 +                        &mut self.machine_st.arena
 +                    ));
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::IntFloorDiv(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        int_floor_div(n1, n2, &mut self.machine_st.arena)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::IDiv(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        idiv(n1, n2, &mut self.machine_st.arena)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Abs(ref a1, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +
 +                    self.machine_st.interms[t - 1] = abs(n1, &mut self.machine_st.arena);
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Sign(ref a1, t) => {
 +                    let n = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +
 +                    self.machine_st.interms[t - 1] = n.sign();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Neg(ref a1, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +
 +                    self.machine_st.interms[t - 1] = neg(n1, &mut self.machine_st.arena);
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::BitwiseComplement(ref a1, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        bitwise_complement(n1, &mut self.machine_st.arena)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Div(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        div(n1, n2)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Shr(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        shr(n1, n2, &mut self.machine_st.arena)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Shl(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        shl(n1, n2, &mut self.machine_st.arena)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Xor(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        xor(n1, n2, &mut self.machine_st.arena)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::And(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        and(n1, n2, &mut self.machine_st.arena)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Or(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        or(n1, n2, &mut self.machine_st.arena)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Mod(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        modulus(n1, n2, &mut self.machine_st.arena)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Rem(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = try_or_throw_gen!(
 +                        &mut self.machine_st,
 +                        remainder(n1, n2, &mut self.machine_st.arena)
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Cos(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, cos(n1))
 +                    ));
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Sin(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, sin(n1))
 +                    ));
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Tan(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, tan(n1))
 +                    ));
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Sqrt(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, sqrt(n1))
 +                    ));
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Log(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, log(n1))
 +                    ));
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Exp(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, exp(n1))
 +                    ));
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ACos(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, acos(n1))
 +                    ));
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ASin(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, asin(n1))
 +                    ));
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ATan(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, atan(n1))
 +                    ));
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ATan2(ref a1, ref a2, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +                    let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(a2));
 +
 +                    self.machine_st.interms[t - 1] = Number::Float(OrderedFloat(
 +                        try_or_throw_gen!(&mut self.machine_st, atan2(n1, n2))
 +                    ));
 +
 +                    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));
 +
 +                    self.machine_st.interms[t - 1] = Number::Float(OrderedFloat(
 +                        try_or_throw_gen!(&mut self.machine_st, float(n1))
 +                    ));
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Truncate(ref a1, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +
 +                    self.machine_st.interms[t - 1] = truncate(n1, &mut self.machine_st.arena);
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Round(ref a1, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +
 +                    self.machine_st.interms[t - 1] =
 +                        try_or_throw_gen!(&mut self.machine_st, round(n1, &mut self.machine_st.arena));
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Ceiling(ref a1, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +
 +                    self.machine_st.interms[t - 1] = ceiling(n1, &mut self.machine_st.arena);
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Floor(ref a1, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +
 +                    self.machine_st.interms[t - 1] = floor(n1, &mut self.machine_st.arena);
 +                    self.machine_st.p += 1;
 +                }
++                &Instruction::FloatFractionalPart(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, float_fractional_part(n1))
++                    ));
++
++                    self.machine_st.p += 1;
++                }
++                &Instruction::FloatIntegerPart(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, float_integer_part(n1))
++                    ));
++
++                    self.machine_st.p += 1;
++                }
 +                &Instruction::Plus(ref a1, t) => {
 +                    let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(a1));
 +
 +                    self.machine_st.interms[t - 1] = n1;
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::DynamicElse(..) => {
 +                    if let FirstOrNext::First = self.machine_st.dynamic_mode {
 +                        self.machine_st.cc = self.machine_st.global_clock;
 +                    }
 +
 +                    let p = self.machine_st.p;
 +
 +                    match self.find_living_dynamic_else(p) {
 +                        Some((p, next_i)) => {
 +                            self.machine_st.p = p;
 +                            self.machine_st.oip = 0;
 +                            self.machine_st.iip = 0;
 +
 +                            match self.machine_st.dynamic_mode {
 +                                FirstOrNext::First if next_i == 0 => {
 +                                    self.machine_st.p += 1;
 +                                }
 +                                FirstOrNext::First => {
 +                                    self.machine_st.cc = self.machine_st.global_clock;
 +
 +                                    match self.find_living_dynamic_else(p + next_i) {
 +                                        Some(_) => {
 +                                            self.machine_st.registers[self.machine_st.num_of_args + 1] =
 +                                                fixnum_as_cell!(Fixnum::build_with(self.machine_st.cc as i64));
 +
 +                                            self.machine_st.num_of_args += 1;
 +                                            self.try_me_else(next_i);
 +                                            self.machine_st.num_of_args -= 1;
 +                                        }
 +                                        None => {
 +                                            self.machine_st.p += 1;
 +                                        }
 +                                    }
 +                                }
 +                                FirstOrNext::Next => {
 +                                    let n = self.machine_st
 +                                        .stack
 +                                        .index_or_frame(self.machine_st.b)
 +                                        .prelude
 +                                        .num_cells;
 +
 +                                    self.machine_st.cc = cell_as_fixnum!(
 +                                        self.machine_st.stack[stack_loc!(OrFrame, self.machine_st.b, n-1)]
 +                                    ).get_num() as usize;
 +
 +                                    if next_i > 0 {
 +                                        match self.find_living_dynamic_else(p + next_i) {
 +                                            Some(_) => {
 +                                                self.retry_me_else(next_i);
 +                                            }
 +                                            None => {
 +                                                self.trust_me();
 +                                            }
 +                                        }
 +                                    } else {
 +                                        self.trust_me();
 +                                    }
 +
 +                                    try_or_throw!(
 +                                        self.machine_st,
 +                                        (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                                    );
 +                                }
 +                            }
 +                        }
 +                        None => {
 +                            self.machine_st.fail = true;
 +                        }
 +                    }
 +
 +                    self.machine_st.dynamic_mode = FirstOrNext::Next;
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::DynamicInternalElse(..) => {
 +                    let p = self.machine_st.p;
 +
 +                    match self.find_living_dynamic_else(p) {
 +                        Some((p, next_i)) => {
 +                            self.machine_st.p = p;
 +                            self.machine_st.oip = 0;
 +                            self.machine_st.iip = 0;
 +
 +                            match self.machine_st.dynamic_mode {
 +                                FirstOrNext::First if next_i == 0 => {
 +                                    self.machine_st.p += 1;
 +                                }
 +                                FirstOrNext::First => {
 +                                    match self.find_living_dynamic_else(p + next_i) {
 +                                        Some(_) => {
 +                                            self.machine_st.registers[self.machine_st.num_of_args + 1] =
 +                                                fixnum_as_cell!(Fixnum::build_with(self.machine_st.cc as i64));
 +
 +                                            self.machine_st.num_of_args += 1;
 +                                            self.try_me_else(next_i);
 +                                            self.machine_st.num_of_args -= 1;
 +                                        }
 +                                        None => {
 +                                            self.machine_st.p += 1;
 +                                        }
 +                                    }
 +                                }
 +                                FirstOrNext::Next => {
 +                                    let n = self.machine_st
 +                                        .stack
 +                                        .index_or_frame(self.machine_st.b)
 +                                        .prelude
 +                                        .num_cells;
 +
 +                                    self.machine_st.cc = cell_as_fixnum!(
 +                                        self.machine_st.stack[stack_loc!(OrFrame, self.machine_st.b, n-1)]
 +                                    ).get_num() as usize;
 +
 +                                    if next_i > 0 {
 +                                        match self.find_living_dynamic_else(p + next_i) {
 +                                            Some(_) => {
 +                                                self.retry_me_else(next_i);
 +                                            }
 +                                            None => {
 +                                                self.trust_me();
 +                                            }
 +                                        }
 +                                    } else {
 +                                        self.trust_me();
 +                                    }
 +
 +                                    try_or_throw!(
 +                                        self.machine_st,
 +                                        (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                                    );
 +                                }
 +                            }
 +                        }
 +                        None => {
 +                            self.machine_st.fail = true;
 +                        }
 +                    }
 +
 +                    self.machine_st.dynamic_mode = FirstOrNext::Next;
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::TryMeElse(offset) => {
 +                    self.try_me_else(offset);
 +                }
 +                &Instruction::DefaultRetryMeElse(offset) => {
 +                    self.retry_me_else(offset);
 +                }
 +                &Instruction::DefaultTrustMe(_) => {
 +                    self.trust_me();
 +                }
 +                &Instruction::RetryMeElse(offset) => {
 +                    self.retry_me_else(offset);
 +
 +                    try_or_throw!(
 +                        self.machine_st,
 +                        (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                    );
 +                }
 +                &Instruction::TrustMe(_) => {
 +                    self.trust_me();
 +
 +                    try_or_throw!(
 +                        self.machine_st,
 +                        (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                    );
 +                }
 +                &Instruction::NeckCut => {
 +                    self.machine_st.neck_cut();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::GetLevel(r) => {
 +                    let b0 = self.machine_st.b0;
 +
 +                    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::as_cutpoint(prev_b as i64));
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::GetCutPoint(r) => {
 +                    self.machine_st[r] = fixnum_as_cell!(Fixnum::as_cutpoint(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);
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                        continue;
 +                    }
 +
 +                    if (self.machine_st.run_cleaners_fn)(self) {
 +                       continue;
 +                    }
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::Allocate(num_cells) => {
 +                    self.machine_st.allocate(num_cells);
 +                }
 +                &Instruction::DefaultCallAcyclicTerm => {
 +                    let addr = self.machine_st.registers[1];
 +
 +                    if self.machine_st.is_cyclic_term(addr) {
 +                        self.machine_st.backtrack();
 +                    } else {
 +                        self.machine_st.p += 1;
 +                    }
 +                }
 +                &Instruction::DefaultExecuteAcyclicTerm => {
 +                    let addr = self.machine_st.registers[1];
 +
 +                    if self.machine_st.is_cyclic_term(addr) {
 +                        self.machine_st.backtrack();
 +                    } else {
 +                        self.machine_st.p = self.machine_st.cp;
 +                    }
 +                }
 +                &Instruction::DefaultCallArg => {
 +                    try_or_throw!(self.machine_st, self.machine_st.try_arg());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.machine_st.compare());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if let Some(Ordering::Greater) = compare_term_test!(self.machine_st, a1, a2) {
 +                        self.machine_st.p += 1;
 +                    } else {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::DefaultExecuteTermGreaterThan => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if let Some(Ordering::Greater) = compare_term_test!(self.machine_st, a1, a2) {
 +                        self.machine_st.p = self.machine_st.cp;
 +                    } else {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::DefaultCallTermLessThan => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if let Some(Ordering::Less) = compare_term_test!(self.machine_st, a1, a2) {
 +                        self.machine_st.p += 1;
 +                    } else {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::DefaultExecuteTermLessThan => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if let Some(Ordering::Less) = compare_term_test!(self.machine_st, a1, a2) {
 +                        self.machine_st.p = self.machine_st.cp;
 +                    } else {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::DefaultCallTermGreaterThanOrEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    match compare_term_test!(self.machine_st, a1, a2) {
 +                        Some(Ordering::Greater | Ordering::Equal) => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::DefaultExecuteTermGreaterThanOrEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    match compare_term_test!(self.machine_st, a1, a2) {
 +                        Some(Ordering::Greater | Ordering::Equal) => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::DefaultCallTermLessThanOrEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    match compare_term_test!(self.machine_st, a1, a2) {
 +                        Some(Ordering::Less | Ordering::Equal) => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::DefaultExecuteTermLessThanOrEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    match compare_term_test!(self.machine_st, a1, a2) {
 +                        Some(Ordering::Less | Ordering::Equal) => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::DefaultCallCopyTerm => {
 +                    self.machine_st.copy_term(AttrVarPolicy::DeepCopy);
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::DefaultExecuteCopyTerm => {
 +                    self.machine_st.copy_term(AttrVarPolicy::DeepCopy);
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                    } else {
 +                        self.machine_st.p = self.machine_st.cp;
 +                    }
 +                }
 +                &Instruction::DefaultCallTermEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if self.machine_st.eq_test(a1, a2) {
 +                        self.machine_st.backtrack();
 +                    } else {
 +                        self.machine_st.p += 1;
 +                    }
 +                }
 +                &Instruction::DefaultExecuteTermEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if self.machine_st.eq_test(a1, a2) {
 +                        self.machine_st.backtrack();
 +                    } else {
 +                        self.machine_st.p = self.machine_st.cp;
 +                    }
 +                }
 +                &Instruction::DefaultCallGround => {
 +                    if self.machine_st.ground_test() {
 +                        self.machine_st.backtrack();
 +                    } else {
 +                        self.machine_st.p += 1;
 +                    }
 +                }
 +                &Instruction::DefaultExecuteGround => {
 +                    if self.machine_st.ground_test() {
 +                        self.machine_st.backtrack();
 +                    } else {
 +                        self.machine_st.p = self.machine_st.cp;
 +                    }
 +                }
 +                &Instruction::DefaultCallFunctor => {
 +                    try_or_throw!(self.machine_st, self.machine_st.try_functor());
 +
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::DefaultExecuteFunctor => {
 +                    try_or_throw!(self.machine_st, self.machine_st.try_functor());
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                    } else {
 +                        self.machine_st.p = self.machine_st.cp;
 +                    }
 +                }
 +                &Instruction::DefaultCallTermNotEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if let Some(Ordering::Equal) = compare_term_test!(self.machine_st, a1, a2) {
 +                        self.machine_st.backtrack();
 +                    } else {
 +                        self.machine_st.p += 1;
 +                    }
 +                }
 +                &Instruction::DefaultExecuteTermNotEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if let Some(Ordering::Equal) = compare_term_test!(self.machine_st, a1, a2) {
 +                        self.machine_st.backtrack();
 +                    } else {
 +                        self.machine_st.p = self.machine_st.cp;
 +                    }
 +                }
 +                &Instruction::DefaultCallSort => {
 +                    try_or_throw!(self.machine_st, self.machine_st.sort());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    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(VarComparison::Distinct));
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                    } else {
 +                        self.machine_st.p = self.machine_st.cp;
 +                    }
 +                }
 +                &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) => {
 +                    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 => {
 +                    let addr = self.machine_st.registers[1];
 +
 +                    if self.machine_st.is_cyclic_term(addr) {
 +                        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::ExecuteAcyclicTerm => {
 +                    let addr = self.machine_st.registers[1];
 +
 +                    if self.machine_st.is_cyclic_term(addr) {
 +                        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::CallArg => {
 +                    try_or_throw!(self.machine_st, self.machine_st.try_arg());
 +
 +                    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::ExecuteArg => {
 +                    try_or_throw!(self.machine_st, self.machine_st.try_arg());
 +
 +                    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::CallCompare => {
 +                    try_or_throw!(self.machine_st, self.machine_st.compare());
 +
 +                    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::ExecuteCompare => {
 +                    try_or_throw!(self.machine_st, self.machine_st.compare());
 +
 +                    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::CallTermGreaterThan => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if let Some(Ordering::Greater) = compare_term_test!(self.machine_st, a1, a2) {
 +                        try_or_throw!(
 +                            self.machine_st,
 +                            (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                        );
 +
 +                        self.machine_st.p += 1;
 +                    } else {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::ExecuteTermGreaterThan => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if let Some(Ordering::Greater) = compare_term_test!(self.machine_st, a1, a2) {
 +                        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;
 +                    } else {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::CallTermLessThan => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if let Some(Ordering::Less) = compare_term_test!(self.machine_st, a1, a2) {
 +                        try_or_throw!(
 +                            self.machine_st,
 +                            (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                        );
 +
 +                        self.machine_st.p += 1;
 +                    } else {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::ExecuteTermLessThan => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if let Some(Ordering::Less) = compare_term_test!(self.machine_st, a1, a2) {
 +                        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;
 +                    } else {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::CallTermGreaterThanOrEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    match compare_term_test!(self.machine_st, a1, a2) {
 +                        Some(Ordering::Greater | Ordering::Equal) => {
 +                            try_or_throw!(
 +                                self.machine_st,
 +                                (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                            );
 +
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::ExecuteTermGreaterThanOrEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    match compare_term_test!(self.machine_st, a1, a2) {
 +                        Some(Ordering::Greater | Ordering::Equal) => {
 +                            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;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::CallTermLessThanOrEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    match compare_term_test!(self.machine_st, a1, a2) {
 +                        Some(Ordering::Less | Ordering::Equal) => {
 +                            try_or_throw!(
 +                                self.machine_st,
 +                                (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                            );
 +
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::ExecuteTermLessThanOrEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    match compare_term_test!(self.machine_st, a1, a2) {
 +                        Some(Ordering::Less | Ordering::Equal) => {
 +                            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;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::CallCopyTerm => {
 +                    self.machine_st.copy_term(AttrVarPolicy::DeepCopy);
 +
 +                    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::ExecuteCopyTerm => {
 +                    self.machine_st.copy_term(AttrVarPolicy::DeepCopy);
 +
 +                    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::CallTermEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if self.machine_st.eq_test(a1, a2) {
 +                        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::ExecuteTermEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if self.machine_st.eq_test(a1, a2) {
 +                        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::CallGround => {
 +                    if self.machine_st.ground_test() {
 +                        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::ExecuteGround => {
 +                    if self.machine_st.ground_test() {
 +                        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::CallFunctor => {
 +                    try_or_throw!(self.machine_st, self.machine_st.try_functor());
 +
 +                    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::ExecuteFunctor => {
 +                    try_or_throw!(self.machine_st, self.machine_st.try_functor());
 +
 +                    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::CallTermNotEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if let Some(Ordering::Equal) = compare_term_test!(self.machine_st, a1, a2) {
 +                        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::ExecuteTermNotEqual => {
 +                    let a1 = self.machine_st.registers[1];
 +                    let a2 = self.machine_st.registers[2];
 +
 +                    if let Some(Ordering::Equal) = compare_term_test!(self.machine_st, a1, a2) {
 +                        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::CallSort => {
 +                    try_or_throw!(self.machine_st, self.machine_st.sort());
 +
 +                    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::ExecuteSort => {
 +                    try_or_throw!(self.machine_st, self.machine_st.sort());
 +
 +                    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::CallKeySort => {
 +                    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 += 1;
 +                    }
 +                }
 +                &Instruction::ExecuteKeySort => {
 +                    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();
 +                    } 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::CallIs(r, at) => {
 +                    try_or_throw!(self.machine_st, self.machine_st.is(r, at));
 +
 +                    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::ExecuteIs(r, at) => {
 +                    try_or_throw!(self.machine_st, self.machine_st.is(r, at));
 +
 +                    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::CallN(arity) => {
 +                    let pred = self.machine_st.registers[1];
 +
 +                    for i in 2..arity + 1 {
 +                        self.machine_st.registers[i - 1] = self.machine_st.registers[i];
 +                    }
 +
 +                    self.machine_st.registers[arity] = pred;
 +
 +                    try_or_throw!(
 +                        self.machine_st,
 +                        self.call_n(atom!("user"), arity)
 +                    );
 +
 +                    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)
 +                        );
 +                    }
 +                }
 +                &Instruction::ExecuteN(arity) => {
 +                    let pred = self.machine_st.registers[1];
 +
 +                    for i in 2..arity + 1 {
 +                        self.machine_st.registers[i - 1] = self.machine_st.registers[i];
 +                    }
 +
 +                    self.machine_st.registers[arity] = pred;
 +
 +                    try_or_throw!(
 +                        self.machine_st,
 +                        self.execute_n(atom!("user"), arity)
 +                    );
 +
 +                    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)
 +                        );
 +                    }
 +                }
 +                &Instruction::DefaultCallN(arity) => {
 +                    let pred = self.machine_st.registers[1];
 +
 +                    for i in 2..arity + 1 {
 +                        self.machine_st.registers[i - 1] = self.machine_st.registers[i];
 +                    }
 +
 +                    self.machine_st.registers[arity] = pred;
 +
 +                    try_or_throw!(
 +                        self.machine_st,
 +                        self.call_n(atom!("user"), arity)
 +                    );
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::DefaultExecuteN(arity) => {
 +                    let pred = self.machine_st.registers[1];
 +
 +                    for i in 2..arity + 1 {
 +                        self.machine_st.registers[i - 1] = self.machine_st.registers[i];
 +                    }
 +
 +                    self.machine_st.registers[arity] = pred;
 +
 +                    try_or_throw!(
 +                        self.machine_st,
 +                        self.execute_n(atom!("user"), arity)
 +                    );
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Less | Ordering::Equal => {
 +                            try_or_throw!(
 +                                self.machine_st,
 +                                (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                            );
 +
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Less | Ordering::Equal => {
 +                            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;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Equal => {
 +                            try_or_throw!(
 +                                self.machine_st,
 +                                (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                            );
 +
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Equal => {
 +                            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;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Equal => {
 +                            self.machine_st.backtrack();
 +                        }
 +                        _ => {
 +                            try_or_throw!(
 +                                self.machine_st,
 +                                (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                            );
 +
 +                            self.machine_st.p += 1;
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Equal => {
 +                            self.machine_st.backtrack();
 +                        }
 +                        _ => {
 +                            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::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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Greater | Ordering::Equal => {
 +                            try_or_throw!(
 +                                self.machine_st,
 +                                (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                            );
 +
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Greater | Ordering::Equal => {
 +                            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;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Greater => {
 +                            try_or_throw!(
 +                                self.machine_st,
 +                                (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                            );
 +
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Greater => {
 +                            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;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Less => {
 +                            try_or_throw!(
 +                                self.machine_st,
 +                                (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                            );
 +
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Less => {
 +                            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;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Less | Ordering::Equal => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Less | Ordering::Equal => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Equal => {
 +                            self.machine_st.backtrack();
 +                        }
 +                        _ => {
 +                            self.machine_st.p += 1;
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Equal => {
 +                            self.machine_st.backtrack();
 +                        }
 +                        _ => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Equal => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Equal => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Greater | Ordering::Equal => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Greater | Ordering::Equal => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Greater => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Greater => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Less => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &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));
 +
 +                    match n1.cmp(&n2) {
 +                        Ordering::Less => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                //
 +                &Instruction::CallIsAtom(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    read_heap_cell!(d,
 +                        (HeapCellValueTag::Atom, (_name, arity)) => {
 +                            if arity == 0 {
 +                                self.machine_st.p += 1;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        (HeapCellValueTag::Str, s) => {
 +                            let arity = cell_as_atom_cell!(self.machine_st.heap[s])
 +                                .get_arity();
 +
 +                            if arity == 0 {
 +                                self.machine_st.p += 1;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        (HeapCellValueTag::Char) => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    );
 +                }
 +                &Instruction::ExecuteIsAtom(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    read_heap_cell!(d,
 +                        (HeapCellValueTag::Atom, (_name, arity)) => {
 +                            if arity == 0 {
 +                                self.machine_st.p = self.machine_st.cp;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        (HeapCellValueTag::Str, s) => {
 +                            let arity = cell_as_atom_cell!(self.machine_st.heap[s])
 +                                .get_arity();
 +
 +                            if arity == 0 {
 +                                self.machine_st.p = self.machine_st.cp;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        (HeapCellValueTag::Char) => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    );
 +                }
 +                &Instruction::CallIsAtomic(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    read_heap_cell!(d,
 +                        (HeapCellValueTag::Char | HeapCellValueTag::Fixnum | HeapCellValueTag::F64 |
 +                         HeapCellValueTag::Cons) => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        (HeapCellValueTag::Atom, (_name, arity)) => {
 +                            if arity == 0 {
 +                                self.machine_st.p += 1;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        (HeapCellValueTag::Str, s) => {
 +                            let arity = cell_as_atom_cell!(self.machine_st.heap[s])
 +                                .get_arity();
 +
 +                            if arity == 0 {
 +                                self.machine_st.p += 1;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    );
 +                }
 +                &Instruction::ExecuteIsAtomic(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    read_heap_cell!(d,
 +                        (HeapCellValueTag::Char | HeapCellValueTag::Fixnum | HeapCellValueTag::F64 |
 +                         HeapCellValueTag::Cons) => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        (HeapCellValueTag::Atom, (_name, arity)) => {
 +                            if arity == 0 {
 +                                self.machine_st.p = self.machine_st.cp;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        (HeapCellValueTag::Str, s) => {
 +                            let arity = cell_as_atom_cell!(self.machine_st.heap[s])
 +                                .get_arity();
 +
 +                            if arity == 0 {
 +                                self.machine_st.p = self.machine_st.cp;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    );
 +                }
 +                &Instruction::CallIsCompound(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    read_heap_cell!(d,
 +                        (HeapCellValueTag::Lis |
 +                         HeapCellValueTag::PStrLoc |
 +                         HeapCellValueTag::CStr) => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        (HeapCellValueTag::Str, s) => {
 +                            let arity = cell_as_atom_cell!(self.machine_st.heap[s])
 +                                .get_arity();
 +
 +                            if arity > 0 {
 +                                self.machine_st.p += 1;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        (HeapCellValueTag::Atom, (_name, arity)) => {
 +                            if arity > 0 {
 +                                self.machine_st.p += 1;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    );
 +                }
 +                &Instruction::ExecuteIsCompound(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    read_heap_cell!(d,
 +                        (HeapCellValueTag::Lis |
 +                         HeapCellValueTag::PStrLoc |
 +                         HeapCellValueTag::CStr) => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        (HeapCellValueTag::Str, s) => {
 +                            let arity = cell_as_atom_cell!(self.machine_st.heap[s])
 +                                .get_arity();
 +
 +                            if arity > 0 {
 +                                self.machine_st.p = self.machine_st.cp;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        (HeapCellValueTag::Atom, (_name, arity)) => {
 +                            if arity > 0 {
 +                                self.machine_st.p = self.machine_st.cp;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    );
 +                }
 +                &Instruction::CallIsInteger(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    match Number::try_from(d) {
 +                        Ok(Number::Fixnum(_) | Number::Integer(_)) => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        Ok(Number::Rational(n)) => {
 +                            if n.denom() == &1 {
 +                                self.machine_st.p += 1;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::ExecuteIsInteger(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    match Number::try_from(d) {
 +                        Ok(Number::Fixnum(_) | Number::Integer(_)) => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        Ok(Number::Rational(n)) => {
 +                            if n.denom() == &1 {
 +                                self.machine_st.p = self.machine_st.cp;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::CallIsNumber(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    match Number::try_from(d) {
 +                        Ok(_) => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::ExecuteIsNumber(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    match Number::try_from(d) {
 +                        Ok(_) => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::CallIsRational(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    read_heap_cell!(d,
 +                        (HeapCellValueTag::Cons, ptr) => {
 +                            match_untyped_arena_ptr!(ptr,
 +                                 (ArenaHeaderTag::Rational | ArenaHeaderTag::Integer, _r) => {
 +                                     self.machine_st.p += 1;
 +                                 }
 +                                 _ => {
 +                                     self.machine_st.backtrack();
 +                                 }
 +                            );
 +                        }
 +                        (HeapCellValueTag::Fixnum) => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    );
 +                }
 +                &Instruction::ExecuteIsRational(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    read_heap_cell!(d,
 +                        (HeapCellValueTag::Cons, ptr) => {
 +                            match_untyped_arena_ptr!(ptr,
 +                                 (ArenaHeaderTag::Rational | ArenaHeaderTag::Integer, _r) => {
 +                                     self.machine_st.p = self.machine_st.cp;
 +                                 }
 +                                 _ => {
 +                                     self.machine_st.backtrack();
 +                                 }
 +                            );
 +                        }
 +                        (HeapCellValueTag::Fixnum) => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    );
 +                }
 +                &Instruction::CallIsFloat(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    match Number::try_from(d) {
 +                        Ok(Number::Float(_)) => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::ExecuteIsFloat(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    match Number::try_from(d) {
 +                        Ok(Number::Float(_)) => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::CallIsNonVar(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    match d.get_tag() {
 +                        HeapCellValueTag::AttrVar |
 +                        HeapCellValueTag::Var |
 +                        HeapCellValueTag::StackVar => {
 +                            self.machine_st.backtrack();
 +                        }
 +                        _ => {
 +                            self.machine_st.p += 1;
 +                        }
 +                    }
 +                }
 +                &Instruction::ExecuteIsNonVar(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    match d.get_tag() {
 +                        HeapCellValueTag::AttrVar |
 +                        HeapCellValueTag::Var |
 +                        HeapCellValueTag::StackVar => {
 +                            self.machine_st.backtrack();
 +                        }
 +                        _ => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                    }
 +                }
 +                &Instruction::CallIsVar(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    match d.get_tag() {
 +                        HeapCellValueTag::AttrVar |
 +                        HeapCellValueTag::Var |
 +                        HeapCellValueTag::StackVar => {
 +                            self.machine_st.p += 1;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::ExecuteIsVar(r) => {
 +                    let d = self.machine_st.store(self.machine_st.deref(self.machine_st[r]));
 +
 +                    match d.get_tag() {
 +                        HeapCellValueTag::AttrVar |
 +                        HeapCellValueTag::Var |
 +                        HeapCellValueTag::StackVar => {
 +                            self.machine_st.p = self.machine_st.cp;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                        }
 +                    }
 +                }
 +                &Instruction::CallNamed(arity, name, ref idx) => {
 +                    let idx = idx.get();
 +
 +                    try_or_throw!(
 +                        self.machine_st,
 +                        self.try_call(name, arity, idx)
 +                    );
 +
 +                    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)
 +                        );
 +                    }
 +                }
 +                &Instruction::ExecuteNamed(arity, name, ref idx) => {
 +                    let idx = idx.get();
 +
 +                    try_or_throw!(
 +                        self.machine_st,
 +                        self.try_execute(name, arity, idx)
 +                    );
 +
 +                    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)
 +                        );
 +                    }
 +                }
 +                &Instruction::DefaultCallNamed(arity, name, ref idx) => {
 +                    let idx = idx.get();
 +
 +                    try_or_throw!(
 +                        self.machine_st,
 +                        self.try_call(name, arity, idx)
 +                    );
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::DefaultExecuteNamed(arity, name, ref idx) => {
 +                    let idx = idx.get();
 +
 +                    try_or_throw!(
 +                        self.machine_st,
 +                        self.try_execute(name, arity, idx)
 +                    );
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::Deallocate => {
 +                    self.machine_st.deallocate()
 +                }
 +                &Instruction::JmpByCall(offset) => {
 +                    self.machine_st.p += offset;
 +                }
 +                &Instruction::RevJmpBy(offset) => {
 +                    self.machine_st.p -= offset;
 +                }
 +                &Instruction::Proceed => {
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::GetConstant(_, c, reg) => {
 +                    let value = self.machine_st.deref(self.machine_st[reg]);
 +                    self.machine_st.write_literal_to_var(value, c);
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::GetList(_, reg) => {
 +                    let deref_v = self.machine_st.deref(self.machine_st[reg]);
 +                    let store_v = self.machine_st.store(deref_v);
 +
 +                    read_heap_cell!(store_v,
 +                        (HeapCellValueTag::PStrLoc, h) => {
 +                            let (h, n) = pstr_loc_and_offset(&self.machine_st.heap, h);
 +
 +                            self.machine_st.s = HeapPtr::PStrChar(h, n.get_num() as usize);
 +                            self.machine_st.s_offset = 0;
 +                            self.machine_st.mode = MachineMode::Read;
 +                        }
 +                        (HeapCellValueTag::CStr) => {
 +                            let h = self.machine_st.heap.len();
 +                            self.machine_st.heap.push(store_v);
 +
 +                            self.machine_st.s = HeapPtr::PStrChar(h, 0);
 +                            self.machine_st.s_offset = 0;
 +                            self.machine_st.mode = MachineMode::Read;
 +                        }
 +                        (HeapCellValueTag::Str, s) => {
 +                            let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s])
 +                                .get_name_and_arity();
 +
 +                            if name == atom!(".") && arity == 2 {
 +                                self.machine_st.s = HeapPtr::HeapCell(s+1);
 +                                self.machine_st.s_offset = 0;
 +                                self.machine_st.mode = MachineMode::Read;
 +                            } else {
 +                                self.machine_st.backtrack();
 +                                continue;
 +                            }
 +                        }
 +                        (HeapCellValueTag::Lis, l) => {
 +                            self.machine_st.s = HeapPtr::HeapCell(l);
 +                            self.machine_st.s_offset = 0;
 +                            self.machine_st.mode = MachineMode::Read;
 +                        }
 +                        (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
 +                            let h = self.machine_st.heap.len();
 +
 +                            self.machine_st.heap.push(list_loc_as_cell!(h+1));
 +                            self.machine_st.bind(store_v.as_var().unwrap(), heap_loc_as_cell!(h));
 +
 +                            self.machine_st.mode = MachineMode::Write;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                            continue;
 +                        }
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::GetPartialString(_, string, reg, has_tail) => {
 +                    let deref_v = self.machine_st.deref(self.machine_st[reg]);
 +                    let store_v = self.machine_st.store(deref_v);
 +
 +                    read_heap_cell!(store_v,
 +                        (HeapCellValueTag::Str |
 +                         HeapCellValueTag::Lis |
 +                         HeapCellValueTag::PStrLoc |
 +                         HeapCellValueTag::CStr) => {
 +                            self.machine_st.match_partial_string(store_v, string, has_tail);
 +                        }
 +                        (HeapCellValueTag::AttrVar |
 +                         HeapCellValueTag::StackVar |
 +                         HeapCellValueTag::Var) => {
 +                            let target_cell = self.machine_st.push_str_to_heap(
 +                                string.as_str(),
 +                                has_tail,
 +                            );
 +
 +                            self.machine_st.bind(
 +                                store_v.as_var().unwrap(),
 +                                target_cell,
 +                            );
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                            continue;
 +                        }
 +                    );
 +
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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);
 +
 +                    read_heap_cell!(store_v,
 +                        (HeapCellValueTag::Str, a) => {
 +                            read_heap_cell!(self.machine_st.heap[a],
 +                                (HeapCellValueTag::Atom, (result_name, result_arity)) => {
 +                                    if arity == result_arity && name == result_name {
 +                                        self.machine_st.s = HeapPtr::HeapCell(a + 1);
 +                                        self.machine_st.s_offset = 0;
 +                                        self.machine_st.mode = MachineMode::Read;
 +                                    } else {
 +                                        self.machine_st.backtrack();
 +                                        continue;
 +                                    }
 +                                }
 +                                _ => {
 +                                    unreachable!();
 +                                }
 +                            );
 +                        }
 +                        (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
 +                            let h = self.machine_st.heap.len();
 +
 +                            self.machine_st.heap.push(str_loc_as_cell!(h+1));
 +                            self.machine_st.heap.push(atom_as_cell!(name, arity));
 +
 +                            self.machine_st.bind(store_v.as_var().unwrap(), heap_loc_as_cell!(h));
 +                            self.machine_st.mode = MachineMode::Write;
 +                        }
 +                        _ => {
 +                            self.machine_st.backtrack();
 +                            continue;
 +                        }
 +                    );
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::GetVariable(norm, arg) => {
 +                    self.machine_st[norm] = self.machine_st.registers[arg];
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::GetValue(norm, arg) => {
 +                    let norm_addr = self.machine_st[norm];
 +                    let reg_addr = self.machine_st.registers[arg];
 +
 +                    unify_fn!(&mut self.machine_st, norm_addr, reg_addr);
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                        continue;
 +                    }
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::UnifyConstant(v) => {
 +                    match self.machine_st.mode {
 +                        MachineMode::Read => {
 +                            let addr = self.machine_st.read_s();
 +
 +                            self.machine_st.write_literal_to_var(addr, v);
 +
 +                            if self.machine_st.fail {
 +                                self.machine_st.backtrack();
 +                                continue;
 +                            } else {
 +                                self.machine_st.s_offset += 1;
 +                            }
 +                        }
 +                        MachineMode::Write => {
 +                            self.machine_st.heap.push(v);
 +                        }
 +                    }
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::UnifyLocalValue(reg) => {
 +                    match self.machine_st.mode {
 +                        MachineMode::Read => {
 +                            let reg_addr = self.machine_st[reg];
 +                            let value = self.machine_st.read_s();
 +
 +                            unify_fn!(&mut self.machine_st, reg_addr, value);
 +
 +                            if self.machine_st.fail {
 +                                self.machine_st.backtrack();
 +                                continue;
 +                            } else {
 +                                self.machine_st.s_offset += 1;
 +                            }
 +                        }
 +                        MachineMode::Write => {
 +                            let value = self.machine_st.store(self.machine_st.deref(self.machine_st[reg]));
 +                            let h = self.machine_st.heap.len();
 +
 +                            read_heap_cell!(value,
 +                                (HeapCellValueTag::Var | HeapCellValueTag::AttrVar, hc) => {
 +                                    let value = self.machine_st.heap[hc];
 +
 +                                    self.machine_st.heap.push(value);
 +                                    self.machine_st.s_offset += 1;
 +                                }
 +                                _ => {
 +                                    self.machine_st.heap.push(heap_loc_as_cell!(h));
 +                                    (self.machine_st.bind_fn)(
 +                                        &mut self.machine_st,
 +                                        Ref::heap_cell(h),
 +                                        value,
 +                                    );
 +                                }
 +                            );
 +                        }
 +                    }
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::UnifyVariable(reg) => {
 +                    match self.machine_st.mode {
 +                        MachineMode::Read => {
 +                            self.machine_st[reg] = self.machine_st.read_s();
 +                            self.machine_st.s_offset += 1;
 +                        }
 +                        MachineMode::Write => {
 +                            let h = self.machine_st.heap.len();
 +
 +                            self.machine_st.heap.push(heap_loc_as_cell!(h));
 +                            self.machine_st[reg] = heap_loc_as_cell!(h);
 +                        }
 +                    }
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::UnifyValue(reg) => {
 +                    match self.machine_st.mode {
 +                        MachineMode::Read => {
 +                            let reg_addr = self.machine_st[reg];
 +                            let value = self.machine_st.read_s();
 +
 +                            unify_fn!(&mut self.machine_st, reg_addr, value);
 +
 +                            if self.machine_st.fail {
 +                                self.machine_st.backtrack();
 +                                continue;
 +                            } else {
 +                                self.machine_st.s_offset += 1;
 +                            }
 +                        }
 +                        MachineMode::Write => {
 +                            let h = self.machine_st.heap.len();
 +                            self.machine_st.heap.push(heap_loc_as_cell!(h));
 +
 +                            let addr = self.machine_st.store(self.machine_st[reg]);
 +                            (self.machine_st.bind_fn)(&mut self.machine_st, Ref::heap_cell(h), addr);
 +
 +                            // the former code of this match arm was:
 +
 +                            // let addr = self.machine_st.store(self.machine_st[reg]);
 +                            // self.machine_st.heap.push(HeapCellValue::Addr(addr));
 +
 +                            // the old code didn't perform the occurs
 +                            // check when enabled and so it was changed to
 +                            // the above, which is only slightly less
 +                            // efficient when the occurs_check is disabled.
 +                        }
 +                    }
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::UnifyVoid(n) => {
 +                    match self.machine_st.mode {
 +                        MachineMode::Read => {
 +                            self.machine_st.s_offset += n;
 +                        }
 +                        MachineMode::Write => {
 +                            let h = self.machine_st.heap.len();
 +
 +                            for i in h..h + n {
 +                                self.machine_st.heap.push(heap_loc_as_cell!(i));
 +                            }
 +                        }
 +                    }
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::IndexingCode(ref indexing_lines) => {
 +                    match &indexing_lines[self.machine_st.oip as usize] {
 +                        IndexingLine::Indexing(_) => {
 +                            self.execute_switch_on_term();
 +
 +                            if self.machine_st.fail {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                        IndexingLine::IndexedChoice(ref indexed_choice) => {
 +                            match &indexed_choice[self.machine_st.iip as usize] {
 +                                &IndexedChoiceInstruction::Try(offset) => {
 +                                    self.indexed_try(offset);
 +                                }
 +                                &IndexedChoiceInstruction::Retry(l) => {
 +                                    self.retry(l);
 +
 +                                    try_or_throw!(
 +                                        self.machine_st,
 +                                        (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                                    );
 +                                }
 +                                &IndexedChoiceInstruction::Trust(l) => {
 +                                    self.trust(l);
 +
 +                                    try_or_throw!(
 +                                        self.machine_st,
 +                                        (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                                    );
 +                                }
 +                            }
 +                        }
 +                        IndexingLine::DynamicIndexedChoice(_) => {
 +                            let p = self.machine_st.p;
 +
 +                            match self.find_living_dynamic(self.machine_st.oip, self.machine_st.iip) {
 +                                Some((offset, oi, ii, is_next_clause)) => {
 +                                    self.machine_st.p = p;
 +                                    self.machine_st.oip = oi;
 +                                    self.machine_st.iip = ii;
 +
 +                                    match self.machine_st.dynamic_mode {
 +                                        FirstOrNext::First if !is_next_clause => {
 +                                            self.machine_st.p = p + offset;
 +                                        }
 +                                        FirstOrNext::First => {
 +                                            // there's a leading DynamicElse that sets self.machine_st.cc.
 +                                            // self.machine_st.cc = self.machine_st.global_clock;
 +
 +                                            // see that there is a following dynamic_else
 +                                            // clause so we avoid generating a choice
 +                                            // point in case there isn't.
 +                                            match self.find_living_dynamic(oi, ii + 1) {
 +                                                Some(_) => {
 +                                                    self.machine_st.registers[self.machine_st.num_of_args + 1] =
 +                                                        fixnum_as_cell!(Fixnum::build_with(self.machine_st.cc as i64));
 +
 +                                                    self.machine_st.num_of_args += 1;
 +                                                    self.indexed_try(offset);
 +                                                    self.machine_st.num_of_args -= 1;
 +                                                }
 +                                                None => {
 +                                                    self.machine_st.p = p + offset;
 +                                                    self.machine_st.oip = 0;
 +                                                    self.machine_st.iip = 0;
 +                                                }
 +                                            }
 +                                        }
 +                                        FirstOrNext::Next => {
 +                                            let b = self.machine_st.b;
 +                                            let n = self.machine_st
 +                                                .stack
 +                                                .index_or_frame(b)
 +                                                .prelude
 +                                                .num_cells;
 +
 +                                            self.machine_st.cc = cell_as_fixnum!(
 +                                                self.machine_st.stack[stack_loc!(OrFrame, b, n-1)]
 +                                            ).get_num() as usize;
 +
 +                                            if is_next_clause {
 +                                                match self.find_living_dynamic(self.machine_st.oip, self.machine_st.iip + 1) {
 +                                                    // if we're executing the last instruction
 +                                                    // of the internal block pointed to by
 +                                                    // self.machine_st.iip, we want trust, not retry.
 +                                                    // this is true iff ii + 1 < len.
 +                                                    Some(_) => {
 +                                                        self.retry(offset);
 +
 +                                                        try_or_throw!(
 +                                                            self.machine_st,
 +                                                            (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                                                        );
 +                                                    }
 +                                                    _ => {
 +                                                        self.trust(offset);
 +
 +                                                        try_or_throw!(
 +                                                            self.machine_st,
 +                                                            (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                                                        );
 +                                                    }
 +                                                }
 +                                            } else {
 +                                                self.trust(offset);
 +
 +                                                try_or_throw!(
 +                                                    self.machine_st,
 +                                                    (self.machine_st.increment_call_count_fn)(&mut self.machine_st)
 +                                                );
 +                                            }
 +                                        }
 +                                    }
 +                                }
 +                                None => {
 +                                    self.machine_st.fail = true;
 +                                }
 +                            }
 +
 +                            self.machine_st.dynamic_mode = FirstOrNext::Next;
 +
 +                            if self.machine_st.fail {
 +                                self.machine_st.backtrack();
 +                            }
 +                        }
 +                    }
 +                }
 +                &Instruction::PutConstant(_, c, reg) => {
 +                    self.machine_st[reg] = c;
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::PutList(_, reg) => {
 +                    self.machine_st[reg] = list_loc_as_cell!(self.machine_st.heap.len());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::PutPartialString(_, string, reg, has_tail) => {
 +                    let pstr_addr = if has_tail {
 +                        if string != atom!("") {
 +                            let h = self.machine_st.heap.len();
 +                            self.machine_st.heap.push(string_as_pstr_cell!(string));
 +
 +                            // the tail will be pushed by the next
 +                            // instruction, so don't push one here.
 +
 +                            pstr_loc_as_cell!(h)
 +                        } else {
 +                            empty_list_as_cell!()
 +                        }
 +                    } else {
 +                        string_as_cstr_cell!(string)
 +                    };
 +
 +                    self.machine_st[reg] = pstr_addr;
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::PutStructure(name, arity, reg) => {
 +                    let h = self.machine_st.heap.len();
 +
 +                    self.machine_st.heap.push(atom_as_cell!(name, arity));
 +                    self.machine_st[reg] = str_loc_as_cell!(h);
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &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) {
 +                        self.machine_st.registers[arg] = addr;
 +                    } else {
 +                        let h = self.machine_st.heap.len();
 +
 +                        self.machine_st.heap.push(heap_loc_as_cell!(h));
 +                        (self.machine_st.bind_fn)(&mut self.machine_st, Ref::heap_cell(h), addr);
 +
 +                        self.machine_st.registers[arg] = heap_loc_as_cell!(h);
 +                    }
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::PutValue(norm, arg) => {
 +                    self.machine_st.registers[arg] = self.machine_st[norm];
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::PutVariable(norm, arg) => {
 +                    match norm {
 +                        RegType::Perm(n) => {
 +                            self.machine_st[norm] = stack_loc_as_cell!(AndFrame, self.machine_st.e, n);
 +                            self.machine_st.registers[arg] = self.machine_st[norm];
 +                        }
 +                        RegType::Temp(_) => {
 +                            let h = self.machine_st.heap.len();
 +                            self.machine_st.heap.push(heap_loc_as_cell!(h));
 +
 +                            self.machine_st[norm] = heap_loc_as_cell!(h);
 +                            self.machine_st.registers[arg] = heap_loc_as_cell!(h);
 +                        }
 +                    };
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::SetConstant(c) => {
 +                    self.machine_st.heap.push(c);
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::SetLocalValue(reg) => {
 +                    let addr = self.machine_st.deref(self.machine_st[reg]);
 +                    let stored_v = self.machine_st.store(addr);
 +
 +                    if stored_v.is_stack_var() {
 +                        let h = self.machine_st.heap.len();
 +                        self.machine_st.heap.push(heap_loc_as_cell!(h));
 +                        (self.machine_st.bind_fn)(&mut self.machine_st, Ref::heap_cell(h), stored_v);
 +                    } else {
 +                        self.machine_st.heap.push(stored_v);
 +                    }
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::SetVariable(reg) => {
 +                    let h = self.machine_st.heap.len();
 +
 +                    self.machine_st.heap.push(heap_loc_as_cell!(h));
 +                    self.machine_st[reg] = heap_loc_as_cell!(h);
 +
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::SetValue(reg) => {
 +                    let heap_val = self.machine_st.store(self.machine_st[reg]);
 +                    self.machine_st.heap.push(heap_val);
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::SetVoid(n) => {
 +                    let h = self.machine_st.heap.len();
 +
 +                    for i in h..h + n {
 +                        self.machine_st.heap.push(heap_loc_as_cell!(i));
 +                    }
 +
 +                    self.machine_st.p += 1;
 +                }
 +                //
 +                &Instruction::CallAtomChars => {
 +                    self.atom_chars();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteAtomChars => {
 +                    self.atom_chars();
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                    } else {
 +                        self.machine_st.p = self.machine_st.cp;
 +                    }
 +                }
 +                &Instruction::CallAtomCodes => {
 +                    try_or_throw!(self.machine_st, self.atom_codes());
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                    } else {
 +                        self.machine_st.p += 1;
 +                    }
 +                }
 +                &Instruction::ExecuteAtomCodes => {
 +                    try_or_throw!(self.machine_st, self.atom_codes());
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                    } else {
 +                        self.machine_st.p = self.machine_st.cp;
 +                    }
 +                }
 +                &Instruction::CallAtomLength => {
 +                    self.atom_length();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteAtomLength => {
 +                    self.atom_length();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallBindFromRegister => {
 +                    self.bind_from_register();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteBindFromRegister => {
 +                    self.bind_from_register();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallContinuation => {
 +                    try_or_throw!(self.machine_st, self.call_continuation(false));
 +                }
 +                &Instruction::ExecuteContinuation => {
 +                    try_or_throw!(self.machine_st, self.call_continuation(true));
 +                }
 +                &Instruction::CallCharCode => {
 +                    try_or_throw!(self.machine_st, self.char_code());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.char_type();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteCharType => {
 +                    self.char_type();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCharsToNumber => {
 +                    try_or_throw!(self.machine_st, self.chars_to_number());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.codes_to_number());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.copy_term_without_attr_vars();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteCopyTermWithoutAttrVars => {
 +                    self.copy_term_without_attr_vars();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCheckCutPoint => {
 +                    self.check_cut_point();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteCheckCutPoint => {
 +                    self.check_cut_point();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallClose => {
 +                    try_or_throw!(self.machine_st, self.close());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteClose => {
 +                    try_or_throw!(self.machine_st, self.close());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallCopyToLiftedHeap => {
 +                    self.copy_to_lifted_heap();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteCopyToLiftedHeap => {
 +                    self.copy_to_lifted_heap();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallCreatePartialString => {
 +                    self.create_partial_string();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteCreatePartialString => {
 +                    self.create_partial_string();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCurrentHostname => {
 +                    self.current_hostname();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteCurrentHostname => {
 +                    self.current_hostname();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCurrentInput => {
 +                    try_or_throw!(self.machine_st, self.current_input());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.current_output());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.directory_files());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.file_size();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteFileSize => {
 +                    self.file_size();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallFileExists => {
 +                    self.file_exists();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteFileExists => {
 +                    self.file_exists();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallDirectoryExists => {
 +                    self.directory_exists();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteDirectoryExists => {
 +                    self.directory_exists();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallDirectorySeparator => {
 +                    self.directory_separator();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteDirectorySeparator => {
 +                    self.directory_separator();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallMakeDirectory => {
 +                    self.make_directory();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteMakeDirectory => {
 +                    self.make_directory();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallMakeDirectoryPath => {
 +                    self.make_directory_path();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteMakeDirectoryPath => {
 +                    self.make_directory_path();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallDeleteFile => {
 +                    self.delete_file();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteDeleteFile => {
 +                    self.delete_file();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallRenameFile => {
 +                    self.rename_file();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteRenameFile => {
 +                    self.rename_file();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +              &Instruction::CallFileCopy => {
 +                  self.file_copy();
 +                  step_or_fail!(self, self.machine_st.p += 1);
 +              }
 +              &Instruction::ExecuteFileCopy => {
 +                  self.file_copy();
 +                  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);
 +                }
 +                &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 => {
 +                    self.delete_directory();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteDeleteDirectory => {
 +                    self.delete_directory();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallPathCanonical => {
 +                    try_or_throw!(self.machine_st, self.path_canonical());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.file_time();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteFileTime => {
 +                    self.file_time();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallDynamicModuleResolution(arity) => {
 +                    let (module_name, key) = try_or_throw!(
 +                        self.machine_st,
 +                        self.dynamic_module_resolution(arity - 2)
 +                    );
 +
 +                    try_or_throw!(
 +                        self.machine_st,
 +                        self.call_clause(module_name, key)
 +                    );
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::ExecuteDynamicModuleResolution(arity) => {
 +                    let (module_name, key) = try_or_throw!(
 +                        self.machine_st,
 +                        self.dynamic_module_resolution(arity - 2)
 +                    );
 +
 +                    try_or_throw!(
 +                        self.machine_st,
 +                        self.execute_clause(module_name, key)
 +                    );
 +
 +                    if self.machine_st.fail {
 +                        self.machine_st.backtrack();
 +                    }
 +                }
 +                &Instruction::CallFetchGlobalVar => {
 +                    self.fetch_global_var();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteFetchGlobalVar => {
 +                    self.fetch_global_var();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallFirstStream => {
 +                    self.first_stream();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteFirstStream => {
 +                    self.first_stream();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallFlushOutput => {
 +                    try_or_throw!(self.machine_st, self.flush_output());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteFlushOutput => {
 +                    try_or_throw!(self.machine_st, self.flush_output());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallGetByte => {
 +                    try_or_throw!(self.machine_st, self.get_byte());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.get_char());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.get_n_chars());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.get_code());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.get_single_char());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.truncate_if_no_lifted_heap_growth_diff();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteTruncateIfNoLiftedHeapGrowthDiff => {
 +                    self.truncate_if_no_lifted_heap_growth_diff();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallTruncateIfNoLiftedHeapGrowth => {
 +                    self.truncate_if_no_lifted_heap_growth();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteTruncateIfNoLiftedHeapGrowth => {
 +                    self.truncate_if_no_lifted_heap_growth();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallGetAttributedVariableList => {
 +                    self.get_attributed_variable_list();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteGetAttributedVariableList => {
 +                    self.get_attributed_variable_list();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallGetAttrVarQueueDelimiter => {
 +                    self.get_attr_var_queue_delimiter();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteGetAttrVarQueueDelimiter => {
 +                    self.get_attr_var_queue_delimiter();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallGetAttrVarQueueBeyond => {
 +                    self.get_attr_var_queue_beyond();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteGetAttrVarQueueBeyond => {
 +                    self.get_attr_var_queue_beyond();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallGetBValue => {
 +                    self.get_b_value();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteGetBValue => {
 +                    self.get_b_value();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallGetContinuationChunk => {
 +                    self.get_continuation_chunk();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteGetContinuationChunk => {
 +                    self.get_continuation_chunk();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallLookupDBRef => {
 +                    self.lookup_db_ref();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteLookupDBRef => {
 +                    self.lookup_db_ref();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallGetNextOpDBRef => {
 +                    self.get_next_op_db_ref();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteGetNextOpDBRef => {
 +                    self.get_next_op_db_ref();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallIsPartialString => {
 +                    self.is_partial_string();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteIsPartialString => {
 +                    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::CallGetLiftedHeapFromOffset => {
 +                    self.get_lifted_heap_from_offset();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteGetLiftedHeapFromOffset => {
 +                    self.get_lifted_heap_from_offset();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallGetLiftedHeapFromOffsetDiff => {
 +                    self.get_lifted_heap_from_offset_diff();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteGetLiftedHeapFromOffsetDiff => {
 +                    self.get_lifted_heap_from_offset_diff();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallGetSCCCleaner => {
 +                    self.get_scc_cleaner();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteGetSCCCleaner => {
 +                    self.get_scc_cleaner();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallHeadIsDynamic => {
 +                    self.head_is_dynamic();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteHeadIsDynamic => {
 +                    self.head_is_dynamic();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallInstallSCCCleaner => {
 +                    self.install_scc_cleaner();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteInstallSCCCleaner => {
 +                    self.install_scc_cleaner();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallInstallInferenceCounter => {
 +                    try_or_throw!(self.machine_st, self.install_inference_counter());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.lifted_heap_length();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteLiftedHeapLength => {
 +                    self.lifted_heap_length();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallLoadLibraryAsStream => {
 +                    try_or_throw!(self.machine_st, self.load_library_as_stream());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.module_exists();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteModuleExists => {
 +                    self.module_exists();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallNextEP => {
 +                    self.next_ep();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteNextEP => {
 +                    self.next_ep();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallNoSuchPredicate => {
 +                    try_or_throw!(self.machine_st, self.no_such_predicate());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.number_to_chars();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteNumberToChars => {
 +                    self.number_to_chars();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallNumberToCodes => {
 +                    self.number_to_codes();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteNumberToCodes => {
 +                    self.number_to_codes();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallOpDeclaration => {
 +                    try_or_throw!(self.machine_st, self.op_declaration());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteOpDeclaration => {
 +                    try_or_throw!(self.machine_st, self.op_declaration());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallOpen => {
 +                    try_or_throw!(self.machine_st, self.open());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteOpen => {
 +                    try_or_throw!(self.machine_st, self.open());
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallSetStreamOptions => {
 +                    try_or_throw!(self.machine_st, self.set_stream_options());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteSetStreamOptions => {
 +                    try_or_throw!(self.machine_st, self.set_stream_options());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallNextStream => {
 +                    self.next_stream();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteNextStream => {
 +                    self.next_stream();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallPartialStringTail => {
 +                    self.partial_string_tail();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecutePartialStringTail => {
 +                    self.partial_string_tail();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallPeekByte => {
 +                    try_or_throw!(self.machine_st, self.peek_byte());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.peek_char());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.peek_code());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.points_to_continuation_reset_marker();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecutePointsToContinuationResetMarker => {
 +                    self.points_to_continuation_reset_marker();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallPutByte => {
 +                    try_or_throw!(self.machine_st, self.put_byte());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecutePutByte => {
 +                    try_or_throw!(self.machine_st, self.put_byte());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallPutChar => {
 +                    try_or_throw!(self.machine_st, self.put_char());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecutePutChar => {
 +                    try_or_throw!(self.machine_st, self.put_char());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallPutChars => {
 +                    try_or_throw!(self.machine_st, self.put_chars());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.put_code());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecutePutCode => {
 +                    try_or_throw!(self.machine_st, self.put_code());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallReadQueryTerm => {
 +                    try_or_throw!(self.machine_st, self.read_query_term());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.read_term());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.redo_attr_var_binding();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteRedoAttrVarBinding => {
 +                    self.redo_attr_var_binding();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallRemoveCallPolicyCheck => {
 +                    self.remove_call_policy_check();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteRemoveCallPolicyCheck => {
 +                    self.remove_call_policy_check();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallRemoveInferenceCounter => {
 +                    self.remove_inference_counter();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteRemoveInferenceCounter => {
 +                    self.remove_inference_counter();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallResetContinuationMarker => {
 +                    self.reset_continuation_marker();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteResetContinuationMarker => {
 +                    self.reset_continuation_marker();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallRestoreCutPolicy => {
 +                    self.restore_cut_policy();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteRestoreCutPolicy => {
 +                    self.restore_cut_policy();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallSetCutPoint(r) => {
 +                    if !self.set_cut_point(r) {
 +                        step_or_fail!(self, self.machine_st.p += 1);
 +                    }
 +                }
 +                &Instruction::ExecuteSetCutPoint(r) => {
 +                    let cp = self.machine_st.cp;
 +
 +                    if !self.set_cut_point(r) {
 +                        step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                    } else {
 +                        // run_cleaners in set_cut_point calls call_by_index.
 +                        // replace the effect of call_by_index with that
 +                        // of execute_by_index here.
 +
 +                        self.machine_st.cp = cp;
 +                    }
 +                }
 +                &Instruction::CallSetInput => {
 +                    try_or_throw!(self.machine_st, self.set_input());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteSetInput => {
 +                    try_or_throw!(self.machine_st, self.set_input());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallSetOutput => {
 +                    try_or_throw!(self.machine_st, self.set_output());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteSetOutput => {
 +                    try_or_throw!(self.machine_st, self.set_output());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallStoreBacktrackableGlobalVar => {
 +                    self.store_backtrackable_global_var();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteStoreBacktrackableGlobalVar => {
 +                    self.store_backtrackable_global_var();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallStoreGlobalVar => {
 +                    self.store_global_var();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteStoreGlobalVar => {
 +                    self.store_global_var();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallStreamProperty => {
 +                    try_or_throw!(self.machine_st, self.stream_property());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.set_stream_position());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.inference_level();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteInferenceLevel => {
 +                    self.inference_level();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCleanUpBlock => {
 +                    self.clean_up_block();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteCleanUpBlock => {
 +                    self.clean_up_block();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallFail | &Instruction::ExecuteFail => {
 +                    self.machine_st.backtrack();
 +                }
 +                &Instruction::CallGetBall => {
 +                    self.get_ball();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteGetBall => {
 +                    self.get_ball();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallGetCurrentBlock => {
 +                    self.get_current_block();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteGetCurrentBlock => {
 +                    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);
 +                }
 +                &Instruction::ExecuteGetCutPoint => {
 +                    self.get_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);
 +                }
 +                &Instruction::ExecuteGetDoubleQuotes => {
 +                    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);
 +                }
 +                &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 => {
 +                    self.maybe();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteMaybe => {
 +                    self.maybe();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCpuNow => {
 +                    self.cpu_now();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteCpuNow => {
 +                    self.cpu_now();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallDeterministicLengthRundown => {
 +                    try_or_throw!(self.machine_st, self.det_length_rundown());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.http_open());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                  try_or_throw!(self.machine_st, self.http_listen());
 +                  step_or_fail!(self, self.machine_st.p += 1);
 +              }
 +              &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 => {
 +                  try_or_throw!(self.machine_st, self.http_accept());
 +                  step_or_fail!(self, self.machine_st.p += 1);
 +              }
 +              &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 => {
 +                  try_or_throw!(self.machine_st, self.http_answer());
 +                  step_or_fail!(self, self.machine_st.p += 1);
 +              }
 +              &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 => {
 +                  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);
 +                }
 +                &Instruction::ExecuteCurrentTime => {
 +                    self.current_time();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallQuotedToken => {
 +                    self.quoted_token();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteQuotedToken => {
 +                    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);
 +                }
 +                &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 => {
 +                    self.reset_block();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteResetBlock => {
 +                    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();
 +                }
 +                &Instruction::CallSetBall => {
 +                    self.set_ball();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteSetBall => {
 +                    self.set_ball();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallPushBallStack => {
 +                    self.push_ball_stack();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecutePushBallStack => {
 +                    self.push_ball_stack();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallPopBallStack => {
 +                    self.pop_ball_stack();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecutePopBallStack => {
 +                    self.pop_ball_stack();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallPopFromBallStack => {
 +                    self.pop_from_ball_stack();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecutePopFromBallStack => {
 +                    self.pop_from_ball_stack();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallSetCutPointByDefault(r) => {
 +                    self.set_cut_point_by_default(r);
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteSetCutPointByDefault(r) => {
 +                    self.set_cut_point_by_default(r);
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallSetDoubleQuotes => {
 +                    self.set_double_quotes();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteSetDoubleQuotes => {
 +                    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);
 +                }
 +                &Instruction::ExecuteSetSeed => {
 +                    self.set_seed();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &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 => {
 +                    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 => {
 +                    self.sleep();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteSleep => {
 +                    self.sleep();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallSocketClientOpen => {
 +                    try_or_throw!(self.machine_st, self.socket_client_open());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.socket_server_open());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.socket_server_accept());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.socket_server_close());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteSocketServerClose => {
 +                    try_or_throw!(self.machine_st, self.socket_server_close());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallTLSAcceptClient => {
 +                    try_or_throw!(self.machine_st, self.tls_accept_client());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.tls_client_connect());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteSucceed => {
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallTermAttributedVariables => {
 +                    self.term_attributed_variables();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteTermAttributedVariables => {
 +                    self.term_attributed_variables();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallTermVariables => {
 +                    self.term_variables();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteTermVariables => {
 +                    self.term_variables();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallTermVariablesUnderMaxDepth => {
 +                    self.term_variables_under_max_depth();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteTermVariablesUnderMaxDepth => {
 +                    self.term_variables_under_max_depth();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallTruncateLiftedHeapTo => {
 +                    self.truncate_lifted_heap_to();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteTruncateLiftedHeapTo => {
 +                    self.truncate_lifted_heap_to();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallUnifyWithOccursCheck => {
 +                    self.unify_with_occurs_check();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteUnifyWithOccursCheck => {
 +                    self.unify_with_occurs_check();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallUnwindEnvironments => {
 +                    if !self.unwind_environments() {
 +                        self.machine_st.p += 1;
 +                    }
 +                }
 +                &Instruction::ExecuteUnwindEnvironments => {
 +                    if !self.unwind_environments() {
 +                        self.machine_st.p = self.machine_st.cp;
 +                    }
 +                }
 +                &Instruction::CallUnwindStack | &Instruction::ExecuteUnwindStack => {
 +                    self.machine_st.unwind_stack();
 +                    self.machine_st.backtrack();
 +                }
 +                &Instruction::CallWAMInstructions => {
 +                    try_or_throw!(self.machine_st, self.wam_instructions());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    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);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.write_term_to_chars());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.scryer_prolog_version();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteScryerPrologVersion => {
 +                    self.scryer_prolog_version();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCryptoRandomByte => {
 +                    self.crypto_random_byte();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteCryptoRandomByte => {
 +                    self.crypto_random_byte();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCryptoDataHash => {
 +                    self.crypto_data_hash();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteCryptoDataHash => {
 +                    self.crypto_data_hash();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCryptoDataHKDF => {
 +                    self.crypto_data_hkdf();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteCryptoDataHKDF => {
 +                    self.crypto_data_hkdf();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCryptoPasswordHash => {
 +                    self.crypto_password_hash();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteCryptoPasswordHash => {
 +                    self.crypto_password_hash();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCryptoDataEncrypt => {
 +                    self.crypto_data_encrypt();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteCryptoDataEncrypt => {
 +                    self.crypto_data_encrypt();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCryptoDataDecrypt => {
 +                    self.crypto_data_decrypt();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteCryptoDataDecrypt => {
 +                    self.crypto_data_decrypt();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCryptoCurveScalarMult => {
 +                    self.crypto_curve_scalar_mult();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteCryptoCurveScalarMult => {
 +                    self.crypto_curve_scalar_mult();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallEd25519Sign => {
 +                    self.ed25519_sign();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteEd25519Sign => {
 +                    self.ed25519_sign();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallEd25519Verify => {
 +                    self.ed25519_verify();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteEd25519Verify => {
 +                    self.ed25519_verify();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallEd25519NewKeyPair => {
 +                    self.ed25519_new_key_pair();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteEd25519NewKeyPair => {
 +                    self.ed25519_new_key_pair();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallEd25519KeyPairPublicKey => {
 +                    self.ed25519_key_pair_public_key();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteEd25519KeyPairPublicKey => {
 +                    self.ed25519_key_pair_public_key();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCurve25519ScalarMult => {
 +                    self.curve25519_scalar_mult();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteCurve25519ScalarMult => {
 +                    self.curve25519_scalar_mult();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallFirstNonOctet => {
 +                    self.first_non_octet();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteFirstNonOctet => {
 +                    self.first_non_octet();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallLoadHTML => {
 +                    self.load_html();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteLoadHTML => {
 +                    self.load_html();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallLoadXML => {
 +                    self.load_xml();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteLoadXML => {
 +                    self.load_xml();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallGetEnv => {
 +                    self.get_env();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteGetEnv => {
 +                    self.get_env();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallSetEnv => {
 +                    self.set_env();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteSetEnv => {
 +                    self.set_env();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallUnsetEnv => {
 +                    self.unset_env();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteUnsetEnv => {
 +                    self.unset_env();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallShell => {
 +                    self.shell();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteShell => {
 +                    self.shell();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallPID => {
 +                    self.pid();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecutePID => {
 +                    self.pid();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallCharsBase64 => {
 +                    try_or_throw!(self.machine_st, self.chars_base64());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.devour_whitespace());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.is_sto_enabled();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteIsSTOEnabled => {
 +                    self.is_sto_enabled();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallSetSTOAsUnify => {
 +                    self.set_sto_as_unify();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteSetSTOAsUnify => {
 +                    self.set_sto_as_unify();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallSetNSTOAsUnify => {
 +                    self.set_nsto_as_unify();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteSetNSTOAsUnify => {
 +                    self.set_nsto_as_unify();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallSetSTOWithErrorAsUnify => {
 +                    self.set_sto_with_error_as_unify();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteSetSTOWithErrorAsUnify => {
 +                    self.set_sto_with_error_as_unify();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallHomeDirectory => {
 +                    self.home_directory();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteHomeDirectory => {
 +                    self.home_directory();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallDebugHook => {
 +                    self.debug_hook();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteDebugHook => {
 +                    self.debug_hook();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallPopCount => {
 +                    self.pop_count();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecutePopCount => {
 +                    self.pop_count();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallAddDiscontiguousPredicate => {
 +                    try_or_throw!(self.machine_st, self.add_discontiguous_predicate());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteAddDiscontiguousPredicate => {
 +                    try_or_throw!(self.machine_st, self.add_discontiguous_predicate());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallAddDynamicPredicate => {
 +                    try_or_throw!(self.machine_st, self.add_dynamic_predicate());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteAddDynamicPredicate => {
 +                    try_or_throw!(self.machine_st, self.add_dynamic_predicate());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallAddMultifilePredicate => {
 +                    try_or_throw!(self.machine_st, self.add_multifile_predicate());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteAddMultifilePredicate => {
 +                    try_or_throw!(self.machine_st, self.add_multifile_predicate());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallAddGoalExpansionClause => {
 +                    try_or_throw!(self.machine_st, self.add_goal_expansion_clause());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteAddGoalExpansionClause => {
 +                    try_or_throw!(self.machine_st, self.add_goal_expansion_clause());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallAddTermExpansionClause => {
 +                    try_or_throw!(self.machine_st, self.add_term_expansion_clause());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteAddTermExpansionClause => {
 +                    try_or_throw!(self.machine_st, self.add_term_expansion_clause());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallAddInSituFilenameModule => {
 +                    try_or_throw!(self.machine_st, self.add_in_situ_filename_module());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteAddInSituFilenameModule => {
 +                    try_or_throw!(self.machine_st, self.add_in_situ_filename_module());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallClauseToEvacuable => {
 +                    try_or_throw!(self.machine_st, self.clause_to_evacuable());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteClauseToEvacuable => {
 +                    try_or_throw!(self.machine_st, self.clause_to_evacuable());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallScopedClauseToEvacuable => {
 +                    try_or_throw!(self.machine_st, self.scoped_clause_to_evacuable());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteScopedClauseToEvacuable => {
 +                    try_or_throw!(self.machine_st, self.scoped_clause_to_evacuable());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallConcludeLoad => {
 +                    try_or_throw!(self.machine_st, self.conclude_load());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteConcludeLoad => {
 +                    try_or_throw!(self.machine_st, self.conclude_load());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallDeclareModule => {
 +                    try_or_throw!(self.machine_st, self.declare_module());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteDeclareModule => {
 +                    try_or_throw!(self.machine_st, self.declare_module());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallLoadCompiledLibrary => {
 +                    try_or_throw!(self.machine_st, self.load_compiled_library());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.load_context_source();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteLoadContextSource => {
 +                    self.load_context_source();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallLoadContextFile => {
 +                    self.load_context_file();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteLoadContextFile => {
 +                    self.load_context_file();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallLoadContextDirectory => {
 +                    self.load_context_directory();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteLoadContextDirectory => {
 +                    self.load_context_directory();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallLoadContextModule => {
 +                    self.load_context_module(self.machine_st.registers[1]);
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.load_context_stream();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteLoadContextStream => {
 +                    self.load_context_stream();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallPopLoadContext => {
 +                    self.pop_load_context();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecutePopLoadContext => {
 +                    self.pop_load_context();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallPopLoadStatePayload => {
 +                    self.pop_load_state_payload();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecutePopLoadStatePayload => {
 +                    self.pop_load_state_payload();
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallPushLoadContext => {
 +                    try_or_throw!(self.machine_st, self.push_load_context());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecutePushLoadContext => {
 +                    try_or_throw!(self.machine_st, self.push_load_context());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallPushLoadStatePayload => {
 +                    self.push_load_state_payload();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecutePushLoadStatePayload => {
 +                    self.push_load_state_payload();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallUseModule => {
 +                    try_or_throw!(self.machine_st, self.use_module());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteUseModule => {
 +                    try_or_throw!(self.machine_st, self.use_module());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallBuiltInProperty => {
 +                    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 => {
 +                    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 => {
 +                    self.meta_predicate_property();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteMetaPredicateProperty => {
 +                    self.meta_predicate_property();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallMultifileProperty => {
 +                    self.multifile_property();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteMultifileProperty => {
 +                    self.multifile_property();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallDiscontiguousProperty => {
 +                    self.discontiguous_property();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteDiscontiguousProperty => {
 +                    self.discontiguous_property();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallDynamicProperty => {
 +                    self.dynamic_property();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteDynamicProperty => {
 +                    self.dynamic_property();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallAbolishClause => {
 +                    try_or_throw!(self.machine_st, self.abolish_clause());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteAbolishClause => {
 +                    try_or_throw!(self.machine_st, self.abolish_clause());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallAsserta => {
 +                    try_or_throw!(self.machine_st, self.compile_assert(AppendOrPrepend::Prepend));
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteAsserta => {
 +                    try_or_throw!(self.machine_st, self.compile_assert(AppendOrPrepend::Prepend));
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallAssertz => {
 +                    try_or_throw!(self.machine_st, self.compile_assert(AppendOrPrepend::Append));
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteAssertz => {
 +                    try_or_throw!(self.machine_st, self.compile_assert(AppendOrPrepend::Append));
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallRetract => {
 +                    try_or_throw!(self.machine_st, self.retract_clause());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteRetract => {
 +                    try_or_throw!(self.machine_st, self.retract_clause());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &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 => {
 +                    try_or_throw!(self.machine_st, self.is_consistent_with_term_queue());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::CallFlushTermQueue => {
 +                    try_or_throw!(self.machine_st, self.flush_term_queue());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteFlushTermQueue => {
 +                    try_or_throw!(self.machine_st, self.flush_term_queue());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallRemoveModuleExports => {
 +                    try_or_throw!(self.machine_st, self.remove_module_exports());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteRemoveModuleExports => {
 +                    try_or_throw!(self.machine_st, self.remove_module_exports());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallAddNonCountedBacktracking => {
 +                    try_or_throw!(self.machine_st, self.add_non_counted_backtracking());
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteAddNonCountedBacktracking => {
 +                    try_or_throw!(self.machine_st, self.add_non_counted_backtracking());
 +                    self.machine_st.p = self.machine_st.cp;
 +                }
 +                &Instruction::CallPredicateDefined => {
 +                    self.machine_st.fail = !self.predicate_defined();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecutePredicateDefined => {
 +                    self.machine_st.fail = !self.predicate_defined();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &Instruction::CallStripModule => {
 +                    self.strip_module();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteStripModule => {
 +                    self.strip_module();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &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) => {
 +                    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 => {
 +                    try_or_throw!(self.machine_st, self.compile_inline_or_expanded_goal());
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &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 => {
 +                    self.machine_st.fail = !self.is_expanded_or_inlined();
 +                    step_or_fail!(self, self.machine_st.p += 1);
 +                }
 +                &Instruction::ExecuteIsExpandedOrInlined => {
 +                    self.machine_st.fail = !self.is_expanded_or_inlined();
 +                    step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
 +                }
 +                &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.fast_call(arity, call_at_index));
 +
 +                    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)
 +                        );
 +                    }
 +                }
 +                &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.fast_call(arity, call_at_index));
 +
 +                    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)
 +                        );
 +                    }
 +                }
 +                &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);
 +                }
 +                &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);
 +                }
 +                &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);
 +                }
 +                &Instruction::CallDeleteAllAttributesFromVar => {
 +                    self.delete_all_attributes_from_var();
 +                    self.machine_st.p += 1;
 +                }
 +                &Instruction::ExecuteDeleteAllAttributesFromVar => {
 +                    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();
 +                    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);
 +                }
 +            }
 +        }
 +
 +        let interrupted = INTERRUPT.load(std::sync::atomic::Ordering::Relaxed);
 +
 +        match INTERRUPT.compare_exchange(
 +            interrupted,
 +            false,
 +            std::sync::atomic::Ordering::Relaxed,
 +            std::sync::atomic::Ordering::Relaxed,
 +        ) {
 +            Ok(interruption) => {
 +                if interruption {
 +                    self.machine_st.throw_interrupt_exception();
 +                    self.machine_st.backtrack();
 +                }
 +            }
 +            Err(_) => unreachable!(),
 +        }
 +        }
 +    }
 +}