]> Repositorios git - scryer-prolog.git/commitdiff
use new heap term representation
authorMark Thom <[email protected]>
Sun, 14 Nov 2021 20:39:56 +0000 (13:39 -0700)
committerMark Thom <[email protected]>
Fri, 7 Jan 2022 04:44:38 +0000 (21:44 -0700)
72 files changed:
.gitignore
Cargo.lock
Cargo.toml
build.rs
crates/prolog_parser/Cargo.lock [deleted file]
crates/prolog_parser/src/ast.rs [deleted file]
crates/prolog_parser/src/put_back_n.rs [deleted file]
crates/prolog_parser/src/tabled_rc.rs [deleted file]
crates/prolog_parser/tests/bom.rs [deleted file]
crates/prolog_parser/tests/parse_tokens.rs [deleted file]
crates/static-string-indexing/Cargo.toml [new file with mode: 0644]
crates/static-string-indexing/src/lib.rs [new file with mode: 0644]
src/allocator.rs
src/arena.rs [new file with mode: 0644]
src/arithmetic.rs
src/atom_table.rs [new file with mode: 0644]
src/bin/scryer-prolog.rs
src/clause_types.rs
src/codegen.rs
src/debray_allocator.rs
src/fixtures.rs
src/forms.rs
src/heap_iter.rs
src/heap_print.rs
src/indexing.rs
src/instructions.rs
src/iterators.rs
src/lib.rs
src/lib/atts.pl
src/lib/between.pl
src/lib/builtins.pl
src/lib/iso_ext.pl
src/lib/lists.pl
src/lib/serialization/abnf.pl
src/lib/serialization/json.pl
src/lib/uuid.pl
src/loader.pl
src/machine/arithmetic_ops.rs
src/machine/attributed_variables.rs
src/machine/code_repo.rs
src/machine/compile.rs
src/machine/copier.rs
src/machine/gc.rs [new file with mode: 0644]
src/machine/heap.rs
src/machine/load_state.rs
src/machine/loader.rs
src/machine/machine_errors.rs
src/machine/machine_indices.rs
src/machine/machine_state.rs
src/machine/machine_state_impl.rs
src/machine/mock_wam.rs [new file with mode: 0644]
src/machine/mod.rs
src/machine/partial_string.rs
src/machine/preprocessor.rs
src/machine/stack.rs
src/machine/streams.rs
src/machine/system_calls.rs
src/machine/term_stream.rs
src/macros.rs
src/parser/ast.rs [new file with mode: 0644]
src/parser/char_reader.rs [new file with mode: 0644]
src/parser/lexer.rs [moved from crates/prolog_parser/src/lexer.rs with 53% similarity]
src/parser/macros.rs [moved from crates/prolog_parser/src/macros.rs with 100% similarity]
src/parser/mod.rs [moved from crates/prolog_parser/src/lib.rs with 57% similarity]
src/parser/parser.rs [moved from crates/prolog_parser/src/parser.rs with 68% similarity]
src/raw_block.rs [new file with mode: 0644]
src/read.rs
src/targets.rs
src/toplevel.pl
src/types.rs [new file with mode: 0644]
src/write.rs
tests/scryer/helper.rs

index eafdaf522bcc9e81dc3982c1de68755832050ca4..f63be9e995dbf7eb57fa698a265de129f3bf3502 100644 (file)
@@ -1,3 +1,5 @@
+src/static_atoms.rs
 target/
 
 
+
index 7e94a23e58d237409f2f9f9d0514bac7a763e000..d5b4dc14662540ad4c62f4183858b5cda2f290ac 100644 (file)
@@ -1,5 +1,7 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+version = 3
+
 [[package]]
 name = "arrayvec"
 version = "0.5.2"
@@ -8,9 +10,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
 
 [[package]]
 name = "assert_cmd"
-version = "1.0.3"
+version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2475b58cd94eb4f70159f4fd8844ba3b807532fe3131b3373fae060bbe30396"
+checksum = "c98233c6673d8601ab23e77eb38f999c51100d46c5703b17288c57fddf3a1ffe"
 dependencies = [
  "bstr",
  "doc-comment",
@@ -34,9 +36,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
 
 [[package]]
 name = "az"
-version = "1.1.0"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d84e1d907bfc5795a6addb95ef8666141ee73c8f2f5250ff2a46bf4e4f4aec8a"
+checksum = "9d6dff4a1892b54d70af377bf7a17064192e822865791d812957f21e3108c325"
 
 [[package]]
 name = "base64"
@@ -61,9 +63,9 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
 
 [[package]]
 name = "bitflags"
-version = "1.2.1"
+version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "blake2"
@@ -100,9 +102,9 @@ dependencies = [
 
 [[package]]
 name = "bstr"
-version = "0.2.15"
+version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d"
+checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
 dependencies = [
  "lazy_static",
  "memchr",
@@ -111,9 +113,9 @@ dependencies = [
 
 [[package]]
 name = "bumpalo"
-version = "3.6.0"
+version = "3.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9"
+checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
 
 [[package]]
 name = "byte-tools"
@@ -123,15 +125,15 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
 
 [[package]]
 name = "byteorder"
-version = "1.4.2"
+version = "1.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
 [[package]]
 name = "cc"
-version = "1.0.66"
+version = "1.0.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
+checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
 
 [[package]]
 name = "cfg-if"
@@ -153,7 +155,7 @@ checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
 dependencies = [
  "libc",
  "num-integer",
- "num-traits 0.2.14",
+ "num-traits",
  "time",
  "winapi 0.3.9",
 ]
@@ -169,9 +171,9 @@ dependencies = [
 
 [[package]]
 name = "core-foundation"
-version = "0.9.1"
+version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
+checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -179,9 +181,9 @@ dependencies = [
 
 [[package]]
 name = "core-foundation-sys"
-version = "0.8.2"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
 
 [[package]]
 name = "cpu-time"
@@ -229,10 +231,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "difference"
-version = "2.0.0"
+name = "difflib"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
+checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
 
 [[package]]
 name = "digest"
@@ -282,6 +284,21 @@ version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d"
 
+[[package]]
+name = "ed25519"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4620d40f6d2601794401d6dd95a5cf69b6c157852539470eeda433a99b3c0efc"
+dependencies = [
+ "signature",
+]
+
+[[package]]
+name = "either"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+
 [[package]]
 name = "foreign-types"
 version = "0.3.2"
@@ -341,18 +358,18 @@ dependencies = [
 
 [[package]]
 name = "generic-array"
-version = "0.12.3"
+version = "0.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
+checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
 dependencies = [
  "typenum",
 ]
 
 [[package]]
 name = "getrandom"
-version = "0.2.2"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
+checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
@@ -361,9 +378,9 @@ dependencies = [
 
 [[package]]
 name = "git-version"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94918e83f1e01dedc2e361d00ce9487b14c58c7f40bab148026fa39d42cb41e2"
+checksum = "f6b0decc02f4636b9ccad390dcbe77b722a77efedfa393caf8379a51d5c61899"
 dependencies = [
  "git-version-macro",
  "proc-macro-hack",
@@ -371,21 +388,21 @@ dependencies = [
 
 [[package]]
 name = "git-version-macro"
-version = "0.3.4"
+version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34a97a52fdee1870a34fa6e4b77570cba531b27d1838874fef4429a791a3d657"
+checksum = "fe69f1cbdb6e28af2bac214e943b99ce8a0a06b447d15d3e61161b0423139f3f"
 dependencies = [
  "proc-macro-hack",
- "proc-macro2 1.0.24",
- "quote 1.0.8",
- "syn 1.0.60",
+ "proc-macro2 1.0.32",
+ "quote 1.0.10",
+ "syn 1.0.81",
 ]
 
 [[package]]
 name = "gmp-mpfr-sys"
-version = "1.4.2"
+version = "1.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a57fdb339d49833021b1fded600ed240ae907e33909d5511a61dff884df7f16e"
+checksum = "a146a7357ce9573bdcc416fc4a99b960e166e72d8eaffa7c59966d51866b5bfb"
 dependencies = [
  "libc",
  "winapi 0.3.9",
@@ -393,9 +410,9 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.9.1"
+version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
 
 [[package]]
 name = "hostname"
@@ -424,9 +441,9 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "1.6.1"
+version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b"
+checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
 dependencies = [
  "autocfg 1.0.1",
  "hashbrown",
@@ -441,17 +458,26 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "itertools"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
+dependencies = [
+ "either",
+]
+
 [[package]]
 name = "itoa"
-version = "0.4.7"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
+checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
 
 [[package]]
 name = "js-sys"
-version = "0.3.47"
+version = "0.3.55"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65"
+checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -503,19 +529,20 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.85"
+version = "0.2.107"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3"
+checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
 
 [[package]]
 name = "libsodium-sys"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a685b64f837b339074115f2e7f7b431ac73681d08d75b389db7498b8892b8a58"
+checksum = "6b779387cd56adfbc02ea4a668e704f729be8d6a6abd2c27ca5ee537849a92fd"
 dependencies = [
  "cc",
  "libc",
  "pkg-config",
+ "walkdir",
 ]
 
 [[package]]
@@ -558,7 +585,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f1af46a727284117e09780d05038b1ce6fc9c76cc6df183c3dae5a8955a25e21"
 dependencies = [
  "log",
- "phf",
+ "phf 0.7.24",
  "phf_codegen",
  "serde",
  "serde_derive",
@@ -576,9 +603,9 @@ checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
 
 [[package]]
 name = "memchr"
-version = "2.3.4"
+version = "2.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
+checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
 
 [[package]]
 name = "mio"
@@ -611,11 +638,30 @@ dependencies = [
  "ws2_32-sys",
 ]
 
+[[package]]
+name = "modular-bitfield"
+version = "0.11.2"
+source = "git+https://github.com/mthom/modular-bitfield#213535c684af277563678179d8496f11b84a283f"
+dependencies = [
+ "modular-bitfield-impl",
+ "static_assertions",
+]
+
+[[package]]
+name = "modular-bitfield-impl"
+version = "0.11.2"
+source = "git+https://github.com/mthom/modular-bitfield#213535c684af277563678179d8496f11b84a283f"
+dependencies = [
+ "proc-macro2 1.0.32",
+ "quote 1.0.10",
+ "syn 1.0.81",
+]
+
 [[package]]
 name = "native-tls"
-version = "0.2.7"
+version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4"
+checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d"
 dependencies = [
  "lazy_static",
  "libc",
@@ -679,7 +725,7 @@ checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
 dependencies = [
  "autocfg 1.0.1",
  "num-integer",
- "num-traits 0.2.14",
+ "num-traits",
 ]
 
 [[package]]
@@ -689,7 +735,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
 dependencies = [
  "autocfg 1.0.1",
- "num-traits 0.2.14",
+ "num-traits",
 ]
 
 [[package]]
@@ -701,7 +747,7 @@ dependencies = [
  "autocfg 1.0.1",
  "num-bigint",
  "num-integer",
- "num-traits 0.2.14",
+ "num-traits",
 ]
 
 [[package]]
@@ -712,16 +758,7 @@ dependencies = [
  "num-bigint",
  "num-integer",
  "num-rational",
- "num-traits 0.2.14",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.1.43"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
-dependencies = [
- "num-traits 0.2.14",
+ "num-traits",
 ]
 
 [[package]]
@@ -735,9 +772,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.5.2"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
+checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
 
 [[package]]
 name = "opaque-debug"
@@ -747,38 +784,38 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
 
 [[package]]
 name = "openssl"
-version = "0.10.32"
+version = "0.10.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70"
+checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
 dependencies = [
  "bitflags",
  "cfg-if 1.0.0",
  "foreign-types",
- "lazy_static",
  "libc",
+ "once_cell",
  "openssl-sys",
 ]
 
 [[package]]
 name = "openssl-probe"
-version = "0.1.2"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
+checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
 
 [[package]]
 name = "openssl-src"
-version = "111.13.0+1.1.1i"
+version = "300.0.2+3.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "045e4dc48af57aad93d665885789b43222ae26f4886494da12d1ed58d309dcb6"
+checksum = "14a760a11390b1a5daf72074d4f6ff1a6e772534ae191f999f57e9ee8146d1fb"
 dependencies = [
  "cc",
 ]
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.60"
+version = "0.9.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6"
+checksum = "c6517987b3f8226b5da3661dad65ff7f300cc59fb5ea8333ca191fc65fde3edf"
 dependencies = [
  "autocfg 1.0.1",
  "cc",
@@ -790,12 +827,11 @@ dependencies = [
 
 [[package]]
 name = "ordered-float"
-version = "0.5.2"
+version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7eb5259643245d3f292c7a146b2df53bba24d7eab159410e648eb73dc164669d"
+checksum = "97c9d06878b3a851e8026ef94bf7fef9ba93062cd412601da4d9cf369b1cc62d"
 dependencies = [
- "num-traits 0.1.43",
- "unreachable",
+ "num-traits",
 ]
 
 [[package]]
@@ -828,7 +864,18 @@ version = "0.7.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18"
 dependencies = [
- "phf_shared",
+ "phf_shared 0.7.24",
+]
+
+[[package]]
+name = "phf"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ac8b67553a7ca9457ce0e526948cad581819238f4a9d1ea74545851fa24f37"
+dependencies = [
+ "phf_macros",
+ "phf_shared 0.9.0",
+ "proc-macro-hack",
 ]
 
 [[package]]
@@ -837,8 +884,8 @@ version = "0.7.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e"
 dependencies = [
- "phf_generator",
- "phf_shared",
+ "phf_generator 0.7.24",
+ "phf_shared 0.7.24",
 ]
 
 [[package]]
@@ -847,30 +894,63 @@ version = "0.7.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662"
 dependencies = [
- "phf_shared",
+ "phf_shared 0.7.24",
  "rand 0.6.5",
 ]
 
+[[package]]
+name = "phf_generator"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d43f3220d96e0080cc9ea234978ccd80d904eafb17be31bb0f76daaea6493082"
+dependencies = [
+ "phf_shared 0.9.0",
+ "rand 0.8.4",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b706f5936eb50ed880ae3009395b43ed19db5bff2ebd459c95e7bf013a89ab86"
+dependencies = [
+ "phf_generator 0.9.1",
+ "phf_shared 0.9.0",
+ "proc-macro-hack",
+ "proc-macro2 1.0.32",
+ "quote 1.0.10",
+ "syn 1.0.81",
+]
+
 [[package]]
 name = "phf_shared"
 version = "0.7.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
 dependencies = [
- "siphasher",
+ "siphasher 0.2.3",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a68318426de33640f02be62b4ae8eb1261be2efbc337b60c54d845bf4484e0d9"
+dependencies = [
+ "siphasher 0.3.7",
 ]
 
 [[package]]
 name = "pkg-config"
-version = "0.3.19"
+version = "0.3.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
+checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.10"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
+checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
 
 [[package]]
 name = "precomputed-hash"
@@ -880,11 +960,12 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
 
 [[package]]
 name = "predicates"
-version = "1.0.7"
+version = "2.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eeb433456c1a57cc93554dea3ce40b4c19c4057e41c55d4a0f3d84ea71c325aa"
+checksum = "5c6ce811d0b2e103743eec01db1c50612221f173084ce2f7941053e94b6bb474"
 dependencies = [
- "difference",
+ "difflib",
+ "itertools",
  "predicates-core",
 ]
 
@@ -896,12 +977,12 @@ checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451"
 
 [[package]]
 name = "predicates-tree"
-version = "1.0.2"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15f553275e5721409451eb85e15fd9a860a6e5ab4496eb215987502b5f5391f2"
+checksum = "338c7be2905b732ae3984a2f40032b5e94fd8f52505b186c7d4d68d193445df7"
 dependencies = [
  "predicates-core",
- "treeline",
+ "termtree",
 ]
 
 [[package]]
@@ -921,23 +1002,11 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.24"
+version = "1.0.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
+checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
 dependencies = [
- "unicode-xid 0.2.1",
-]
-
-[[package]]
-name = "prolog_parser"
-version = "0.8.68"
-dependencies = [
- "indexmap",
- "lexical",
- "num-rug-adapter",
- "ordered-float",
- "rug",
- "unicode_reader",
+ "unicode-xid 0.2.2",
 ]
 
 [[package]]
@@ -951,11 +1020,11 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.8"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
+checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
 dependencies = [
- "proc-macro2 1.0.24",
+ "proc-macro2 1.0.32",
 ]
 
 [[package]]
@@ -979,14 +1048,14 @@ dependencies = [
 
 [[package]]
 name = "rand"
-version = "0.8.3"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
+checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
 dependencies = [
  "libc",
- "rand_chacha 0.3.0",
- "rand_core 0.6.1",
- "rand_hc 0.3.0",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.3",
+ "rand_hc 0.3.1",
 ]
 
 [[package]]
@@ -1001,12 +1070,12 @@ dependencies = [
 
 [[package]]
 name = "rand_chacha"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
 dependencies = [
  "ppv-lite86",
- "rand_core 0.6.1",
+ "rand_core 0.6.3",
 ]
 
 [[package]]
@@ -1026,9 +1095,9 @@ checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
 
 [[package]]
 name = "rand_core"
-version = "0.6.1"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5"
+checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
 dependencies = [
  "getrandom",
 ]
@@ -1044,11 +1113,11 @@ dependencies = [
 
 [[package]]
 name = "rand_hc"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
+checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
 dependencies = [
- "rand_core 0.6.1",
+ "rand_core 0.6.3",
 ]
 
 [[package]]
@@ -1121,9 +1190,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.4"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570"
+checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
 dependencies = [
  "bitflags",
 ]
@@ -1135,7 +1204,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
 dependencies = [
  "getrandom",
- "redox_syscall 0.2.4",
+ "redox_syscall 0.2.10",
 ]
 
 [[package]]
@@ -1146,12 +1215,9 @@ checksum = "d813022b2e00774a48eaf43caaa3c20b45f040ba8cbf398e2e8911a06668dbe6"
 
 [[package]]
 name = "regex-automata"
-version = "0.1.9"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4"
-dependencies = [
- "byteorder",
-]
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
 
 [[package]]
 name = "remove_dir_all"
@@ -1199,9 +1265,9 @@ dependencies = [
 
 [[package]]
 name = "rug"
-version = "1.11.0"
+version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e538d00da450a8e48aac7e6322e67b2dc86ec71a1feeac0e3954c4f07f01bc45"
+checksum = "ee0c6e98de59509e62e09f3456b23cebb75dad21928882016f169bb628843459"
 dependencies = [
  "az",
  "gmp-mpfr-sys",
@@ -1235,6 +1301,15 @@ version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
 
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
 [[package]]
 name = "schannel"
 version = "0.1.19"
@@ -1253,7 +1328,7 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
 name = "scryer-prolog"
-version = "0.8.128"
+version = "0.9.0"
 dependencies = [
  "assert_cmd",
  "base64",
@@ -1268,14 +1343,17 @@ dependencies = [
  "hostname",
  "indexmap",
  "lazy_static",
+ "lexical",
  "libc",
+ "modular-bitfield",
  "native-tls",
  "nix 0.15.0",
  "num-rug-adapter",
  "openssl",
  "ordered-float",
+ "phf 0.9.0",
  "predicates-core",
- "prolog_parser",
+ "proc-macro2 1.0.32",
  "ref_thread_local",
  "ring",
  "ripemd160",
@@ -1285,15 +1363,17 @@ dependencies = [
  "select",
  "sha3",
  "slice-deque",
+ "smallvec",
  "sodiumoxide",
- "unicode_reader",
+ "static-string-indexing",
+ "static_assertions",
 ]
 
 [[package]]
 name = "security-framework"
-version = "2.0.0"
+version = "2.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69"
+checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87"
 dependencies = [
  "bitflags",
  "core-foundation",
@@ -1304,9 +1384,9 @@ dependencies = [
 
 [[package]]
 name = "security-framework-sys"
-version = "2.0.0"
+version = "2.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b"
+checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -1324,26 +1404,26 @@ dependencies = [
 
 [[package]]
 name = "serde"
-version = "1.0.123"
+version = "1.0.130"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
+checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
 
 [[package]]
 name = "serde_derive"
-version = "1.0.123"
+version = "1.0.130"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
+checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
 dependencies = [
- "proc-macro2 1.0.24",
- "quote 1.0.8",
- "syn 1.0.60",
+ "proc-macro2 1.0.32",
+ "quote 1.0.10",
+ "syn 1.0.81",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.61"
+version = "1.0.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a"
+checksum = "e277c495ac6cd1a01a58d0a0c574568b4d1ddf14f59965c6a58b8d96400b54f3"
 dependencies = [
  "itoa",
  "ryu",
@@ -1376,24 +1456,36 @@ dependencies = [
 
 [[package]]
 name = "signal-hook-registry"
-version = "1.3.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
+checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
 dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "signature"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788"
+
 [[package]]
 name = "siphasher"
 version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
 
+[[package]]
+name = "siphasher"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
+
 [[package]]
 name = "slab"
-version = "0.4.2"
+version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
+checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
 
 [[package]]
 name = "slice-deque"
@@ -1408,16 +1500,17 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
-version = "1.6.1"
+version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
+checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
 
 [[package]]
 name = "sodiumoxide"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7038b67c941e23501573cb7242ffb08709abe9b11eb74bceff875bbda024a6a8"
+checksum = "e26be3acb6c2d9a7aac28482586a7856436af4cfe7100031d219de2d2ecb0028"
 dependencies = [
+ "ed25519",
  "libc",
  "libsodium-sys",
  "serde",
@@ -1429,6 +1522,17 @@ version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
 
+[[package]]
+name = "static-string-indexing"
+version = "0.1.0"
+dependencies = [
+ "indexmap",
+ "proc-macro2 1.0.32",
+ "quote 1.0.10",
+ "syn 1.0.81",
+ "walkdir",
+]
+
 [[package]]
 name = "static_assertions"
 version = "1.1.0"
@@ -1443,7 +1547,7 @@ checksum = "89c058a82f9fd69b1becf8c274f412281038877c553182f1d02eb027045a2d67"
 dependencies = [
  "lazy_static",
  "new_debug_unreachable",
- "phf_shared",
+ "phf_shared 0.7.24",
  "precomputed-hash",
  "serde",
  "string_cache_codegen",
@@ -1456,10 +1560,10 @@ version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f0f45ed1b65bf9a4bf2f7b7dc59212d1926e9eaf00fa998988e420fd124467c6"
 dependencies = [
- "phf_generator",
- "phf_shared",
- "proc-macro2 1.0.24",
- "quote 1.0.8",
+ "phf_generator 0.7.24",
+ "phf_shared 0.7.24",
+ "proc-macro2 1.0.32",
+ "quote 1.0.10",
  "string_cache_shared",
 ]
 
@@ -1488,13 +1592,13 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.60"
+version = "1.0.81"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
+checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
 dependencies = [
- "proc-macro2 1.0.24",
- "quote 1.0.8",
- "unicode-xid 0.2.1",
+ "proc-macro2 1.0.32",
+ "quote 1.0.10",
+ "unicode-xid 0.2.2",
 ]
 
 [[package]]
@@ -1505,8 +1609,8 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
- "rand 0.8.3",
- "redox_syscall 0.2.4",
+ "rand 0.8.4",
+ "redox_syscall 0.2.10",
  "remove_dir_all",
  "winapi 0.3.9",
 ]
@@ -1522,6 +1626,12 @@ dependencies = [
  "utf-8",
 ]
 
+[[package]]
+name = "termtree"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13a4ec180a2de59b57434704ccfad967f789b12737738798fa08798cd5824c16"
+
 [[package]]
 name = "time"
 version = "0.1.43"
@@ -1532,29 +1642,23 @@ dependencies = [
  "winapi 0.3.9",
 ]
 
-[[package]]
-name = "treeline"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
-
 [[package]]
 name = "typenum"
-version = "1.12.0"
+version = "1.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
+checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
 
 [[package]]
 name = "unicode-segmentation"
-version = "1.7.1"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
+checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
 
 [[package]]
 name = "unicode-width"
-version = "0.1.8"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
 
 [[package]]
 name = "unicode-xid"
@@ -1564,28 +1668,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
 
 [[package]]
 name = "unicode-xid"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
-
-[[package]]
-name = "unicode_reader"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b639121690b27acd92c97ed2b52c5e5e8d3d39482e943b4559695cef62f771a"
-dependencies = [
- "smallvec",
- "unicode-segmentation",
-]
-
-[[package]]
-name = "unreachable"
-version = "1.0.0"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
-dependencies = [
- "void",
-]
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
 
 [[package]]
 name = "untrusted"
@@ -1595,9 +1680,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
 
 [[package]]
 name = "utf-8"
-version = "0.7.5"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
 
 [[package]]
 name = "utf8parse"
@@ -1607,9 +1692,9 @@ checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
 
 [[package]]
 name = "vcpkg"
-version = "0.2.11"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
 
 [[package]]
 name = "void"
@@ -1626,6 +1711,17 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi 0.3.9",
+ "winapi-util",
+]
+
 [[package]]
 name = "wasi"
 version = "0.10.2+wasi-snapshot-preview1"
@@ -1634,9 +1730,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.70"
+version = "0.2.78"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be"
+checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
 dependencies = [
  "cfg-if 1.0.0",
  "wasm-bindgen-macro",
@@ -1644,53 +1740,53 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.70"
+version = "0.2.78"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7"
+checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
 dependencies = [
  "bumpalo",
  "lazy_static",
  "log",
- "proc-macro2 1.0.24",
- "quote 1.0.8",
- "syn 1.0.60",
+ "proc-macro2 1.0.32",
+ "quote 1.0.10",
+ "syn 1.0.81",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.70"
+version = "0.2.78"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c"
+checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
 dependencies = [
- "quote 1.0.8",
+ "quote 1.0.10",
  "wasm-bindgen-macro-support",
 ]
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.70"
+version = "0.2.78"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385"
+checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
 dependencies = [
- "proc-macro2 1.0.24",
- "quote 1.0.8",
- "syn 1.0.60",
+ "proc-macro2 1.0.32",
+ "quote 1.0.10",
+ "syn 1.0.81",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.70"
+version = "0.2.78"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64"
+checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
 
 [[package]]
 name = "web-sys"
-version = "0.3.47"
+version = "0.3.55"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3"
+checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -1724,6 +1820,15 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi 0.3.9",
+]
+
 [[package]]
 name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
index 0e4b144e479ceb87c4022aa205421216a7eba795..ee785cb456ceb24b7ac74ca733ad774ae5b80c98 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "scryer-prolog"
-version = "0.8.128"
+version = "0.9.0"
 authors = ["Mark Thom <[email protected]>"]
 edition = "2021"
 description = "A modern Prolog implementation written mostly in Rust."
@@ -12,14 +12,19 @@ categories = ["command-line-utilities"]
 build = "build.rs"
 
 [workspace]
-members = ["crates/prolog_parser", "crates/num-rug-adapter"]
+members = ["crates/num-rug-adapter", "crates/static-string-indexing"]
+
+[features]
+num = ["num-rug-adapter"]
+# no default features to make num tests work
+# workaround for --no-default-features and --features not working intuitively for workspaces with a root package
+# see rust-lang/cargo#7160
+default = ["rug"]
 
 [build-dependencies]
 indexmap = "1.0.2"
-
-[features]
-default = ["rug", "prolog_parser/rug"]
-num = ["num-rug-adapter", "prolog_parser/num"]
+static-string-indexing = { path = "./crates/static-string-indexing" }
+proc-macro2 = "*"
 
 [dependencies]
 cpu-time = "1.0.0"
@@ -31,15 +36,17 @@ git-version = "0.3.4"
 hostname = "0.3.1"
 indexmap = "1.0.2"
 lazy_static = "1.4.0"
+lexical = "5.2.2"
 libc = "0.2.62"
+# temporary to remove unnecessary braces warnings.
+modular-bitfield = { git = "https://github.com/mthom/modular-bitfield" } # modular-bitfield = "0.11.2"
 nix = "0.15.0"
 num-rug-adapter = { optional = true, path = "./crates/num-rug-adapter" }
-ordered-float = "0.5.0"
-prolog_parser = { path = "./crates/prolog_parser", default-features = false }
+ordered-float = "2.1.1"
+phf = { version = "0.9",  features = ["macros"] }
 ref_thread_local = "0.0.0"
-rug = { version = "1.4.0", optional = true }
+rug = { version = "1.12.0", optional = true }
 rustyline = "7.0.0"
-unicode_reader = "1.0.0"
 ring = "0.16.13"
 ripemd160 = "0.8.0"
 sha3 = "0.8.2"
@@ -50,9 +57,11 @@ chrono = "0.4.11"
 select = "0.4.3"
 roxmltree = "0.11.0"
 base64 = "0.12.3"
+smallvec = "*"
 sodiumoxide = "0.2.6"
+static_assertions = "1.1.0"
 slice-deque = "0.3.0"
 
 [dev-dependencies]
 assert_cmd = "1.0.3"
-predicates-core = "1.0.2"
+predicates-core = "1.0.2"
\ No newline at end of file
index 0133c93ecb8c45ceb01df9c010dea21484a83c2e..88267cdc5b7d99e1dd39cad486eef7831c95ddf1 100644 (file)
--- a/build.rs
+++ b/build.rs
@@ -1,8 +1,11 @@
+use static_string_indexing::index_static_strings;
+
 use std::env;
 use std::fs;
 use std::fs::File;
 use std::io::Write;
 use std::path::Path;
+use std::process::Command;
 
 fn find_prolog_files(libraries: &mut File, prefix: &str, current_dir: &Path) {
     let entries = match current_dir.read_dir() {
@@ -48,6 +51,21 @@ fn main() {
         let mut m = IndexMap::new();\n",
         )
         .unwrap();
+
     find_prolog_files(&mut libraries, "", &lib_path);
     libraries.write_all(b"\n        m\n    };\n}\n").unwrap();
+
+    let static_atoms_path = Path::new("src/static_atoms.rs");
+    let mut static_atoms_file = File::create(&static_atoms_path).unwrap();
+
+    let quoted_output = index_static_strings();
+
+    static_atoms_file
+        .write_all(quoted_output.to_string().as_bytes())
+        .unwrap();
+
+    Command::new("rustfmt")
+        .arg(static_atoms_path.as_os_str())
+        .spawn().unwrap()
+        .wait().unwrap();
 }
diff --git a/crates/prolog_parser/Cargo.lock b/crates/prolog_parser/Cargo.lock
deleted file mode 100644 (file)
index 57d6288..0000000
+++ /dev/null
@@ -1,265 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-[[package]]
-name = "arrayvec"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
-dependencies = [
- "nodrop",
-]
-
-[[package]]
-name = "autocfg"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
-
-[[package]]
-name = "az"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d84e1d907bfc5795a6addb95ef8666141ee73c8f2f5250ff2a46bf4e4f4aec8a"
-
-[[package]]
-name = "cfg-if"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-
-[[package]]
-name = "gmp-mpfr-sys"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a57fdb339d49833021b1fded600ed240ae907e33909d5511a61dff884df7f16e"
-dependencies = [
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "lexical"
-version = "2.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e0d09e60c187a6d0a3fa418aec8587c6a4ae9de872f6126f2134f319b5ed10d"
-dependencies = [
- "cfg-if",
- "lexical-core",
- "rustc_version",
-]
-
-[[package]]
-name = "lexical-core"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2304bccb228c4b020f3a4835d247df0a02a7c4686098d4167762cfbbe4c5cb14"
-dependencies = [
- "arrayvec",
- "cfg-if",
- "rustc_version",
- "ryu",
- "static_assertions",
-]
-
-[[package]]
-name = "libc"
-version = "0.2.85"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3"
-
-[[package]]
-name = "nodrop"
-version = "0.1.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
-
-[[package]]
-name = "num-bigint"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits 0.2.14",
-]
-
-[[package]]
-name = "num-integer"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
-dependencies = [
- "autocfg",
- "num-traits 0.2.14",
-]
-
-[[package]]
-name = "num-rational"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef"
-dependencies = [
- "autocfg",
- "num-bigint",
- "num-integer",
- "num-traits 0.2.14",
-]
-
-[[package]]
-name = "num-rug-adapter"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7470b6acf85abce0771203112db4181d03f7b8a6be49f0e842a78030192f8a58"
-dependencies = [
- "libc",
- "num-bigint",
- "num-integer",
- "num-rational",
- "num-traits 0.2.14",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.1.43"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
-dependencies = [
- "num-traits 0.2.14",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "ordered-float"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7eb5259643245d3f292c7a146b2df53bba24d7eab159410e648eb73dc164669d"
-dependencies = [
- "num-traits 0.1.43",
- "unreachable",
-]
-
-[[package]]
-name = "prolog_parser"
-version = "0.8.68"
-dependencies = [
- "lexical",
- "num-rug-adapter",
- "ordered-float",
- "rug",
- "unicode_reader",
-]
-
-[[package]]
-name = "rug"
-version = "1.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e538d00da450a8e48aac7e6322e67b2dc86ec71a1feeac0e3954c4f07f01bc45"
-dependencies = [
- "az",
- "gmp-mpfr-sys",
- "libc",
-]
-
-[[package]]
-name = "rustc_version"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
-dependencies = [
- "semver",
-]
-
-[[package]]
-name = "ryu"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
-
-[[package]]
-name = "semver"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
-dependencies = [
- "semver-parser",
-]
-
-[[package]]
-name = "semver-parser"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
-
-[[package]]
-name = "smallvec"
-version = "1.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
-
-[[package]]
-name = "static_assertions"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
-
-[[package]]
-name = "unicode-segmentation"
-version = "1.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
-
-[[package]]
-name = "unicode_reader"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b639121690b27acd92c97ed2b52c5e5e8d3d39482e943b4559695cef62f771a"
-dependencies = [
- "smallvec",
- "unicode-segmentation",
-]
-
-[[package]]
-name = "unreachable"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
-dependencies = [
- "void",
-]
-
-[[package]]
-name = "void"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/crates/prolog_parser/src/ast.rs b/crates/prolog_parser/src/ast.rs
deleted file mode 100644 (file)
index ac794e6..0000000
+++ /dev/null
@@ -1,782 +0,0 @@
-use crate::rug::{Integer, Rational};
-use crate::tabled_rc::*;
-use ordered_float::*;
-
-use crate::put_back_n::*;
-
-use std::cell::Cell;
-use std::cmp::Ordering;
-use std::fmt;
-use std::hash::{Hash, Hasher};
-use std::io::{Bytes, Error as IOError, Read};
-use std::ops::Deref;
-use std::rc::Rc;
-use std::vec::Vec;
-
-use indexmap::IndexMap;
-use unicode_reader::CodePoints;
-
-pub type Atom = String;
-
-pub type Var = String;
-
-pub type Specifier = u32;
-
-pub const MAX_ARITY: usize = 1023;
-
-pub const XFX: u32 = 0x0001;
-pub const XFY: u32 = 0x0002;
-pub const YFX: u32 = 0x0004;
-pub const XF: u32 = 0x0010;
-pub const YF: u32 = 0x0020;
-pub const FX: u32 = 0x0040;
-pub const FY: u32 = 0x0080;
-pub const DELIMITER: u32 = 0x0100;
-pub const TERM: u32 = 0x1000;
-pub const LTERM: u32 = 0x3000;
-
-pub const NEGATIVE_SIGN: u32 = 0x0200;
-
-#[macro_export]
-macro_rules! clause_name {
-    ($name: expr, $tbl: expr) => {
-        $crate::ast::ClauseName::User($crate::tabled_rc::TabledRc::new($name, $tbl.clone()))
-    };
-    ($name: expr) => {
-        $crate::ast::ClauseName::BuiltIn($name)
-    };
-}
-
-#[macro_export]
-macro_rules! atom {
-    ($e:expr, $tbl:expr) => {
-        $crate::ast::Constant::Atom(
-            $crate::ast::ClauseName::User($crate::tabled_rc!($e, $tbl)),
-            None,
-        )
-    };
-    ($e:expr) => {
-        $crate::ast::Constant::Atom($crate::clause_name!($e), None)
-    };
-}
-
-#[macro_export]
-macro_rules! rc_atom {
-    ($e:expr) => {
-        Rc::new(String::from($e))
-    };
-}
-macro_rules! is_term {
-    ($x:expr) => {
-        ($x & $crate::ast::TERM) != 0
-    };
-}
-
-macro_rules! is_lterm {
-    ($x:expr) => {
-        ($x & $crate::ast::LTERM) != 0
-    };
-}
-
-macro_rules! is_op {
-    ($x:expr) => {
-        $x & ($crate::ast::XF
-            | $crate::ast::YF
-            | $crate::ast::FX
-            | $crate::ast::FY
-            | $crate::ast::XFX
-            | $crate::ast::XFY
-            | $crate::ast::YFX)
-            != 0
-    };
-}
-
-macro_rules! is_negate {
-    ($x:expr) => {
-        ($x & $crate::ast::NEGATIVE_SIGN) != 0
-    };
-}
-
-#[macro_export]
-macro_rules! is_prefix {
-    ($x:expr) => {
-        $x & ($crate::ast::FX | $crate::ast::FY) != 0
-    };
-}
-
-#[macro_export]
-macro_rules! is_postfix {
-    ($x:expr) => {
-        $x & ($crate::ast::XF | $crate::ast::YF) != 0
-    };
-}
-
-#[macro_export]
-macro_rules! is_infix {
-    ($x:expr) => {
-        ($x & ($crate::ast::XFX | $crate::ast::XFY | $crate::ast::YFX)) != 0
-    };
-}
-
-#[macro_export]
-macro_rules! is_xfx {
-    ($x:expr) => {
-        ($x & $crate::ast::XFX) != 0
-    };
-}
-
-#[macro_export]
-macro_rules! is_xfy {
-    ($x:expr) => {
-        ($x & $crate::ast::XFY) != 0
-    };
-}
-
-#[macro_export]
-macro_rules! is_yfx {
-    ($x:expr) => {
-        ($x & $crate::ast::YFX) != 0
-    };
-}
-
-#[macro_export]
-macro_rules! is_yf {
-    ($x:expr) => {
-        ($x & $crate::ast::YF) != 0
-    };
-}
-
-#[macro_export]
-macro_rules! is_xf {
-    ($x:expr) => {
-        ($x & $crate::ast::XF) != 0
-    };
-}
-
-#[macro_export]
-macro_rules! is_fx {
-    ($x:expr) => {
-        ($x & $crate::ast::FX) != 0
-    };
-}
-
-#[macro_export]
-macro_rules! is_fy {
-    ($x:expr) => {
-        ($x & $crate::ast::FY) != 0
-    };
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum RegType {
-    Perm(usize),
-    Temp(usize),
-}
-
-impl Default for RegType {
-    fn default() -> Self {
-        RegType::Temp(0)
-    }
-}
-
-impl RegType {
-    pub fn reg_num(self) -> usize {
-        match self {
-            RegType::Perm(reg_num) | RegType::Temp(reg_num) => reg_num,
-        }
-    }
-
-    pub fn is_perm(self) -> bool {
-        matches!(self, RegType::Perm(_))
-    }
-}
-
-impl fmt::Display for RegType {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            RegType::Perm(val) => write!(f, "Y{}", val),
-            RegType::Temp(val) => write!(f, "X{}", val),
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum VarReg {
-    ArgAndNorm(RegType, usize),
-    Norm(RegType),
-}
-
-impl VarReg {
-    pub fn norm(self) -> RegType {
-        match self {
-            VarReg::ArgAndNorm(reg, _) | VarReg::Norm(reg) => reg,
-        }
-    }
-}
-
-impl fmt::Display for VarReg {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            VarReg::Norm(RegType::Perm(reg)) => write!(f, "Y{}", reg),
-            VarReg::Norm(RegType::Temp(reg)) => write!(f, "X{}", reg),
-            VarReg::ArgAndNorm(RegType::Perm(reg), arg) => write!(f, "Y{} A{}", reg, arg),
-            VarReg::ArgAndNorm(RegType::Temp(reg), arg) => write!(f, "X{} A{}", reg, arg),
-        }
-    }
-}
-
-impl Default for VarReg {
-    fn default() -> Self {
-        VarReg::Norm(RegType::default())
-    }
-}
-
-#[macro_export]
-macro_rules! temp_v {
-    ($x:expr) => {
-        $crate::ast::RegType::Temp($x)
-    };
-}
-
-#[macro_export]
-macro_rules! perm_v {
-    ($x:expr) => {
-        $crate::ast::RegType::Perm($x)
-    };
-}
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub enum GenContext {
-    Head,
-    Mid(usize),
-    Last(usize), // Mid & Last: chunk_num
-}
-
-impl GenContext {
-    pub fn chunk_num(self) -> usize {
-        match self {
-            GenContext::Head => 0,
-            GenContext::Mid(cn) | GenContext::Last(cn) => cn,
-        }
-    }
-}
-
-pub type OpDirKey = (ClauseName, Fixity);
-
-#[derive(Debug, Clone)]
-pub struct OpDirValue(pub SharedOpDesc);
-
-impl OpDirValue {
-    pub fn new(spec: Specifier, priority: usize) -> Self {
-        OpDirValue(SharedOpDesc::new(priority, spec))
-    }
-
-    #[inline]
-    pub fn shared_op_desc(&self) -> SharedOpDesc {
-        self.0.clone()
-    }
-}
-
-// name and fixity -> operator type and precedence.
-pub type OpDir = IndexMap<OpDirKey, OpDirValue>;
-
-#[derive(Debug, Clone, Copy)]
-pub struct MachineFlags {
-    pub double_quotes: DoubleQuotes,
-}
-
-impl Default for MachineFlags {
-    fn default() -> Self {
-        MachineFlags {
-            double_quotes: DoubleQuotes::default(),
-        }
-    }
-}
-
-#[derive(Debug, Clone, Copy)]
-pub enum DoubleQuotes {
-    Atom,
-    Chars,
-    Codes,
-}
-
-impl DoubleQuotes {
-    pub fn is_chars(self) -> bool {
-        matches!(self, DoubleQuotes::Chars)
-    }
-
-    pub fn is_atom(self) -> bool {
-        matches!(self, DoubleQuotes::Atom)
-    }
-
-    pub fn is_codes(self) -> bool {
-        matches!(self, DoubleQuotes::Codes)
-    }
-}
-
-impl Default for DoubleQuotes {
-    fn default() -> Self {
-        DoubleQuotes::Chars
-    }
-}
-
-pub fn default_op_dir() -> OpDir {
-    let mut op_dir = OpDir::new();
-
-    op_dir.insert((clause_name!(":-"), Fixity::In), OpDirValue::new(XFX, 1200));
-    op_dir.insert((clause_name!(":-"), Fixity::Pre), OpDirValue::new(FX, 1200));
-    op_dir.insert((clause_name!("?-"), Fixity::Pre), OpDirValue::new(FX, 1200));
-    op_dir.insert((clause_name!(","), Fixity::In), OpDirValue::new(XFY, 1000));
-
-    op_dir
-}
-
-#[derive(Debug, Clone)]
-pub enum ArithmeticError {
-    NonEvaluableFunctor(Constant, usize),
-    UninstantiatedVar,
-}
-
-#[derive(Debug)]
-pub enum ParserError {
-    BackQuotedString(usize, usize),
-    UnexpectedChar(char, usize, usize),
-    UnexpectedEOF,
-    IO(IOError),
-    IncompleteReduction(usize, usize),
-    InvalidSingleQuotedCharacter(char),
-    MissingQuote(usize, usize),
-    NonPrologChar(usize, usize),
-    ParseBigInt(usize, usize),
-    Utf8Error(usize, usize),
-}
-
-impl ParserError {
-    pub fn line_and_col_num(&self) -> Option<(usize, usize)> {
-        match self {
-            &ParserError::BackQuotedString(line_num, col_num)
-            | &ParserError::UnexpectedChar(_, line_num, col_num)
-            | &ParserError::IncompleteReduction(line_num, col_num)
-            | &ParserError::MissingQuote(line_num, col_num)
-            | &ParserError::NonPrologChar(line_num, col_num)
-            | &ParserError::ParseBigInt(line_num, col_num)
-            | &ParserError::Utf8Error(line_num, col_num) => Some((line_num, col_num)),
-            _ => None,
-        }
-    }
-
-    pub fn as_str(&self) -> &'static str {
-        match self {
-            ParserError::BackQuotedString(..) => "back_quoted_string",
-            ParserError::UnexpectedChar(..) => "unexpected_char",
-            ParserError::UnexpectedEOF => "unexpected_end_of_file",
-            ParserError::IncompleteReduction(..) => "incomplete_reduction",
-            ParserError::InvalidSingleQuotedCharacter(..) => "invalid_single_quoted_character",
-            ParserError::IO(_) => "input_output_error",
-            ParserError::MissingQuote(..) => "missing_quote",
-            ParserError::NonPrologChar(..) => "non_prolog_character",
-            ParserError::ParseBigInt(..) => "cannot_parse_big_int",
-            ParserError::Utf8Error(..) => "utf8_conversion_error",
-        }
-    }
-}
-
-impl From<IOError> for ParserError {
-    fn from(err: IOError) -> ParserError {
-        ParserError::IO(err)
-    }
-}
-
-impl From<&IOError> for ParserError {
-    fn from(error: &IOError) -> ParserError {
-        if error.get_ref().filter(|e| e.is::<BadUtf8Error>()).is_some() {
-            ParserError::Utf8Error(0, 0)
-        } else {
-            ParserError::IO(error.kind().into())
-        }
-    }
-}
-
-#[derive(Debug, Clone, Copy)]
-pub struct CompositeOpDir<'a, 'b> {
-    pub primary_op_dir: Option<&'b OpDir>,
-    pub secondary_op_dir: &'a OpDir,
-}
-
-impl<'a, 'b> CompositeOpDir<'a, 'b> {
-    #[inline]
-    pub fn new(secondary_op_dir: &'a OpDir, primary_op_dir: Option<&'b OpDir>) -> Self {
-        CompositeOpDir {
-            primary_op_dir,
-            secondary_op_dir,
-        }
-    }
-
-    #[inline]
-    pub(crate) fn get(&self, name: ClauseName, fixity: Fixity) -> Option<&OpDirValue> {
-        let entry = if let Some(ref primary_op_dir) = &self.primary_op_dir {
-            primary_op_dir.get(&(name.clone(), fixity))
-        } else {
-            None
-        };
-
-        entry.or_else(move || self.secondary_op_dir.get(&(name, fixity)))
-    }
-}
-
-#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
-pub enum Fixity {
-    In,
-    Post,
-    Pre,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub struct SharedOpDesc(Rc<Cell<(usize, Specifier)>>);
-
-impl SharedOpDesc {
-    #[inline]
-    pub fn new(priority: usize, spec: Specifier) -> Self {
-        SharedOpDesc(Rc::new(Cell::new((priority, spec))))
-    }
-
-    #[inline]
-    pub fn ptr_eq(lop_desc: &SharedOpDesc, rop_desc: &SharedOpDesc) -> bool {
-        Rc::ptr_eq(&lop_desc.0, &rop_desc.0)
-    }
-
-    #[inline]
-    pub fn arity(&self) -> usize {
-        if self.get().1 & (XFX | XFY | YFX) == 0 {
-            1
-        } else {
-            2
-        }
-    }
-
-    #[inline]
-    pub fn get(&self) -> (usize, Specifier) {
-        self.0.get()
-    }
-
-    #[inline]
-    pub fn set(&self, prec: usize, spec: Specifier) {
-        self.0.set((prec, spec));
-    }
-
-    #[inline]
-    pub fn prec(&self) -> usize {
-        self.0.get().0
-    }
-
-    #[inline]
-    pub fn assoc(&self) -> Specifier {
-        self.0.get().1
-    }
-}
-
-impl Deref for SharedOpDesc {
-    type Target = Cell<(usize, Specifier)>;
-
-    #[inline]
-    fn deref(&self) -> &Self::Target {
-        self.0.deref()
-    }
-}
-
-// this ensures that SharedOpDesc (which is not consistently placed in
-// every atom!) doesn't affect the value of an atom hash. If
-// SharedOpDesc values are to be indexed, a BTreeMap or BTreeSet
-// should be used, obviously.
-impl Hash for SharedOpDesc {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        0.hash(state)
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum Constant {
-    Atom(ClauseName, Option<SharedOpDesc>),
-    Char(char),
-    EmptyList,
-    Fixnum(isize),
-    Integer(Rc<Integer>),
-    Rational(Rc<Rational>),
-    Float(OrderedFloat<f64>),
-    String(Rc<String>),
-    Usize(usize),
-}
-
-impl fmt::Display for Constant {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            Constant::Atom(ref atom, _) => {
-                if atom.as_str().chars().any(|c| "`.$'\" ".contains(c)) {
-                    write!(f, "'{}'", atom.as_str())
-                } else {
-                    write!(f, "{}", atom.as_str())
-                }
-            }
-            Constant::Char(c) => write!(f, "'{}'", *c as u32),
-            Constant::EmptyList => write!(f, "[]"),
-            Constant::Fixnum(n) => write!(f, "{}", n),
-            Constant::Integer(ref n) => write!(f, "{}", n),
-            Constant::Rational(ref n) => write!(f, "{}", n),
-            Constant::Float(ref n) => write!(f, "{}", n),
-            Constant::String(ref s) => write!(f, "\"{}\"", &s),
-            Constant::Usize(integer) => write!(f, "u{}", integer),
-        }
-    }
-}
-
-impl Constant {
-    pub fn to_atom(&self) -> Option<ClauseName> {
-        match self {
-            Constant::Atom(a, _) => Some(a.defrock_brackets()),
-            _ => None,
-        }
-    }
-}
-
-#[derive(Debug, Clone)]
-pub enum ClauseName {
-    BuiltIn(&'static str),
-    User(TabledRc<Atom>),
-}
-
-impl fmt::Display for ClauseName {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", self.as_str())
-    }
-}
-
-impl Hash for ClauseName {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        (*self.as_str()).hash(state)
-    }
-}
-
-impl PartialEq for ClauseName {
-    fn eq(&self, other: &ClauseName) -> bool {
-        *self.as_str() == *other.as_str()
-    }
-}
-
-impl Eq for ClauseName {}
-
-impl Ord for ClauseName {
-    fn cmp(&self, other: &ClauseName) -> Ordering {
-        (*self.as_str()).cmp(other.as_str())
-    }
-}
-
-impl PartialOrd for ClauseName {
-    fn partial_cmp(&self, other: &ClauseName) -> Option<Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl<'a> From<&'a TabledRc<Atom>> for ClauseName {
-    fn from(name: &'a TabledRc<Atom>) -> ClauseName {
-        ClauseName::User(name.clone())
-    }
-}
-
-impl ClauseName {
-    #[inline]
-    pub fn owning_module(&self) -> Self {
-        match self {
-            ClauseName::User(ref name) => {
-                let module = name.owning_module();
-                ClauseName::User(TabledRc {
-                    atom: module.clone(),
-                    table: TabledData::new(module),
-                })
-            }
-            _ => clause_name!("user"),
-        }
-    }
-
-    #[inline]
-    pub fn to_rc(&self) -> Rc<String> {
-        match self {
-            ClauseName::BuiltIn(s) => Rc::new(s.to_string()),
-            ClauseName::User(ref rc) => rc.inner(),
-        }
-    }
-
-    #[inline]
-    pub fn with_table(self, atom_tbl: TabledData<Atom>) -> Self {
-        match self {
-            ClauseName::BuiltIn(_) => self,
-            ClauseName::User(mut name) => {
-                name.table = atom_tbl;
-                ClauseName::User(name)
-            }
-        }
-    }
-
-    #[inline]
-    pub fn has_table(&self, atom_tbl: &TabledData<Atom>) -> bool {
-        match self {
-            ClauseName::BuiltIn(_) => false,
-            ClauseName::User(ref name) => &name.table == atom_tbl,
-        }
-    }
-
-    #[inline]
-    pub fn has_table_of(&self, other: &ClauseName) -> bool {
-        match self {
-            ClauseName::BuiltIn(_) => {
-                matches!(other, ClauseName::BuiltIn(_))
-            }
-            ClauseName::User(ref name) => other.has_table(&name.table),
-        }
-    }
-
-    #[inline]
-    pub fn as_str(&self) -> &str {
-        match self {
-            ClauseName::BuiltIn(s) => s,
-            ClauseName::User(ref name) => name.as_ref(),
-        }
-    }
-
-    #[inline]
-    pub fn is_char(&self) -> bool {
-        !self.as_str().is_empty() && self.as_str().chars().nth(1).is_none()
-    }
-
-    pub fn defrock_brackets(&self) -> Self {
-        fn defrock_brackets(s: &str) -> &str {
-            if s.starts_with('(') && s.ends_with(')') {
-                &s[1..s.len() - 1]
-            } else {
-                s
-            }
-        }
-
-        match self {
-            ClauseName::BuiltIn(s) => ClauseName::BuiltIn(defrock_brackets(s)),
-            ClauseName::User(s) => {
-                ClauseName::User(tabled_rc!(defrock_brackets(s.as_str()).to_owned(), s.table))
-            }
-        }
-    }
-}
-
-impl AsRef<str> for ClauseName {
-    #[inline]
-    fn as_ref(&self) -> &str {
-        self.as_str()
-    }
-}
-
-#[derive(Debug, Clone)]
-pub enum Term {
-    AnonVar,
-    Clause(
-        Cell<RegType>,
-        ClauseName,
-        Vec<Box<Term>>,
-        Option<SharedOpDesc>,
-    ),
-    Cons(Cell<RegType>, Box<Term>, Box<Term>),
-    Constant(Cell<RegType>, Constant),
-    Var(Cell<VarReg>, Rc<Var>),
-}
-
-impl Term {
-    pub fn shared_op_desc(&self) -> Option<SharedOpDesc> {
-        match self {
-            Term::Clause(_, _, _, ref spec) => spec.clone(),
-            Term::Constant(_, Constant::Atom(_, ref spec)) => spec.clone(),
-            _ => None,
-        }
-    }
-
-    pub fn into_constant(self) -> Option<Constant> {
-        match self {
-            Term::Constant(_, c) => Some(c),
-            _ => None,
-        }
-    }
-
-    pub fn first_arg(&self) -> Option<&Term> {
-        match self {
-            Term::Clause(_, _, ref terms, _) => terms.first().map(|bt| bt.as_ref()),
-            _ => None,
-        }
-    }
-
-    pub fn set_name(&mut self, new_name: ClauseName) {
-        match self {
-            Term::Constant(_, Constant::Atom(ref mut atom, _))
-            | Term::Clause(_, ref mut atom, ..) => {
-                *atom = new_name;
-            }
-            _ => {}
-        }
-    }
-
-    pub fn name(&self) -> Option<ClauseName> {
-        match self {
-            &Term::Constant(_, Constant::Atom(ref atom, _)) | &Term::Clause(_, ref atom, ..) => {
-                Some(atom.clone())
-            }
-            _ => None,
-        }
-    }
-
-    pub fn arity(&self) -> usize {
-        match self {
-            Term::Clause(_, _, ref child_terms, ..) => child_terms.len(),
-            _ => 0,
-        }
-    }
-}
-
-fn unfold_by_str_once(term: &mut Term, s: &str) -> Option<(Term, Term)> {
-    if let Term::Clause(_, ref name, ref mut subterms, _) = term {
-        if name.as_str() == s && subterms.len() == 2 {
-            let snd = *subterms.pop().unwrap();
-            let fst = *subterms.pop().unwrap();
-
-            return Some((fst, snd));
-        }
-    }
-
-    None
-}
-
-pub fn unfold_by_str(mut term: Term, s: &str) -> Vec<Term> {
-    let mut terms = vec![];
-
-    while let Some((fst, snd)) = unfold_by_str_once(&mut term, s) {
-        terms.push(fst);
-        term = snd;
-    }
-
-    terms.push(term);
-    terms
-}
-
-pub type ParsingStream<R> = PutBackN<CodePoints<Bytes<R>>>;
-
-use unicode_reader::BadUtf8Error;
-
-#[inline]
-pub fn parsing_stream<R: Read>(src: R) -> Result<ParsingStream<R>, ParserError> {
-    let mut stream = put_back_n(CodePoints::from(src.bytes()));
-    match stream.peek() {
-        None => Ok(stream), // empty stream is handled gracefully by Lexer::eof
-        Some(Err(error)) => Err(ParserError::from(error)),
-        Some(Ok(c)) => {
-            if *c == '\u{feff}' {
-                // skip UTF-8 BOM
-                stream.next();
-            }
-            Ok(stream)
-        }
-    }
-}
diff --git a/crates/prolog_parser/src/put_back_n.rs b/crates/prolog_parser/src/put_back_n.rs
deleted file mode 100644 (file)
index 8bef7f3..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-use std::iter::Peekable;
-
-#[derive(Debug, Clone)]
-pub struct PutBackN<I: Iterator> {
-    top: Vec<I::Item>,
-    iter: Peekable<I>,
-}
-
-pub fn put_back_n<I>(iterable: I) -> PutBackN<I::IntoIter>
-    where I: IntoIterator
-{
-    PutBackN {
-        top: Vec::new(),
-        iter: iterable.into_iter().peekable(),
-    }
-}
-
-impl<I: Iterator> PutBackN<I> {
-    #[inline]
-    pub(crate)
-    fn put_back(&mut self, item: I::Item) {
-        self.top.push(item);
-    }
-
-    #[inline]
-    pub fn take_buf(&mut self) -> Vec<I::Item> {
-        std::mem::replace(&mut self.top, vec![])
-    }
-
-    #[inline]
-    pub(crate)
-    fn peek(&mut self) -> Option<&I::Item> {
-        if self.top.is_empty() {
-            /* This is a kludge for Ctrl-D not being
-             * handled properly if self.iter().peek() isn't called
-             * first. */
-            match self.iter.peek() {
-                Some(_) => {
-                    self.iter.next().and_then(move |item| {
-                        self.top.push(item);
-                        self.top.last()
-                    })
-                }
-                None => {
-                    None
-                }
-            }
-        } else {
-            self.top.last()
-        }
-    }
-
-    #[inline]
-    pub(crate)
-    fn put_back_all<DEI: DoubleEndedIterator<Item = I::Item>>(&mut self, iter: DEI) {
-        self.top.extend(iter.rev());
-    }
-}
-
-impl<I: Iterator> Iterator for PutBackN<I> {
-    type Item = I::Item;
-
-    #[inline]
-    fn next(&mut self) -> Option<I::Item> {
-        if self.top.is_empty() {
-            self.iter.next()
-        } else {
-            self.top.pop()
-        }
-    }
-}
diff --git a/crates/prolog_parser/src/tabled_rc.rs b/crates/prolog_parser/src/tabled_rc.rs
deleted file mode 100644 (file)
index 47f87d3..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-use std::cell::{RefCell, RefMut};
-use std::cmp::Ordering;
-use std::collections::HashSet;
-use std::fmt;
-use std::hash::{Hash, Hasher};
-use std::ops::Deref;
-use std::rc::Rc;
-
-pub struct TabledData<T> {
-    table: Rc<RefCell<HashSet<Rc<T>>>>,
-    pub(crate) module_name: Rc<String>,
-}
-
-impl<T: Hash + Eq + fmt::Debug> fmt::Debug for TabledData<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("TabledData")
-            .field("table", &self.table)
-            .field("module_name", &self.table)
-            .finish()
-    }
-}
-
-impl<T> Clone for TabledData<T> {
-    fn clone(&self) -> Self {
-        TabledData {
-            table: self.table.clone(),
-            module_name: self.module_name.clone(),
-        }
-    }
-}
-
-impl<T: PartialEq> PartialEq for TabledData<T> {
-    fn eq(&self, other: &TabledData<T>) -> bool {
-        Rc::ptr_eq(&self.table, &other.table) && self.module_name == other.module_name
-    }
-}
-
-impl<T: Hash + Eq> TabledData<T> {
-    #[inline]
-    pub fn new(module_name: Rc<String>) -> Self {
-        TabledData {
-            table: Rc::new(RefCell::new(HashSet::new())),
-            module_name,
-        }
-    }
-
-    #[inline]
-    pub fn borrow_mut(&self) -> RefMut<HashSet<Rc<T>>> {
-        self.table.borrow_mut()
-    }
-}
-
-pub struct TabledRc<T: Hash + Eq> {
-    pub(crate) atom: Rc<T>,
-    pub table: TabledData<T>,
-}
-
-impl<T: Hash + Eq + fmt::Debug> fmt::Debug for TabledRc<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("TabledRc")
-            .field("atom", &self.atom)
-            .field("table", &self.table)
-            .finish()
-    }
-}
-
-// this Clone instance is manually defined to prevent the compiler
-// from complaining when deriving Clone for StringList.
-impl<T: Hash + Eq> Clone for TabledRc<T> {
-    fn clone(&self) -> Self {
-        TabledRc {
-            atom: self.atom.clone(),
-            table: self.table.clone(),
-        }
-    }
-}
-
-impl<T: Ord + Hash + Eq> PartialOrd for TabledRc<T> {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        Some(self.atom.cmp(&other.atom))
-    }
-}
-
-impl<T: Ord + Hash + Eq> Ord for TabledRc<T> {
-    fn cmp(&self, other: &Self) -> Ordering {
-        self.atom.cmp(&other.atom)
-    }
-}
-
-impl<T: Hash + Eq> PartialEq for TabledRc<T> {
-    fn eq(&self, other: &TabledRc<T>) -> bool {
-        self.atom == other.atom
-    }
-}
-
-impl<T: Hash + Eq> Eq for TabledRc<T> {}
-
-impl<T: Hash + Eq> Hash for TabledRc<T> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.atom.hash(state)
-    }
-}
-
-impl<T: Hash + Eq + ToString> TabledRc<T> {
-    pub fn new(atom: T, table: TabledData<T>) -> Self {
-        let atom = match table.borrow_mut().take(&atom) {
-            Some(atom) => atom,
-            None => Rc::new(atom),
-        };
-
-        table.borrow_mut().insert(atom.clone());
-
-        TabledRc { atom, table }
-    }
-
-    #[inline]
-    pub fn inner(&self) -> Rc<T> {
-        self.atom.clone()
-    }
-
-    #[inline]
-    pub(crate) fn owning_module(&self) -> Rc<String> {
-        self.table.module_name.clone()
-    }
-}
-
-impl<T: Hash + Eq> Drop for TabledRc<T> {
-    fn drop(&mut self) {
-        if Rc::strong_count(&self.atom) == 2 {
-            self.table.borrow_mut().remove(&self.atom);
-        }
-    }
-}
-
-impl<T: Hash + Eq> Deref for TabledRc<T> {
-    type Target = T;
-
-    fn deref(&self) -> &Self::Target {
-        &*self.atom
-    }
-}
-
-impl<T: Hash + Eq + fmt::Display> fmt::Display for TabledRc<T> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", &*self.atom)
-    }
-}
-
-#[macro_export]
-macro_rules! tabled_rc {
-    ($e:expr, $tbl:expr) => {
-        $crate::tabled_rc::TabledRc::new(String::from($e), $tbl.clone())
-    };
-}
diff --git a/crates/prolog_parser/tests/bom.rs b/crates/prolog_parser/tests/bom.rs
deleted file mode 100644 (file)
index 8a92c12..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-use prolog_parser::ast::*;
-use prolog_parser::lexer::{Lexer, Token};
-use prolog_parser::tabled_rc::TabledData;
-
-use std::rc::Rc;
-
-#[test]
-fn valid_token() {
-    let stream = parsing_stream("valid text".as_bytes());
-    assert!(stream.is_ok());
-}
-
-#[test]
-fn empty_stream() {
-    let bytes: &[u8] = &[];
-    assert!(parsing_stream(bytes).is_ok());
-}
-
-#[test]
-fn skip_utf8_bom() {
-    let atom_tbl = TabledData::new(Rc::new("my_module".to_string()));
-    let flags = MachineFlags::default();
-    let bytes: &[u8] = &[0xEF, 0xBB, 0xBF, '4' as u8, '\n' as u8];
-    let mut stream = parsing_stream(bytes).expect("valid stream");
-    let mut lexer = Lexer::new(atom_tbl, flags, &mut stream);
-    match lexer.next_token() {
-        Ok(Token::Constant(Constant::Fixnum(4))) => (),
-        _ => assert!(false),
-    }
-}
-
-#[test]
-fn invalid_utf16_bom() {
-    let bytes: &[u8] = &[0xFF, 0xFE, 'a' as u8, '\n' as u8];
-    let stream = parsing_stream(bytes);
-    match stream {
-        Err(ParserError::Utf8Error(0, 0)) => (),
-        _ => assert!(false),
-    }
-}
diff --git a/crates/prolog_parser/tests/parse_tokens.rs b/crates/prolog_parser/tests/parse_tokens.rs
deleted file mode 100644 (file)
index 69b6328..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-use prolog_parser::ast::*;
-use prolog_parser::lexer::{Lexer, Token};
-use prolog_parser::tabled_rc::TabledData;
-
-use std::rc::Rc;
-
-fn read_all_tokens(text: &str) -> Result<Vec<Token>, ParserError> {
-    let atom_tbl = TabledData::new(Rc::new("my_module".to_string()));
-    let flags = MachineFlags::default();
-    let mut stream = parsing_stream(text.as_bytes())?;
-    let mut lexer = Lexer::new(atom_tbl, flags, &mut stream);
-
-    let mut tokens = Vec::new();
-    while !lexer.eof()? {
-        let token = lexer.next_token()?;
-        tokens.push(token);
-    }
-    Ok(tokens)
-}
-
-#[test]
-fn empty_multiline_comment() -> Result<(), ParserError> {
-    let tokens = read_all_tokens("/**/ 4\n")?;
-    assert_eq!(tokens, [Token::Constant(Constant::Fixnum(4))]);
-    Ok(())
-}
-
-#[test]
-fn any_char_multiline_comment() -> Result<(), ParserError> {
-    let tokens = read_all_tokens("/* █╗╚═══╝ © */ 4\n")?;
-    assert_eq!(tokens, [Token::Constant(Constant::Fixnum(4))]);
-    Ok(())
-}
-
-#[test]
-fn simple_char() -> Result<(), ParserError> {
-    let tokens = read_all_tokens("'a'\n")?;
-    assert_eq!(tokens, [Token::Constant(Constant::Char('a'))]);
-    Ok(())
-}
-
-#[test]
-fn char_with_meta_seq() -> Result<(), ParserError> {
-    let tokens = read_all_tokens(r#"'\\' '\'' '\"' '\`' "#)?; // use literal string so \ are escaped
-    assert_eq!(
-        tokens,
-        [
-            Token::Constant(Constant::Char('\\')),
-            Token::Constant(Constant::Char('\'')),
-            Token::Constant(Constant::Char('"')),
-            Token::Constant(Constant::Char('`'))
-        ]
-    );
-    Ok(())
-}
-
-#[test]
-fn char_with_control_seq() -> Result<(), ParserError> {
-    let tokens = read_all_tokens(r"'\a' '\b' '\r' '\f' '\t' '\n' '\v' ")?;
-    assert_eq!(
-        tokens,
-        [
-            Token::Constant(Constant::Char('\u{07}')),
-            Token::Constant(Constant::Char('\u{08}')),
-            Token::Constant(Constant::Char('\r')),
-            Token::Constant(Constant::Char('\u{0c}')),
-            Token::Constant(Constant::Char('\t')),
-            Token::Constant(Constant::Char('\n')),
-            Token::Constant(Constant::Char('\u{0b}')),
-        ]
-    );
-    Ok(())
-}
-
-#[test]
-fn char_with_octseq() -> Result<(), ParserError> {
-    let tokens = read_all_tokens(r"'\60433\' ")?;
-    assert_eq!(tokens, [Token::Constant(Constant::Char('愛'))]); // Japanese character
-    Ok(())
-}
-
-#[test]
-fn char_with_octseq_0() -> Result<(), ParserError> {
-    let tokens = read_all_tokens(r"'\0\' ")?;
-    assert_eq!(tokens, [Token::Constant(Constant::Char('\u{0000}'))]);
-    Ok(())
-}
-
-#[test]
-fn char_with_hexseq() -> Result<(), ParserError> {
-    let tokens = read_all_tokens(r"'\x2124\' ")?;
-    assert_eq!(tokens, [Token::Constant(Constant::Char('ℤ'))]); // Z math symbol
-    Ok(())
-}
-
-#[test]
-fn char_with_hexseq_invalid() {
-    assert!(read_all_tokens(r"'\x\' ").is_err());
-}
-
-#[test]
-fn empty() -> Result<(), ParserError> {
-    let tokens = read_all_tokens("")?;
-    assert!(tokens.is_empty());
-    Ok(())
-}
-
-#[test]
-fn comment_then_eof() -> Result<(), ParserError> {
-    assert!(read_all_tokens("% only a comment").is_err());
-    Ok(())
-}
diff --git a/crates/static-string-indexing/Cargo.toml b/crates/static-string-indexing/Cargo.toml
new file mode 100644 (file)
index 0000000..a6da96b
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name = "static-string-indexing"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+proc-macro2 = "*"
+syn = { version = "*", features = ['full', 'visit', 'extra-traits'] }
+indexmap = "*"
+walkdir = "2"
+quote = "*"
diff --git a/crates/static-string-indexing/src/lib.rs b/crates/static-string-indexing/src/lib.rs
new file mode 100644 (file)
index 0000000..22b87cc
--- /dev/null
@@ -0,0 +1,161 @@
+use proc_macro2::TokenStream;
+use syn::*;
+use syn::parse::*;
+use syn::visit::*;
+
+use indexmap::IndexSet;
+
+struct StaticStrVisitor {
+    static_strs: IndexSet<String>,
+}
+
+impl StaticStrVisitor {
+    fn new() -> Self {
+        Self { static_strs: IndexSet::new() }
+    }
+}
+
+struct MacroFnArgs {
+    args: Vec<Expr>,
+}
+
+struct ReadHeapCellExprAndArms {
+    expr: Expr,
+    arms: Vec<Arm>,
+}
+
+impl Parse for ReadHeapCellExprAndArms {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let mut arms = vec![];
+        let expr = input.parse()?;
+
+        input.parse::<Token![,]>()?;
+        arms.push(input.parse()?);
+
+        while !input.is_empty() {
+            if let Ok(_) = input.parse::<Token![,]>() {}
+            arms.push(input.parse()?);
+        }
+
+        Ok(ReadHeapCellExprAndArms { expr, arms })
+    }
+}
+
+impl Parse for MacroFnArgs {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let mut args = vec![];
+
+        if !input.is_empty() {
+            args.push(input.parse()?);
+        }
+
+        while !input.is_empty() {
+            if let Ok(_) = input.parse::<Token![,]>() {}
+            args.push(input.parse()?);
+        }
+
+        Ok(MacroFnArgs { args })
+    }
+}
+
+impl<'ast> Visit<'ast> for StaticStrVisitor {
+    fn visit_macro(&mut self, m: &'ast Macro) {
+        let Macro { path, .. } = m;
+
+        if path.is_ident("atom") {
+            if let Some(Lit::Str(string)) = m.parse_body::<Lit>().ok() {
+                self.static_strs.insert(string.value());
+            }
+        } else if path.is_ident("read_heap_cell") {
+            if let Some(m) = m.parse_body::<ReadHeapCellExprAndArms>().ok() {
+                self.visit_expr(&m.expr);
+
+                for e in m.arms {
+                    self.visit_arm(&e);
+                }
+            }
+        } else {
+            if let Some(m) = m.parse_body::<MacroFnArgs>().ok() {
+                for e in m.args {
+                    self.visit_expr(&e);
+                }
+            }
+        }
+    }
+}
+
+pub fn index_static_strings() -> TokenStream {
+    use quote::*;
+
+    use std::ffi::OsStr;
+    use std::fs::File;
+    use std::io::Read;
+
+    use walkdir::WalkDir;
+
+    fn filter_rust_files(e: &walkdir::DirEntry) -> bool {
+        if e.path().is_dir() {
+            return true;
+        }
+
+        e.path().extension().and_then(OsStr::to_str) == Some("rs")
+    }
+
+    let mut visitor = StaticStrVisitor::new();
+
+    for entry in WalkDir::new("src/").into_iter().filter_entry(filter_rust_files) {
+        let entry = entry.unwrap();
+
+        if entry.path().is_dir() {
+            continue;
+        }
+
+        let mut file = match File::open(entry.path()) {
+            Ok(file) => file,
+            Err(_) => continue,
+        };
+
+        let mut src = String::new();
+
+        match file.read_to_string(&mut src) {
+            Ok(_) => {}
+            Err(e) => {
+                panic!("error reading file: {:?}", e);
+            }
+        }
+
+        let syntax = match syn::parse_file(&src) {
+            Ok(s) => s,
+            Err(e) => {
+                panic!("parse error: {} in file {:?}", e, entry.path());
+            }
+        };
+
+        visitor.visit_file(&syntax);
+    }
+
+    let indices = (0 .. visitor.static_strs.len()).map(|i| i << 3);
+    let indices_iter = indices.clone();
+
+    let static_strs_len = visitor.static_strs.len();
+    let static_strs: &Vec<_> = &visitor.static_strs.into_iter().collect();
+
+    quote! {
+        use phf;
+
+        static STRINGS: [&'static str; #static_strs_len] = [
+            #(
+                #static_strs,
+            )*
+        ];
+
+        #[macro_export]
+        macro_rules! atom {
+            #((#static_strs) => { Atom { index: #indices_iter } };)*
+        }
+
+        static STATIC_ATOMS_MAP: phf::Map<&'static str, Atom> = phf::phf_map! {
+            #(#static_strs => { Atom { index: #indices } },)*
+        };
+    }
+}
index cc8de305c660d635856041c40fb569686224e60c..c29adc9283c0b7498da74a749f591be567a44d9a 100644 (file)
@@ -1,10 +1,9 @@
-use prolog_parser::ast::*;
-use prolog_parser::temp_v;
+use crate::parser::ast::*;
+use crate::temp_v;
 
 use crate::fixtures::*;
 use crate::forms::*;
 use crate::machine::machine_indices::*;
-use crate::targets::*;
 
 use std::cell::Cell;
 use std::rc::Rc;
@@ -14,7 +13,7 @@ pub(crate) trait Allocator<'a> {
 
     fn mark_anon_var<Target>(&mut self, _: Level, _: GenContext, _: &mut Vec<Target>)
     where
-        Target: CompilationTarget<'a>;
+        Target: crate::targets::CompilationTarget<'a>;
     fn mark_non_var<Target>(
         &mut self,
         _: Level,
@@ -22,10 +21,10 @@ pub(crate) trait Allocator<'a> {
         _: &'a Cell<RegType>,
         _: &mut Vec<Target>,
     ) where
-        Target: CompilationTarget<'a>;
+        Target: crate::targets::CompilationTarget<'a>;
     fn mark_reserved_var<Target>(
         &mut self,
-        _: Rc<Var>,
+        _: Rc<String>,
         _: Level,
         _: &'a Cell<VarReg>,
         _: GenContext,
@@ -33,21 +32,21 @@ pub(crate) trait Allocator<'a> {
         _: RegType,
         _: bool,
     ) where
-        Target: CompilationTarget<'a>;
+        Target: crate::targets::CompilationTarget<'a>;
     fn mark_var<Target>(
         &mut self,
-        _: Rc<Var>,
+        _: Rc<String>,
         _: Level,
         _: &'a Cell<VarReg>,
         _: GenContext,
         _: &mut Vec<Target>,
     ) where
-        Target: CompilationTarget<'a>;
+        Target: crate::targets::CompilationTarget<'a>;
 
     fn reset(&mut self);
     fn reset_contents(&mut self) {}
     fn reset_arg(&mut self, _: usize);
-    fn reset_at_head(&mut self, _: &Vec<Box<Term>>);
+    fn reset_at_head(&mut self, args: &Vec<Term>);
 
     fn advance_arg(&mut self);
 
@@ -83,17 +82,17 @@ pub(crate) trait Allocator<'a> {
         perm_vs
     }
 
-    fn get(&self, var: Rc<Var>) -> RegType {
+    fn get(&self, var: Rc<String>) -> RegType {
         self.bindings()
             .get(&var)
             .map_or(temp_v!(0), |v| v.as_reg_type())
     }
 
-    fn is_unbound(&self, var: Rc<Var>) -> bool {
+    fn is_unbound(&self, var: Rc<String>) -> bool {
         self.get(var).reg_num() == 0
     }
 
-    fn record_register(&mut self, var: Rc<Var>, r: RegType) {
+    fn record_register(&mut self, var: Rc<String>, r: RegType) {
         match self.bindings_mut().get_mut(&var).unwrap() {
             &mut VarData::Temp(_, ref mut s, _) => *s = r.reg_num(),
             &mut VarData::Perm(ref mut s) => *s = r.reg_num(),
diff --git a/src/arena.rs b/src/arena.rs
new file mode 100644 (file)
index 0000000..2ba73d2
--- /dev/null
@@ -0,0 +1,797 @@
+use crate::machine::loader::LiveLoadState;
+use crate::machine::machine_indices::*;
+use crate::machine::streams::*;
+use crate::read::*;
+
+use modular_bitfield::prelude::*;
+use ordered_float::OrderedFloat;
+use rug::{Integer, Rational};
+
+use std::alloc;
+use std::fmt;
+use std::hash::{Hash, Hasher};
+use std::mem;
+use std::net::TcpListener;
+use std::ops::{Deref, DerefMut};
+use std::ptr;
+
+#[macro_export]
+macro_rules! arena_alloc {
+    ($e:expr, $arena:expr) => {{
+        let result = $e;
+        #[allow(unused_unsafe)]
+        unsafe { $arena.alloc(result) }
+    }};
+}
+
+#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq)]
+#[bits = 7]
+pub enum ArenaHeaderTag {
+    F64 = 0b01,
+    Integer = 0b10,
+    Rational = 0b11,
+    OssifiedOpDir = 0b0000100,
+    LiveLoadState = 0b0001000,
+    InactiveLoadState = 0b1011000,
+    InputFileStream = 0b10000,
+    OutputFileStream = 0b10100,
+    NamedTcpStream = 0b011100,
+    NamedTlsStream = 0b100000,
+    // PausedPrologStream = 0b101100,
+    ReadlineStream = 0b110000,
+    StaticStringStream = 0b110100,
+    ByteStream = 0b111000,
+    // StandardInputStream = 0b100,
+    StandardOutputStream = 0b1100,
+    StandardErrorStream = 0b11000,
+    NullStream = 0b111100,
+    TcpListener = 0b1000000,
+    Dropped = 0b1000100,
+}
+
+#[bitfield]
+#[derive(Copy, Clone, Debug)]
+pub struct ArenaHeader {
+    size: B56,
+    m: bool,
+    tag: ArenaHeaderTag,
+}
+
+const_assert!(mem::size_of::<ArenaHeader>() == 8);
+
+impl ArenaHeader {
+    #[inline]
+    pub fn build_with(size: u64, tag: ArenaHeaderTag) -> Self {
+        ArenaHeader::new()
+            .with_size(size)
+            .with_tag(tag)
+            .with_m(false)
+    }
+
+    #[inline]
+    pub fn get_tag(self) -> ArenaHeaderTag {
+        self.tag()
+    }
+}
+
+#[derive(Debug, PartialEq, PartialOrd, Eq, Ord)]
+pub struct TypedArenaPtr<T: ?Sized>(ptr::NonNull<T>);
+
+impl<T: ?Sized + Hash> Hash for TypedArenaPtr<T> {
+    #[inline(always)]
+    fn hash<H: Hasher>(&self, hasher: &mut H) {
+        (&*self as &T).hash(hasher)
+    }
+}
+
+impl<T: ?Sized> Clone for TypedArenaPtr<T> {
+    fn clone(&self) -> Self {
+        TypedArenaPtr(self.0)
+    }
+}
+
+impl<T: ?Sized> Copy for TypedArenaPtr<T> {}
+
+impl<T: ?Sized> Deref for TypedArenaPtr<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe { self.0.as_ref() }
+    }
+}
+
+impl<T: ?Sized> DerefMut for TypedArenaPtr<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        unsafe { self.0.as_mut() }
+    }
+}
+
+impl<T: fmt::Display> fmt::Display for TypedArenaPtr<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", **self)
+    }
+}
+
+impl<T: ?Sized> TypedArenaPtr<T> {
+    #[inline]
+    pub const fn new(data: *mut T) -> Self {
+        unsafe { TypedArenaPtr(ptr::NonNull::new_unchecked(data)) }
+    }
+
+    #[inline]
+    pub fn as_ptr(&self) -> *mut T {
+        self.0.as_ptr()
+    }
+
+    #[inline]
+    pub fn header_ptr(&self) -> *const ArenaHeader {
+        let mut ptr = self.as_ptr() as *const u8 as usize;
+        ptr -= mem::size_of::<*const ArenaHeader>();
+        ptr as *const ArenaHeader
+    }
+
+    #[inline]
+    fn header_ptr_mut(&mut self) -> *mut ArenaHeader {
+        let mut ptr = self.as_ptr() as *const u8 as usize;
+        ptr -= mem::size_of::<*const ArenaHeader>();
+        ptr as *mut ArenaHeader
+    }
+
+    #[inline]
+    pub fn get_mark_bit(&self) -> bool {
+        unsafe { (*self.header_ptr()).m() }
+    }
+
+    #[inline]
+    pub fn set_tag(&mut self, tag: ArenaHeaderTag) {
+        unsafe { (*self.header_ptr_mut()).set_tag(tag); }
+    }
+
+    #[inline]
+    pub fn get_tag(&self) -> ArenaHeaderTag {
+        unsafe { (*self.header_ptr()).get_tag() }
+    }
+
+    #[inline]
+    pub fn mark(&mut self) {
+        unsafe {
+            (*self.header_ptr_mut()).set_m(true);
+        }
+    }
+
+    #[inline]
+    pub fn unmark(&mut self) {
+        unsafe {
+            (*self.header_ptr_mut()).set_m(false);
+        }
+    }
+}
+
+pub trait ArenaAllocated {
+    type PtrToAllocated;
+
+    fn tag() -> ArenaHeaderTag;
+    fn size(&self) -> usize;
+    fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated
+    where
+        Self: Sized;
+}
+
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub struct F64Ptr(pub TypedArenaPtr<OrderedFloat<f64>>);
+
+impl fmt::Display for F64Ptr {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", *self)
+    }
+}
+
+impl Deref for F64Ptr {
+    type Target = TypedArenaPtr<OrderedFloat<f64>>;
+
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl DerefMut for F64Ptr {
+    #[inline]
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
+impl ArenaAllocated for OrderedFloat<f64> {
+    type PtrToAllocated = F64Ptr;
+
+    #[inline]
+    fn tag() -> ArenaHeaderTag {
+        ArenaHeaderTag::F64
+    }
+
+    #[inline]
+    fn size(&self) -> usize {
+        mem::size_of::<Self>()
+    }
+
+    #[inline]
+    fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
+        unsafe {
+            ptr::write(dst, self);
+            F64Ptr(TypedArenaPtr::new(dst as *mut Self))
+        }
+    }
+}
+
+impl ArenaAllocated for Integer {
+    type PtrToAllocated = TypedArenaPtr<Integer>;
+
+    #[inline]
+    fn tag() -> ArenaHeaderTag {
+        ArenaHeaderTag::Integer
+    }
+
+    #[inline]
+    fn size(&self) -> usize {
+        mem::size_of::<Self>()
+    }
+
+    #[inline]
+    fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
+        unsafe {
+            ptr::write(dst, self);
+            TypedArenaPtr::new(dst as *mut Self)
+        }
+    }
+}
+
+impl ArenaAllocated for Rational {
+    type PtrToAllocated = TypedArenaPtr<Rational>;
+
+    #[inline]
+    fn tag() -> ArenaHeaderTag {
+        ArenaHeaderTag::Rational
+    }
+
+    #[inline]
+    fn size(&self) -> usize {
+        mem::size_of::<Self>()
+    }
+
+    #[inline]
+    fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
+        unsafe {
+            ptr::write(dst, self);
+            TypedArenaPtr::new(dst as *mut Self)
+        }
+    }
+}
+
+impl ArenaAllocated for OssifiedOpDir {
+    type PtrToAllocated = TypedArenaPtr<OssifiedOpDir>;
+
+    #[inline]
+    fn tag() -> ArenaHeaderTag {
+        ArenaHeaderTag::OssifiedOpDir
+    }
+
+    #[inline]
+    fn size(&self) -> usize {
+        mem::size_of::<Self>()
+    }
+
+    #[inline]
+    fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
+        unsafe {
+            ptr::write(dst, self);
+            TypedArenaPtr::new(dst as *mut Self)
+        }
+    }
+}
+
+impl ArenaAllocated for LiveLoadState {
+    type PtrToAllocated = TypedArenaPtr<LiveLoadState>;
+
+    #[inline]
+    fn tag() -> ArenaHeaderTag {
+        ArenaHeaderTag::LiveLoadState
+    }
+
+    #[inline]
+    fn size(&self) -> usize {
+        mem::size_of::<Self>()
+    }
+
+    #[inline]
+    fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
+        unsafe {
+            ptr::write(dst, self);
+            TypedArenaPtr::new(dst as *mut Self)
+        }
+    }
+}
+
+impl ArenaAllocated for TcpListener {
+    type PtrToAllocated = TypedArenaPtr<TcpListener>;
+
+    #[inline]
+    fn tag() -> ArenaHeaderTag {
+        ArenaHeaderTag::TcpListener
+    }
+
+    #[inline]
+    fn size(&self) -> usize {
+        mem::size_of::<Self>()
+    }
+
+    #[inline]
+    fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
+        unsafe {
+            ptr::write(dst, self);
+            TypedArenaPtr::new(dst as *mut Self)
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+struct AllocSlab {
+    next: *mut AllocSlab,
+    header: ArenaHeader,
+}
+
+#[derive(Debug)]
+pub struct Arena(*mut AllocSlab);
+
+unsafe impl Send for Arena {}
+unsafe impl Sync for Arena {}
+
+impl Arena {
+    #[inline]
+    pub fn new() -> Self {
+        Arena(ptr::null_mut())
+    }
+
+    pub unsafe fn alloc<T: ArenaAllocated>(&mut self, value: T) -> T::PtrToAllocated {
+        let size = value.size() + mem::size_of::<AllocSlab>();
+
+        let align = mem::align_of::<AllocSlab>();
+        let layout = alloc::Layout::from_size_align_unchecked(size, align);
+
+        let slab = alloc::alloc(layout) as *mut AllocSlab;
+
+        (*slab).next = self.0;
+        (*slab).header = ArenaHeader::build_with(value.size() as u64, T::tag());
+
+        let offset = (*slab).payload_offset();
+        let result = value.copy_to_arena(offset as *mut T);
+
+        self.0 = slab;
+
+        result
+    }
+}
+
+unsafe fn drop_slab_in_place(value: &mut AllocSlab) {
+    match value.header.tag() {
+        ArenaHeaderTag::Integer => {
+            ptr::drop_in_place(value.payload_offset() as *mut Integer);
+        }
+        ArenaHeaderTag::Rational => {
+            ptr::drop_in_place(value.payload_offset() as *mut Rational);
+        }
+        ArenaHeaderTag::InputFileStream => {
+            ptr::drop_in_place(value.payload_offset() as *mut InputFileStream);
+        }
+        ArenaHeaderTag::OutputFileStream => {
+            ptr::drop_in_place(value.payload_offset() as *mut OutputFileStream);
+        }
+        ArenaHeaderTag::NamedTcpStream => {
+            ptr::drop_in_place(value.payload_offset() as *mut NamedTcpStream);
+        }
+        ArenaHeaderTag::NamedTlsStream => {
+            ptr::drop_in_place(value.payload_offset() as *mut NamedTlsStream);
+        }
+        // ArenaHeaderTag::PausedPrologStream => {
+        //     // idea: PausedPrologStream with only the buffer of unread characters.
+        //     // no stream to be wrapped, no nuttin'.
+        //     ptr::drop_in_place(value.payload_offset() as *mut PausedPrologStream);
+        // }
+        ArenaHeaderTag::ReadlineStream => {
+            ptr::drop_in_place(value.payload_offset() as *mut ReadlineStream);
+        }
+        ArenaHeaderTag::StaticStringStream => {
+            ptr::drop_in_place(value.payload_offset() as *mut StaticStringStream);
+        }
+        ArenaHeaderTag::ByteStream => {
+            ptr::drop_in_place(value.payload_offset() as *mut ByteStream);
+        }
+        ArenaHeaderTag::OssifiedOpDir => {
+            ptr::drop_in_place(
+                mem::transmute::<_, *mut OssifiedOpDir>(value.payload_offset())
+            );
+        }
+        ArenaHeaderTag::LiveLoadState | ArenaHeaderTag::InactiveLoadState => {
+            ptr::drop_in_place(
+                mem::transmute::<_, *mut LiveLoadState>(value.payload_offset())
+            );
+        }
+        ArenaHeaderTag::Dropped => {
+        }
+        ArenaHeaderTag::TcpListener => {
+            ptr::drop_in_place(value.payload_offset() as *mut TcpListener);
+        }
+        ArenaHeaderTag::F64 | ArenaHeaderTag::StandardOutputStream |
+        ArenaHeaderTag::StandardErrorStream | ArenaHeaderTag::NullStream => {
+        }
+    }
+}
+
+impl Drop for Arena {
+    fn drop(&mut self) {
+        let mut ptr = self.0;
+
+        while !ptr.is_null() {
+            unsafe {
+                let ptr_r = &*ptr;
+
+                let layout = alloc::Layout::from_size_align_unchecked(
+                    ptr_r.slab_size(),
+                    mem::align_of::<AllocSlab>(),
+                );
+
+                drop_slab_in_place(&mut *ptr);
+
+                let next_ptr = ptr_r.next;
+                alloc::dealloc(ptr as *mut u8, layout);
+                ptr = next_ptr;
+            }
+        }
+
+        self.0 = ptr::null_mut();
+    }
+}
+
+const_assert!(mem::size_of::<AllocSlab>() == 16);
+
+impl AllocSlab {
+    #[inline]
+    fn slab_size(&self) -> usize {
+        self.header.size() as usize + mem::size_of::<AllocSlab>()
+    }
+
+    fn payload_offset(&self) -> *const u8 {
+        let mut ptr = (self as *const AllocSlab) as usize;
+        ptr += mem::size_of::<AllocSlab>();
+        ptr as *const u8
+    }
+}
+
+const_assert!(mem::size_of::<OrderedFloat<f64>>() == 8);
+
+#[cfg(test)]
+mod tests {
+    use crate::machine::mock_wam::*;
+    use crate::machine::partial_string::*;
+
+    use ordered_float::OrderedFloat;
+    use rug::{Integer, Rational};
+
+    #[test]
+    fn float_ptr_cast() {
+        let mut wam = MockWAM::new();
+
+        let f = OrderedFloat(0f64);
+        let mut fp = arena_alloc!(f, &mut wam.machine_st.arena);
+        let cell = HeapCellValue::from(fp);
+
+        assert_eq!(cell.get_tag(), HeapCellValueTag::F64);
+        assert_eq!(fp.get_mark_bit(), false);
+        assert_eq!(**fp, f);
+
+        fp.mark();
+
+        assert_eq!(fp.get_mark_bit(), true);
+
+        read_heap_cell!(cell,
+            (HeapCellValueTag::F64, ptr) => {
+                assert_eq!(**ptr, f)
+            }
+            _ => { unreachable!() }
+        );
+    }
+
+    #[test]
+    fn heap_cell_value_const_cast() {
+        let mut wam = MockWAM::new();
+        let const_value = HeapCellValue::from(ConsPtr::build_with(
+            0x0000_5555_ff00_0431 as *const _,
+            ConsPtrMaskTag::Cons,
+        ));
+
+        match const_value.to_untyped_arena_ptr() {
+            Some(arena_ptr) => {
+                assert_eq!(arena_ptr.into_bytes(), const_value.into_bytes());
+            }
+            None => {
+                assert!(false);
+            }
+        }
+
+        let stream = Stream::from_static_string("test", &mut wam.machine_st.arena);
+        let stream_cell =
+            HeapCellValue::from(ConsPtr::build_with(stream.as_ptr(), ConsPtrMaskTag::Cons));
+
+        match stream_cell.to_untyped_arena_ptr() {
+            Some(arena_ptr) => {
+                assert_eq!(arena_ptr.into_bytes(), stream_cell.into_bytes());
+            }
+            None => {
+                assert!(false);
+            }
+        }
+    }
+
+    #[test]
+    fn heap_put_literal_tests() {
+        let mut wam = MockWAM::new();
+
+        // integer
+
+        let big_int = 2 * Integer::from(1u64 << 63);
+        let big_int_ptr: TypedArenaPtr<Integer> = arena_alloc!(big_int, &mut wam.machine_st.arena);
+
+        assert!(!big_int_ptr.as_ptr().is_null());
+
+        let cell = HeapCellValue::from(Literal::Integer(big_int_ptr));
+        assert_eq!(cell.get_tag(), HeapCellValueTag::Cons);
+
+        let untyped_arena_ptr = match cell.to_untyped_arena_ptr() {
+            Some(ptr) => ptr,
+            None => {
+                assert!(false);
+                unreachable!()
+            }
+        };
+
+        match_untyped_arena_ptr!(untyped_arena_ptr,
+           (ArenaHeaderTag::Integer, n) => {
+               assert_eq!(&*n, &(2 * Integer::from(1u64 << 63)))
+           }
+           _ => unreachable!()
+        );
+
+        read_heap_cell!(cell,
+           (HeapCellValueTag::Cons, cons_ptr) => {
+               match_untyped_arena_ptr!(cons_ptr,
+                  (ArenaHeaderTag::Integer, n) => {
+                      assert_eq!(&*n, &(2 * Integer::from(1u64 << 63)))
+                  }
+                  _ => { unreachable!() }
+               )
+           }
+           _ => { unreachable!() }
+        );
+
+        // rational
+
+        let big_rat = 2 * Rational::from(1u64 << 63);
+        let big_rat_ptr: TypedArenaPtr<Rational> = arena_alloc!(big_rat, &mut wam.machine_st.arena);
+
+        assert!(!big_rat_ptr.as_ptr().is_null());
+
+        let rat_cell = typed_arena_ptr_as_cell!(big_rat_ptr);
+        assert_eq!(cell.get_tag(), HeapCellValueTag::Cons);
+
+        match rat_cell.to_untyped_arena_ptr() {
+            Some(untyped_arena_ptr) => {
+                assert_eq!(
+                    Some(big_rat_ptr.header_ptr()),
+                    Some(untyped_arena_ptr.into()),
+                );
+            }
+            None => {
+                assert!(false); // we fail.
+            }
+        }
+
+        // assert_eq!(wam.machine_st.heap[1usize].get_tag(), HeapCellValueTag::Cons);
+
+        read_heap_cell!(rat_cell,
+          (HeapCellValueTag::Cons, cons_ptr) => {
+              match_untyped_arena_ptr!(cons_ptr,
+                 (ArenaHeaderTag::Rational, n) => {
+                     assert_eq!(&*n, &(2 * Rational::from(1u64 << 63)));
+                 }
+                 _ => unreachable!()
+              )
+          }
+          _ => { unreachable!() }
+        );
+
+        // atom
+
+        let f_atom = atom!("f");
+        let g_atom = atom!("g");
+
+        assert_eq!(f_atom.as_str(), "f");
+        assert_eq!(g_atom.as_str(), "g");
+
+        let f_atom_cell = atom_as_cell!(f_atom);
+        let g_atom_cell = atom_as_cell!(g_atom);
+
+        assert_eq!(f_atom_cell.get_tag(), HeapCellValueTag::Atom);
+
+        match f_atom_cell.to_atom() {
+            Some(atom) => {
+                assert_eq!(f_atom, atom);
+                assert_eq!(atom.as_str(), "f");
+            }
+            None => {
+                assert!(false);
+            }
+        }
+
+        read_heap_cell!(f_atom_cell,
+            (HeapCellValueTag::Atom, (atom, arity)) => {
+                assert_eq!(f_atom, atom);
+                assert_eq!(arity, 0);
+                assert_eq!(atom.as_str(), "f");
+            }
+            _ => { unreachable!() }
+        );
+
+        read_heap_cell!(g_atom_cell,
+            (HeapCellValueTag::Atom, (atom, arity)) => {
+                assert_eq!(g_atom, atom);
+                assert_eq!(arity, 0);
+                assert_eq!(atom.as_str(), "g");
+            }
+            _ => { unreachable!() }
+        );
+
+        // complete string
+
+        let pstr_var_cell = put_partial_string(&mut wam.machine_st.heap, "ronan", &mut wam.machine_st.atom_tbl);
+        let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize];
+
+        assert_eq!(pstr_cell.get_tag(), HeapCellValueTag::PStr);
+
+        match pstr_cell.to_pstr() {
+            Some(pstr) => {
+                assert_eq!(pstr.as_str_from(0), "ronan");
+            }
+            None => {
+                assert!(false);
+            }
+        }
+
+        read_heap_cell!(pstr_cell,
+            (HeapCellValueTag::PStr, pstr_atom) => {
+                let pstr = PartialString::from(pstr_atom);
+                assert_eq!(pstr.as_str_from(0), "ronan");
+            }
+            _ => { unreachable!() }
+        );
+
+        // fixnum
+
+        let fixnum_cell = fixnum_as_cell!(Fixnum::build_with(3));
+
+        assert_eq!(fixnum_cell.get_tag(), HeapCellValueTag::Fixnum);
+
+        match fixnum_cell.to_fixnum() {
+            Some(n) => assert_eq!(n.get_num(), 3),
+            None => assert!(false),
+        }
+
+        read_heap_cell!(fixnum_cell,
+            (HeapCellValueTag::Fixnum, n) => {
+                assert_eq!(n.get_num(), 3);
+            }
+            _ => { unreachable!() }
+        );
+
+        let fixnum_b_cell = fixnum_as_cell!(Fixnum::build_with(1 << 55));
+
+        assert_eq!(fixnum_b_cell.get_tag(), HeapCellValueTag::Fixnum);
+
+        match fixnum_b_cell.to_fixnum() {
+            Some(n) => assert_eq!(n.get_num(), 1 << 55),
+            None => assert!(false),
+        }
+
+        match Fixnum::build_with_checked(1 << 57) {
+            Ok(_) => assert!(false),
+            _ => assert!(true),
+        }
+
+        match Fixnum::build_with_checked(i64::MAX) {
+            Ok(_) => assert!(false),
+            _ => assert!(true),
+        }
+
+        match Fixnum::build_with_checked(i64::MIN) {
+            Ok(_) => assert!(false),
+            _ => assert!(true),
+        }
+
+        match Fixnum::build_with_checked(-1) {
+            Ok(n) => assert_eq!(n.get_num(), -1),
+            _ => assert!(false),
+        }
+
+        match Fixnum::build_with_checked((1 << 56) - 1) {
+            Ok(n) => assert_eq!(n.get_num(), (1 << 56) - 1),
+            _ => assert!(false),
+        }
+
+        match Fixnum::build_with_checked(-(1 << 56)) {
+            Ok(n) => assert_eq!(n.get_num(), -(1 << 56)),
+            _ => assert!(false),
+        }
+
+        match Fixnum::build_with_checked(-(1 << 56) - 1) {
+            Ok(_n) => assert!(false),
+            _ => assert!(true),
+        }
+
+        match Fixnum::build_with_checked(-1) {
+            Ok(n) => assert_eq!(-n, Fixnum::build_with(1)),
+            _ => assert!(false),
+        }
+
+        // float
+
+        let float = OrderedFloat(3.1415926f64);
+        let float_ptr = arena_alloc!(float, &mut wam.machine_st.arena);
+
+        assert!(!float_ptr.as_ptr().is_null());
+
+        let float_cell = typed_arena_ptr_as_cell!(float_ptr);
+        assert_eq!(cell.get_tag(), HeapCellValueTag::Cons);
+
+        match float_cell.to_untyped_arena_ptr() {
+            Some(untyped_arena_ptr) => {
+                assert_eq!(Some(float_ptr.header_ptr()), Some(untyped_arena_ptr.into()),);
+            }
+            None => {
+                assert!(false); // we fail.
+            }
+        }
+
+        // char
+
+        let c = 'c';
+        let char_cell = char_as_cell!(c);
+
+        read_heap_cell!(char_cell,
+            (HeapCellValueTag::Char, c) => {
+                assert_eq!(c, 'c');
+            }
+            _ => { unreachable!() }
+        );
+
+        let c = 'Ћ';
+        let cyrillic_char_cell = char_as_cell!(c);
+
+        read_heap_cell!(cyrillic_char_cell,
+            (HeapCellValueTag::Char, c) => {
+                assert_eq!(c, 'Ћ');
+            }
+            _ => { unreachable!() }
+        );
+
+        // empty list
+
+        let cell = empty_list_as_cell!();
+
+        read_heap_cell!(cell,
+            (HeapCellValueTag::Atom, (el, _arity)) => {
+                assert_eq!(el.flat_index() as usize, empty_list_as_cell!().get_value());
+                assert_eq!(el.as_str(), "[]");
+            }
+            _ => { unreachable!() }
+        );
+    }
+}
index f5a3beeb2f956db86e540cd3438693d675b8b751..d09187f815e6013a5cd4825bc7064c39f4e34d18 100644 (file)
@@ -1,18 +1,19 @@
-use prolog_parser::ast::*;
-use prolog_parser::{atom, clause_name};
-
+use crate::arena::*;
+use crate::atom_table::*;
 use crate::clause_types::*;
 use crate::fixtures::*;
 use crate::forms::*;
 use crate::instructions::*;
 use crate::iterators::*;
+use crate::types::*;
+
+use crate::parser::ast::*;
+use crate::parser::rug::ops::PowAssign;
+use crate::parser::rug::{Assign, Integer, Rational};
 
-use crate::machine::heap::*;
 use crate::machine::machine_errors::*;
 use crate::machine::machine_indices::*;
 
-use crate::rug::ops::PowAssign;
-use crate::rug::{Assign, Integer, Rational};
 use ordered_float::*;
 
 use std::cell::Cell;
@@ -20,7 +21,7 @@ use std::cmp::{max, min, Ordering};
 use std::convert::TryFrom;
 use std::f64;
 use std::num::FpCategory;
-use std::ops::{Add, Div, Mul, Neg, Sub};
+use std::ops::Div;
 use std::rc::Rc;
 use std::vec::Vec;
 
@@ -37,31 +38,30 @@ impl<'a> ArithInstructionIterator<'a> {
             .push(TermIterState::subterm_to_state(lvl, term));
     }
 
-    fn new(term: &'a Term) -> Result<Self, ArithmeticError> {
+    fn from(term: &'a Term) -> Result<Self, ArithmeticError> {
         let state = match term {
-            &Term::AnonVar => return Err(ArithmeticError::UninstantiatedVar),
-            &Term::Clause(ref cell, ref name, ref terms, ref fixity) => {
-                match ClauseType::from(name.clone(), terms.len(), fixity.clone()) {
-                    ct @ ClauseType::Named(..) | ct @ ClauseType::Op(..) => {
-                        Ok(TermIterState::Clause(Level::Shallow, 0, cell, ct, terms))
-                    }
-                    ClauseType::Inlined(InlinedClauseType::IsFloat(_)) => {
-                        let ct = ClauseType::Named(clause_name!("float"), 1, CodeIndex::default());
-                        Ok(TermIterState::Clause(Level::Shallow, 0, cell, ct, terms))
-                    }
-                    _ => Err(ArithmeticError::NonEvaluableFunctor(
-                        Constant::Atom(name.clone(), fixity.clone()),
-                        terms.len(),
-                    )),
-                }?
-            }
-            &Term::Constant(ref cell, ref cons) => {
-                TermIterState::Constant(Level::Shallow, cell, cons)
-            }
-            &Term::Cons(_, _, _) => {
-                return Err(ArithmeticError::NonEvaluableFunctor(atom!("'.'"), 2))
-            }
-            &Term::Var(ref cell, ref var) => TermIterState::Var(Level::Shallow, cell, var.clone()),
+            Term::AnonVar => return Err(ArithmeticError::UninstantiatedVar),
+            Term::Clause(cell, name, terms) => match ClauseType::from(*name, terms.len()) {
+                ct @ ClauseType::Named(..) => {
+                    Ok(TermIterState::Clause(Level::Shallow, 0, cell, ct, terms))
+                }
+                ClauseType::Inlined(InlinedClauseType::IsFloat(_)) => {
+                    let ct = ClauseType::Named(atom!("float"), 1, CodeIndex::default());
+                    Ok(TermIterState::Clause(Level::Shallow, 0, cell, ct, terms))
+                }
+                _ => Err(ArithmeticError::NonEvaluableFunctor(
+                    Literal::Atom(*name),
+                    terms.len(),
+                )),
+            }?,
+            Term::Literal(cell, cons) => TermIterState::Literal(Level::Shallow, cell, cons),
+            Term::Cons(..) | Term::PartialString(..) => {
+                return Err(ArithmeticError::NonEvaluableFunctor(
+                    Literal::Atom(atom!(".")),
+                    2,
+                ))
+            }
+            Term::Var(cell, var) => TermIterState::Var(Level::Shallow, cell, var.clone()),
         };
 
         Ok(ArithInstructionIterator {
@@ -72,9 +72,9 @@ impl<'a> ArithInstructionIterator<'a> {
 
 #[derive(Debug)]
 pub(crate) enum ArithTermRef<'a> {
-    Constant(&'a Constant),
-    Op(ClauseName, usize), // name, arity.
-    Var(&'a Cell<VarReg>, Rc<Var>),
+    Literal(&'a Literal),
+    Op(Atom, usize), // name, arity.
+    Var(&'a Cell<VarReg>, Rc<String>),
 }
 
 impl<'a> Iterator for ArithInstructionIterator<'a> {
@@ -97,14 +97,20 @@ impl<'a> Iterator for ArithInstructionIterator<'a> {
                             ct,
                             subterms,
                         ));
-                        self.push_subterm(lvl, subterms[child_num].as_ref());
+
+                        self.push_subterm(lvl, &subterms[child_num]);
                     }
                 }
-                TermIterState::Constant(_, _, c) => return Some(Ok(ArithTermRef::Constant(c))),
+                TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(c))),
                 TermIterState::Var(_, cell, var) => {
-                    return Some(Ok(ArithTermRef::Var(cell, var.clone())))
+                    return Some(Ok(ArithTermRef::Var(cell, var.clone())));
+                }
+                _ => {
+                    return Some(Err(ArithmeticError::NonEvaluableFunctor(
+                        Literal::Atom(atom!(".")),
+                        2,
+                    )));
                 }
-                _ => return Some(Err(ArithmeticError::NonEvaluableFunctor(atom!("'.'"), 2))),
             };
         }
 
@@ -129,8 +135,29 @@ impl<'a> ArithmeticTermIter<'a> for &'a Term {
     type Iter = ArithInstructionIterator<'a>;
 
     fn iter(self) -> Result<Self::Iter, ArithmeticError> {
-        ArithInstructionIterator::new(self)
+        ArithInstructionIterator::from(self)
+    }
+}
+
+fn push_literal(interm: &mut Vec<ArithmeticTerm>, c: &Literal) -> Result<(), ArithmeticError> {
+    match c {
+        Literal::Fixnum(n) => interm.push(ArithmeticTerm::Number(Number::Fixnum(*n))),
+        Literal::Integer(n) => interm.push(ArithmeticTerm::Number(Number::Integer(*n))),
+        Literal::Float(n) => interm.push(ArithmeticTerm::Number(Number::Float(***n))),
+        Literal::Rational(n) => interm.push(ArithmeticTerm::Number(Number::Rational(*n))),
+        Literal::Atom(name) if name == &atom!("e") => interm.push(ArithmeticTerm::Number(
+            Number::Float(OrderedFloat(f64::consts::E)),
+        )),
+        Literal::Atom(name) if name == &atom!("pi") => interm.push(ArithmeticTerm::Number(
+            Number::Float(OrderedFloat(f64::consts::PI)),
+        )),
+        Literal::Atom(name) if name == &atom!("epsilon") => interm.push(ArithmeticTerm::Number(
+            Number::Float(OrderedFloat(f64::EPSILON)),
+        )),
+        _ => return Err(ArithmeticError::NonEvaluableFunctor(*c, 0)),
     }
+
+    Ok(())
 }
 
 impl<'a> ArithmeticEvaluator<'a> {
@@ -143,68 +170,64 @@ 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)),
-            "sign" => Ok(ArithmeticInstruction::Sign(a1, t)),
-            "\\" => Ok(ArithmeticInstruction::BitwiseComplement(a1, t)),
-            _ => Err(ArithmeticError::NonEvaluableFunctor(
-                Constant::Atom(name, None),
-                1,
-            )),
+        match name {
+            atom!("abs") => Ok(ArithmeticInstruction::Abs(a1, t)),
+            atom!("-") => Ok(ArithmeticInstruction::Neg(a1, t)),
+            atom!("+") => Ok(ArithmeticInstruction::Plus(a1, t)),
+            atom!("cos") => Ok(ArithmeticInstruction::Cos(a1, t)),
+            atom!("sin") => Ok(ArithmeticInstruction::Sin(a1, t)),
+            atom!("tan") => Ok(ArithmeticInstruction::Tan(a1, t)),
+            atom!("log") => Ok(ArithmeticInstruction::Log(a1, t)),
+            atom!("exp") => Ok(ArithmeticInstruction::Exp(a1, t)),
+            atom!("sqrt") => Ok(ArithmeticInstruction::Sqrt(a1, t)),
+            atom!("acos") => Ok(ArithmeticInstruction::ACos(a1, t)),
+            atom!("asin") => Ok(ArithmeticInstruction::ASin(a1, t)),
+            atom!("atan") => Ok(ArithmeticInstruction::ATan(a1, t)),
+            atom!("float") => Ok(ArithmeticInstruction::Float(a1, t)),
+            atom!("truncate") => Ok(ArithmeticInstruction::Truncate(a1, t)),
+            atom!("round") => Ok(ArithmeticInstruction::Round(a1, t)),
+            atom!("ceiling") => Ok(ArithmeticInstruction::Ceiling(a1, t)),
+            atom!("floor") => Ok(ArithmeticInstruction::Floor(a1, t)),
+            atom!("sign") => Ok(ArithmeticInstruction::Sign(a1, t)),
+            atom!("\\") => Ok(ArithmeticInstruction::BitwiseComplement(a1, t)),
+            _ => Err(ArithmeticError::NonEvaluableFunctor(Literal::Atom(name), 1)),
         }
     }
 
     fn get_binary_instr(
-        name: ClauseName,
+        &self,
+        name: Atom,
         a1: ArithmeticTerm,
         a2: ArithmeticTerm,
         t: usize,
     ) -> Result<ArithmeticInstruction, ArithmeticError> {
-        match name.as_str() {
-            "+" => Ok(ArithmeticInstruction::Add(a1, a2, t)),
-            "-" => Ok(ArithmeticInstruction::Sub(a1, a2, t)),
-            "/" => Ok(ArithmeticInstruction::Div(a1, a2, t)),
-            "//" => Ok(ArithmeticInstruction::IDiv(a1, a2, t)),
-            "max" => Ok(ArithmeticInstruction::Max(a1, a2, t)),
-            "min" => Ok(ArithmeticInstruction::Min(a1, a2, t)),
-            "div" => Ok(ArithmeticInstruction::IntFloorDiv(a1, a2, t)),
-            "rdiv" => Ok(ArithmeticInstruction::RDiv(a1, a2, t)),
-            "*" => Ok(ArithmeticInstruction::Mul(a1, a2, t)),
-            "**" => Ok(ArithmeticInstruction::Pow(a1, a2, t)),
-            "^" => Ok(ArithmeticInstruction::IntPow(a1, a2, t)),
-            ">>" => Ok(ArithmeticInstruction::Shr(a1, a2, t)),
-            "<<" => Ok(ArithmeticInstruction::Shl(a1, a2, t)),
-            "/\\" => Ok(ArithmeticInstruction::And(a1, a2, t)),
-            "\\/" => Ok(ArithmeticInstruction::Or(a1, a2, t)),
-            "xor" => Ok(ArithmeticInstruction::Xor(a1, a2, t)),
-            "mod" => Ok(ArithmeticInstruction::Mod(a1, a2, t)),
-            "rem" => Ok(ArithmeticInstruction::Rem(a1, a2, t)),
-            "gcd" => Ok(ArithmeticInstruction::Gcd(a1, a2, t)),
-            "atan2" => Ok(ArithmeticInstruction::ATan2(a1, a2, t)),
-            _ => Err(ArithmeticError::NonEvaluableFunctor(
-                Constant::Atom(name, None),
-                2,
-            )),
+        match name {
+            atom!("+") => Ok(ArithmeticInstruction::Add(a1, a2, t)),
+            atom!("-") => Ok(ArithmeticInstruction::Sub(a1, a2, t)),
+            atom!("/") => Ok(ArithmeticInstruction::Div(a1, a2, t)),
+            atom!("//") => Ok(ArithmeticInstruction::IDiv(a1, a2, t)),
+            atom!("max") => Ok(ArithmeticInstruction::Max(a1, a2, t)),
+            atom!("min") => Ok(ArithmeticInstruction::Min(a1, a2, t)),
+            atom!("div") => Ok(ArithmeticInstruction::IntFloorDiv(a1, a2, t)),
+            atom!("rdiv") => Ok(ArithmeticInstruction::RDiv(a1, a2, t)),
+            atom!("*") => Ok(ArithmeticInstruction::Mul(a1, a2, t)),
+            atom!("**") => Ok(ArithmeticInstruction::Pow(a1, a2, t)),
+            atom!("^") => Ok(ArithmeticInstruction::IntPow(a1, a2, t)),
+            atom!(">>") => Ok(ArithmeticInstruction::Shr(a1, a2, t)),
+            atom!("<<") => Ok(ArithmeticInstruction::Shl(a1, a2, t)),
+            atom!("/\\") => Ok(ArithmeticInstruction::And(a1, a2, t)),
+            atom!("\\/") => Ok(ArithmeticInstruction::Or(a1, a2, t)),
+            atom!("xor") => Ok(ArithmeticInstruction::Xor(a1, a2, t)),
+            atom!("mod") => Ok(ArithmeticInstruction::Mod(a1, a2, t)),
+            atom!("rem") => Ok(ArithmeticInstruction::Rem(a1, a2, t)),
+            atom!("gcd") => Ok(ArithmeticInstruction::Gcd(a1, a2, t)),
+            atom!("atan2") => Ok(ArithmeticInstruction::ATan2(a1, a2, t)),
+            _ => Err(ArithmeticError::NonEvaluableFunctor(Literal::Atom(name), 2)),
         }
     }
 
@@ -219,7 +242,7 @@ impl<'a> ArithmeticEvaluator<'a> {
 
     fn instr_from_clause(
         &mut self,
-        name: ClauseName,
+        name: Atom,
         arity: usize,
     ) -> Result<ArithmeticInstruction, ArithmeticError> {
         match arity {
@@ -233,7 +256,7 @@ impl<'a> ArithmeticEvaluator<'a> {
                     a1.interm_or(0)
                 };
 
-                Self::get_unary_instr(name, a1, ninterm)
+                self.get_unary_instr(name, a1, ninterm)
             }
             2 => {
                 let a2 = self.interm.pop().unwrap();
@@ -257,60 +280,22 @@ impl<'a> ArithmeticEvaluator<'a> {
                     min_interm
                 };
 
-                Self::get_binary_instr(name, a1, a2, ninterm)
+                self.get_binary_instr(name, a1, a2, ninterm)
             }
             _ => Err(ArithmeticError::NonEvaluableFunctor(
-                Constant::Atom(name, None),
+                Literal::Atom(name),
                 arity,
             )),
         }
     }
 
-    fn push_constant(&mut self, c: &Constant) -> Result<(), ArithmeticError> {
-        match c {
-            &Constant::Fixnum(n) => self.interm.push(ArithmeticTerm::Number(Number::Fixnum(n))),
-            &Constant::Integer(ref n) => self
-                .interm
-                .push(ArithmeticTerm::Number(Number::Integer(n.clone()))),
-            &Constant::Float(ref n) => self
-                .interm
-                .push(ArithmeticTerm::Number(Number::Float(n.clone()))),
-            &Constant::Rational(ref n) => self
-                .interm
-                .push(ArithmeticTerm::Number(Number::Rational(n.clone()))),
-            &Constant::Atom(ref name, _) if name.as_str() == "e" => {
-                self.interm
-                    .push(ArithmeticTerm::Number(Number::Float(OrderedFloat(
-                        f64::consts::E,
-                    ))))
-            }
-            &Constant::Atom(ref name, _) if name.as_str() == "pi" => {
-                self.interm
-                    .push(ArithmeticTerm::Number(Number::Float(OrderedFloat(
-                        f64::consts::PI,
-                    ))))
-            }
-            &Constant::Atom(ref name, _) if name.as_str() == "epsilon" => {
-                self.interm
-                    .push(ArithmeticTerm::Number(Number::Float(OrderedFloat(
-                        f64::EPSILON,
-                    ))))
-            }
-            _ => return Err(ArithmeticError::NonEvaluableFunctor(c.clone(), 0)),
-        }
-
-        Ok(())
-    }
-
-    pub(crate) fn eval<Iter>(&mut self, src: Iter) -> Result<ArithCont, ArithmeticError>
-    where
-        Iter: ArithmeticTermIter<'a>,
-    {
+    pub(crate) fn eval(&mut self, src: &'a Term) -> Result<ArithCont, ArithmeticError> {
         let mut code = vec![];
+        let mut iter = src.iter()?;
 
-        for term_ref in src.iter()? {
+        while let Some(term_ref) = iter.next() {
             match term_ref? {
-                ArithTermRef::Constant(c) => self.push_constant(c)?,
+                ArithTermRef::Literal(c) => push_literal(&mut self.interm, c)?,
                 ArithTermRef::Var(cell, name) => {
                     let r = if cell.get().norm().reg_num() == 0 {
                         match self.bindings.get(&name) {
@@ -335,27 +320,31 @@ impl<'a> ArithmeticEvaluator<'a> {
 }
 
 // integer division rounding function -- 9.1.3.1.
-pub(crate) fn rnd_i<'a>(n: &'a Number) -> RefOrOwned<'a, Number> {
+pub(crate) fn rnd_i<'a>(n: &'a Number, arena: &mut Arena) -> Number {
     match n {
-        &Number::Integer(_) => RefOrOwned::Borrowed(n),
-        &Number::Float(OrderedFloat(f)) => RefOrOwned::Owned(Number::from(
-            Integer::from_f64(f.floor()).unwrap_or_else(|| Integer::from(0)),
-        )),
-        &Number::Fixnum(n) => RefOrOwned::Owned(Number::from(n)),
+        &Number::Integer(_) | &Number::Fixnum(_) => *n,
+        &Number::Float(OrderedFloat(f)) => fixnum!(Number, f.round() as i64, arena),
         &Number::Rational(ref r) => {
             let r_ref = r.fract_floor_ref();
             let (mut fract, mut floor) = (Rational::new(), Integer::new());
             (&mut fract, &mut floor).assign(r_ref);
 
-            RefOrOwned::Owned(Number::from(floor))
+            Number::Integer(arena_alloc!(floor, arena))
         }
     }
 }
 
+impl From<Fixnum> for Integer {
+    #[inline]
+    fn from(n: Fixnum) -> Integer {
+        Integer::from(n.get_num())
+    }
+}
+
 // floating point rounding function -- 9.1.4.1.
 pub(crate) fn rnd_f(n: &Number) -> f64 {
     match n {
-        &Number::Fixnum(n) => n as f64,
+        &Number::Fixnum(n) => n.get_num() as f64,
         &Number::Integer(ref n) => n.to_f64(),
         &Number::Float(OrderedFloat(f)) => f,
         &Number::Rational(ref r) => r.to_f64(),
@@ -392,27 +381,27 @@ where
 }
 
 #[inline]
-fn float_fn_to_f(n: isize) -> Result<f64, EvalError> {
+pub(crate) fn float_fn_to_f(n: i64) -> Result<f64, EvalError> {
     classify_float(n as f64, rnd_f)
 }
 
 #[inline]
-fn float_i_to_f(n: &Integer) -> Result<f64, EvalError> {
+pub(crate) fn float_i_to_f(n: &Integer) -> Result<f64, EvalError> {
     classify_float(n.to_f64(), rnd_f)
 }
 
 #[inline]
-fn float_r_to_f(r: &Rational) -> Result<f64, EvalError> {
+pub(crate) fn float_r_to_f(r: &Rational) -> Result<f64, EvalError> {
     classify_float(r.to_f64(), rnd_f)
 }
 
 #[inline]
-fn add_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
+pub(crate) fn add_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
     Ok(OrderedFloat(classify_float(f1 + f2, rnd_f)?))
 }
 
 #[inline]
-fn mul_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
+pub(crate) fn mul_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
     Ok(OrderedFloat(classify_float(f1 * f2, rnd_f)?))
 }
 
@@ -425,161 +414,36 @@ fn div_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
     }
 }
 
-impl Add<Number> for Number {
-    type Output = Result<Number, EvalError>;
-
-    fn add(self, rhs: Number) -> Self::Output {
-        match (self, rhs) {
-            (Number::Fixnum(n1), Number::Fixnum(n2)) => {
-                Ok(if let Some(result) = n1.checked_add(n2) {
-                    Number::Fixnum(result)
-                } else {
-                    Number::from(Integer::from(n1) + Integer::from(n2))
-                })
-            }
-            (Number::Fixnum(n1), Number::Integer(n2))
-            | (Number::Integer(n2), Number::Fixnum(n1)) => {
-                Ok(Number::from(Integer::from(n1) + &*n2))
-            }
-            (Number::Fixnum(n1), Number::Rational(n2))
-            | (Number::Rational(n2), Number::Fixnum(n1)) => {
-                Ok(Number::from(Rational::from(n1) + &*n2))
-            }
-            (Number::Fixnum(n1), Number::Float(OrderedFloat(n2)))
-            | (Number::Float(OrderedFloat(n2)), Number::Fixnum(n1)) => {
-                Ok(Number::Float(add_f(float_fn_to_f(n1)?, n2)?))
-            }
-            (Number::Integer(n1), Number::Integer(n2)) => {
-                Ok(Number::from(Integer::from(&*n1) + &*n2)) // add_i
-            }
-            (Number::Integer(n1), Number::Float(OrderedFloat(n2)))
-            | (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
-                Ok(Number::Float(add_f(float_i_to_f(&n1)?, n2)?))
-            }
-            (Number::Integer(n1), Number::Rational(n2))
-            | (Number::Rational(n2), Number::Integer(n1)) => {
-                Ok(Number::from(Rational::from(&*n1) + &*n2))
-            }
-            (Number::Rational(n1), Number::Float(OrderedFloat(n2)))
-            | (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
-                Ok(Number::Float(add_f(float_r_to_f(&n1)?, n2)?))
-            }
-            (Number::Float(OrderedFloat(f1)), Number::Float(OrderedFloat(f2))) => {
-                Ok(Number::Float(add_f(f1, f2)?))
-            }
-            (Number::Rational(r1), Number::Rational(r2)) => {
-                Ok(Number::from(Rational::from(&*r1) + &*r2))
-            }
-        }
-    }
-}
-
-impl Neg for Number {
-    type Output = Number;
-
-    fn neg(self) -> Self::Output {
-        match self {
-            Number::Fixnum(n) => {
-                if let Some(n) = n.checked_neg() {
-                    Number::Fixnum(n)
-                } else {
-                    Number::from(-Integer::from(n))
-                }
-            }
-            Number::Integer(n) => Number::Integer(Rc::new(-Integer::from(&*n))),
-            Number::Float(OrderedFloat(f)) => Number::Float(OrderedFloat(-f)),
-            Number::Rational(r) => Number::Rational(Rc::new(-Rational::from(&*r))),
-        }
-    }
-}
-
-impl Sub<Number> for Number {
-    type Output = Result<Number, EvalError>;
-
-    fn sub(self, rhs: Number) -> Self::Output {
-        self.add(-rhs)
-    }
-}
-
-impl Mul<Number> for Number {
-    type Output = Result<Number, EvalError>;
-
-    fn mul(self, rhs: Number) -> Self::Output {
-        match (self, rhs) {
-            (Number::Fixnum(n1), Number::Fixnum(n2)) => {
-                Ok(if let Some(result) = n1.checked_mul(n2) {
-                    Number::Fixnum(result)
-                } else {
-                    Number::from(Integer::from(n1) * Integer::from(n2))
-                })
-            }
-            (Number::Fixnum(n1), Number::Integer(n2))
-            | (Number::Integer(n2), Number::Fixnum(n1)) => {
-                Ok(Number::from(Integer::from(n1) * &*n2))
-            }
-            (Number::Fixnum(n1), Number::Rational(n2))
-            | (Number::Rational(n2), Number::Fixnum(n1)) => {
-                Ok(Number::from(Rational::from(n1) * &*n2))
-            }
-            (Number::Fixnum(n1), Number::Float(OrderedFloat(n2)))
-            | (Number::Float(OrderedFloat(n2)), Number::Fixnum(n1)) => {
-                Ok(Number::Float(mul_f(float_fn_to_f(n1)?, n2)?))
-            }
-            (Number::Integer(n1), Number::Integer(n2)) => {
-                Ok(Number::Integer(Rc::new(Integer::from(&*n1) * &*n2))) // mul_i
-            }
-            (Number::Integer(n1), Number::Float(OrderedFloat(n2)))
-            | (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
-                Ok(Number::Float(mul_f(float_i_to_f(&n1)?, n2)?))
-            }
-            (Number::Integer(n1), Number::Rational(n2))
-            | (Number::Rational(n2), Number::Integer(n1)) => {
-                Ok(Number::Rational(Rc::new(Rational::from(&*n1) * &*n2)))
-            }
-            (Number::Rational(n1), Number::Float(OrderedFloat(n2)))
-            | (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
-                Ok(Number::Float(mul_f(float_r_to_f(&n1)?, n2)?))
-            }
-            (Number::Float(OrderedFloat(f1)), Number::Float(OrderedFloat(f2))) => {
-                Ok(Number::Float(mul_f(f1, f2)?))
-            }
-            (Number::Rational(r1), Number::Rational(r2)) => {
-                Ok(Number::Rational(Rc::new(Rational::from(&*r1) * &*r2)))
-            }
-        }
-    }
-}
-
 impl Div<Number> for Number {
     type Output = Result<Number, EvalError>;
 
     fn div(self, rhs: Number) -> Self::Output {
         match (self, rhs) {
             (Number::Fixnum(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
-                float_fn_to_f(n1)?,
-                float_fn_to_f(n2)?,
+                float_fn_to_f(n1.get_num())?,
+                float_fn_to_f(n2.get_num())?,
             )?)),
             (Number::Fixnum(n1), Number::Integer(n2)) => Ok(Number::Float(div_f(
-                float_fn_to_f(n1)?,
+                float_fn_to_f(n1.get_num())?,
                 float_i_to_f(&n2)?,
             )?)),
             (Number::Integer(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
                 float_i_to_f(&n1)?,
-                float_fn_to_f(n2)?,
+                float_fn_to_f(n2.get_num())?,
             )?)),
             (Number::Fixnum(n1), Number::Rational(n2)) => Ok(Number::Float(div_f(
-                float_fn_to_f(n1)?,
+                float_fn_to_f(n1.get_num())?,
                 float_r_to_f(&n2)?,
             )?)),
             (Number::Rational(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
                 float_r_to_f(&n1)?,
-                float_fn_to_f(n2)?,
+                float_fn_to_f(n2.get_num())?,
             )?)),
             (Number::Fixnum(n1), Number::Float(OrderedFloat(n2))) => {
-                Ok(Number::Float(div_f(float_fn_to_f(n1)?, n2)?))
+                Ok(Number::Float(div_f(float_fn_to_f(n1.get_num())?, n2)?))
             }
             (Number::Float(OrderedFloat(n1)), Number::Fixnum(n2)) => {
-                Ok(Number::Float(div_f(n1, float_fn_to_f(n2)?)?))
+                Ok(Number::Float(div_f(n1, float_fn_to_f(n2.get_num())?)?))
             }
             (Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Float(div_f(
                 float_i_to_f(&n1)?,
@@ -620,14 +484,14 @@ impl PartialEq for Number {
     fn eq(&self, rhs: &Self) -> bool {
         match (self, rhs) {
             (&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.eq(&n2),
-            (&Number::Fixnum(n1), &Number::Integer(ref n2)) => n1.eq(&**n2),
-            (&Number::Integer(ref n1), &Number::Fixnum(n2)) => (&**n1).eq(&n2),
-            (&Number::Fixnum(n1), &Number::Rational(ref n2)) => n1.eq(&**n2),
-            (&Number::Rational(ref n1), &Number::Fixnum(n2)) => (&**n1).eq(&n2),
-            (&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1 as f64).eq(&n2),
-            (&Number::Float(n1), &Number::Fixnum(n2)) => n1.eq(&OrderedFloat(n2 as f64)),
+            (&Number::Fixnum(n1), &Number::Integer(ref n2)) => n1.get_num().eq(&**n2),
+            (&Number::Integer(ref n1), &Number::Fixnum(n2)) => (&**n1).eq(&n2.get_num()),
+            (&Number::Fixnum(n1), &Number::Rational(ref n2)) => n1.get_num().eq(&**n2),
+            (&Number::Rational(ref n1), &Number::Fixnum(n2)) => (&**n1).eq(&n2.get_num()),
+            (&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).eq(&n2),
+            (&Number::Float(n1), &Number::Fixnum(n2)) => n1.eq(&OrderedFloat(n2.get_num() as f64)),
             (&Number::Integer(ref n1), &Number::Integer(ref n2)) => n1.eq(n2),
-            (&Number::Integer(ref n1), Number::Float(n2)) => OrderedFloat(n1.to_f64()).eq(&n2),
+            (&Number::Integer(ref n1), Number::Float(n2)) => OrderedFloat(n1.to_f64()).eq(n2),
             (&Number::Float(n1), &Number::Integer(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64())),
             (&Number::Integer(ref n1), &Number::Rational(ref n2)) => {
                 #[cfg(feature = "num")]
@@ -659,6 +523,46 @@ impl PartialEq for Number {
 
 impl Eq for Number {}
 
+impl PartialOrd<usize> for Number {
+    #[inline]
+    fn partial_cmp(&self, rhs: &usize) -> Option<Ordering> {
+        match self {
+            Number::Fixnum(n) => {
+                let n = n.get_num();
+
+                if n < 0i64 {
+                    Some(Ordering::Less)
+                } else {
+                    (n as usize).partial_cmp(rhs)
+                }
+            }
+            Number::Integer(n) => (&**n).partial_cmp(rhs),
+            Number::Rational(r) => (&**r).partial_cmp(rhs),
+            Number::Float(f) => f.partial_cmp(&OrderedFloat(*rhs as f64)),
+        }
+    }
+}
+
+impl PartialEq<usize> for Number {
+    #[inline]
+    fn eq(&self, rhs: &usize) -> bool {
+        match self {
+            Number::Fixnum(n) => {
+                let n = n.get_num();
+
+                if n < 0i64 {
+                    false
+                } else {
+                    (n as usize).eq(rhs)
+                }
+            }
+            Number::Integer(n) => (&**n).eq(rhs),
+            Number::Rational(r) => (&**r).eq(rhs),
+            Number::Float(f) => f.eq(&OrderedFloat(*rhs as f64)),
+        }
+    }
+}
+
 impl PartialOrd for Number {
     fn partial_cmp(&self, rhs: &Number) -> Option<Ordering> {
         Some(self.cmp(rhs))
@@ -668,15 +572,17 @@ impl PartialOrd for Number {
 impl Ord for Number {
     fn cmp(&self, rhs: &Number) -> Ordering {
         match (self, rhs) {
-            (&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.cmp(&n2),
-            (&Number::Fixnum(n1), Number::Integer(n2)) => Integer::from(n1).cmp(&*n2),
-            (Number::Integer(n1), &Number::Fixnum(n2)) => (&**n1).cmp(&Integer::from(n2)),
-            (&Number::Fixnum(n1), Number::Rational(n2)) => Rational::from(n1).cmp(&*n2),
-            (Number::Rational(n1), &Number::Fixnum(n2)) => (&**n1).cmp(&Rational::from(n2)),
-            (&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1 as f64).cmp(&n2),
-            (&Number::Float(n1), &Number::Fixnum(n2)) => n1.cmp(&OrderedFloat(n2 as f64)),
+            (&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.get_num().cmp(&n2.get_num()),
+            (&Number::Fixnum(n1), Number::Integer(n2)) => Integer::from(n1.get_num()).cmp(&*n2),
+            (Number::Integer(n1), &Number::Fixnum(n2)) => (&**n1).cmp(&Integer::from(n2.get_num())),
+            (&Number::Fixnum(n1), Number::Rational(n2)) => Rational::from(n1.get_num()).cmp(&*n2),
+            (Number::Rational(n1), &Number::Fixnum(n2)) => {
+                (&**n1).cmp(&Rational::from(n2.get_num()))
+            }
+            (&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).cmp(&n2),
+            (&Number::Float(n1), &Number::Fixnum(n2)) => n1.cmp(&OrderedFloat(n2.get_num() as f64)),
             (&Number::Integer(ref n1), &Number::Integer(ref n2)) => n1.cmp(n2),
-            (&Number::Integer(ref n1), Number::Float(n2)) => OrderedFloat(n1.to_f64()).cmp(&n2),
+            (&Number::Integer(ref n1), Number::Float(n2)) => OrderedFloat(n1.to_f64()).cmp(n2),
             (&Number::Float(n1), &Number::Integer(ref n2)) => n1.cmp(&OrderedFloat(n2.to_f64())),
             (&Number::Integer(ref n1), &Number::Rational(ref n2)) => {
                 #[cfg(feature = "num")]
@@ -706,54 +612,38 @@ impl Ord for Number {
     }
 }
 
-impl<'a> TryFrom<(Addr, &'a Heap)> for Number {
+impl TryFrom<HeapCellValue> for Number {
     type Error = ();
 
-    fn try_from((addr, heap): (Addr, &'a Heap)) -> Result<Number, Self::Error> {
-        match addr {
-            Addr::Fixnum(n) => Ok(Number::from(n)),
-            Addr::Float(n) => Ok(Number::Float(n)),
-            Addr::Usize(n) => {
-                if let Ok(n) = isize::try_from(n) {
-                    Ok(Number::from(n))
-                } else {
-                    Ok(Number::from(Integer::from(n)))
-                }
-            }
-            Addr::Con(h) => Number::try_from(&heap[h]),
-            _ => Err(()),
-        }
-    }
-}
-
-impl<'a> TryFrom<&'a HeapCellValue> for Number {
-    type Error = ();
-
-    fn try_from(value: &'a HeapCellValue) -> Result<Number, Self::Error> {
-        match value {
-            HeapCellValue::Addr(addr) => match addr {
-                &Addr::Fixnum(n) => Ok(Number::from(n)),
-                &Addr::Float(n) => Ok(Number::Float(n)),
-                &Addr::Usize(n) => {
-                    if let Ok(n) = isize::try_from(n) {
-                        Ok(Number::from(n))
-                    } else {
-                        Ok(Number::from(Integer::from(n)))
-                    }
-                }
-                _ => Err(()),
-            },
-            HeapCellValue::Integer(n) => Ok(Number::Integer(n.clone())),
-            HeapCellValue::Rational(n) => Ok(Number::Rational(n.clone())),
-            _ => Err(()),
-        }
-    }
-}
-
-impl<'a> From<&'a Integer> for Number {
     #[inline]
-    fn from(src: &'a Integer) -> Self {
-        Number::Integer(Rc::new(Integer::from(src)))
+    fn try_from(value: HeapCellValue) -> Result<Number, Self::Error> {
+        read_heap_cell!(value,
+           (HeapCellValueTag::Cons, c) => {
+               match_untyped_arena_ptr!(c,
+                  (ArenaHeaderTag::F64, n) => {
+                      Ok(Number::Float(*n))
+                  }
+                  (ArenaHeaderTag::Integer, n) => {
+                      Ok(Number::Integer(n))
+                  }
+                  (ArenaHeaderTag::Rational, n) => {
+                      Ok(Number::Rational(n))
+                  }
+                  _ => {
+                      Err(())
+                  }
+               )
+           }
+           (HeapCellValueTag::F64, n) => {
+               Ok(Number::Float(**n))
+           }
+           (HeapCellValueTag::Fixnum, n) => {
+               Ok(Number::Fixnum(n))
+           }
+           _ => {
+               Err(())
+           }
+        )
     }
 }
 
diff --git a/src/atom_table.rs b/src/atom_table.rs
new file mode 100644 (file)
index 0000000..71771cc
--- /dev/null
@@ -0,0 +1,366 @@
+use crate::parser::ast::MAX_ARITY;
+use crate::raw_block::*;
+use crate::types::*;
+
+use std::borrow::Borrow;
+use std::cmp::Ordering;
+use std::hash::{Hash, Hasher};
+use std::mem;
+use std::ptr;
+use std::slice;
+use std::str;
+
+use indexmap::IndexSet;
+
+use modular_bitfield::prelude::*;
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct Atom {
+    pub index: usize,
+}
+
+const_assert!(mem::size_of::<Atom>() == 8);
+
+include!("./static_atoms.rs");
+
+impl<'a> From<&'a Atom> for Atom {
+    #[inline]
+    fn from(atom: &'a Atom) -> Self {
+        *atom
+    }
+}
+
+impl From<bool> for Atom {
+    #[inline]
+    fn from(value: bool) -> Self {
+        if value { atom!("true") } else { atom!("false") }
+    }
+}
+
+#[cfg(test)]
+use std::cell::RefCell;
+
+const ATOM_TABLE_INIT_SIZE: usize = 1 << 16;
+const ATOM_TABLE_ALIGN: usize = 8;
+
+#[cfg(test)]
+thread_local! {
+    static ATOM_TABLE_BUF_BASE: RefCell<*const u8> = RefCell::new(ptr::null_mut());
+}
+
+#[cfg(not(test))]
+static mut ATOM_TABLE_BUF_BASE: *const u8 = ptr::null_mut();
+
+#[cfg(test)]
+fn set_atom_tbl_buf_base(ptr: *const u8) {
+    ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| {
+        *atom_table_buf_base.borrow_mut() = ptr;
+    });
+}
+
+#[cfg(test)]
+pub(crate) fn get_atom_tbl_buf_base() -> *const u8 {
+    ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| *atom_table_buf_base.borrow())
+}
+
+#[cfg(not(test))]
+fn set_atom_tbl_buf_base(ptr: *const u8) {
+    unsafe {
+        ATOM_TABLE_BUF_BASE = ptr;
+    }
+}
+
+#[cfg(not(test))]
+pub(crate) fn get_atom_tbl_buf_base() -> *const u8 {
+    unsafe { ATOM_TABLE_BUF_BASE }
+}
+
+impl RawBlockTraits for AtomTable {
+    #[inline]
+    fn init_size() -> usize {
+        ATOM_TABLE_INIT_SIZE
+    }
+
+    #[inline]
+    fn align() -> usize {
+        ATOM_TABLE_ALIGN
+    }
+}
+
+#[bitfield]
+#[derive(Copy, Clone, Debug)]
+struct AtomHeader {
+    #[allow(unused)] m: bool,
+    len: B50,
+    #[allow(unused)] padding: B13,
+}
+
+impl AtomHeader {
+    fn build_with(len: u64) -> Self {
+        AtomHeader::new().with_len(len).with_m(false)
+    }
+}
+
+impl Borrow<str> for Atom {
+    #[inline]
+    fn borrow(&self) -> &str {
+        self.as_str()
+    }
+}
+
+impl Hash for Atom {
+    #[inline]
+    fn hash<H: Hasher>(&self, hasher: &mut H) {
+        self.as_str().hash(hasher)
+        // hasher.write_usize(self.index)
+    }
+}
+
+#[macro_export]
+macro_rules! is_char {
+    ($s:expr) => {
+        !$s.is_empty() && $s.chars().nth(1).is_none()
+    };
+}
+
+impl Atom {
+    #[inline]
+    pub fn buf(self) -> *const u8 {
+        let ptr = self.as_ptr();
+
+        if ptr.is_null() {
+            return ptr::null();
+        }
+
+        (ptr as usize + mem::size_of::<AtomHeader>()) as *const u8
+    }
+
+    #[inline(always)]
+    pub fn is_static(self) -> bool {
+        self.index < STRINGS.len() << 3
+    }
+
+    #[inline(always)]
+    pub fn as_ptr(self) -> *const u8 {
+        if self.is_static() {
+            ptr::null()
+        } else {
+            (get_atom_tbl_buf_base() as usize + self.index - (STRINGS.len() << 3)) as *const u8
+        }
+    }
+
+    #[inline(always)]
+    pub fn from(index: usize) -> Self {
+        Self { index }
+    }
+
+    #[inline(always)]
+    pub fn len(self) -> usize {
+        if self.is_static() {
+            STRINGS[self.index >> 3].len()
+        } else {
+            unsafe { ptr::read(self.as_ptr() as *const AtomHeader).len() as _ }
+        }
+    }
+
+    #[inline(always)]
+    pub fn flat_index(self) -> u64 {
+        (self.index >> 3) as u64
+    }
+
+    pub fn as_char(self) -> Option<char> {
+        let s = self.as_str();
+        let mut it = s.chars();
+
+        let c1 = it.next();
+        let c2 = it.next();
+
+        if c2.is_none() { c1 } else { None }
+    }
+
+    #[inline]
+    pub fn chars(&self) -> str::Chars {
+        self.as_str().chars()
+    }
+
+    #[inline]
+    pub fn as_str(&self) -> &str {
+        unsafe {
+            let ptr = self.as_ptr();
+
+            if ptr.is_null() {
+                return STRINGS[self.index >> 3];
+            }
+
+            let header = ptr::read::<AtomHeader>(ptr as *const _);
+            let len = header.len() as usize;
+            let buf = (ptr as usize + mem::size_of::<AtomHeader>()) as *mut u8;
+
+            str::from_utf8_unchecked(slice::from_raw_parts(buf, len))
+        }
+    }
+
+    pub fn defrock_brackets(&self, atom_tbl: &mut AtomTable) -> Self {
+        let s = self.as_str();
+
+        let s = if s.starts_with('(') && s.ends_with(')') {
+            &s['('.len_utf8()..s.len() - ')'.len_utf8()]
+        } else {
+            return *self;
+        };
+
+        atom_tbl.build_with(s)
+    }
+}
+
+unsafe fn write_to_ptr(string: &str, ptr: *mut u8) {
+    ptr::write(ptr as *mut _, AtomHeader::build_with(string.len() as u64));
+    let str_ptr = (ptr as usize + mem::size_of::<AtomHeader>()) as *mut u8;
+    ptr::copy_nonoverlapping(string.as_ptr(), str_ptr as *mut u8, string.len());
+}
+
+impl PartialOrd for Atom {
+    #[inline]
+    fn partial_cmp(&self, other: &Atom) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for Atom {
+    #[inline]
+    fn cmp(&self, other: &Atom) -> Ordering {
+        self.as_str().cmp(other.as_str())
+    }
+}
+
+#[derive(Debug)]
+pub struct AtomTable {
+    block: RawBlock<AtomTable>,
+    pub table: IndexSet<Atom>,
+}
+
+impl Drop for AtomTable {
+    fn drop(&mut self) {
+        self.block.deallocate();
+    }
+}
+
+impl AtomTable {
+    #[inline]
+    pub fn new() -> Self {
+        let table = Self {
+            block: RawBlock::new(),
+            table: IndexSet::new(),
+        };
+
+        set_atom_tbl_buf_base(table.block.base);
+        table
+    }
+
+    #[inline]
+    pub fn buf(&self) -> *const u8 {
+        self.block.base as *const u8
+    }
+
+    #[inline]
+    pub fn top(&self) -> *const u8 {
+        self.block.top
+    }
+
+    #[inline(always)]
+    fn lookup_str(&self, string: &str) -> Option<Atom> {
+        STATIC_ATOMS_MAP.get(string).or_else(|| self.table.get(string)).cloned()
+    }
+
+    pub fn build_with(&mut self, string: &str) -> Atom {
+        if let Some(atom) = self.lookup_str(string) {
+            return atom;
+        }
+
+        unsafe {
+            let size = mem::size_of::<AtomHeader>() + string.len();
+            let align_offset = 8 * mem::align_of::<AtomHeader>();
+            let size = (size & !(align_offset - 1)) + align_offset;
+
+            let len_ptr = {
+                let mut ptr;
+
+                loop {
+                    ptr = self.block.alloc(size);
+
+                    if ptr.is_null() {
+                        self.block.grow();
+                        set_atom_tbl_buf_base(self.block.base);
+                    } else {
+                        break;
+                    }
+                }
+
+                ptr
+            };
+
+            let ptr_base = self.block.base as usize;
+
+            write_to_ptr(string, len_ptr);
+
+            let atom = Atom {
+                index: (STRINGS.len() << 3) + len_ptr as usize - ptr_base,
+            };
+
+            self.table.insert(atom);
+
+            atom
+        }
+    }
+}
+
+#[bitfield]
+#[repr(u64)]
+#[derive(Copy, Clone, Debug)]
+pub struct AtomCell {
+    name: B46,
+    arity: B10,
+    #[allow(unused)] f: bool,
+    #[allow(unused)] m: bool,
+    #[allow(unused)] tag: B6,
+}
+
+impl AtomCell {
+    #[inline]
+    pub fn build_with(name: u64, arity: u16, tag: HeapCellValueTag) -> Self {
+        if arity > 0 {
+            debug_assert!(arity as usize <= MAX_ARITY);
+
+            AtomCell::new()
+                .with_name(name)
+                .with_arity(arity)
+                .with_f(false)
+                .with_tag(tag as u8)
+        } else {
+            AtomCell::new()
+                .with_name(name)
+                .with_f(false)
+                .with_tag(tag as u8)
+        }
+    }
+
+    #[inline]
+    pub fn get_index(self) -> usize {
+        self.name() as usize
+    }
+
+    #[inline]
+    pub fn get_name(self) -> Atom {
+        Atom::from(self.get_index() << 3)
+    }
+
+    #[inline]
+    pub fn get_arity(self) -> usize {
+        self.arity() as usize
+    }
+
+    #[inline]
+    pub fn get_name_and_arity(self) -> (Atom, usize) {
+        (Atom::from(self.get_index() << 3), self.get_arity())
+    }
+}
index a1cf2a99967222a0df03dcb7276cb387e6f94368..f8d3aa6e913866e784a8bbf8971a2922c0934871 100644 (file)
@@ -1,16 +1,12 @@
 fn main() {
     use nix::sys::signal;
-    use scryer_prolog::read::readline;
     use scryer_prolog::*;
 
     let handler = signal::SigHandler::Handler(handle_sigint);
     unsafe { signal::signal(signal::Signal::SIGINT, handler) }.unwrap();
 
-    let mut wam = machine::Machine::new(
-        readline::input_stream(),
-        machine::Stream::stdout(),
-        machine::Stream::stderr(),
-    );
+    let mut wam = machine::Machine::new();
+
     wam.run_top_level();
 }
 
index cb65486769be0779b71fc9583e9262f4fb37b54e..e7c2c8534890867db76853585a2359d22c80df45 100644 (file)
@@ -1,16 +1,15 @@
-use prolog_parser::ast::*;
-use prolog_parser::{clause_name, temp_v};
-
-use crate::forms::Number;
+use crate::atom_table::*;
 use crate::machine::machine_indices::*;
-use crate::rug::rand::RandState;
+use crate::parser::ast::*;
+use crate::parser::rug::rand::RandState;
 
-use ref_thread_local::{ref_thread_local, RefThreadLocal};
+use crate::forms::Number;
+use crate::temp_v;
 
-use std::collections::BTreeMap;
+use ref_thread_local::{ref_thread_local};
 
 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
-pub(crate) enum CompareNumberQT {
+pub enum CompareNumberQT {
     GreaterThan,
     LessThan,
     GreaterThanOrEqual,
@@ -20,20 +19,20 @@ pub(crate) enum CompareNumberQT {
 }
 
 impl CompareNumberQT {
-    fn name(self) -> &'static str {
+    fn name(self) -> Atom {
         match self {
-            CompareNumberQT::GreaterThan => ">",
-            CompareNumberQT::LessThan => "<",
-            CompareNumberQT::GreaterThanOrEqual => ">=",
-            CompareNumberQT::LessThanOrEqual => "=<",
-            CompareNumberQT::NotEqual => "=\\=",
-            CompareNumberQT::Equal => "=:=",
+            CompareNumberQT::GreaterThan => atom!(">"),
+            CompareNumberQT::LessThan => atom!("<"),
+            CompareNumberQT::GreaterThanOrEqual => atom!(">="),
+            CompareNumberQT::LessThanOrEqual => atom!("=<"),
+            CompareNumberQT::NotEqual => atom!("=\\="),
+            CompareNumberQT::Equal => atom!("=:="),
         }
     }
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub(crate) enum CompareTermQT {
+pub enum CompareTermQT {
     LessThan,
     LessThanOrEqual,
     GreaterThanOrEqual,
@@ -41,18 +40,18 @@ pub(crate) enum CompareTermQT {
 }
 
 impl CompareTermQT {
-    fn name<'a>(self) -> &'a str {
+    fn name(self) -> Atom {
         match self {
-            CompareTermQT::GreaterThan => "@>",
-            CompareTermQT::LessThan => "@<",
-            CompareTermQT::GreaterThanOrEqual => "@>=",
-            CompareTermQT::LessThanOrEqual => "@=<",
+            CompareTermQT::GreaterThan => atom!("@>"),
+            CompareTermQT::LessThan => atom!("@<"),
+            CompareTermQT::GreaterThanOrEqual => atom!("@>="),
+            CompareTermQT::LessThanOrEqual => atom!("@=<"),
         }
     }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub(crate) enum ArithmeticTerm {
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum ArithmeticTerm {
     Reg(RegType),
     Interm(usize),
     Number(Number),
@@ -68,8 +67,8 @@ impl ArithmeticTerm {
     }
 }
 
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub(crate) enum InlinedClauseType {
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum InlinedClauseType {
     CompareNumber(CompareNumberQT, ArithmeticTerm, ArithmeticTerm),
     IsAtom(RegType),
     IsAtomic(RegType),
@@ -83,73 +82,106 @@ pub(crate) enum InlinedClauseType {
 }
 
 ref_thread_local! {
-    pub(crate)static managed RANDOM_STATE: RandState<'static> = RandState::new();
+    pub(crate) static managed RANDOM_STATE: RandState<'static> = RandState::new();
 }
 
-ref_thread_local! {
-    pub(crate)static managed CLAUSE_TYPE_FORMS: BTreeMap<(&'static str, usize), ClauseType> = {
-        let mut m = BTreeMap::new();
-
-        let r1 = temp_v!(1);
-        let r2 = temp_v!(2);
-
-        m.insert((">", 2),
-                 ClauseType::Inlined(InlinedClauseType::CompareNumber(CompareNumberQT::GreaterThan, ar_reg!(r1), ar_reg!(r2))));
-        m.insert(("<", 2),
-                 ClauseType::Inlined(InlinedClauseType::CompareNumber(CompareNumberQT::LessThan, ar_reg!(r1), ar_reg!(r2))));
-        m.insert((">=", 2), ClauseType::Inlined(InlinedClauseType::CompareNumber(CompareNumberQT::GreaterThanOrEqual, ar_reg!(r1), ar_reg!(r2))));
-        m.insert(("=<", 2), ClauseType::Inlined(InlinedClauseType::CompareNumber(CompareNumberQT::LessThanOrEqual, ar_reg!(r1), ar_reg!(r2))));
-        m.insert(("=:=", 2), ClauseType::Inlined(InlinedClauseType::CompareNumber(CompareNumberQT::Equal, ar_reg!(r1), ar_reg!(r2))));
-        m.insert(("=\\=", 2), ClauseType::Inlined(InlinedClauseType::CompareNumber(CompareNumberQT::NotEqual, ar_reg!(r1), ar_reg!(r2))));
-        m.insert(("atom", 1), ClauseType::Inlined(InlinedClauseType::IsAtom(r1)));
-        m.insert(("atomic", 1), ClauseType::Inlined(InlinedClauseType::IsAtomic(r1)));
-        m.insert(("compound", 1), ClauseType::Inlined(InlinedClauseType::IsCompound(r1)));
-        m.insert(("integer", 1), ClauseType::Inlined(InlinedClauseType::IsInteger(r1)));
-        m.insert(("number", 1), ClauseType::Inlined(InlinedClauseType::IsNumber(r1)));
-        m.insert(("rational", 1), ClauseType::Inlined(InlinedClauseType::IsRational(r1)));
-        m.insert(("float", 1), ClauseType::Inlined(InlinedClauseType::IsFloat(r1)));
-        m.insert(("nonvar", 1), ClauseType::Inlined(InlinedClauseType::IsNonVar(r1)));
-        m.insert(("var", 1), ClauseType::Inlined(InlinedClauseType::IsVar(r1)));
-        m.insert(("acyclic_term", 1), ClauseType::BuiltIn(BuiltInClauseType::AcyclicTerm));
-        m.insert(("arg", 3), ClauseType::BuiltIn(BuiltInClauseType::Arg));
-        m.insert(("compare", 3), ClauseType::BuiltIn(BuiltInClauseType::Compare));
-        m.insert(("@>", 2), ClauseType::BuiltIn(BuiltInClauseType::CompareTerm(CompareTermQT::GreaterThan)));
-        m.insert(("@<", 2), ClauseType::BuiltIn(BuiltInClauseType::CompareTerm(CompareTermQT::LessThan)));
-        m.insert(("@>=", 2), ClauseType::BuiltIn(BuiltInClauseType::CompareTerm(CompareTermQT::GreaterThanOrEqual)));
-        m.insert(("@=<", 2), ClauseType::BuiltIn(BuiltInClauseType::CompareTerm(CompareTermQT::LessThanOrEqual)));
-        m.insert(("copy_term", 2), ClauseType::BuiltIn(BuiltInClauseType::CopyTerm));
-        m.insert(("==", 2), ClauseType::BuiltIn(BuiltInClauseType::Eq));
-        m.insert(("functor", 3), ClauseType::BuiltIn(BuiltInClauseType::Functor));
-        m.insert(("ground", 1), ClauseType::BuiltIn(BuiltInClauseType::Ground));
-        m.insert(("is", 2), ClauseType::BuiltIn(BuiltInClauseType::Is(r1, ar_reg!(r2))));
-        m.insert(("keysort", 2), ClauseType::BuiltIn(BuiltInClauseType::KeySort));
-        m.insert(("\\==", 2), ClauseType::BuiltIn(BuiltInClauseType::NotEq));
-        m.insert(("read", 2), ClauseType::BuiltIn(BuiltInClauseType::Read));
-        m.insert(("sort", 2), ClauseType::BuiltIn(BuiltInClauseType::Sort));
-
-        m
-    };
+pub fn clause_type_form(name: Atom, arity: usize) -> Option<ClauseType> {
+    match (name, arity) {
+        (atom!(">"), 2) => Some(ClauseType::Inlined(InlinedClauseType::CompareNumber(
+            CompareNumberQT::GreaterThan,
+            ar_reg!(temp_v!(1)),
+            ar_reg!(temp_v!(2)),
+        ))),
+        (atom!("<"), 2) => Some(ClauseType::Inlined(InlinedClauseType::CompareNumber(
+            CompareNumberQT::LessThan,
+            ar_reg!(temp_v!(1)),
+            ar_reg!(temp_v!(2)),
+        ))),
+        (atom!(">="), 2) => Some(ClauseType::Inlined(InlinedClauseType::CompareNumber(
+            CompareNumberQT::GreaterThanOrEqual,
+            ar_reg!(temp_v!(1)),
+            ar_reg!(temp_v!(2)),
+        ))),
+        (atom!("=<"), 2) => Some(ClauseType::Inlined(InlinedClauseType::CompareNumber(
+            CompareNumberQT::LessThanOrEqual,
+            ar_reg!(temp_v!(1)),
+            ar_reg!(temp_v!(2)),
+        ))),
+        (atom!("=:="), 2) => Some(ClauseType::Inlined(InlinedClauseType::CompareNumber(
+            CompareNumberQT::Equal,
+            ar_reg!(temp_v!(1)),
+            ar_reg!(temp_v!(2)),
+        ))),
+        (atom!("=\\="), 2) => Some(ClauseType::Inlined(InlinedClauseType::CompareNumber(
+            CompareNumberQT::NotEqual,
+            ar_reg!(temp_v!(1)),
+            ar_reg!(temp_v!(2)),
+        ))),
+        (atom!("atom"), 1) => Some(ClauseType::Inlined(InlinedClauseType::IsAtom(temp_v!(1)))),
+        (atom!("atomic"), 1) => Some(ClauseType::Inlined(InlinedClauseType::IsAtomic(temp_v!(1)))),
+        (atom!("compound"), 1) => Some(ClauseType::Inlined(InlinedClauseType::IsCompound(temp_v!(
+            1
+        )))),
+        (atom!("integer"), 1) => Some(ClauseType::Inlined(InlinedClauseType::IsInteger(temp_v!(
+            1
+        )))),
+        (atom!("number"), 1) => Some(ClauseType::Inlined(InlinedClauseType::IsNumber(temp_v!(1)))),
+        (atom!("rational"), 1) => Some(ClauseType::Inlined(InlinedClauseType::IsRational(temp_v!(
+            1
+        )))),
+        (atom!("float"), 1) => Some(ClauseType::Inlined(InlinedClauseType::IsFloat(temp_v!(1)))),
+        (atom!("nonvar"), 1) => Some(ClauseType::Inlined(InlinedClauseType::IsNonVar(temp_v!(1)))),
+        (atom!("var"), 1) => Some(ClauseType::Inlined(InlinedClauseType::IsVar(temp_v!(1)))),
+        (atom!("acyclic_term"), 1) => Some(ClauseType::BuiltIn(BuiltInClauseType::AcyclicTerm)),
+        (atom!("arg"), 3) => Some(ClauseType::BuiltIn(BuiltInClauseType::Arg)),
+        (atom!("compare"), 3) => Some(ClauseType::BuiltIn(BuiltInClauseType::Compare)),
+        (atom!("@>"), 2) => Some(ClauseType::BuiltIn(BuiltInClauseType::CompareTerm(
+            CompareTermQT::GreaterThan,
+        ))),
+        (atom!("@<"), 2) => Some(ClauseType::BuiltIn(BuiltInClauseType::CompareTerm(
+            CompareTermQT::LessThan,
+        ))),
+        (atom!("@>="), 2) => Some(ClauseType::BuiltIn(BuiltInClauseType::CompareTerm(
+            CompareTermQT::GreaterThanOrEqual,
+        ))),
+        (atom!("@=<"), 2) => Some(ClauseType::BuiltIn(BuiltInClauseType::CompareTerm(
+            CompareTermQT::LessThanOrEqual,
+        ))),
+        (atom!("copy_term"), 2) => Some(ClauseType::BuiltIn(BuiltInClauseType::CopyTerm)),
+        (atom!("=="), 2) => Some(ClauseType::BuiltIn(BuiltInClauseType::Eq)),
+        (atom!("functor"), 3) => Some(ClauseType::BuiltIn(BuiltInClauseType::Functor)),
+        (atom!("ground"), 1) => Some(ClauseType::BuiltIn(BuiltInClauseType::Ground)),
+        (atom!("is"), 2) => Some(ClauseType::BuiltIn(BuiltInClauseType::Is(
+            temp_v!(1),
+            ar_reg!(temp_v!(2)),
+        ))),
+        (atom!("keysort"), 2) => Some(ClauseType::BuiltIn(BuiltInClauseType::KeySort)),
+        (atom!("\\=="), 2) => Some(ClauseType::BuiltIn(BuiltInClauseType::NotEq)),
+        (atom!("read"), 1) => Some(ClauseType::BuiltIn(BuiltInClauseType::Read)),
+        (atom!("sort"), 2) => Some(ClauseType::BuiltIn(BuiltInClauseType::Sort)),
+        _ => None,
+    }
 }
 
 impl InlinedClauseType {
-    pub(crate) fn name(&self) -> &'static str {
+    pub(crate) fn name(&self) -> Atom {
         match self {
             &InlinedClauseType::CompareNumber(qt, ..) => qt.name(),
-            &InlinedClauseType::IsAtom(..) => "atom",
-            &InlinedClauseType::IsAtomic(..) => "atomic",
-            &InlinedClauseType::IsCompound(..) => "compound",
-            &InlinedClauseType::IsNumber(..) => "number",
-            &InlinedClauseType::IsInteger(..) => "integer",
-            &InlinedClauseType::IsRational(..) => "rational",
-            &InlinedClauseType::IsFloat(..) => "float",
-            &InlinedClauseType::IsNonVar(..) => "nonvar",
-            &InlinedClauseType::IsVar(..) => "var",
+            &InlinedClauseType::IsAtom(..) => atom!("atom"),
+            &InlinedClauseType::IsAtomic(..) => atom!("atomic"),
+            &InlinedClauseType::IsCompound(..) => atom!("compound"),
+            &InlinedClauseType::IsNumber(..) => atom!("number"),
+            &InlinedClauseType::IsInteger(..) => atom!("integer"),
+            &InlinedClauseType::IsRational(..) => atom!("rational"),
+            &InlinedClauseType::IsFloat(..) => atom!("float"),
+            &InlinedClauseType::IsNonVar(..) => atom!("nonvar"),
+            &InlinedClauseType::IsVar(..) => atom!("var"),
         }
     }
 }
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub(crate) enum SystemClauseType {
+pub enum SystemClauseType {
     AtomChars,
     AtomCodes,
     AtomLength,
@@ -176,8 +208,8 @@ pub(crate) enum SystemClauseType {
     MakeDirectoryPath,
     DeleteFile,
        RenameFile,
-    DeleteDirectory,
     WorkingDirectory,
+    DeleteDirectory,
     PathCanonical,
     FileTime,
     DeleteAttribute,
@@ -203,8 +235,6 @@ pub(crate) enum SystemClauseType {
     GetNextDBRef,
     GetNextOpDBRef,
     IsPartialString,
-    LookupDBRef,
-    LookupOpDBRef,
     Halt,
     GetLiftedHeapFromOffset,
     GetLiftedHeapFromOffsetDiff,
@@ -278,11 +308,11 @@ pub(crate) enum SystemClauseType {
     Succeed,
     TermAttributedVariables,
     TermVariables,
+    TermVariablesUnderMaxDepth,
     TruncateLiftedHeapTo,
     UnifyWithOccursCheck,
     UnwindEnvironments,
     UnwindStack,
-    Variant,
     WAMInstructions,
     WriteTerm,
     WriteTermToChars,
@@ -319,566 +349,509 @@ pub(crate) enum SystemClauseType {
 }
 
 impl SystemClauseType {
-    pub(crate) fn name(&self) -> ClauseName {
+    pub(crate) fn name(&self) -> Atom {
         match self {
-            &SystemClauseType::AtomChars => clause_name!("$atom_chars"),
-            &SystemClauseType::AtomCodes => clause_name!("$atom_codes"),
-            &SystemClauseType::AtomLength => clause_name!("$atom_length"),
-            &SystemClauseType::BindFromRegister => clause_name!("$bind_from_register"),
-            &SystemClauseType::CallContinuation => clause_name!("$call_continuation"),
-            &SystemClauseType::CharCode => clause_name!("$char_code"),
-            &SystemClauseType::CharType => clause_name!("$char_type"),
-            &SystemClauseType::CharsToNumber => clause_name!("$chars_to_number"),
-            &SystemClauseType::CheckCutPoint => clause_name!("$check_cp"),
-            &SystemClauseType::CodesToNumber => clause_name!("$codes_to_number"),
+            &SystemClauseType::AtomChars => atom!("$atom_chars"),
+            &SystemClauseType::AtomCodes => atom!("$atom_codes"),
+            &SystemClauseType::AtomLength => atom!("$atom_length"),
+            &SystemClauseType::BindFromRegister => atom!("$bind_from_register"),
+            &SystemClauseType::CallContinuation => atom!("$call_continuation"),
+            &SystemClauseType::CharCode => atom!("$char_code"),
+            &SystemClauseType::CharType => atom!("$char_type"),
+            &SystemClauseType::CharsToNumber => atom!("$chars_to_number"),
+            &SystemClauseType::CheckCutPoint => atom!("$check_cp"),
+            &SystemClauseType::CodesToNumber => atom!("$codes_to_number"),
             &SystemClauseType::CopyTermWithoutAttrVars => {
-                clause_name!("$copy_term_without_attr_vars")
-            }
-            &SystemClauseType::CreatePartialString => clause_name!("$create_partial_string"),
-            &SystemClauseType::CurrentInput => clause_name!("$current_input"),
-            &SystemClauseType::CurrentHostname => clause_name!("$current_hostname"),
-            &SystemClauseType::CurrentOutput => clause_name!("$current_output"),
-            &SystemClauseType::DirectoryFiles => clause_name!("$directory_files"),
-            &SystemClauseType::FileSize => clause_name!("$file_size"),
-            &SystemClauseType::FileExists => clause_name!("$file_exists"),
-            &SystemClauseType::DirectoryExists => clause_name!("$directory_exists"),
-            &SystemClauseType::DirectorySeparator => clause_name!("$directory_separator"),
-            &SystemClauseType::MakeDirectory => clause_name!("$make_directory"),
-            &SystemClauseType::MakeDirectoryPath => clause_name!("$make_directory_path"),
-            &SystemClauseType::DeleteFile => clause_name!("$delete_file"),
-            &SystemClauseType::RenameFile => clause_name!("$rename_file"),
-            &SystemClauseType::DeleteDirectory => clause_name!("$delete_directory"),
-            &SystemClauseType::WorkingDirectory => clause_name!("$working_directory"),
-            &SystemClauseType::PathCanonical => clause_name!("$path_canonical"),
-            &SystemClauseType::FileTime => clause_name!("$file_time"),
+                atom!("$copy_term_without_attr_vars")
+            }
+            &SystemClauseType::CreatePartialString => atom!("$create_partial_string"),
+            &SystemClauseType::CurrentInput => atom!("$current_input"),
+            &SystemClauseType::CurrentHostname => atom!("$current_hostname"),
+            &SystemClauseType::CurrentOutput => atom!("$current_output"),
+            &SystemClauseType::DirectoryFiles => atom!("$directory_files"),
+            &SystemClauseType::FileSize => atom!("$file_size"),
+            &SystemClauseType::FileExists => atom!("$file_exists"),
+            &SystemClauseType::DirectoryExists => atom!("$directory_exists"),
+            &SystemClauseType::DirectorySeparator => atom!("$directory_separator"),
+            &SystemClauseType::MakeDirectory => atom!("$make_directory"),
+            &SystemClauseType::MakeDirectoryPath => atom!("$make_directory_path"),
+            &SystemClauseType::DeleteFile => atom!("$delete_file"),
+            &SystemClauseType::RenameFile => atom!("$rename_file"),
+            &SystemClauseType::DeleteDirectory => atom!("$delete_directory"),
+            &SystemClauseType::WorkingDirectory => atom!("$working_directory"),
+            &SystemClauseType::PathCanonical => atom!("$path_canonical"),
+            &SystemClauseType::FileTime => atom!("$file_time"),
             &SystemClauseType::REPL(REPLCodePtr::AddDiscontiguousPredicate) => {
-                clause_name!("$add_discontiguous_predicate")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::AddDynamicPredicate) => {
-                clause_name!("$add_dynamic_predicate")
+                atom!("$add_discontiguous_predicate")
             }
+            &SystemClauseType::REPL(REPLCodePtr::AddDynamicPredicate) => atom!("$add_dynamic_predicate"),
             &SystemClauseType::REPL(REPLCodePtr::AddMultifilePredicate) => {
-                clause_name!("$add_multifile_predicate")
+                atom!("$add_multifile_predicate")
             }
             &SystemClauseType::REPL(REPLCodePtr::AddGoalExpansionClause) => {
-                clause_name!("$add_goal_expansion_clause")
+                atom!("$add_goal_expansion_clause")
             }
             &SystemClauseType::REPL(REPLCodePtr::AddTermExpansionClause) => {
-                clause_name!("$add_term_expansion_clause")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::ClauseToEvacuable) => {
-                clause_name!("$clause_to_evacuable")
+                atom!("$add_term_expansion_clause")
             }
+            &SystemClauseType::REPL(REPLCodePtr::ClauseToEvacuable) => atom!("$clause_to_evacuable"),
             &SystemClauseType::REPL(REPLCodePtr::ScopedClauseToEvacuable) => {
-                clause_name!("$scoped_clause_to_evacuable")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::ConcludeLoad) => clause_name!("$conclude_load"),
-            &SystemClauseType::REPL(REPLCodePtr::DeclareModule) => clause_name!("$declare_module"),
-            &SystemClauseType::REPL(REPLCodePtr::LoadCompiledLibrary) => {
-                clause_name!("$load_compiled_library")
+                atom!("$scoped_clause_to_evacuable")
             }
+            &SystemClauseType::REPL(REPLCodePtr::ConcludeLoad) => atom!("$conclude_load"),
+            &SystemClauseType::REPL(REPLCodePtr::DeclareModule) => atom!("$declare_module"),
+            &SystemClauseType::REPL(REPLCodePtr::LoadCompiledLibrary) => atom!("$load_compiled_library"),
             &SystemClauseType::REPL(REPLCodePtr::PushLoadStatePayload) => {
-                clause_name!("$push_load_state_payload")
+                atom!("$push_load_state_payload")
             }
             &SystemClauseType::REPL(REPLCodePtr::AddInSituFilenameModule) => {
-                clause_name!("$add_in_situ_filename_module")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::Asserta) => clause_name!("$asserta"),
-            &SystemClauseType::REPL(REPLCodePtr::Assertz) => clause_name!("$assertz"),
-            &SystemClauseType::REPL(REPLCodePtr::Retract) => clause_name!("$retract_clause"),
-            &SystemClauseType::REPL(REPLCodePtr::UseModule) => clause_name!("$use_module"),
-            &SystemClauseType::REPL(REPLCodePtr::PushLoadContext) => {
-                clause_name!("$push_load_context")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::PopLoadContext) => {
-                clause_name!("$pop_load_context")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::PopLoadStatePayload) => {
-                clause_name!("$pop_load_state_payload")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::LoadContextSource) => {
-                clause_name!("$prolog_lc_source")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::LoadContextFile) => {
-                clause_name!("$prolog_lc_file")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::LoadContextDirectory) => {
-                clause_name!("$prolog_lc_dir")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::LoadContextModule) => {
-                clause_name!("$prolog_lc_module")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::LoadContextStream) => {
-                clause_name!("$prolog_lc_stream")
-            }
+                atom!("$add_in_situ_filename_module")
+            }
+            &SystemClauseType::REPL(REPLCodePtr::Asserta) => atom!("$asserta"),
+            &SystemClauseType::REPL(REPLCodePtr::Assertz) => atom!("$assertz"),
+            &SystemClauseType::REPL(REPLCodePtr::Retract) => atom!("$retract_clause"),
+            &SystemClauseType::REPL(REPLCodePtr::UseModule) => atom!("$use_module"),
+            &SystemClauseType::REPL(REPLCodePtr::PushLoadContext) => atom!("$push_load_context"),
+            &SystemClauseType::REPL(REPLCodePtr::PopLoadContext) => atom!("$pop_load_context"),
+            &SystemClauseType::REPL(REPLCodePtr::PopLoadStatePayload) => atom!("$pop_load_state_payload"),
+            &SystemClauseType::REPL(REPLCodePtr::LoadContextSource) => atom!("$prolog_lc_source"),
+            &SystemClauseType::REPL(REPLCodePtr::LoadContextFile) => atom!("$prolog_lc_file"),
+            &SystemClauseType::REPL(REPLCodePtr::LoadContextDirectory) => atom!("$prolog_lc_dir"),
+            &SystemClauseType::REPL(REPLCodePtr::LoadContextModule) => atom!("$prolog_lc_module"),
+            &SystemClauseType::REPL(REPLCodePtr::LoadContextStream) => atom!("$prolog_lc_stream"),
             &SystemClauseType::REPL(REPLCodePtr::MetaPredicateProperty) => {
-                clause_name!("$cpp_meta_predicate_property")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::BuiltInProperty) => {
-                clause_name!("$cpp_built_in_property")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::DynamicProperty) => {
-                clause_name!("$cpp_dynamic_property")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::MultifileProperty) => {
-                clause_name!("$cpp_multifile_property")
+                atom!("$cpp_meta_predicate_property")
             }
+            &SystemClauseType::REPL(REPLCodePtr::BuiltInProperty) => atom!("$cpp_built_in_property"),
+            &SystemClauseType::REPL(REPLCodePtr::DynamicProperty) => atom!("$cpp_dynamic_property"),
+            &SystemClauseType::REPL(REPLCodePtr::MultifileProperty) => atom!("$cpp_multifile_property"),
             &SystemClauseType::REPL(REPLCodePtr::DiscontiguousProperty) => {
-                clause_name!("$cpp_discontiguous_property")
+                atom!("$cpp_discontiguous_property")
             }
-            &SystemClauseType::REPL(REPLCodePtr::AbolishClause) => clause_name!("$abolish_clause"),
+            &SystemClauseType::REPL(REPLCodePtr::AbolishClause) => atom!("$abolish_clause"),
             &SystemClauseType::REPL(REPLCodePtr::IsConsistentWithTermQueue) => {
-                clause_name!("$is_consistent_with_term_queue")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::FlushTermQueue) => {
-                clause_name!("$flush_term_queue")
-            }
-            &SystemClauseType::REPL(REPLCodePtr::RemoveModuleExports) => {
-                clause_name!("$remove_module_exports")
+                atom!("$is_consistent_with_term_queue")
             }
+            &SystemClauseType::REPL(REPLCodePtr::FlushTermQueue) => atom!("$flush_term_queue"),
+            &SystemClauseType::REPL(REPLCodePtr::RemoveModuleExports) => atom!("$remove_module_exports"),
             &SystemClauseType::REPL(REPLCodePtr::AddNonCountedBacktracking) => {
-                clause_name!("$add_non_counted_backtracking")
-            }
-            &SystemClauseType::Close => clause_name!("$close"),
-            &SystemClauseType::CopyToLiftedHeap => clause_name!("$copy_to_lh"),
-            &SystemClauseType::DeleteAttribute => clause_name!("$del_attr_non_head"),
-            &SystemClauseType::DeleteHeadAttribute => clause_name!("$del_attr_head"),
-            &SystemClauseType::DynamicModuleResolution(_) => clause_name!("$module_call"),
-            &SystemClauseType::EnqueueAttributedVar => clause_name!("$enqueue_attr_var"),
-            &SystemClauseType::FetchGlobalVar => clause_name!("$fetch_global_var"),
-            &SystemClauseType::FirstStream => clause_name!("$first_stream"),
-            &SystemClauseType::FlushOutput => clause_name!("$flush_output"),
-            &SystemClauseType::GetByte => clause_name!("$get_byte"),
-            &SystemClauseType::GetChar => clause_name!("$get_char"),
-            &SystemClauseType::GetNChars => clause_name!("$get_n_chars"),
-            &SystemClauseType::GetCode => clause_name!("$get_code"),
-            &SystemClauseType::GetSingleChar => clause_name!("$get_single_char"),
-            &SystemClauseType::ResetAttrVarState => clause_name!("$reset_attr_var_state"),
-            &SystemClauseType::TruncateIfNoLiftedHeapGrowth => {
-                clause_name!("$truncate_if_no_lh_growth")
-            }
-            &SystemClauseType::TruncateIfNoLiftedHeapGrowthDiff => {
-                clause_name!("$truncate_if_no_lh_growth_diff")
-            }
-            &SystemClauseType::GetAttributedVariableList => clause_name!("$get_attr_list"),
-            &SystemClauseType::GetAttrVarQueueDelimiter => {
-                clause_name!("$get_attr_var_queue_delim")
-            }
-            &SystemClauseType::GetAttrVarQueueBeyond => clause_name!("$get_attr_var_queue_beyond"),
-            &SystemClauseType::GetContinuationChunk => clause_name!("$get_cont_chunk"),
-            &SystemClauseType::GetLiftedHeapFromOffset => clause_name!("$get_lh_from_offset"),
-            &SystemClauseType::GetLiftedHeapFromOffsetDiff => {
-                clause_name!("$get_lh_from_offset_diff")
-            }
-            &SystemClauseType::GetBValue => clause_name!("$get_b_value"),
-            //          &SystemClauseType::GetClause => clause_name!("$get_clause"),
-            &SystemClauseType::GetNextDBRef => clause_name!("$get_next_db_ref"),
-            &SystemClauseType::GetNextOpDBRef => clause_name!("$get_next_op_db_ref"),
-            &SystemClauseType::LookupDBRef => clause_name!("$lookup_db_ref"),
-            &SystemClauseType::LookupOpDBRef => clause_name!("$lookup_op_db_ref"),
-            &SystemClauseType::GetDoubleQuotes => clause_name!("$get_double_quotes"),
-            //          &SystemClauseType::GetModuleClause => clause_name!("$get_module_clause"),
-            &SystemClauseType::GetSCCCleaner => clause_name!("$get_scc_cleaner"),
-            &SystemClauseType::Halt => clause_name!("$halt"),
-            &SystemClauseType::HeadIsDynamic => clause_name!("$head_is_dynamic"),
-            &SystemClauseType::Open => clause_name!("$open"),
-            &SystemClauseType::SetStreamOptions => clause_name!("$set_stream_options"),
-            &SystemClauseType::OpDeclaration => clause_name!("$op"),
-            &SystemClauseType::InstallSCCCleaner => clause_name!("$install_scc_cleaner"),
-            &SystemClauseType::InstallInferenceCounter => {
-                clause_name!("$install_inference_counter")
-            }
-            &SystemClauseType::IsPartialString => clause_name!("$is_partial_string"),
-            &SystemClauseType::PartialStringTail => clause_name!("$partial_string_tail"),
-            &SystemClauseType::PeekByte => clause_name!("$peek_byte"),
-            &SystemClauseType::PeekChar => clause_name!("$peek_char"),
-            &SystemClauseType::PeekCode => clause_name!("$peek_code"),
-            &SystemClauseType::LiftedHeapLength => clause_name!("$lh_length"),
-            &SystemClauseType::Maybe => clause_name!("maybe"),
-            &SystemClauseType::CpuNow => clause_name!("$cpu_now"),
-            &SystemClauseType::CurrentTime => clause_name!("$current_time"),
-            // &SystemClauseType::ModuleAssertDynamicPredicateToFront => {
-            //     clause_name!("$module_asserta")
-            // }
-            // &SystemClauseType::ModuleAssertDynamicPredicateToBack => {
-            //     clause_name!("$module_assertz")
-            // }
-            //          &SystemClauseType::ModuleHeadIsDynamic => clause_name!("$module_head_is_dynamic"),
-            &SystemClauseType::ModuleExists => clause_name!("$module_exists"),
-            &SystemClauseType::NextStream => clause_name!("$next_stream"),
-            &SystemClauseType::NoSuchPredicate => clause_name!("$no_such_predicate"),
-            &SystemClauseType::NumberToChars => clause_name!("$number_to_chars"),
-            &SystemClauseType::NumberToCodes => clause_name!("$number_to_codes"),
+                atom!("$add_non_counted_backtracking")
+            }
+            &SystemClauseType::Close => atom!("$close"),
+            &SystemClauseType::CopyToLiftedHeap => atom!("$copy_to_lh"),
+            &SystemClauseType::DeleteAttribute => atom!("$del_attr_non_head"),
+            &SystemClauseType::DeleteHeadAttribute => atom!("$del_attr_head"),
+            &SystemClauseType::DynamicModuleResolution(_) => atom!("$module_call"),
+            &SystemClauseType::EnqueueAttributedVar => atom!("$enqueue_attr_var"),
+            &SystemClauseType::FetchGlobalVar => atom!("$fetch_global_var"),
+            &SystemClauseType::FirstStream => atom!("$first_stream"),
+            &SystemClauseType::FlushOutput => atom!("$flush_output"),
+            &SystemClauseType::GetByte => atom!("$get_byte"),
+            &SystemClauseType::GetChar => atom!("$get_char"),
+            &SystemClauseType::GetNChars => atom!("$get_n_chars"),
+            &SystemClauseType::GetCode => atom!("$get_code"),
+            &SystemClauseType::GetSingleChar => atom!("$get_single_char"),
+            &SystemClauseType::ResetAttrVarState => atom!("$reset_attr_var_state"),
+            &SystemClauseType::TruncateIfNoLiftedHeapGrowth => atom!("$truncate_if_no_lh_growth"),
+            &SystemClauseType::TruncateIfNoLiftedHeapGrowthDiff => atom!("$truncate_if_no_lh_growth_diff"),
+            &SystemClauseType::GetAttributedVariableList => atom!("$get_attr_list"),
+            &SystemClauseType::GetAttrVarQueueDelimiter => atom!("$get_attr_var_queue_delim"),
+            &SystemClauseType::GetAttrVarQueueBeyond => atom!("$get_attr_var_queue_beyond"),
+            &SystemClauseType::GetContinuationChunk => atom!("$get_cont_chunk"),
+            &SystemClauseType::GetLiftedHeapFromOffset => atom!("$get_lh_from_offset"),
+            &SystemClauseType::GetLiftedHeapFromOffsetDiff => atom!("$get_lh_from_offset_diff"),
+            &SystemClauseType::GetBValue => atom!("$get_b_value"),
+            &SystemClauseType::GetNextDBRef => atom!("$get_next_db_ref"),
+            &SystemClauseType::GetNextOpDBRef => atom!("$get_next_op_db_ref"),
+            &SystemClauseType::GetDoubleQuotes => atom!("$get_double_quotes"),
+            &SystemClauseType::GetSCCCleaner => atom!("$get_scc_cleaner"),
+            &SystemClauseType::Halt => atom!("$halt"),
+            &SystemClauseType::HeadIsDynamic => atom!("$head_is_dynamic"),
+            &SystemClauseType::Open => atom!("$open"),
+            &SystemClauseType::OpDeclaration => atom!("$op"),
+            &SystemClauseType::InstallSCCCleaner => atom!("$install_scc_cleaner"),
+            &SystemClauseType::InstallInferenceCounter => atom!("$install_inference_counter"),
+            &SystemClauseType::IsPartialString => atom!("$is_partial_string"),
+            &SystemClauseType::PartialStringTail => atom!("$partial_string_tail"),
+            &SystemClauseType::PeekByte => atom!("$peek_byte"),
+            &SystemClauseType::PeekChar => atom!("$peek_char"),
+            &SystemClauseType::PeekCode => atom!("$peek_code"),
+            &SystemClauseType::LiftedHeapLength => atom!("$lh_length"),
+            &SystemClauseType::Maybe => atom!("maybe"),
+            &SystemClauseType::CpuNow => atom!("$cpu_now"),
+            &SystemClauseType::CurrentTime => atom!("$current_time"),
+            //          &SystemClauseType::ModuleHeadIsDynamic => atom!("$module_head_is_dynamic"),
+            &SystemClauseType::ModuleExists => atom!("$module_exists"),
+            &SystemClauseType::NextStream => atom!("$next_stream"),
+            &SystemClauseType::NoSuchPredicate => atom!("$no_such_predicate"),
+            &SystemClauseType::NumberToChars => atom!("$number_to_chars"),
+            &SystemClauseType::NumberToCodes => atom!("$number_to_codes"),
             &SystemClauseType::PointsToContinuationResetMarker => {
-                clause_name!("$points_to_cont_reset_marker")
+                atom!("$points_to_cont_reset_marker")
             }
             &SystemClauseType::PutByte => {
-                clause_name!("$put_byte")
+                atom!("$put_byte")
             }
             &SystemClauseType::PutChar => {
-                clause_name!("$put_char")
+                atom!("$put_char")
             }
             &SystemClauseType::PutChars => {
-                clause_name!("$put_chars")
+                atom!("$put_chars")
             }
             &SystemClauseType::PutCode => {
-                clause_name!("$put_code")
+                atom!("$put_code")
             }
             &SystemClauseType::QuotedToken => {
-                clause_name!("$quoted_token")
-            }
-            &SystemClauseType::RedoAttrVarBinding => clause_name!("$redo_attr_var_binding"),
-            &SystemClauseType::RemoveCallPolicyCheck => clause_name!("$remove_call_policy_check"),
-            &SystemClauseType::RemoveInferenceCounter => clause_name!("$remove_inference_counter"),
-            &SystemClauseType::RestoreCutPolicy => clause_name!("$restore_cut_policy"),
-            &SystemClauseType::SetCutPoint(_) => clause_name!("$set_cp"),
-            &SystemClauseType::SetInput => clause_name!("$set_input"),
-            &SystemClauseType::SetOutput => clause_name!("$set_output"),
-            &SystemClauseType::SetSeed => clause_name!("$set_seed"),
-            &SystemClauseType::StreamProperty => clause_name!("$stream_property"),
-            &SystemClauseType::SetStreamPosition => clause_name!("$set_stream_position"),
+                atom!("$quoted_token")
+            }
+            &SystemClauseType::RedoAttrVarBinding => atom!("$redo_attr_var_binding"),
+            &SystemClauseType::RemoveCallPolicyCheck => atom!("$remove_call_policy_check"),
+            &SystemClauseType::RemoveInferenceCounter => atom!("$remove_inference_counter"),
+            &SystemClauseType::RestoreCutPolicy => atom!("$restore_cut_policy"),
+            &SystemClauseType::SetCutPoint(_) => atom!("$set_cp"),
+            &SystemClauseType::SetInput => atom!("$set_input"),
+            &SystemClauseType::SetOutput => atom!("$set_output"),
+            &SystemClauseType::SetSeed => atom!("$set_seed"),
+            &SystemClauseType::StreamProperty => atom!("$stream_property"),
+            &SystemClauseType::SetStreamPosition => atom!("$set_stream_position"),
+            &SystemClauseType::SetStreamOptions => atom!("$set_stream_options"),
             &SystemClauseType::StoreBacktrackableGlobalVar => {
-                clause_name!("$store_back_trackable_global_var")
-            }
-            &SystemClauseType::StoreGlobalVar => clause_name!("$store_global_var"),
-            &SystemClauseType::InferenceLevel => clause_name!("$inference_level"),
-            &SystemClauseType::CleanUpBlock => clause_name!("$clean_up_block"),
-            &SystemClauseType::EraseBall => clause_name!("$erase_ball"),
-            &SystemClauseType::Fail => clause_name!("$fail"),
-            &SystemClauseType::GetBall => clause_name!("$get_ball"),
-            &SystemClauseType::GetCutPoint => clause_name!("$get_cp"),
-            &SystemClauseType::GetCurrentBlock => clause_name!("$get_current_block"),
-            &SystemClauseType::InstallNewBlock => clause_name!("$install_new_block"),
-            &SystemClauseType::NextEP => clause_name!("$nextEP"),
-            &SystemClauseType::ReadQueryTerm => clause_name!("$read_query_term"),
-            &SystemClauseType::ReadTerm => clause_name!("$read_term"),
-            &SystemClauseType::ReadTermFromChars => clause_name!("$read_term_from_chars"),
-            &SystemClauseType::ResetBlock => clause_name!("$reset_block"),
-            &SystemClauseType::ResetContinuationMarker => clause_name!("$reset_cont_marker"),
-            &SystemClauseType::ReturnFromVerifyAttr => clause_name!("$return_from_verify_attr"),
-            &SystemClauseType::SetBall => clause_name!("$set_ball"),
-            &SystemClauseType::SetCutPointByDefault(_) => clause_name!("$set_cp_by_default"),
-            &SystemClauseType::SetDoubleQuotes => clause_name!("$set_double_quotes"),
-            &SystemClauseType::SkipMaxList => clause_name!("$skip_max_list"),
-            &SystemClauseType::Sleep => clause_name!("$sleep"),
-            &SystemClauseType::SocketClientOpen => clause_name!("$socket_client_open"),
-            &SystemClauseType::SocketServerOpen => clause_name!("$socket_server_open"),
-            &SystemClauseType::SocketServerAccept => clause_name!("$socket_server_accept"),
-            &SystemClauseType::SocketServerClose => clause_name!("$socket_server_close"),
-            &SystemClauseType::TLSAcceptClient => clause_name!("$tls_accept_client"),
-            &SystemClauseType::TLSClientConnect => clause_name!("$tls_client_connect"),
-            &SystemClauseType::Succeed => clause_name!("$succeed"),
+                atom!("$store_back_trackable_global_var")
+            }
+            &SystemClauseType::StoreGlobalVar => atom!("$store_global_var"),
+            &SystemClauseType::InferenceLevel => atom!("$inference_level"),
+            &SystemClauseType::CleanUpBlock => atom!("$clean_up_block"),
+            &SystemClauseType::EraseBall => atom!("$erase_ball"),
+            &SystemClauseType::Fail => atom!("$fail"),
+            &SystemClauseType::GetBall => atom!("$get_ball"),
+            &SystemClauseType::GetCutPoint => atom!("$get_cp"),
+            &SystemClauseType::GetCurrentBlock => atom!("$get_current_block"),
+            &SystemClauseType::InstallNewBlock => atom!("$install_new_block"),
+            &SystemClauseType::NextEP => atom!("$nextEP"),
+            &SystemClauseType::ReadQueryTerm => atom!("$read_query_term"),
+            &SystemClauseType::ReadTerm => atom!("$read_term"),
+            &SystemClauseType::ReadTermFromChars => atom!("$read_term_from_chars"),
+            &SystemClauseType::ResetBlock => atom!("$reset_block"),
+            &SystemClauseType::ResetContinuationMarker => atom!("$reset_cont_marker"),
+            &SystemClauseType::ReturnFromVerifyAttr => atom!("$return_from_verify_attr"),
+            &SystemClauseType::SetBall => atom!("$set_ball"),
+            &SystemClauseType::SetCutPointByDefault(_) => atom!("$set_cp_by_default"),
+            &SystemClauseType::SetDoubleQuotes => atom!("$set_double_quotes"),
+            &SystemClauseType::SkipMaxList => atom!("$skip_max_list"),
+            &SystemClauseType::Sleep => atom!("$sleep"),
+            &SystemClauseType::SocketClientOpen => atom!("$socket_client_open"),
+            &SystemClauseType::SocketServerOpen => atom!("$socket_server_open"),
+            &SystemClauseType::SocketServerAccept => atom!("$socket_server_accept"),
+            &SystemClauseType::SocketServerClose => atom!("$socket_server_close"),
+            &SystemClauseType::TLSAcceptClient => atom!("$tls_accept_client"),
+            &SystemClauseType::TLSClientConnect => atom!("$tls_client_connect"),
+            &SystemClauseType::Succeed => atom!("$succeed"),
             &SystemClauseType::TermAttributedVariables => {
-                clause_name!("$term_attributed_variables")
-            }
-            &SystemClauseType::TermVariables => clause_name!("$term_variables"),
-            &SystemClauseType::TruncateLiftedHeapTo => clause_name!("$truncate_lh_to"),
-            &SystemClauseType::UnifyWithOccursCheck => clause_name!("$unify_with_occurs_check"),
-            &SystemClauseType::UnwindEnvironments => clause_name!("$unwind_environments"),
-            &SystemClauseType::UnwindStack => clause_name!("$unwind_stack"),
-            &SystemClauseType::Variant => clause_name!("$variant"),
-            &SystemClauseType::WAMInstructions => clause_name!("$wam_instructions"),
-            &SystemClauseType::WriteTerm => clause_name!("$write_term"),
-            &SystemClauseType::WriteTermToChars => clause_name!("$write_term_to_chars"),
-            &SystemClauseType::ScryerPrologVersion => clause_name!("$scryer_prolog_version"),
-            &SystemClauseType::CryptoRandomByte => clause_name!("$crypto_random_byte"),
-            &SystemClauseType::CryptoDataHash => clause_name!("$crypto_data_hash"),
-            &SystemClauseType::CryptoDataHKDF => clause_name!("$crypto_data_hkdf"),
-            &SystemClauseType::CryptoPasswordHash => clause_name!("$crypto_password_hash"),
-            &SystemClauseType::CryptoDataEncrypt => clause_name!("$crypto_data_encrypt"),
-            &SystemClauseType::CryptoDataDecrypt => clause_name!("$crypto_data_decrypt"),
-            &SystemClauseType::CryptoCurveScalarMult => clause_name!("$crypto_curve_scalar_mult"),
-            &SystemClauseType::Ed25519Sign => clause_name!("$ed25519_sign"),
-            &SystemClauseType::Ed25519Verify => clause_name!("$ed25519_verify"),
-            &SystemClauseType::Ed25519NewKeyPair => clause_name!("$ed25519_new_keypair"),
+                atom!("$term_attributed_variables")
+            }
+            &SystemClauseType::TermVariables => atom!("$term_variables"),
+            &SystemClauseType::TermVariablesUnderMaxDepth => atom!("$term_variables_under_max_depth"),
+            &SystemClauseType::TruncateLiftedHeapTo => atom!("$truncate_lh_to"),
+            &SystemClauseType::UnifyWithOccursCheck => atom!("$unify_with_occurs_check"),
+            &SystemClauseType::UnwindEnvironments => atom!("$unwind_environments"),
+            &SystemClauseType::UnwindStack => atom!("$unwind_stack"),
+            &SystemClauseType::WAMInstructions => atom!("$wam_instructions"),
+            &SystemClauseType::WriteTerm => atom!("$write_term"),
+            &SystemClauseType::WriteTermToChars => atom!("$write_term_to_chars"),
+            &SystemClauseType::ScryerPrologVersion => atom!("$scryer_prolog_version"),
+            &SystemClauseType::CryptoRandomByte => atom!("$crypto_random_byte"),
+            &SystemClauseType::CryptoDataHash => atom!("$crypto_data_hash"),
+            &SystemClauseType::CryptoDataHKDF => atom!("$crypto_data_hkdf"),
+            &SystemClauseType::CryptoPasswordHash => atom!("$crypto_password_hash"),
+            &SystemClauseType::CryptoDataEncrypt => atom!("$crypto_data_encrypt"),
+            &SystemClauseType::CryptoDataDecrypt => atom!("$crypto_data_decrypt"),
+            &SystemClauseType::CryptoCurveScalarMult => atom!("$crypto_curve_scalar_mult"),
+            &SystemClauseType::Ed25519Sign => atom!("$ed25519_sign"),
+            &SystemClauseType::Ed25519Verify => atom!("$ed25519_verify"),
+            &SystemClauseType::Ed25519NewKeyPair => atom!("$ed25519_new_keypair"),
             &SystemClauseType::Ed25519KeyPairPublicKey => {
-                clause_name!("$ed25519_keypair_public_key")
-            }
-            &SystemClauseType::Curve25519ScalarMult => clause_name!("$curve25519_scalar_mult"),
-            &SystemClauseType::FirstNonOctet => clause_name!("$first_non_octet"),
-            &SystemClauseType::LoadHTML => clause_name!("$load_html"),
-            &SystemClauseType::LoadXML => clause_name!("$load_xml"),
-            &SystemClauseType::GetEnv => clause_name!("$getenv"),
-            &SystemClauseType::SetEnv => clause_name!("$setenv"),
-            &SystemClauseType::UnsetEnv => clause_name!("$unsetenv"),
-            &SystemClauseType::Shell => clause_name!("$shell"),
-            &SystemClauseType::PID => clause_name!("$pid"),
-            &SystemClauseType::CharsBase64 => clause_name!("$chars_base64"),
-            &SystemClauseType::LoadLibraryAsStream => clause_name!("$load_library_as_stream"),
-            &SystemClauseType::DevourWhitespace => clause_name!("$devour_whitespace"),
-            &SystemClauseType::IsSTOEnabled => clause_name!("$is_sto_enabled"),
-            &SystemClauseType::SetSTOAsUnify => clause_name!("$set_sto_as_unify"),
-            &SystemClauseType::SetNSTOAsUnify => clause_name!("$set_nsto_as_unify"),
-            &SystemClauseType::HomeDirectory => clause_name!("$home_directory"),
+                atom!("$ed25519_keypair_public_key")
+            }
+            &SystemClauseType::Curve25519ScalarMult => atom!("$curve25519_scalar_mult"),
+            &SystemClauseType::FirstNonOctet => atom!("$first_non_octet"),
+            &SystemClauseType::LoadHTML => atom!("$load_html"),
+            &SystemClauseType::LoadXML => atom!("$load_xml"),
+            &SystemClauseType::GetEnv => atom!("$getenv"),
+            &SystemClauseType::SetEnv => atom!("$setenv"),
+            &SystemClauseType::UnsetEnv => atom!("$unsetenv"),
+            &SystemClauseType::Shell => atom!("$shell"),
+            &SystemClauseType::PID => atom!("$pid"),
+            &SystemClauseType::CharsBase64 => atom!("$chars_base64"),
+            &SystemClauseType::LoadLibraryAsStream => atom!("$load_library_as_stream"),
+            &SystemClauseType::DevourWhitespace => atom!("$devour_whitespace"),
+            &SystemClauseType::IsSTOEnabled => atom!("$is_sto_enabled"),
+            &SystemClauseType::SetSTOAsUnify => atom!("$set_sto_as_unify"),
+            &SystemClauseType::SetNSTOAsUnify => atom!("$set_nsto_as_unify"),
+            &SystemClauseType::HomeDirectory => atom!("$home_directory"),
             &SystemClauseType::SetSTOWithErrorAsUnify => {
-                clause_name!("$set_sto_with_error_as_unify")
+                atom!("$set_sto_with_error_as_unify")
             }
-            &SystemClauseType::DebugHook => clause_name!("$debug_hook"),
-            &SystemClauseType::PopCount => clause_name!("$popcount"),
+            &SystemClauseType::DebugHook => atom!("$debug_hook"),
+            &SystemClauseType::PopCount => atom!("$popcount"),
         }
     }
 
-    pub(crate) fn from(name: &str, arity: usize) -> Option<SystemClauseType> {
+    pub(crate) fn from(name: Atom, arity: usize) -> Option<SystemClauseType> {
         match (name, arity) {
-            ("$abolish_clause", 3) => Some(SystemClauseType::REPL(REPLCodePtr::AbolishClause)),
-            ("$add_dynamic_predicate", 4) => {
+            (atom!("$abolish_clause"), 3) => Some(SystemClauseType::REPL(REPLCodePtr::AbolishClause)),
+            (atom!("$add_dynamic_predicate"), 4) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::AddDynamicPredicate))
             }
-            ("$add_multifile_predicate", 4) => {
+            (atom!("$add_multifile_predicate"), 4) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::AddMultifilePredicate))
             }
-            ("$add_discontiguous_predicate", 4) => Some(SystemClauseType::REPL(
+            (atom!("$add_discontiguous_predicate"), 4) => Some(SystemClauseType::REPL(
                 REPLCodePtr::AddDiscontiguousPredicate,
             )),
-            ("$add_goal_expansion_clause", 3) => {
+            (atom!("$add_goal_expansion_clause"), 3) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::AddGoalExpansionClause))
             }
-            ("$add_term_expansion_clause", 2) => {
+            (atom!("$add_term_expansion_clause"), 2) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::AddTermExpansionClause))
             }
-            ("$atom_chars", 2) => Some(SystemClauseType::AtomChars),
-            ("$atom_codes", 2) => Some(SystemClauseType::AtomCodes),
-            ("$atom_length", 2) => Some(SystemClauseType::AtomLength),
-            ("$bind_from_register", 2) => Some(SystemClauseType::BindFromRegister),
-            ("$call_continuation", 1) => Some(SystemClauseType::CallContinuation),
-            ("$char_code", 2) => Some(SystemClauseType::CharCode),
-            ("$char_type", 2) => Some(SystemClauseType::CharType),
-            ("$chars_to_number", 2) => Some(SystemClauseType::CharsToNumber),
-            ("$codes_to_number", 2) => Some(SystemClauseType::CodesToNumber),
-            ("$copy_term_without_attr_vars", 2) => Some(SystemClauseType::CopyTermWithoutAttrVars),
-            ("$create_partial_string", 3) => Some(SystemClauseType::CreatePartialString),
-            ("$check_cp", 1) => Some(SystemClauseType::CheckCutPoint),
-            ("$copy_to_lh", 2) => Some(SystemClauseType::CopyToLiftedHeap),
-            ("$close", 2) => Some(SystemClauseType::Close),
-            ("$current_hostname", 1) => Some(SystemClauseType::CurrentHostname),
-            ("$current_input", 1) => Some(SystemClauseType::CurrentInput),
-            ("$current_output", 1) => Some(SystemClauseType::CurrentOutput),
-            ("$first_stream", 1) => Some(SystemClauseType::FirstStream),
-            ("$next_stream", 2) => Some(SystemClauseType::NextStream),
-            ("$flush_output", 1) => Some(SystemClauseType::FlushOutput),
-            ("$del_attr_non_head", 1) => Some(SystemClauseType::DeleteAttribute),
-            ("$del_attr_head", 1) => Some(SystemClauseType::DeleteHeadAttribute),
-            ("$get_next_db_ref", 2) => Some(SystemClauseType::GetNextDBRef),
-            ("$get_next_op_db_ref", 2) => Some(SystemClauseType::GetNextOpDBRef),
-            ("$lookup_db_ref", 3) => Some(SystemClauseType::LookupDBRef),
-            ("$lookup_op_db_ref", 4) => Some(SystemClauseType::LookupOpDBRef),
-            ("$module_call", _) => Some(SystemClauseType::DynamicModuleResolution(arity - 2)),
-            ("$enqueue_attr_var", 1) => Some(SystemClauseType::EnqueueAttributedVar),
-            ("$partial_string_tail", 2) => Some(SystemClauseType::PartialStringTail),
-            ("$peek_byte", 2) => Some(SystemClauseType::PeekByte),
-            ("$peek_char", 2) => Some(SystemClauseType::PeekChar),
-            ("$peek_code", 2) => Some(SystemClauseType::PeekCode),
-            ("$is_partial_string", 1) => Some(SystemClauseType::IsPartialString),
-            ("$fetch_global_var", 2) => Some(SystemClauseType::FetchGlobalVar),
-            ("$get_byte", 2) => Some(SystemClauseType::GetByte),
-            ("$get_char", 2) => Some(SystemClauseType::GetChar),
-            ("$get_n_chars", 3) => Some(SystemClauseType::GetNChars),
-            ("$get_code", 2) => Some(SystemClauseType::GetCode),
-            ("$get_single_char", 1) => Some(SystemClauseType::GetSingleChar),
-            ("$points_to_cont_reset_marker", 1) => {
+            (atom!("$atom_chars"), 2) => Some(SystemClauseType::AtomChars),
+            (atom!("$atom_codes"), 2) => Some(SystemClauseType::AtomCodes),
+            (atom!("$atom_length"), 2) => Some(SystemClauseType::AtomLength),
+            (atom!("$bind_from_register"), 2) => Some(SystemClauseType::BindFromRegister),
+            (atom!("$call_continuation"), 1) => Some(SystemClauseType::CallContinuation),
+            (atom!("$char_code"), 2) => Some(SystemClauseType::CharCode),
+            (atom!("$char_type"), 2) => Some(SystemClauseType::CharType),
+            (atom!("$chars_to_number"), 2) => Some(SystemClauseType::CharsToNumber),
+            (atom!("$codes_to_number"), 2) => Some(SystemClauseType::CodesToNumber),
+            (atom!("$copy_term_without_attr_vars"), 2) => Some(SystemClauseType::CopyTermWithoutAttrVars),
+            (atom!("$create_partial_string"), 3) => Some(SystemClauseType::CreatePartialString),
+            (atom!("$check_cp"), 1) => Some(SystemClauseType::CheckCutPoint),
+            (atom!("$copy_to_lh"), 2) => Some(SystemClauseType::CopyToLiftedHeap),
+            (atom!("$close"), 2) => Some(SystemClauseType::Close),
+            (atom!("$current_hostname"), 1) => Some(SystemClauseType::CurrentHostname),
+            (atom!("$current_input"), 1) => Some(SystemClauseType::CurrentInput),
+            (atom!("$current_output"), 1) => Some(SystemClauseType::CurrentOutput),
+            (atom!("$first_stream"), 1) => Some(SystemClauseType::FirstStream),
+            (atom!("$next_stream"), 2) => Some(SystemClauseType::NextStream),
+            (atom!("$flush_output"), 1) => Some(SystemClauseType::FlushOutput),
+            (atom!("$del_attr_non_head"), 1) => Some(SystemClauseType::DeleteAttribute),
+            (atom!("$del_attr_head"), 1) => Some(SystemClauseType::DeleteHeadAttribute),
+            (atom!("$get_next_db_ref"), 2) => Some(SystemClauseType::GetNextDBRef),
+            (atom!("$get_next_op_db_ref"), 2) => Some(SystemClauseType::GetNextOpDBRef),
+            (atom!("$module_call"), _) => Some(SystemClauseType::DynamicModuleResolution(arity - 2)),
+            (atom!("$enqueue_attr_var"), 1) => Some(SystemClauseType::EnqueueAttributedVar),
+            (atom!("$partial_string_tail"), 2) => Some(SystemClauseType::PartialStringTail),
+            (atom!("$peek_byte"), 2) => Some(SystemClauseType::PeekByte),
+            (atom!("$peek_char"), 2) => Some(SystemClauseType::PeekChar),
+            (atom!("$peek_code"), 2) => Some(SystemClauseType::PeekCode),
+            (atom!("$is_partial_string"), 1) => Some(SystemClauseType::IsPartialString),
+            (atom!("$fetch_global_var"), 2) => Some(SystemClauseType::FetchGlobalVar),
+            (atom!("$get_byte"), 2) => Some(SystemClauseType::GetByte),
+            (atom!("$get_char"), 2) => Some(SystemClauseType::GetChar),
+            (atom!("$get_n_chars"), 3) => Some(SystemClauseType::GetNChars),
+            (atom!("$get_code"), 2) => Some(SystemClauseType::GetCode),
+            (atom!("$get_single_char"), 1) => Some(SystemClauseType::GetSingleChar),
+            (atom!("$points_to_cont_reset_marker"), 1) => {
                 Some(SystemClauseType::PointsToContinuationResetMarker)
             }
-            ("$put_byte", 2) => Some(SystemClauseType::PutByte),
-            ("$put_char", 2) => Some(SystemClauseType::PutChar),
-            ("$put_chars", 2) => Some(SystemClauseType::PutChars),
-            ("$put_code", 2) => Some(SystemClauseType::PutCode),
-            ("$reset_attr_var_state", 0) => Some(SystemClauseType::ResetAttrVarState),
-            ("$truncate_if_no_lh_growth", 1) => {
+            (atom!("$put_byte"), 2) => Some(SystemClauseType::PutByte),
+            (atom!("$put_char"), 2) => Some(SystemClauseType::PutChar),
+            (atom!("$put_chars"), 2) => Some(SystemClauseType::PutChars),
+            (atom!("$put_code"), 2) => Some(SystemClauseType::PutCode),
+            (atom!("$reset_attr_var_state"), 0) => Some(SystemClauseType::ResetAttrVarState),
+            (atom!("$truncate_if_no_lh_growth"), 1) => {
                 Some(SystemClauseType::TruncateIfNoLiftedHeapGrowth)
             }
-            ("$truncate_if_no_lh_growth_diff", 2) => {
+            (atom!("$truncate_if_no_lh_growth_diff"), 2) => {
                 Some(SystemClauseType::TruncateIfNoLiftedHeapGrowthDiff)
             }
-            ("$get_attr_list", 2) => Some(SystemClauseType::GetAttributedVariableList),
-            ("$get_b_value", 1) => Some(SystemClauseType::GetBValue),
-            ("$get_lh_from_offset", 2) => Some(SystemClauseType::GetLiftedHeapFromOffset),
-            ("$get_lh_from_offset_diff", 3) => Some(SystemClauseType::GetLiftedHeapFromOffsetDiff),
-            ("$get_double_quotes", 1) => Some(SystemClauseType::GetDoubleQuotes),
-            ("$get_scc_cleaner", 1) => Some(SystemClauseType::GetSCCCleaner),
-            ("$halt", 1) => Some(SystemClauseType::Halt),
-            ("$head_is_dynamic", 2) => Some(SystemClauseType::HeadIsDynamic),
-            ("$install_scc_cleaner", 2) => Some(SystemClauseType::InstallSCCCleaner),
-            ("$install_inference_counter", 3) => Some(SystemClauseType::InstallInferenceCounter),
-            ("$lh_length", 1) => Some(SystemClauseType::LiftedHeapLength),
-            ("$maybe", 0) => Some(SystemClauseType::Maybe),
-            ("$cpu_now", 1) => Some(SystemClauseType::CpuNow),
-            ("$current_time", 1) => Some(SystemClauseType::CurrentTime),
-            ("$module_exists", 1) => Some(SystemClauseType::ModuleExists),
-            ("$no_such_predicate", 2) => Some(SystemClauseType::NoSuchPredicate),
-            ("$number_to_chars", 2) => Some(SystemClauseType::NumberToChars),
-            ("$number_to_codes", 2) => Some(SystemClauseType::NumberToCodes),
-            ("$op", 3) => Some(SystemClauseType::OpDeclaration),
-            ("$open", 7) => Some(SystemClauseType::Open),
-            ("$set_stream_options", 5) => Some(SystemClauseType::SetStreamOptions),
-            ("$redo_attr_var_binding", 2) => Some(SystemClauseType::RedoAttrVarBinding),
-            ("$remove_call_policy_check", 1) => Some(SystemClauseType::RemoveCallPolicyCheck),
-            ("$remove_inference_counter", 2) => Some(SystemClauseType::RemoveInferenceCounter),
-            ("$restore_cut_policy", 0) => Some(SystemClauseType::RestoreCutPolicy),
-            ("$set_cp", 1) => Some(SystemClauseType::SetCutPoint(temp_v!(1))),
-            ("$set_input", 1) => Some(SystemClauseType::SetInput),
-            ("$set_output", 1) => Some(SystemClauseType::SetOutput),
-            ("$stream_property", 3) => Some(SystemClauseType::StreamProperty),
-            ("$set_stream_position", 2) => Some(SystemClauseType::SetStreamPosition),
-            ("$inference_level", 2) => Some(SystemClauseType::InferenceLevel),
-            ("$clean_up_block", 1) => Some(SystemClauseType::CleanUpBlock),
-            ("$erase_ball", 0) => Some(SystemClauseType::EraseBall),
-            ("$fail", 0) => Some(SystemClauseType::Fail),
-            ("$get_attr_var_queue_beyond", 2) => Some(SystemClauseType::GetAttrVarQueueBeyond),
-            ("$get_attr_var_queue_delim", 1) => Some(SystemClauseType::GetAttrVarQueueDelimiter),
-            ("$get_ball", 1) => Some(SystemClauseType::GetBall),
-            ("$get_cont_chunk", 3) => Some(SystemClauseType::GetContinuationChunk),
-            ("$get_current_block", 1) => Some(SystemClauseType::GetCurrentBlock),
-            ("$get_cp", 1) => Some(SystemClauseType::GetCutPoint),
-            ("$install_new_block", 1) => Some(SystemClauseType::InstallNewBlock),
-            ("$quoted_token", 1) => Some(SystemClauseType::QuotedToken),
-            ("$nextEP", 3) => Some(SystemClauseType::NextEP),
-            ("$read_query_term", 5) => Some(SystemClauseType::ReadQueryTerm),
-            ("$read_term", 5) => Some(SystemClauseType::ReadTerm),
-            ("$read_term_from_chars", 2) => Some(SystemClauseType::ReadTermFromChars),
-            ("$reset_block", 1) => Some(SystemClauseType::ResetBlock),
-            ("$reset_cont_marker", 0) => Some(SystemClauseType::ResetContinuationMarker),
-            ("$return_from_verify_attr", 0) => Some(SystemClauseType::ReturnFromVerifyAttr),
-            ("$set_ball", 1) => Some(SystemClauseType::SetBall),
-            ("$set_cp_by_default", 1) => Some(SystemClauseType::SetCutPointByDefault(temp_v!(1))),
-            ("$set_double_quotes", 1) => Some(SystemClauseType::SetDoubleQuotes),
-            ("$set_seed", 1) => Some(SystemClauseType::SetSeed),
-            ("$skip_max_list", 4) => Some(SystemClauseType::SkipMaxList),
-            ("$sleep", 1) => Some(SystemClauseType::Sleep),
-            ("$socket_client_open", 7) => Some(SystemClauseType::SocketClientOpen),
-            ("$socket_server_open", 3) => Some(SystemClauseType::SocketServerOpen),
-            ("$socket_server_accept", 7) => Some(SystemClauseType::SocketServerAccept),
-            ("$socket_server_close", 1) => Some(SystemClauseType::SocketServerClose),
-            ("$tls_accept_client", 4) => Some(SystemClauseType::TLSAcceptClient),
-            ("$tls_client_connect", 3) => Some(SystemClauseType::TLSClientConnect),
-            ("$store_global_var", 2) => Some(SystemClauseType::StoreGlobalVar),
-            ("$store_backtrackable_global_var", 2) => {
+            (atom!("$get_attr_list"), 2) => Some(SystemClauseType::GetAttributedVariableList),
+            (atom!("$get_b_value"), 1) => Some(SystemClauseType::GetBValue),
+            (atom!("$get_lh_from_offset"), 2) => Some(SystemClauseType::GetLiftedHeapFromOffset),
+            (atom!("$get_lh_from_offset_diff"), 3) => Some(SystemClauseType::GetLiftedHeapFromOffsetDiff),
+            (atom!("$get_double_quotes"), 1) => Some(SystemClauseType::GetDoubleQuotes),
+            (atom!("$get_scc_cleaner"), 1) => Some(SystemClauseType::GetSCCCleaner),
+            (atom!("$halt"), 1) => Some(SystemClauseType::Halt),
+            (atom!("$head_is_dynamic"), 2) => Some(SystemClauseType::HeadIsDynamic),
+            (atom!("$install_scc_cleaner"), 2) => Some(SystemClauseType::InstallSCCCleaner),
+            (atom!("$install_inference_counter"), 3) => Some(SystemClauseType::InstallInferenceCounter),
+            (atom!("$lh_length"), 1) => Some(SystemClauseType::LiftedHeapLength),
+            (atom!("$maybe"), 0) => Some(SystemClauseType::Maybe),
+            (atom!("$cpu_now"), 1) => Some(SystemClauseType::CpuNow),
+            (atom!("$current_time"), 1) => Some(SystemClauseType::CurrentTime),
+            (atom!("$module_exists"), 1) => Some(SystemClauseType::ModuleExists),
+            (atom!("$no_such_predicate"), 2) => Some(SystemClauseType::NoSuchPredicate),
+            (atom!("$number_to_chars"), 2) => Some(SystemClauseType::NumberToChars),
+            (atom!("$number_to_codes"), 2) => Some(SystemClauseType::NumberToCodes),
+            (atom!("$op"), 3) => Some(SystemClauseType::OpDeclaration),
+            (atom!("$open"), 7) => Some(SystemClauseType::Open),
+            (atom!("$set_stream_options"), 5) => Some(SystemClauseType::SetStreamOptions),
+            (atom!("$redo_attr_var_binding"), 2) => Some(SystemClauseType::RedoAttrVarBinding),
+            (atom!("$remove_call_policy_check"), 1) => Some(SystemClauseType::RemoveCallPolicyCheck),
+            (atom!("$remove_inference_counter"), 2) => Some(SystemClauseType::RemoveInferenceCounter),
+            (atom!("$restore_cut_policy"), 0) => Some(SystemClauseType::RestoreCutPolicy),
+            (atom!("$set_cp"), 1) => Some(SystemClauseType::SetCutPoint(temp_v!(1))),
+            (atom!("$set_input"), 1) => Some(SystemClauseType::SetInput),
+            (atom!("$set_output"), 1) => Some(SystemClauseType::SetOutput),
+            (atom!("$stream_property"), 3) => Some(SystemClauseType::StreamProperty),
+            (atom!("$set_stream_position"), 2) => Some(SystemClauseType::SetStreamPosition),
+            (atom!("$inference_level"), 2) => Some(SystemClauseType::InferenceLevel),
+            (atom!("$clean_up_block"), 1) => Some(SystemClauseType::CleanUpBlock),
+            (atom!("$erase_ball"), 0) => Some(SystemClauseType::EraseBall),
+            (atom!("$fail"), 0) => Some(SystemClauseType::Fail),
+            (atom!("$get_attr_var_queue_beyond"), 2) => Some(SystemClauseType::GetAttrVarQueueBeyond),
+            (atom!("$get_attr_var_queue_delim"), 1) => Some(SystemClauseType::GetAttrVarQueueDelimiter),
+            (atom!("$get_ball"), 1) => Some(SystemClauseType::GetBall),
+            (atom!("$get_cont_chunk"), 3) => Some(SystemClauseType::GetContinuationChunk),
+            (atom!("$get_current_block"), 1) => Some(SystemClauseType::GetCurrentBlock),
+            (atom!("$get_cp"), 1) => Some(SystemClauseType::GetCutPoint),
+            (atom!("$install_new_block"), 1) => Some(SystemClauseType::InstallNewBlock),
+            (atom!("$quoted_token"), 1) => Some(SystemClauseType::QuotedToken),
+            (atom!("$nextEP"), 3) => Some(SystemClauseType::NextEP),
+            (atom!("$read_query_term"), 5) => Some(SystemClauseType::ReadQueryTerm),
+            (atom!("$read_term"), 5) => Some(SystemClauseType::ReadTerm),
+            (atom!("$read_term_from_chars"), 2) => Some(SystemClauseType::ReadTermFromChars),
+            (atom!("$reset_block"), 1) => Some(SystemClauseType::ResetBlock),
+            (atom!("$reset_cont_marker"), 0) => Some(SystemClauseType::ResetContinuationMarker),
+            (atom!("$return_from_verify_attr"), 0) => Some(SystemClauseType::ReturnFromVerifyAttr),
+            (atom!("$set_ball"), 1) => Some(SystemClauseType::SetBall),
+            (atom!("$set_cp_by_default"), 1) => Some(SystemClauseType::SetCutPointByDefault(temp_v!(1))),
+            (atom!("$set_double_quotes"), 1) => Some(SystemClauseType::SetDoubleQuotes),
+            (atom!("$set_seed"), 1) => Some(SystemClauseType::SetSeed),
+            (atom!("$skip_max_list"), 4) => Some(SystemClauseType::SkipMaxList),
+            (atom!("$sleep"), 1) => Some(SystemClauseType::Sleep),
+            (atom!("$tls_accept_client"), 4) => Some(SystemClauseType::TLSAcceptClient),
+            (atom!("$tls_client_connect"), 3) => Some(SystemClauseType::TLSClientConnect),
+            (atom!("$socket_client_open"), 8) => Some(SystemClauseType::SocketClientOpen),
+            (atom!("$socket_server_open"), 3) => Some(SystemClauseType::SocketServerOpen),
+            (atom!("$socket_server_accept"), 7) => Some(SystemClauseType::SocketServerAccept),
+            (atom!("$socket_server_close"), 1) => Some(SystemClauseType::SocketServerClose),
+            (atom!("$store_global_var"), 2) => Some(SystemClauseType::StoreGlobalVar),
+            (atom!("$store_backtrackable_global_var"), 2) => {
                 Some(SystemClauseType::StoreBacktrackableGlobalVar)
             }
-            ("$term_attributed_variables", 2) => Some(SystemClauseType::TermAttributedVariables),
-            ("$term_variables", 2) => Some(SystemClauseType::TermVariables),
-            ("$truncate_lh_to", 1) => Some(SystemClauseType::TruncateLiftedHeapTo),
-            ("$unwind_environments", 0) => Some(SystemClauseType::UnwindEnvironments),
-            ("$unwind_stack", 0) => Some(SystemClauseType::UnwindStack),
-            ("$unify_with_occurs_check", 2) => Some(SystemClauseType::UnifyWithOccursCheck),
-            ("$directory_files", 2) => Some(SystemClauseType::DirectoryFiles),
-            ("$file_size", 2) => Some(SystemClauseType::FileSize),
-            ("$file_exists", 1) => Some(SystemClauseType::FileExists),
-            ("$directory_exists", 1) => Some(SystemClauseType::DirectoryExists),
-            ("$directory_separator", 1) => Some(SystemClauseType::DirectorySeparator),
-            ("$make_directory", 1) => Some(SystemClauseType::MakeDirectory),
-            ("$make_directory_path", 1) => Some(SystemClauseType::MakeDirectoryPath),
-            ("$delete_file", 1) => Some(SystemClauseType::DeleteFile),
-            ("$rename_file", 2) => Some(SystemClauseType::RenameFile),
-            ("$delete_directory", 1) => Some(SystemClauseType::DeleteDirectory),
-            ("$working_directory", 2) => Some(SystemClauseType::WorkingDirectory),
-            ("$path_canonical", 2) => Some(SystemClauseType::PathCanonical),
-            ("$file_time", 3) => Some(SystemClauseType::FileTime),
-            ("$clause_to_evacuable", 2) => {
+            (atom!("$term_attributed_variables"), 2) => Some(SystemClauseType::TermAttributedVariables),
+            (atom!("$term_variables"), 2) => Some(SystemClauseType::TermVariables),
+            (atom!("$term_variables_under_max_depth"), 3) => Some(SystemClauseType::TermVariablesUnderMaxDepth),
+            (atom!("$truncate_lh_to"), 1) => Some(SystemClauseType::TruncateLiftedHeapTo),
+            (atom!("$unwind_environments"), 0) => Some(SystemClauseType::UnwindEnvironments),
+            (atom!("$unwind_stack"), 0) => Some(SystemClauseType::UnwindStack),
+            (atom!("$unify_with_occurs_check"), 2) => Some(SystemClauseType::UnifyWithOccursCheck),
+            (atom!("$directory_files"), 2) => Some(SystemClauseType::DirectoryFiles),
+            (atom!("$file_size"), 2) => Some(SystemClauseType::FileSize),
+            (atom!("$file_exists"), 1) => Some(SystemClauseType::FileExists),
+            (atom!("$directory_exists"), 1) => Some(SystemClauseType::DirectoryExists),
+            (atom!("$directory_separator"), 1) => Some(SystemClauseType::DirectorySeparator),
+            (atom!("$make_directory"), 1) => Some(SystemClauseType::MakeDirectory),
+            (atom!("$make_directory_path"), 1) => Some(SystemClauseType::MakeDirectoryPath),
+            (atom!("$delete_file"), 1) => Some(SystemClauseType::DeleteFile),
+            (atom!("$rename_file"), 2) => Some(SystemClauseType::RenameFile),
+            (atom!("$delete_directory"), 1) => Some(SystemClauseType::DeleteDirectory),
+            (atom!("$working_directory"), 2) => Some(SystemClauseType::WorkingDirectory),
+            (atom!("$path_canonical"), 2) => Some(SystemClauseType::PathCanonical),
+            (atom!("$file_time"), 3) => Some(SystemClauseType::FileTime),
+            (atom!("$clause_to_evacuable"), 2) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::ClauseToEvacuable))
             }
-            ("$scoped_clause_to_evacuable", 3) => {
+            (atom!("$scoped_clause_to_evacuable"), 3) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::ScopedClauseToEvacuable))
             }
-            ("$conclude_load", 1) => Some(SystemClauseType::REPL(REPLCodePtr::ConcludeLoad)),
-            ("$use_module", 3) => Some(SystemClauseType::REPL(REPLCodePtr::UseModule)),
-            ("$declare_module", 3) => Some(SystemClauseType::REPL(REPLCodePtr::DeclareModule)),
-            ("$load_compiled_library", 3) => {
+            (atom!("$conclude_load"), 1) => Some(SystemClauseType::REPL(REPLCodePtr::ConcludeLoad)),
+            (atom!("$use_module"), 3) => Some(SystemClauseType::REPL(REPLCodePtr::UseModule)),
+            (atom!("$declare_module"), 3) => Some(SystemClauseType::REPL(REPLCodePtr::DeclareModule)),
+            (atom!("$load_compiled_library"), 3) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::LoadCompiledLibrary))
             }
-            ("$push_load_state_payload", 1) => {
+            (atom!("$push_load_state_payload"), 1) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::PushLoadStatePayload))
             }
-            ("$add_in_situ_filename_module", 1) => {
+            (atom!("$add_in_situ_filename_module"), 1) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::AddInSituFilenameModule))
             }
-            ("$asserta", 5) => Some(SystemClauseType::REPL(REPLCodePtr::Asserta)),
-            ("$assertz", 5) => Some(SystemClauseType::REPL(REPLCodePtr::Assertz)),
-            ("$retract_clause", 4) => Some(SystemClauseType::REPL(REPLCodePtr::Retract)),
-            ("$is_consistent_with_term_queue", 4) => Some(SystemClauseType::REPL(
+            (atom!("$asserta"), 5) => Some(SystemClauseType::REPL(REPLCodePtr::Asserta)),
+            (atom!("$assertz"), 5) => Some(SystemClauseType::REPL(REPLCodePtr::Assertz)),
+            (atom!("$retract_clause"), 4) => Some(SystemClauseType::REPL(REPLCodePtr::Retract)),
+            (atom!("$is_consistent_with_term_queue"), 4) => Some(SystemClauseType::REPL(
                 REPLCodePtr::IsConsistentWithTermQueue,
             )),
-            ("$flush_term_queue", 1) => Some(SystemClauseType::REPL(REPLCodePtr::FlushTermQueue)),
-            ("$remove_module_exports", 2) => {
+            (atom!("$flush_term_queue"), 1) => Some(SystemClauseType::REPL(REPLCodePtr::FlushTermQueue)),
+            (atom!("$remove_module_exports"), 2) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::RemoveModuleExports))
             }
-            ("$add_non_counted_backtracking", 3) => Some(SystemClauseType::REPL(
+            (atom!("$add_non_counted_backtracking"), 3) => Some(SystemClauseType::REPL(
                 REPLCodePtr::AddNonCountedBacktracking,
             )),
-            ("$variant", 2) => Some(SystemClauseType::Variant),
-            ("$wam_instructions", 4) => Some(SystemClauseType::WAMInstructions),
-            ("$write_term", 7) => Some(SystemClauseType::WriteTerm),
-            ("$write_term_to_chars", 7) => Some(SystemClauseType::WriteTermToChars),
-            ("$scryer_prolog_version", 1) => Some(SystemClauseType::ScryerPrologVersion),
-            ("$crypto_random_byte", 1) => Some(SystemClauseType::CryptoRandomByte),
-            ("$crypto_data_hash", 4) => Some(SystemClauseType::CryptoDataHash),
-            ("$crypto_data_hkdf", 7) => Some(SystemClauseType::CryptoDataHKDF),
-            ("$crypto_password_hash", 4) => Some(SystemClauseType::CryptoPasswordHash),
-            ("$crypto_data_encrypt", 7) => Some(SystemClauseType::CryptoDataEncrypt),
-            ("$crypto_data_decrypt", 6) => Some(SystemClauseType::CryptoDataDecrypt),
-            ("$crypto_curve_scalar_mult", 5) => Some(SystemClauseType::CryptoCurveScalarMult),
-            ("$ed25519_sign", 4) => Some(SystemClauseType::Ed25519Sign),
-            ("$ed25519_verify", 4) => Some(SystemClauseType::Ed25519Verify),
-            ("$ed25519_new_keypair", 1) => Some(SystemClauseType::Ed25519NewKeyPair),
-            ("$ed25519_keypair_public_key", 2) => Some(SystemClauseType::Ed25519KeyPairPublicKey),
-            ("$curve25519_scalar_mult", 3) => Some(SystemClauseType::Curve25519ScalarMult),
-            ("$first_non_octet", 2) => Some(SystemClauseType::FirstNonOctet),
-            ("$load_html", 3) => Some(SystemClauseType::LoadHTML),
-            ("$load_xml", 3) => Some(SystemClauseType::LoadXML),
-            ("$getenv", 2) => Some(SystemClauseType::GetEnv),
-            ("$setenv", 2) => Some(SystemClauseType::SetEnv),
-            ("$unsetenv", 1) => Some(SystemClauseType::UnsetEnv),
-            ("$shell", 2) => Some(SystemClauseType::Shell),
-            ("$pid", 1) => Some(SystemClauseType::PID),
-            ("$chars_base64", 4) => Some(SystemClauseType::CharsBase64),
-            ("$load_library_as_stream", 3) => Some(SystemClauseType::LoadLibraryAsStream),
-            ("$push_load_context", 2) => Some(SystemClauseType::REPL(REPLCodePtr::PushLoadContext)),
-            ("$pop_load_state_payload", 1) => {
+            (atom!("$wam_instructions"), 4) => Some(SystemClauseType::WAMInstructions),
+            (atom!("$write_term"), 7) => Some(SystemClauseType::WriteTerm),
+            (atom!("$write_term_to_chars"), 7) => Some(SystemClauseType::WriteTermToChars),
+            (atom!("$scryer_prolog_version"), 1) => Some(SystemClauseType::ScryerPrologVersion),
+            (atom!("$crypto_random_byte"), 1) => Some(SystemClauseType::CryptoRandomByte),
+            (atom!("$crypto_data_hash"), 4) => Some(SystemClauseType::CryptoDataHash),
+            (atom!("$crypto_data_hkdf"), 7) => Some(SystemClauseType::CryptoDataHKDF),
+            (atom!("$crypto_password_hash"), 4) => Some(SystemClauseType::CryptoPasswordHash),
+            (atom!("$crypto_data_encrypt"), 7) => Some(SystemClauseType::CryptoDataEncrypt),
+            (atom!("$crypto_data_decrypt"), 6) => Some(SystemClauseType::CryptoDataDecrypt),
+            (atom!("$crypto_curve_scalar_mult"), 5) => Some(SystemClauseType::CryptoCurveScalarMult),
+            (atom!("$ed25519_sign"), 4) => Some(SystemClauseType::Ed25519Sign),
+            (atom!("$ed25519_verify"), 4) => Some(SystemClauseType::Ed25519Verify),
+            (atom!("$ed25519_new_keypair"), 1) => Some(SystemClauseType::Ed25519NewKeyPair),
+            (atom!("$ed25519_keypair_public_key"), 2) => Some(SystemClauseType::Ed25519KeyPairPublicKey),
+            (atom!("$curve25519_scalar_mult"), 3) => Some(SystemClauseType::Curve25519ScalarMult),
+            (atom!("$load_html"), 3) => Some(SystemClauseType::LoadHTML),
+            (atom!("$load_xml"), 3) => Some(SystemClauseType::LoadXML),
+            (atom!("$getenv"), 2) => Some(SystemClauseType::GetEnv),
+            (atom!("$setenv"), 2) => Some(SystemClauseType::SetEnv),
+            (atom!("$unsetenv"), 1) => Some(SystemClauseType::UnsetEnv),
+            (atom!("$pid"), 1) => Some(SystemClauseType::PID),
+            (atom!("$chars_base64"), 4) => Some(SystemClauseType::CharsBase64),
+            (atom!("$load_library_as_stream"), 3) => Some(SystemClauseType::LoadLibraryAsStream),
+            (atom!("$push_load_context"), 2) => Some(SystemClauseType::REPL(REPLCodePtr::PushLoadContext)),
+            (atom!("$pop_load_state_payload"), 1) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::PopLoadStatePayload))
             }
-            ("$pop_load_context", 0) => Some(SystemClauseType::REPL(REPLCodePtr::PopLoadContext)),
-            ("$prolog_lc_source", 1) => {
+            (atom!("$pop_load_context"), 0) => Some(SystemClauseType::REPL(REPLCodePtr::PopLoadContext)),
+            (atom!("$prolog_lc_source"), 1) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::LoadContextSource))
             }
-            ("$prolog_lc_file", 1) => Some(SystemClauseType::REPL(REPLCodePtr::LoadContextFile)),
-            ("$prolog_lc_dir", 1) => {
+            (atom!("$prolog_lc_file"), 1) => Some(SystemClauseType::REPL(REPLCodePtr::LoadContextFile)),
+            (atom!("$prolog_lc_dir"), 1) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::LoadContextDirectory))
             }
-            ("$prolog_lc_module", 1) => {
+            (atom!("$prolog_lc_module"), 1) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::LoadContextModule))
             }
-            ("$prolog_lc_stream", 1) => {
+            (atom!("$prolog_lc_stream"), 1) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::LoadContextStream))
             }
-            ("$cpp_meta_predicate_property", 4) => {
+            (atom!("$cpp_meta_predicate_property"), 4) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::MetaPredicateProperty))
             }
-            ("$cpp_built_in_property", 2) => {
+            (atom!("$cpp_built_in_property"), 2) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::BuiltInProperty))
             }
-            ("$cpp_dynamic_property", 3) => {
+            (atom!("$cpp_dynamic_property"), 3) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::DynamicProperty))
             }
-            ("$cpp_multifile_property", 3) => {
+            (atom!("$cpp_multifile_property"), 3) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::MultifileProperty))
             }
-            ("$cpp_discontiguous_property", 3) => {
+            (atom!("$cpp_discontiguous_property"), 3) => {
                 Some(SystemClauseType::REPL(REPLCodePtr::DiscontiguousProperty))
             }
-            ("$devour_whitespace", 1) => Some(SystemClauseType::DevourWhitespace),
-            ("$is_sto_enabled", 1) => Some(SystemClauseType::IsSTOEnabled),
-            ("$set_sto_as_unify", 0) => Some(SystemClauseType::SetSTOAsUnify),
-            ("$set_nsto_as_unify", 0) => Some(SystemClauseType::SetNSTOAsUnify),
-            ("$set_sto_with_error_as_unify", 0) => Some(SystemClauseType::SetSTOWithErrorAsUnify),
-            ("$home_directory", 1) => Some(SystemClauseType::HomeDirectory),
-            ("$debug_hook", 0) => Some(SystemClauseType::DebugHook),
-            ("$popcount", 2) => Some(SystemClauseType::PopCount),
+            (atom!("$devour_whitespace"), 1) => Some(SystemClauseType::DevourWhitespace),
+            (atom!("$is_sto_enabled"), 1) => Some(SystemClauseType::IsSTOEnabled),
+            (atom!("$set_sto_as_unify"), 0) => Some(SystemClauseType::SetSTOAsUnify),
+            (atom!("$set_nsto_as_unify"), 0) => Some(SystemClauseType::SetNSTOAsUnify),
+            (atom!("$set_sto_with_error_as_unify"), 0) => Some(SystemClauseType::SetSTOWithErrorAsUnify),
+            (atom!("$home_directory"), 1) => Some(SystemClauseType::HomeDirectory),
+            (atom!("$debug_hook"), 0) => Some(SystemClauseType::DebugHook),
             _ => None,
         }
     }
 }
 
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub(crate) enum BuiltInClauseType {
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum BuiltInClauseType {
     AcyclicTerm,
     Arg,
     Compare,
@@ -895,31 +868,30 @@ pub(crate) enum BuiltInClauseType {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
-pub(crate) enum ClauseType {
+pub enum ClauseType {
     BuiltIn(BuiltInClauseType),
     CallN,
     Inlined(InlinedClauseType),
-    Named(ClauseName, usize, CodeIndex), // name, arity, index.
-    Op(ClauseName, SharedOpDesc, CodeIndex),
+    Named(Atom, usize, CodeIndex), // name, arity, index.
     System(SystemClauseType),
 }
 
 impl BuiltInClauseType {
-    pub(crate) fn name(&self) -> ClauseName {
+    pub(crate) fn name(&self) -> Atom {
         match self {
-            &BuiltInClauseType::AcyclicTerm => clause_name!("acyclic_term"),
-            &BuiltInClauseType::Arg => clause_name!("arg"),
-            &BuiltInClauseType::Compare => clause_name!("compare"),
-            &BuiltInClauseType::CompareTerm(qt) => clause_name!(qt.name()),
-            &BuiltInClauseType::CopyTerm => clause_name!("copy_term"),
-            &BuiltInClauseType::Eq => clause_name!("=="),
-            &BuiltInClauseType::Functor => clause_name!("functor"),
-            &BuiltInClauseType::Ground => clause_name!("ground"),
-            &BuiltInClauseType::Is(..) => clause_name!("is"),
-            &BuiltInClauseType::KeySort => clause_name!("keysort"),
-            &BuiltInClauseType::NotEq => clause_name!("\\=="),
-            &BuiltInClauseType::Read => clause_name!("read"),
-            &BuiltInClauseType::Sort => clause_name!("sort"),
+            &BuiltInClauseType::AcyclicTerm => atom!("acyclic_term"),
+            &BuiltInClauseType::Arg => atom!("arg"),
+            &BuiltInClauseType::Compare => atom!("compare"),
+            &BuiltInClauseType::CompareTerm(qt) => qt.name(),
+            &BuiltInClauseType::CopyTerm => atom!("copy_term"),
+            &BuiltInClauseType::Eq => atom!("=="),
+            &BuiltInClauseType::Functor => atom!("functor"),
+            &BuiltInClauseType::Ground => atom!("ground"),
+            &BuiltInClauseType::Is(..) => atom!("is"),
+            &BuiltInClauseType::KeySort => atom!("keysort"),
+            &BuiltInClauseType::NotEq => atom!("\\=="),
+            &BuiltInClauseType::Read => atom!("read"),
+            &BuiltInClauseType::Sort => atom!("sort"),
         }
     }
 
@@ -936,54 +908,35 @@ impl BuiltInClauseType {
             &BuiltInClauseType::Is(..) => 2,
             &BuiltInClauseType::KeySort => 2,
             &BuiltInClauseType::NotEq => 2,
-            &BuiltInClauseType::Read => 2,
+            &BuiltInClauseType::Read => 1,
             &BuiltInClauseType::Sort => 2,
         }
     }
 }
 
 impl ClauseType {
-    pub(crate) fn spec(&self) -> Option<SharedOpDesc> {
-        match self {
-            &ClauseType::Op(_, ref spec, _) => Some(spec.clone()),
-            &ClauseType::Inlined(InlinedClauseType::CompareNumber(..))
-            | &ClauseType::BuiltIn(BuiltInClauseType::Is(..))
-            | &ClauseType::BuiltIn(BuiltInClauseType::CompareTerm(_))
-            | &ClauseType::BuiltIn(BuiltInClauseType::NotEq)
-            | &ClauseType::BuiltIn(BuiltInClauseType::Eq) => Some(SharedOpDesc::new(700, XFX)),
-            _ => None,
-        }
-    }
-
-    pub(crate) fn name(&self) -> ClauseName {
+    pub(crate) fn name(&self) -> Atom {
         match self {
             &ClauseType::BuiltIn(ref built_in) => built_in.name(),
-            &ClauseType::CallN => clause_name!("$call"),
-            &ClauseType::Inlined(ref inlined) => clause_name!(inlined.name()),
-            &ClauseType::Op(ref name, ..) => name.clone(),
-            &ClauseType::Named(ref name, ..) => name.clone(),
+            &ClauseType::CallN => atom!("$call"),
+            &ClauseType::Inlined(ref inlined) => inlined.name(),
+            &ClauseType::Named(name, ..) => name,
             &ClauseType::System(ref system) => system.name(),
         }
     }
 
-    pub(crate) fn from(name: ClauseName, arity: usize, spec: Option<SharedOpDesc>) -> Self {
-        CLAUSE_TYPE_FORMS
-            .borrow()
-            .get(&(name.as_str(), arity))
-            .cloned()
-            .unwrap_or_else(|| {
-                SystemClauseType::from(name.as_str(), arity)
-                    .map(ClauseType::System)
-                    .unwrap_or_else(|| {
-                        if let Some(spec) = spec {
-                            ClauseType::Op(name, spec, CodeIndex::default())
-                        } else if name.as_str() == "$call" {
-                            ClauseType::CallN
-                        } else {
-                            ClauseType::Named(name, arity, CodeIndex::default())
-                        }
-                    })
-            })
+    pub(crate) fn from(name: Atom, arity: usize) -> Self {
+        clause_type_form(name, arity).unwrap_or_else(|| {
+            SystemClauseType::from(name, arity)
+                .map(ClauseType::System)
+                .unwrap_or_else(|| {
+                    if name == atom!("$call") {
+                        ClauseType::CallN
+                    } else {
+                        ClauseType::Named(name, arity, CodeIndex::default())
+                    }
+                })
+        })
     }
 }
 
index a678549b12618dda3fda026eac016117ea26bc60..243974aa1b6da9f526d7443179eac5971cb69f21 100644 (file)
@@ -1,7 +1,6 @@
-/// Code generation to WAM-like instructions.
-use prolog_parser::ast::*;
-use prolog_parser::tabled_rc::TabledData;
-use prolog_parser::{perm_v, temp_v};
+use crate::atom_table::*;
+use crate::parser::ast::*;
+use crate::{perm_v, temp_v};
 
 use crate::allocator::*;
 use crate::arithmetic::*;
@@ -12,6 +11,7 @@ use crate::indexing::*;
 use crate::instructions::*;
 use crate::iterators::*;
 use crate::targets::*;
+use crate::types::*;
 
 use crate::machine::machine_errors::*;
 
@@ -108,7 +108,11 @@ impl CodeGenSettings {
             ChoiceInstruction::DynamicInternalElse(
                 global_clock_time,
                 Death::Infinity,
-                if offset == 0 { NextOrFail::Next(0) } else { NextOrFail::Next(offset) },
+                if offset == 0 {
+                    NextOrFail::Next(0)
+                } else {
+                    NextOrFail::Next(offset)
+                },
             )
         } else {
             ChoiceInstruction::TryMeElse(offset)
@@ -120,7 +124,11 @@ impl CodeGenSettings {
             ChoiceInstruction::DynamicElse(
                 global_clock_tick,
                 Death::Infinity,
-                if offset == 0 { NextOrFail::Next(0) } else { NextOrFail::Next(offset) },
+                if offset == 0 {
+                    NextOrFail::Next(0)
+                } else {
+                    NextOrFail::Next(offset)
+                },
             )
         } else {
             ChoiceInstruction::TryMeElse(offset)
@@ -132,7 +140,11 @@ impl CodeGenSettings {
             ChoiceInstruction::DynamicInternalElse(
                 global_clock_tick,
                 Death::Infinity,
-                if offset == 0 { NextOrFail::Next(0) } else { NextOrFail::Next(offset) },
+                if offset == 0 {
+                    NextOrFail::Next(0)
+                } else {
+                    NextOrFail::Next(offset)
+                },
             )
         } else {
             ChoiceInstruction::RetryMeElse(offset)
@@ -144,7 +156,11 @@ impl CodeGenSettings {
             ChoiceInstruction::DynamicElse(
                 global_clock_tick,
                 Death::Infinity,
-                if offset == 0 { NextOrFail::Next(0) } else { NextOrFail::Next(offset) },
+                if offset == 0 {
+                    NextOrFail::Next(0)
+                } else {
+                    NextOrFail::Next(offset)
+                },
             )
         } else if self.non_counted_bt {
             ChoiceInstruction::DefaultRetryMeElse(offset)
@@ -169,11 +185,7 @@ impl CodeGenSettings {
 
     pub(crate) fn trust_me(&self) -> ChoiceInstruction {
         if let Some(global_clock_tick) = self.global_clock_tick {
-            ChoiceInstruction::DynamicElse(
-                global_clock_tick,
-                Death::Infinity,
-                NextOrFail::Fail(0),
-            )
+            ChoiceInstruction::DynamicElse(global_clock_tick, Death::Infinity, NextOrFail::Fail(0))
         } else if self.non_counted_bt {
             ChoiceInstruction::DefaultTrustMe(0)
         } else {
@@ -183,18 +195,18 @@ impl CodeGenSettings {
 }
 
 #[derive(Debug)]
-pub(crate) struct CodeGenerator<TermMarker> {
-    atom_tbl: TabledData<Atom>,
+pub(crate) struct CodeGenerator<'a, TermMarker> {
+    pub(crate) atom_tbl: &'a mut AtomTable,
     marker: TermMarker,
-    pub(crate) var_count: IndexMap<Rc<Var>, usize>,
+    pub(crate) var_count: IndexMap<Rc<String>, usize>,
     settings: CodeGenSettings,
     pub(crate) skeleton: PredicateSkeleton,
     pub(crate) jmp_by_locs: Vec<usize>,
     global_jmp_by_locs_offset: usize,
 }
 
-impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
-    pub(crate) fn new(atom_tbl: TabledData<Atom>, settings: CodeGenSettings) -> Self {
+impl<'a, 'b: 'a, TermMarker: Allocator<'a>> CodeGenerator<'b, TermMarker> {
+    pub(crate) fn new(atom_tbl: &'b mut AtomTable, settings: CodeGenSettings) -> Self {
         CodeGenerator {
             atom_tbl,
             marker: Allocator::new(),
@@ -215,13 +227,13 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
         }
     }
 
-    fn get_var_count(&self, var: &'a Var) -> usize {
+    fn get_var_count(&self, var: &'a String) -> usize {
         *self.var_count.get(var).unwrap()
     }
 
     fn mark_var_in_non_callable(
         &mut self,
-        name: Rc<Var>,
+        name: Rc<String>,
         term_loc: GenContext,
         vr: &'a Cell<VarReg>,
         code: &mut Code,
@@ -239,7 +251,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
 
     fn mark_non_callable(
         &mut self,
-        name: Rc<Var>,
+        name: Rc<String>,
         arg: usize,
         term_loc: GenContext,
         vr: &'a Cell<VarReg>,
@@ -261,7 +273,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
 
     fn add_or_increment_void_instr<Target>(target: &mut Vec<Target>)
     where
-        Target: CompilationTarget<'a>,
+        Target: crate::targets::CompilationTarget<'a>,
     {
         if let Some(ref mut instr) = target.last_mut() {
             if Target::is_void_instr(&*instr) {
@@ -273,10 +285,10 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
         target.push(Target::to_void(1));
     }
 
-    fn deep_var_instr<Target: CompilationTarget<'a>>(
+    fn deep_var_instr<Target: crate::targets::CompilationTarget<'a>>(
         &mut self,
         cell: &'a Cell<VarReg>,
-        var: &'a Rc<Var>,
+        var: &'a Rc<String>,
         term_loc: GenContext,
         is_exposed: bool,
         target: &mut Vec<Target>,
@@ -289,7 +301,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
         }
     }
 
-    fn subterm_to_instr<Target: CompilationTarget<'a>>(
+    fn subterm_to_instr<Target: crate::targets::CompilationTarget<'a>>(
         &mut self,
         subterm: &'a Term,
         term_loc: GenContext,
@@ -303,12 +315,14 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
             &Term::AnonVar => {
                 Self::add_or_increment_void_instr(target);
             }
-            &Term::Cons(ref cell, _, _) | &Term::Clause(ref cell, _, _, _) => {
+            &Term::Cons(ref cell, ..)
+            | &Term::Clause(ref cell, ..)
+            | Term::PartialString(ref cell, ..) => {
                 self.marker
                     .mark_non_var(Level::Deep, term_loc, cell, target);
                 target.push(Target::clause_arg_to_instr(cell.get()));
             }
-            &Term::Constant(_, ref constant) => {
+            &Term::Literal(_, ref constant) => {
                 target.push(Target::constant_subterm(constant.clone()));
             }
             &Term::Var(ref cell, ref var) => {
@@ -324,7 +338,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
         is_exposed: bool,
     ) -> Vec<Target>
     where
-        Target: CompilationTarget<'a>,
+        Target: crate::targets::CompilationTarget<'a>,
         Iter: Iterator<Item = TermRef<'a>>,
     {
         let mut target = Vec::new();
@@ -343,7 +357,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
                     target.push(Target::to_structure(ct, terms.len(), cell.get()));
 
                     for subterm in terms {
-                        self.subterm_to_instr(subterm.as_ref(), term_loc, is_exposed, &mut target);
+                        self.subterm_to_instr(subterm, term_loc, is_exposed, &mut target);
                     }
                 }
                 TermRef::Cons(lvl, cell, head, tail) => {
@@ -353,13 +367,13 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
                     self.subterm_to_instr(head, term_loc, is_exposed, &mut target);
                     self.subterm_to_instr(tail, term_loc, is_exposed, &mut target);
                 }
-                TermRef::Constant(lvl @ Level::Shallow, cell, Constant::String(ref string)) => {
+                TermRef::Literal(lvl @ Level::Shallow, cell, Literal::String(ref string)) => {
                     self.marker.mark_non_var(lvl, term_loc, cell, &mut target);
-                    target.push(Target::to_pstr(lvl, string.to_string(), cell.get(), false));
+                    target.push(Target::to_pstr(lvl, *string, cell.get(), false));
                 }
-                TermRef::Constant(lvl @ Level::Shallow, cell, constant) => {
+                TermRef::Literal(lvl @ Level::Shallow, cell, constant) => {
                     self.marker.mark_non_var(lvl, term_loc, cell, &mut target);
-                    target.push(Target::to_constant(lvl, constant.clone(), cell.get()));
+                    target.push(Target::to_constant(lvl, *constant, cell.get()));
                 }
                 TermRef::PartialString(lvl, cell, string, tail) => {
                     self.marker.mark_non_var(lvl, term_loc, cell, &mut target);
@@ -418,6 +432,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
                 };
 
                 self.update_var_count(chunked_term.post_order_iter());
+
                 vs.mark_vars_in_chunk(chunked_term.post_order_iter(), lt_arity, term_loc);
             }
         }
@@ -476,7 +491,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
     fn compile_inlined(
         &mut self,
         ct: &InlinedClauseType,
-        terms: &'a Vec<Box<Term>>,
+        terms: &'a Vec<Term>,
         term_loc: GenContext,
         code: &mut Code,
     ) -> Result<(), CompilationError> {
@@ -484,16 +499,16 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
             &InlinedClauseType::CompareNumber(cmp, ..) => {
                 self.marker.reset_arg(2);
 
-                let (mut lcode, at_1) = self.call_arith_eval(terms[0].as_ref(), 1)?;
-                let (mut rcode, at_2) = self.call_arith_eval(terms[1].as_ref(), 2)?;
+                let (mut lcode, at_1) = self.call_arith_eval(&terms[0], 1)?;
+                let (mut rcode, at_2) = self.call_arith_eval(&terms[1], 2)?;
 
-                let at_1 = if let &Term::Var(ref vr, ref name) = terms[0].as_ref() {
+                let at_1 = if let &Term::Var(ref vr, ref name) = &terms[0] {
                     ArithmeticTerm::Reg(self.mark_non_callable(name.clone(), 1, term_loc, vr, code))
                 } else {
                     at_1.unwrap_or(interm!(1))
                 };
 
-                let at_2 = if let &Term::Var(ref vr, ref name) = terms[1].as_ref() {
+                let at_2 = if let &Term::Var(ref vr, ref name) = &terms[1] {
                     ArithmeticTerm::Reg(self.mark_non_callable(name.clone(), 2, term_loc, vr, code))
                 } else {
                     at_2.unwrap_or(interm!(2))
@@ -504,10 +519,10 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
 
                 code.push(compare_number_instr!(cmp, at_1, at_2));
             }
-            &InlinedClauseType::IsAtom(..) => match terms[0].as_ref() {
-                &Term::Constant(_, Constant::Char(_))
-                | &Term::Constant(_, Constant::EmptyList)
-                | &Term::Constant(_, Constant::Atom(..)) => {
+            &InlinedClauseType::IsAtom(..) => match &terms[0] {
+                &Term::Literal(_, Literal::Char(_))
+                | &Term::Literal(_, Literal::Atom(atom!("[]")))
+                | &Term::Literal(_, Literal::Atom(..)) => {
                     code.push(succeed!());
                 }
                 &Term::Var(ref vr, ref name) => {
@@ -519,11 +534,11 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
                     code.push(fail!());
                 }
             },
-            &InlinedClauseType::IsAtomic(..) => match terms[0].as_ref() {
-                &Term::AnonVar | &Term::Clause(..) | &Term::Cons(..) => {
+            &InlinedClauseType::IsAtomic(..) => match &terms[0] {
+                &Term::AnonVar | &Term::Clause(..) | &Term::Cons(..) | &Term::PartialString(..) => {
                     code.push(fail!());
                 }
-                &Term::Constant(..) => {
+                &Term::Literal(..) => {
                     code.push(succeed!());
                 }
                 &Term::Var(ref vr, ref name) => {
@@ -532,7 +547,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
                     code.push(is_atomic!(r));
                 }
             },
-            &InlinedClauseType::IsCompound(..) => match terms[0].as_ref() {
+            &InlinedClauseType::IsCompound(..) => match &terms[0] {
                 &Term::Clause(..) | &Term::Cons(..) => {
                     code.push(succeed!());
                 }
@@ -545,8 +560,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
                     code.push(fail!());
                 }
             },
-            &InlinedClauseType::IsRational(..) => match terms[0].as_ref() {
-                &Term::Constant(_, Constant::Rational(_)) => {
+            &InlinedClauseType::IsRational(..) => match &terms[0] {
+                &Term::Literal(_, Literal::Rational(_)) => {
                     code.push(succeed!());
                 }
                 &Term::Var(ref vr, ref name) => {
@@ -558,8 +573,8 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
                     code.push(fail!());
                 }
             },
-            &InlinedClauseType::IsFloat(..) => match terms[0].as_ref() {
-                &Term::Constant(_, Constant::Float(_)) => {
+            &InlinedClauseType::IsFloat(..) => match &terms[0] {
+                &Term::Literal(_, Literal::Float(_)) => {
                     code.push(succeed!());
                 }
                 &Term::Var(ref vr, ref name) => {
@@ -571,12 +586,12 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
                     code.push(fail!());
                 }
             },
-            &InlinedClauseType::IsNumber(..) => match terms[0].as_ref() {
-                &Term::Constant(_, Constant::Float(_))
-                | &Term::Constant(_, Constant::Rational(_))
-                | &Term::Constant(_, Constant::Integer(_))
-                | &Term::Constant(_, Constant::Fixnum(_))
-                | &Term::Constant(_, Constant::Usize(_)) => {
+            &InlinedClauseType::IsNumber(..) => match &terms[0] {
+                &Term::Literal(_, Literal::Float(_))
+                | &Term::Literal(_, Literal::Rational(_))
+                | &Term::Literal(_, Literal::Integer(_))
+                | &Term::Literal(_, Literal::Fixnum(_)) => {
+                    // | &Term::Literal(_, Literal::Usize(_)) => {
                     code.push(succeed!());
                 }
                 &Term::Var(ref vr, ref name) => {
@@ -588,7 +603,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
                     code.push(fail!());
                 }
             },
-            &InlinedClauseType::IsNonVar(..) => match terms[0].as_ref() {
+            &InlinedClauseType::IsNonVar(..) => match &terms[0] {
                 &Term::AnonVar => {
                     code.push(fail!());
                 }
@@ -601,10 +616,9 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
                     code.push(succeed!());
                 }
             },
-            &InlinedClauseType::IsInteger(..) => match terms[0].as_ref() {
-                &Term::Constant(_, Constant::Integer(_))
-                | &Term::Constant(_, Constant::Fixnum(_))
-                | &Term::Constant(_, Constant::Usize(_)) => {
+            &InlinedClauseType::IsInteger(..) => match &terms[0] {
+                &Term::Literal(_, Literal::Integer(_)) | &Term::Literal(_, Literal::Fixnum(_)) => {
+                    // | &Term::Literal(_, Literal::Usize(_)) => {
                     code.push(succeed!());
                 }
                 &Term::Var(ref vr, ref name) => {
@@ -616,8 +630,11 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
                     code.push(fail!());
                 }
             },
-            &InlinedClauseType::IsVar(..) => match terms[0].as_ref() {
-                &Term::Constant(..) | &Term::Clause(..) | &Term::Cons(..) => {
+            &InlinedClauseType::IsVar(..) => match &terms[0] {
+                &Term::Literal(..)
+                | &Term::Clause(..)
+                | &Term::Cons(..)
+                | &Term::PartialString(..) => {
                     code.push(fail!());
                 }
                 &Term::AnonVar => {
@@ -635,7 +652,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
     }
 
     fn call_arith_eval(
-        &self,
+        &mut self,
         term: &'a Term,
         target_int: usize,
     ) -> Result<ArithCont, ArithmeticError> {
@@ -645,17 +662,17 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
 
     fn compile_is_call(
         &mut self,
-        terms: &'a Vec<Box<Term>>,
+        terms: &'a Vec<Term>,
         code: &mut Code,
         term_loc: GenContext,
         use_default_call_policy: bool,
     ) -> Result<(), CompilationError> {
-        let (mut acode, at) = self.call_arith_eval(terms[1].as_ref(), 1)?;
+        let (mut acode, at) = self.call_arith_eval(&terms[1], 1)?;
         code.append(&mut acode);
 
         self.marker.reset_arg(2);
 
-        match terms[0].as_ref() {
+        match &terms[0] {
             &Term::Var(ref vr, ref name) => {
                 let mut target = vec![];
 
@@ -666,31 +683,22 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
                     code.extend(target.into_iter().map(Line::Query));
                 }
             }
-            &Term::Constant(_, ref c @ Constant::Integer(_))
-            | &Term::Constant(_, ref c @ Constant::Fixnum(_)) => {
-                code.push(Line::Query(put_constant!(
-                    Level::Shallow,
-                    c.clone(),
-                    temp_v!(1)
-                )));
+            &Term::Literal(_, c @ Literal::Integer(_))
+            | &Term::Literal(_, c @ Literal::Fixnum(_)) => {
+                let v = HeapCellValue::from(c);
+                code.push(Line::Query(put_constant!(Level::Shallow, v, temp_v!(1))));
 
                 self.marker.advance_arg();
             }
-            &Term::Constant(_, ref c @ Constant::Float(_)) => {
-                code.push(Line::Query(put_constant!(
-                    Level::Shallow,
-                    c.clone(),
-                    temp_v!(1)
-                )));
+            &Term::Literal(_, c @ Literal::Float(_)) => {
+                let v = HeapCellValue::from(c);
+                code.push(Line::Query(put_constant!(Level::Shallow, v, temp_v!(1))));
 
                 self.marker.advance_arg();
             }
-            &Term::Constant(_, ref c @ Constant::Rational(_)) => {
-                code.push(Line::Query(put_constant!(
-                    Level::Shallow,
-                    c.clone(),
-                    temp_v!(1)
-                )));
+            &Term::Literal(_, c @ Literal::Rational(_)) => {
+                let v = HeapCellValue::from(c);
+                code.push(Line::Query(put_constant!(Level::Shallow, v, temp_v!(1))));
 
                 self.marker.advance_arg();
             }
@@ -700,7 +708,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
             }
         }
 
-        let at = if let &Term::Var(ref vr, ref name) = terms[1].as_ref() {
+        let at = if let &Term::Var(ref vr, ref name) = &terms[1] {
             ArithmeticTerm::Reg(self.mark_non_callable(name.clone(), 2, term_loc, vr, code))
         } else {
             at.unwrap_or(interm!(1))
@@ -724,7 +732,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
         &mut self,
         code: &mut Code,
         cell: &'a Cell<VarReg>,
-        var: Rc<Var>,
+        var: Rc<String>,
         term_loc: GenContext,
     ) {
         let mut target = Vec::new();
@@ -837,9 +845,9 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
         }
     }
 
-    pub(crate) fn compile_rule<'b: 'a>(
+    pub(crate) fn compile_rule<'c: 'a>(
         &mut self,
-        rule: &'b Rule,
+        rule: &'c Rule,
     ) -> Result<Code, CompilationError> {
         let iter = ChunkedIterator::from_rule(rule);
         let conjunct_info = self.collect_var_data(iter);
@@ -897,10 +905,11 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
         UnsafeVarMarker::from_safe_vars(safe_vars)
     }
 
-    pub(crate) fn compile_fact<'b: 'a>(&mut self, term: &'b Term) -> Code {
+    pub(crate) fn compile_fact<'c: 'a>(&mut self, term: &'c Term) -> Code {
         self.update_var_count(post_order_iter(term));
 
         let mut vs = VariableFixtures::new();
+
         vs.mark_vars_in_chunk(post_order_iter(term), term.arity(), GenContext::Head);
 
         vs.populate_restricting_sets();
@@ -908,7 +917,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
 
         let mut code = Vec::new();
 
-        if let &Term::Clause(_, _, ref args, _) = term {
+        if let &Term::Clause(_, _, ref args) = term {
             self.marker.reset_at_head(args);
 
             let iter = FactInstruction::iter(term);
@@ -979,7 +988,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
                         break;
                     }
                 }
-                match **arg {
+                match arg {
                     Term::AnonVar | Term::Var(..) => (),
                     _ => {
                         match optimal_index {
@@ -1003,7 +1012,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
             for (right_index, clause) in clauses.iter().enumerate() {
                 // Can unwrap safely.
                 if let Some(arg) = clause.args().unwrap().iter().nth(optimal_index) {
-                    match **arg {
+                    match arg {
                         Term::Var(..) | Term::AnonVar => {
                             if left_index < right_index {
                                 subseqs.push((left_index, right_index));
@@ -1025,17 +1034,13 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
         subseqs
     }
 
-    fn compile_pred_subseq<'b: 'a, I: Indexer>(
+    fn compile_pred_subseq<'c: 'a, I: Indexer>(
         &mut self,
-        clauses: &'b [PredicateClause],
+        clauses: &'c [PredicateClause],
         optimal_index: usize,
     ) -> Result<Code, CompilationError> {
         let mut code = VecDeque::new();
-        let mut code_offsets = CodeOffsets::new(
-            self.atom_tbl.clone(),
-            I::new(),
-            optimal_index + 1,
-        );
+        let mut code_offsets = CodeOffsets::new(I::new(), optimal_index + 1);
 
         let mut skip_stub_try_me_else = false;
         let jmp_by_locs_len = self.jmp_by_locs.len();
@@ -1087,7 +1092,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
 
             if let Some(arg) = arg {
                 let index = code.len();
-                code_offsets.index_term(arg, index, &mut clause_index_info);
+                code_offsets.index_term(arg, index, &mut clause_index_info, self.atom_tbl);
             }
 
             if !(clauses.len() == 1 && self.settings.is_extensible) {
@@ -1122,9 +1127,9 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
         Ok(Vec::from(code))
     }
 
-    pub(crate) fn compile_predicate<'b: 'a>(
+    pub(crate) fn compile_predicate<'c: 'a>(
         &mut self,
-        clauses: &'b Vec<PredicateClause>,
+        clauses: &'c Vec<PredicateClause>,
     ) -> Result<Code, CompilationError> {
         let mut code = Code::new();
 
index 7525a79ad0341140aa65434b15510b041512d1da..2b67c56d830c4c959a631b953843b818b27d0d0c 100644 (file)
@@ -1,13 +1,13 @@
 use indexmap::IndexMap;
 
-use prolog_parser::ast::*;
-use prolog_parser::temp_v;
-
 use crate::allocator::*;
 use crate::fixtures::*;
-use crate::forms::*;
+use crate::forms::Level;
 use crate::machine::machine_indices::*;
-use crate::targets::*;
+use crate::parser::ast::*;
+use crate::targets::CompilationTarget;
+
+use crate::temp_v;
 
 use std::cell::Cell;
 use std::collections::BTreeSet;
@@ -15,23 +15,23 @@ use std::rc::Rc;
 
 #[derive(Debug)]
 pub(crate) struct DebrayAllocator {
-    bindings: IndexMap<Rc<Var>, VarData>,
+    bindings: IndexMap<Rc<String>, VarData>,
     arg_c: usize,
     temp_lb: usize,
     arity: usize, // 0 if not at head.
-    contents: IndexMap<usize, Rc<Var>>,
+    contents: IndexMap<usize, Rc<String>>,
     in_use: BTreeSet<usize>,
 }
 
 impl DebrayAllocator {
-    fn is_curr_arg_distinct_from(&self, var: &Var) -> bool {
+    fn is_curr_arg_distinct_from(&self, var: &String) -> bool {
         match self.contents.get(&self.arg_c) {
             Some(t_var) if **t_var != *var => true,
             _ => false,
         }
     }
 
-    fn occurs_shallowly_in_head(&self, var: &Var, r: usize) -> bool {
+    fn occurs_shallowly_in_head(&self, var: &String, r: usize) -> bool {
         match self.bindings.get(var).unwrap() {
             &VarData::Temp(_, _, ref tvd) => tvd.use_set.contains(&(GenContext::Head, r)),
             _ => false,
@@ -44,7 +44,7 @@ impl DebrayAllocator {
         in_use_range || self.in_use.contains(&r)
     }
 
-    fn alloc_with_cr(&self, var: &Var) -> usize {
+    fn alloc_with_cr(&self, var: &String) -> usize {
         match self.bindings.get(var) {
             Some(&VarData::Temp(_, _, ref tvd)) => {
                 for &(_, reg) in tvd.use_set.iter() {
@@ -70,7 +70,7 @@ impl DebrayAllocator {
         }
     }
 
-    fn alloc_with_ca(&self, var: &Var) -> usize {
+    fn alloc_with_ca(&self, var: &String) -> usize {
         match self.bindings.get(var) {
             Some(&VarData::Temp(_, _, ref tvd)) => {
                 for &(_, reg) in tvd.use_set.iter() {
@@ -98,7 +98,7 @@ impl DebrayAllocator {
         }
     }
 
-    fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(Rc<Var>, usize)> {
+    fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(Rc<String>, usize)> {
         // we want to allocate a register to the k^{th} parameter, par_k.
         // par_k may not be a temporary variable.
         let k = self.arg_c;
@@ -149,7 +149,7 @@ impl DebrayAllocator {
 
     fn alloc_reg_to_var<'a, Target>(
         &mut self,
-        var: &Var,
+        var: &String,
         lvl: Level,
         term_loc: GenContext,
         target: &mut Vec<Target>,
@@ -193,7 +193,7 @@ impl DebrayAllocator {
         final_index
     }
 
-    fn in_place(&self, var: &Var, term_loc: GenContext, r: RegType, k: usize) -> bool {
+    fn in_place(&self, var: &String, term_loc: GenContext, r: RegType, k: usize) -> bool {
         match term_loc {
             GenContext::Head if !r.is_perm() => r.reg_num() == k,
             _ => match self.bindings().get(var).unwrap() {
@@ -272,7 +272,7 @@ impl<'a> Allocator<'a> for DebrayAllocator {
 
     fn mark_var<Target>(
         &mut self,
-        var: Rc<Var>,
+        var: Rc<String>,
         lvl: Level,
         cell: &'a Cell<VarReg>,
         term_loc: GenContext,
@@ -302,7 +302,7 @@ impl<'a> Allocator<'a> for DebrayAllocator {
 
     fn mark_reserved_var<Target>(
         &mut self,
-        var: Rc<Var>,
+        var: Rc<String>,
         lvl: Level,
         cell: &'a Cell<VarReg>,
         term_loc: GenContext,
@@ -382,12 +382,12 @@ impl<'a> Allocator<'a> for DebrayAllocator {
         self.bindings
     }
 
-    fn reset_at_head(&mut self, args: &Vec<Box<Term>>) {
+    fn reset_at_head(&mut self, args: &Vec<Term>) {
         self.reset_arg(args.len());
         self.arity = args.len();
 
         for (idx, arg) in args.iter().enumerate() {
-            if let &Term::Var(_, ref var) = arg.as_ref() {
+            if let &Term::Var(_, ref var) = arg {
                 let r = self.get(var.clone());
 
                 if !r.is_perm() && r.reg_num() == 0 {
index 0ecdd9e861dab48abde767e946ed4cfe2ec40320..d6a1eea9e27eed4fc1d788a42ef327e50fed2c43 100644 (file)
@@ -1,4 +1,4 @@
-use prolog_parser::ast::*;
+use crate::parser::ast::*;
 
 use crate::forms::*;
 use crate::instructions::*;
@@ -84,8 +84,8 @@ type VariableFixture<'a> = (VarStatus, Vec<&'a Cell<VarReg>>);
 
 #[derive(Debug)]
 pub(crate) struct VariableFixtures<'a> {
-    perm_vars: IndexMap<Rc<Var>, VariableFixture<'a>>,
-    last_chunk_temp_vars: IndexSet<Rc<Var>>,
+    perm_vars: IndexMap<Rc<String>, VariableFixture<'a>>,
+    last_chunk_temp_vars: IndexSet<Rc<String>>,
 }
 
 impl<'a> VariableFixtures<'a> {
@@ -96,11 +96,11 @@ impl<'a> VariableFixtures<'a> {
         }
     }
 
-    pub(crate) fn insert(&mut self, var: Rc<Var>, vs: VariableFixture<'a>) {
+    pub(crate) fn insert(&mut self, var: Rc<String>, vs: VariableFixture<'a>) {
         self.perm_vars.insert(var, vs);
     }
 
-    pub(crate) fn insert_last_chunk_temp_var(&mut self, var: Rc<Var>) {
+    pub(crate) fn insert_last_chunk_temp_var(&mut self, var: Rc<String>) {
         self.last_chunk_temp_vars.insert(var);
     }
 
@@ -115,7 +115,7 @@ impl<'a> VariableFixtures<'a> {
         // Compute the conflict set of u.
 
         // 1.
-        let mut use_sets: IndexMap<Rc<Var>, OccurrenceSet> = IndexMap::new();
+        let mut use_sets: IndexMap<Rc<String>, OccurrenceSet> = IndexMap::new();
 
         for (var, &mut (ref mut var_status, _)) in self.iter_mut() {
             if let &mut VarStatus::Temp(_, ref mut var_data) = var_status {
@@ -153,11 +153,11 @@ impl<'a> VariableFixtures<'a> {
         }
     }
 
-    fn get_mut(&mut self, u: Rc<Var>) -> Option<&mut VariableFixture<'a>> {
+    fn get_mut(&mut self, u: Rc<String>) -> Option<&mut VariableFixture<'a>> {
         self.perm_vars.get_mut(&u)
     }
 
-    fn iter_mut(&mut self) -> indexmap::map::IterMut<Rc<Var>, VariableFixture<'a>> {
+    fn iter_mut(&mut self) -> indexmap::map::IterMut<Rc<String>, VariableFixture<'a>> {
         self.perm_vars.iter_mut()
     }
 
@@ -218,11 +218,11 @@ impl<'a> VariableFixtures<'a> {
         }
     }
 
-    pub(crate) fn into_iter(self) -> indexmap::map::IntoIter<Rc<Var>, VariableFixture<'a>> {
+    pub(crate) fn into_iter(self) -> indexmap::map::IntoIter<Rc<String>, VariableFixture<'a>> {
         self.perm_vars.into_iter()
     }
 
-    fn values(&self) -> indexmap::map::Values<Rc<Var>, VariableFixture<'a>> {
+    fn values(&self) -> indexmap::map::Values<Rc<String>, VariableFixture<'a>> {
         self.perm_vars.values()
     }
 
index 4f579c8c9dce90692cdd99f64fa963ddccfc2326..4cda02370a1d306f13c642c92189e293b0ec0f1e 100644 (file)
@@ -1,33 +1,38 @@
-use prolog_parser::ast::*;
-use prolog_parser::parser::OpDesc;
-use prolog_parser::{clause_name, is_infix, is_postfix};
+use crate::arena::*;
+use crate::atom_table::*;
+use crate::parser::ast::*;
+use crate::parser::parser::CompositeOpDesc;
+use crate::parser::rug::{Integer, Rational};
+use crate::{is_infix, is_postfix};
 
 use crate::clause_types::*;
+use crate::machine::heap::*;
 use crate::machine::loader::PredicateQueue;
 use crate::machine::machine_errors::*;
 use crate::machine::machine_indices::*;
-use crate::rug::{Integer, Rational};
-use ordered_float::OrderedFloat;
+use crate::types::*;
 
 use indexmap::{IndexMap, IndexSet};
+use ordered_float::OrderedFloat;
 
 use slice_deque::*;
 
 use std::cell::Cell;
+use std::convert::TryFrom;
 use std::ops::AddAssign;
 use std::path::PathBuf;
 use std::rc::Rc;
 
-pub(crate) type PredicateKey = (ClauseName, usize); // name, arity.
+pub type PredicateKey = (Atom, usize); // name, arity.
 
-pub(crate) type Predicate = Vec<PredicateClause>;
+pub type Predicate = Vec<PredicateClause>;
 
 // vars of predicate, toplevel offset.  Vec<Term> is always a vector
 // of vars (we get their adjoining cells this way).
-pub(crate) type JumpStub = Vec<Term>;
+pub type JumpStub = Vec<Term>;
 
 #[derive(Debug, Clone)]
-pub(crate) enum TopLevel {
+pub enum TopLevel {
     Fact(Term), // Term, line_num, col_num
     Predicate(Predicate),
     Query(Vec<QueryTerm>),
@@ -35,7 +40,7 @@ pub(crate) enum TopLevel {
 }
 
 #[derive(Debug, Clone, Copy)]
-pub(crate) enum AppendOrPrepend {
+pub enum AppendOrPrepend {
     Append,
     Prepend,
 }
@@ -51,7 +56,7 @@ impl AppendOrPrepend {
 }
 
 #[derive(Debug, Clone, Copy)]
-pub(crate) enum Level {
+pub enum Level {
     Deep,
     Root,
     Shallow,
@@ -67,12 +72,12 @@ impl Level {
 }
 
 #[derive(Debug, Clone)]
-pub(crate) enum QueryTerm {
+pub enum QueryTerm {
     // register, clause type, subterms, use default call policy.
-    Clause(Cell<RegType>, ClauseType, Vec<Box<Term>>, bool),
+    Clause(Cell<RegType>, ClauseType, Vec<Term>, bool),
     BlockedCut, // a cut which is 'blocked by letters', like the P term in P -> Q.
     UnblockedCut(Cell<VarReg>),
-    GetLevelAndUnify(Cell<VarReg>, Rc<Var>),
+    GetLevelAndUnify(Cell<VarReg>, Rc<String>),
     Jump(JumpStub),
 }
 
@@ -95,25 +100,25 @@ impl QueryTerm {
 }
 
 #[derive(Debug, Clone)]
-pub(crate) struct Rule {
-    pub(crate) head: (ClauseName, Vec<Box<Term>>, QueryTerm),
+pub struct Rule {
+    pub(crate) head: (Atom, Vec<Term>, QueryTerm),
     pub(crate) clauses: Vec<QueryTerm>,
 }
 
 #[derive(Clone, Debug, Hash)]
-pub(crate) enum ListingSource {
+pub enum ListingSource {
     DynamicallyGenerated,
-    File(ClauseName, PathBuf), // filename, path
+    File(Atom, PathBuf), // filename, path
     User,
 }
 
 impl ListingSource {
-    pub(crate) fn from_file_and_path(filename: ClauseName, path_buf: PathBuf) -> Self {
+    pub(crate) fn from_file_and_path(filename: Atom, path_buf: PathBuf) -> Self {
         ListingSource::File(filename, path_buf)
     }
 }
 
-pub(crate) trait ClauseInfo {
+pub trait ClauseInfo {
     fn is_consistent(&self, clauses: &PredicateQueue) -> bool {
         match clauses.first() {
             Some(cl) => {
@@ -123,14 +128,14 @@ pub(crate) trait ClauseInfo {
         }
     }
 
-    fn name(&self) -> Option<ClauseName>;
+    fn name(&self) -> Option<Atom>;
     fn arity(&self) -> usize;
 }
 
 impl ClauseInfo for PredicateKey {
     #[inline]
-    fn name(&self) -> Option<ClauseName> {
-        Some(self.0.clone())
+    fn name(&self) -> Option<Atom> {
+        Some(self.0)
     }
 
     #[inline]
@@ -140,28 +145,32 @@ impl ClauseInfo for PredicateKey {
 }
 
 impl ClauseInfo for Term {
-    fn name(&self) -> Option<ClauseName> {
+    fn name(&self) -> Option<Atom> {
+        //, atom_tbl: &AtomTable) -> Option<StringBuffer> {
         match self {
-            Term::Clause(_, ref name, ref terms, _) => {
+            Term::Clause(_, name, terms) => {
+                // let str_buf = StringBuffer::from(*name, atom_tbl);
+
                 match name.as_str() {
+                    // str_buf.as_str() {
                     ":-" => {
                         match terms.len() {
-                            1 => None, // a declaration.
-                            2 => terms[0].name(),
-                            _ => Some(clause_name!(":-")),
+                            1 => None,            // a declaration.
+                            2 => terms[0].name(), //.map(|name| StringBuffer::from(name, atom_tbl)),
+                            _ => Some(*name),
                         }
                     }
-                    _ => Some(name.clone()),
+                    _ => Some(*name), //str_buf),
                 }
             }
-            Term::Constant(_, Constant::Atom(ref name, _)) => Some(name.clone()),
+            Term::Literal(_, Literal::Atom(name)) => Some(*name), //Some(StringBuffer::from(*name, atom_tbl)),
             _ => None,
         }
     }
 
     fn arity(&self) -> usize {
         match self {
-            Term::Clause(_, ref name, ref terms, _) => match name.as_str() {
+            Term::Clause(_, name, terms) => match name.as_str() {
                 ":-" => match terms.len() {
                     1 => 0,
                     2 => terms[0].arity(),
@@ -175,8 +184,8 @@ impl ClauseInfo for Term {
 }
 
 impl ClauseInfo for Rule {
-    fn name(&self) -> Option<ClauseName> {
-        Some(self.head.0.clone())
+    fn name(&self) -> Option<Atom> {
+        Some(self.head.0)
     }
 
     fn arity(&self) -> usize {
@@ -185,7 +194,7 @@ impl ClauseInfo for Rule {
 }
 
 impl ClauseInfo for PredicateClause {
-    fn name(&self) -> Option<ClauseName> {
+    fn name(&self) -> Option<Atom> {
         match self {
             &PredicateClause::Fact(ref term, ..) => term.name(),
             &PredicateClause::Rule(ref rule, ..) => rule.name(),
@@ -200,23 +209,21 @@ impl ClauseInfo for PredicateClause {
     }
 }
 
-// pub(crate) type CompiledResult = (Predicate, VecDeque<TopLevel>);
-
 #[derive(Debug, Clone)]
-pub(crate) enum PredicateClause {
+pub enum PredicateClause {
     Fact(Term),
     Rule(Rule),
 }
 
 impl PredicateClause {
-    // TODO: add this to `Term` in `prolog_parser` like `first_arg`.
-    pub(crate) fn args(&self) -> Option<&[Box<Term>]> {
-        match *self {
-            PredicateClause::Fact(ref term, ..) => match term {
-                Term::Clause(_, _, args, _) => Some(&args),
+    // TODO: add this to `Term` in `crate::parser` like `first_arg`.
+    pub(crate) fn args(&self) -> Option<&[Term]> {
+        match self {
+            PredicateClause::Fact(term, ..) => match term {
+                Term::Clause(_, _, args) => Some(&args),
                 _ => None,
             },
-            PredicateClause::Rule(ref rule, ..) => {
+            PredicateClause::Rule(rule, ..) => {
                 if rule.head.1.is_empty() {
                     None
                 } else {
@@ -228,36 +235,26 @@ impl PredicateClause {
 }
 
 #[derive(Debug, Clone)]
-pub(crate) enum ModuleSource {
-    Library(ClauseName),
-    File(ClauseName),
+pub enum ModuleSource {
+    Library(Atom),
+    File(Atom),
 }
 
 impl ModuleSource {
     pub(crate) fn as_functor_stub(&self) -> MachineStub {
         match self {
-            ModuleSource::Library(ref name) => {
-                functor!("library", [clause_name(name.clone())])
+            ModuleSource::Library(name) => {
+                functor!(atom!("library"), [atom(name)])
             }
-            ModuleSource::File(ref name) => {
-                functor!(clause_name(name.clone()))
+            ModuleSource::File(name) => {
+                functor!(name)
             }
         }
     }
 }
 
-// pub(crate) type ScopedPredicateKey = (ClauseName, PredicateKey); // module name, predicate indicator.
-
-/*
-#[derive(Debug, Clone)]
-pub(crate) enum MultiFileIndicator {
-    LocalScoped(ClauseName, usize), // name, arity
-    ModuleScoped(ScopedPredicateKey),
-}
-*/
-
 #[derive(Clone, Copy, Hash, Debug)]
-pub(crate) enum MetaSpec {
+pub enum MetaSpec {
     Minus,
     Plus,
     Either,
@@ -265,71 +262,71 @@ pub(crate) enum MetaSpec {
 }
 
 #[derive(Debug, Clone)]
-pub(crate) enum Declaration {
-    Dynamic(ClauseName, usize),
-    MetaPredicate(ClauseName, ClauseName, Vec<MetaSpec>), // module name, name, meta-specs
+pub enum Declaration {
+    Dynamic(Atom, usize),
+    MetaPredicate(Atom, Atom, Vec<MetaSpec>), // module name, name, meta-specs
     Module(ModuleDecl),
-    NonCountedBacktracking(ClauseName, usize), // name, arity
+    NonCountedBacktracking(Atom, usize), // name, arity
     Op(OpDecl),
     UseModule(ModuleSource),
     UseQualifiedModule(ModuleSource, IndexSet<ModuleExport>),
 }
 
-#[derive(Debug, Clone, Eq, Hash, PartialEq, Ord, PartialOrd)]
-pub(crate) struct OpDecl {
-    pub(crate) prec: usize,
-    pub(crate) spec: Specifier,
-    pub(crate) name: ClauseName,
+#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd)]
+pub struct OpDecl {
+    pub(crate) op_desc: OpDesc,
+    pub(crate) name: Atom,
+}
+
+#[inline(always)]
+pub(crate) fn fixity(spec: u32) -> Fixity {
+    match spec {
+        XFY | XFX | YFX => Fixity::In,
+        XF | YF => Fixity::Post,
+        FX | FY => Fixity::Pre,
+        _ => unreachable!(),
+    }
 }
 
+
 impl OpDecl {
     #[inline]
-    pub(crate) fn new(prec: usize, spec: Specifier, name: ClauseName) -> Self {
-        Self { prec, spec, name }
+    pub(crate) fn new(op_desc: OpDesc, name: Atom) -> Self {
+        Self { op_desc, name }
     }
 
     #[inline]
     pub(crate) fn remove(&mut self, op_dir: &mut OpDir) {
-        let prec = self.prec;
-        self.prec = 0;
+        let prec = self.op_desc.get_prec();
+        self.op_desc.set(0, self.op_desc.get_spec());
 
         self.insert_into_op_dir(op_dir);
-        self.prec = prec;
-    }
-
-    #[inline]
-    pub(crate) fn fixity(&self) -> Fixity {
-        match self.spec {
-            XFY | XFX | YFX => Fixity::In,
-            XF | YF => Fixity::Post,
-            FX | FY => Fixity::Pre,
-            _ => unreachable!(),
-        }
+        self.op_desc.set(prec, self.op_desc.get_spec());
     }
 
-    pub(crate) fn insert_into_op_dir(&self, op_dir: &mut OpDir) -> Option<(usize, Specifier)> {
-        let key = (self.name.clone(), self.fixity());
+    pub(crate) fn insert_into_op_dir(&self, op_dir: &mut OpDir) -> Option<OpDesc> {
+        let key = (self.name, fixity(self.op_desc.get_spec() as u32));
 
-        match op_dir.get(&key) {
+        match op_dir.get_mut(&key) {
             Some(cell) => {
-                return Some(cell.shared_op_desc().replace((self.prec, self.spec)));
+                let (old_prec, old_spec) = cell.get();
+                cell.set(self.op_desc.get_prec(), self.op_desc.get_spec());
+                return Some(OpDesc::build_with(old_prec, old_spec));
             }
             None => {}
         }
 
-        op_dir
-            .insert(key, OpDirValue::new(self.spec, self.prec))
-            .map(|op_dir_value| op_dir_value.shared_op_desc().get())
+        op_dir.insert(key, self.op_desc)
     }
 
     pub(crate) fn submit(
         &self,
-        existing_desc: Option<OpDesc>,
+        existing_desc: Option<CompositeOpDesc>,
         op_dir: &mut OpDir,
     ) -> Result<(), SessionError> {
-        let (spec, name) = (self.spec, self.name.clone());
+        let (spec, name) = (self.op_desc.get_spec(), self.name.clone());
 
-        if is_infix!(spec) {
+        if is_infix!(spec as u32) {
             if let Some(desc) = existing_desc {
                 if desc.post > 0 {
                     return Err(SessionError::OpIsInfixAndPostFix(name));
@@ -337,7 +334,7 @@ impl OpDecl {
             }
         }
 
-        if is_postfix!(spec) {
+        if is_postfix!(spec as u32) {
             if let Some(desc) = existing_desc {
                 if desc.inf > 0 {
                     return Err(SessionError::OpIsInfixAndPostFix(name));
@@ -350,22 +347,51 @@ impl OpDecl {
     }
 }
 
+#[derive(Debug)]
+pub enum AtomOrString {
+    Atom(Atom),
+    String(String),
+}
+
+impl AtomOrString {
+    #[inline]
+    pub fn as_str(&self) -> &str {
+        match self {
+            AtomOrString::Atom(atom) => atom.as_str(),
+            AtomOrString::String(string) => string.as_str(),
+        }
+    }
+
+    #[inline]
+    pub fn to_string(self) -> String {
+        match self {
+            AtomOrString::Atom(atom) => {
+                atom.as_str().to_owned()
+            }
+            AtomOrString::String(string) => {
+                string
+            }
+        }
+    }
+}
+
+//TODO: try to rid yourself and the earth of the next two functions.
 pub(crate) fn fetch_atom_op_spec(
-    name: ClauseName,
-    spec: Option<SharedOpDesc>,
+    name: Atom,
+    spec: Option<OpDesc>,
     op_dir: &OpDir,
-) -> Option<SharedOpDesc> {
-    fetch_op_spec_from_existing(name.clone(), 1, spec.clone(), op_dir)
+) -> Option<OpDesc> {
+    fetch_op_spec_from_existing(name, 1, spec, op_dir)
         .or_else(|| fetch_op_spec_from_existing(name, 2, spec, op_dir))
 }
 
 pub(crate) fn fetch_op_spec_from_existing(
-    name: ClauseName,
+    name: Atom,
     arity: usize,
-    spec: Option<SharedOpDesc>,
+    op_desc: Option<OpDesc>,
     op_dir: &OpDir,
-) -> Option<SharedOpDesc> {
-    if let Some(ref op_desc) = &spec {
+) -> Option<OpDesc> {
+    if let Some(ref op_desc) = &op_desc {
         if op_desc.arity() != arity {
             /* it's possible to extend operator functors with
              * additional terms. When that happens,
@@ -374,61 +400,53 @@ pub(crate) fn fetch_op_spec_from_existing(
         }
     }
 
-    spec.or_else(|| fetch_op_spec(name, arity, op_dir))
+    op_desc.or_else(|| fetch_op_spec(name, arity, op_dir))
 }
 
-pub(crate) fn fetch_op_spec(
-    name: ClauseName,
-    arity: usize,
-    op_dir: &OpDir,
-) -> Option<SharedOpDesc> {
+pub(crate) fn fetch_op_spec(name: Atom, arity: usize, op_dir: &OpDir) -> Option<OpDesc> {
     match arity {
-        2 => op_dir
-            .get(&(name, Fixity::In))
-            .and_then(|OpDirValue(spec)| {
-                if spec.prec() > 0 {
-                    Some(spec.clone())
-                } else {
-                    None
-                }
-            }),
+        2 => op_dir.get(&(name, Fixity::In)).and_then(|op_desc| {
+            if op_desc.get_prec() > 0 {
+                Some(*op_desc)
+            } else {
+                None
+            }
+        }),
         1 => {
-            if let Some(OpDirValue(spec)) = op_dir.get(&(name.clone(), Fixity::Pre)) {
-                if spec.prec() > 0 {
-                    return Some(spec.clone());
+            if let Some(op_desc) = op_dir.get(&(name.clone(), Fixity::Pre)) {
+                if op_desc.get_prec() > 0 {
+                    return Some(*op_desc);
                 }
             }
 
-            op_dir
-                .get(&(name.clone(), Fixity::Post))
-                .and_then(|OpDirValue(spec)| {
-                    if spec.prec() > 0 {
-                        Some(spec.clone())
-                    } else {
-                        None
-                    }
-                })
+            op_dir.get(&(name, Fixity::Post)).and_then(|op_desc| {
+                if op_desc.get_prec() > 0 {
+                    Some(*op_desc)
+                } else {
+                    None
+                }
+            })
         }
         _ => None,
     }
 }
 
-pub(crate) type ModuleDir = IndexMap<ClauseName, Module>;
+pub(crate) type ModuleDir = IndexMap<Atom, Module>;
 
 #[derive(Debug, Clone, Eq, Hash, PartialEq)]
-pub(crate) enum ModuleExport {
+pub enum ModuleExport {
     OpDecl(OpDecl),
     PredicateKey(PredicateKey),
 }
 
 #[derive(Debug, Clone)]
-pub(crate) struct ModuleDecl {
-    pub(crate) name: ClauseName,
+pub struct ModuleDecl {
+    pub(crate) name: Atom,
     pub(crate) exports: Vec<ModuleExport>,
 }
 
 #[derive(Debug)]
-pub(crate) struct Module {
+pub struct Module {
     pub(crate) module_decl: ModuleDecl,
     pub(crate) code_dir: CodeDir,
     pub(crate) op_dir: OpDir,
@@ -440,7 +458,10 @@ pub(crate) struct Module {
 
 // Module's and related types are defined in forms.
 impl Module {
-    pub(crate) fn new(module_decl: ModuleDecl, listing_src: ListingSource) -> Self {
+    pub(crate) fn new(
+        module_decl: ModuleDecl,
+        listing_src: ListingSource,
+    ) -> Self {
         Module {
             module_decl,
             code_dir: CodeDir::new(),
@@ -465,61 +486,115 @@ impl Module {
     }
 }
 
-#[derive(Debug, Clone)]
-pub(crate) enum Number {
+#[derive(Debug, Copy, Clone)]
+pub enum Number {
     Float(OrderedFloat<f64>),
-    Integer(Rc<Integer>),
-    Rational(Rc<Rational>),
-    Fixnum(isize),
+    Integer(TypedArenaPtr<Integer>),
+    Rational(TypedArenaPtr<Rational>),
+    Fixnum(Fixnum),
 }
 
-impl From<Integer> for Number {
+impl Default for Number {
     #[inline]
-    fn from(n: Integer) -> Self {
-        Number::Integer(Rc::new(n))
+    fn default() -> Self {
+        Number::Fixnum(Fixnum::build_with(0))
     }
 }
 
-impl From<Rational> for Number {
+pub trait ArenaFrom<T> {
+    fn arena_from(value: T, arena: &mut Arena) -> Self;
+}
+
+impl ArenaFrom<Integer> for Number {
     #[inline]
-    fn from(n: Rational) -> Self {
-        Number::Rational(Rc::new(n))
+    fn arena_from(value: Integer, arena: &mut Arena) -> Number {
+        Number::Integer(arena_alloc!(value, arena))
     }
 }
 
-impl From<isize> for Number {
+impl ArenaFrom<Rational> for Number {
     #[inline]
-    fn from(n: isize) -> Self {
-        Number::Fixnum(n)
+    fn arena_from(value: Rational, arena: &mut Arena) -> Number {
+        Number::Rational(arena_alloc!(value, arena))
     }
 }
 
-impl Default for Number {
-    fn default() -> Self {
-        Number::Float(OrderedFloat(0f64))
+impl ArenaFrom<usize> for Number {
+    #[inline]
+    fn arena_from(value: usize, arena: &mut Arena) -> Number {
+        match i64::try_from(value) {
+            Ok(value) => Fixnum::build_with_checked(value)
+                .map(Number::Fixnum)
+                .unwrap_or_else(|_| Number::Integer(arena_alloc!(Integer::from(value), arena))),
+            Err(_) => Number::Integer(arena_alloc!(Integer::from(value), arena)),
+        }
     }
 }
 
-impl Into<Constant> for Number {
+impl ArenaFrom<u64> for Number {
     #[inline]
-    fn into(self) -> Constant {
-        match self {
-            Number::Fixnum(n) => Constant::Fixnum(n),
-            Number::Integer(n) => Constant::Integer(n),
-            Number::Float(f) => Constant::Float(f),
-            Number::Rational(r) => Constant::Rational(r),
+    fn arena_from(value: u64, arena: &mut Arena) -> Number {
+        match i64::try_from(value) {
+            Ok(value) => Fixnum::build_with_checked(value)
+                .map(Number::Fixnum)
+                .unwrap_or_else(|_| Number::Integer(arena_alloc!(Integer::from(value), arena))),
+            Err(_) => Number::Integer(arena_alloc!(Integer::from(value), arena)),
         }
     }
 }
 
-impl Into<HeapCellValue> for Number {
+impl ArenaFrom<i64> for Number {
     #[inline]
-    fn into(self) -> HeapCellValue {
-        match self {
-            Number::Fixnum(n) => HeapCellValue::Addr(Addr::Fixnum(n)),
-            Number::Integer(n) => HeapCellValue::Integer(n),
-            Number::Float(f) => HeapCellValue::Addr(Addr::Float(f)),
-            Number::Rational(r) => HeapCellValue::Rational(r),
+    fn arena_from(value: i64, arena: &mut Arena) -> Number {
+        Fixnum::build_with_checked(value)
+            .map(Number::Fixnum)
+            .unwrap_or_else(|_| Number::Integer(arena_alloc!(Integer::from(value), arena)))
+    }
+}
+
+impl ArenaFrom<isize> for Number {
+    #[inline]
+    fn arena_from(value: isize, arena: &mut Arena) -> Number {
+        Fixnum::build_with_checked(value as i64)
+            .map(Number::Fixnum)
+            .unwrap_or_else(|_| Number::Integer(arena_alloc!(Integer::from(value), arena)))
+    }
+}
+
+impl ArenaFrom<u32> for Number {
+    #[inline]
+    fn arena_from(value: u32, _arena: &mut Arena) -> Number {
+        Number::Fixnum(Fixnum::build_with(value as i64))
+    }
+}
+
+impl ArenaFrom<i32> for Number {
+    #[inline]
+    fn arena_from(value: i32, _arena: &mut Arena) -> Number {
+        Number::Fixnum(Fixnum::build_with(value as i64))
+    }
+}
+
+impl ArenaFrom<Number> for Literal {
+    #[inline]
+    fn arena_from(value: Number, arena: &mut Arena) -> Literal {
+        match value {
+            Number::Fixnum(n) => Literal::Fixnum(n),
+            Number::Integer(n) => Literal::Integer(n),
+            Number::Float(f) => Literal::Float(arena_alloc!(f, arena)),
+            Number::Rational(r) => Literal::Rational(r),
+        }
+    }
+}
+
+impl ArenaFrom<Number> for HeapCellValue {
+    #[inline]
+    fn arena_from(value: Number, arena: &mut Arena) -> HeapCellValue {
+        match value {
+            Number::Fixnum(n) => fixnum_as_cell!(n),
+            Number::Integer(n) => typed_arena_ptr_as_cell!(n),
+            Number::Float(n) => typed_arena_ptr_as_cell!(arena_alloc!(n, arena)),
+            Number::Rational(n) => typed_arena_ptr_as_cell!(n),
         }
     }
 }
@@ -528,9 +603,9 @@ impl Number {
     #[inline]
     pub(crate) fn is_positive(&self) -> bool {
         match self {
-            &Number::Fixnum(n) => n > 0,
+            &Number::Fixnum(n) => n.get_num() > 0,
             &Number::Integer(ref n) => &**n > &0,
-            &Number::Float(OrderedFloat(f)) => f.is_sign_positive(),
+            &Number::Float(f) => f.is_sign_positive(),
             &Number::Rational(ref r) => &**r > &0,
         }
     }
@@ -538,7 +613,7 @@ impl Number {
     #[inline]
     pub(crate) fn is_negative(&self) -> bool {
         match self {
-            &Number::Fixnum(n) => n < 0,
+            &Number::Fixnum(n) => n.get_num() < 0,
             &Number::Integer(ref n) => &**n < &0,
             &Number::Float(OrderedFloat(f)) => f.is_sign_negative(),
             &Number::Rational(ref r) => &**r < &0,
@@ -548,36 +623,20 @@ impl Number {
     #[inline]
     pub(crate) fn is_zero(&self) -> bool {
         match self {
-            &Number::Fixnum(n) => n == 0,
+            &Number::Fixnum(n) => n.get_num() == 0,
             &Number::Integer(ref n) => &**n == &0,
             &Number::Float(f) => f == OrderedFloat(0f64),
             &Number::Rational(ref r) => &**r == &0,
         }
     }
-
-    #[inline]
-    pub(crate) fn abs(self) -> Self {
-        match self {
-            Number::Fixnum(n) => {
-                if let Some(n) = n.checked_abs() {
-                    Number::from(n)
-                } else {
-                    Number::from(Integer::from(n).abs())
-                }
-            }
-            Number::Integer(n) => Number::from(Integer::from(n.abs_ref())),
-            Number::Float(f) => Number::Float(OrderedFloat(f.abs())),
-            Number::Rational(r) => Number::from(Rational::from(r.abs_ref())),
-        }
-    }
 }
 
 #[derive(Debug, Clone)]
 pub(crate) enum OptArgIndexKey {
-    Constant(usize, usize, Constant, Vec<Constant>), // index, IndexingCode location, opt arg, alternatives
-    List(usize, usize),                              // index, IndexingCode location
+    Literal(usize, usize, Literal, Vec<Literal>), // index, IndexingCode location, opt arg, alternatives
+    List(usize, usize),                           // index, IndexingCode location
     None,
-    Structure(usize, usize, ClauseName, usize), // index, IndexingCode location, name, arity
+    Structure(usize, usize, Atom, usize), // index, IndexingCode location, name, arity
 }
 
 impl OptArgIndexKey {
@@ -589,7 +648,7 @@ impl OptArgIndexKey {
     #[inline]
     pub(crate) fn arg_num(&self) -> usize {
         match &self {
-            OptArgIndexKey::Constant(arg_num, ..)
+            OptArgIndexKey::Literal(arg_num, ..)
             | OptArgIndexKey::Structure(arg_num, ..)
             | OptArgIndexKey::List(arg_num, _) => {
                 // these are always at least 1.
@@ -607,7 +666,7 @@ impl OptArgIndexKey {
     #[inline]
     pub(crate) fn switch_on_term_loc(&self) -> Option<usize> {
         match &self {
-            OptArgIndexKey::Constant(_, loc, ..)
+            OptArgIndexKey::Literal(_, loc, ..)
             | OptArgIndexKey::Structure(_, loc, ..)
             | OptArgIndexKey::List(_, loc) => Some(*loc),
             OptArgIndexKey::None => None,
@@ -617,7 +676,7 @@ impl OptArgIndexKey {
     #[inline]
     pub(crate) fn set_switch_on_term_loc(&mut self, value: usize) {
         match self {
-            OptArgIndexKey::Constant(_, ref mut loc, ..)
+            OptArgIndexKey::Literal(_, ref mut loc, ..)
             | OptArgIndexKey::Structure(_, ref mut loc, ..)
             | OptArgIndexKey::List(_, ref mut loc) => {
                 *loc = value;
@@ -631,7 +690,7 @@ impl AddAssign<usize> for OptArgIndexKey {
     #[inline]
     fn add_assign(&mut self, n: usize) {
         match self {
-            OptArgIndexKey::Constant(_, ref mut o, ..)
+            OptArgIndexKey::Literal(_, ref mut o, ..)
             | OptArgIndexKey::List(_, ref mut o)
             | OptArgIndexKey::Structure(_, ref mut o, ..) => {
                 *o += n;
@@ -700,6 +759,7 @@ pub(crate) struct LocalPredicateSkeleton {
     pub(crate) is_multifile: bool,
     pub(crate) clause_clause_locs: SliceDeque<usize>,
     pub(crate) clause_assert_margin: usize,
+    pub(crate) retracted_dynamic_clauses: Option<Vec<ClauseIndexInfo>>, // always None if non-dynamic.
 }
 
 impl LocalPredicateSkeleton {
@@ -711,6 +771,7 @@ impl LocalPredicateSkeleton {
             is_multifile: false,
             clause_clause_locs: sdeq![],
             clause_assert_margin: 0,
+            retracted_dynamic_clauses: Some(vec![]),
         }
     }
 
@@ -730,6 +791,20 @@ impl LocalPredicateSkeleton {
         self.clause_clause_locs.clear();
         self.clause_assert_margin = 0;
     }
+
+    #[inline]
+    pub(crate) fn add_retracted_dynamic_clause_info(&mut self, clause_info: ClauseIndexInfo) {
+        debug_assert_eq!(self.is_dynamic, true);
+
+        if self.retracted_dynamic_clauses.is_none() {
+            self.retracted_dynamic_clauses = Some(vec![]);
+        }
+
+        self.retracted_dynamic_clauses
+            .as_mut()
+            .unwrap()
+            .push(clause_info);
+    }
 }
 
 #[derive(Clone, Debug)]
@@ -781,3 +856,12 @@ impl PredicateSkeleton {
         }
     }
 }
+
+#[derive(Debug, Clone, Copy)]
+pub(crate) 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.
+}
+
index fd057ac3c45d2e3b1cd344259373948dda29896d..b5c83a22360184fd701ec67f800a4d9b12ec458a 100644 (file)
-use crate::machine::machine_indices::*;
-use crate::machine::machine_state::*;
+pub(crate) use crate::machine::gc::{IteratorUMP, StacklessPreOrderHeapIter};
+use crate::machine::heap::*;
 
-use indexmap::IndexSet;
+use crate::atom_table::*;
+use crate::types::*;
+
+use modular_bitfield::prelude::*;
 
-use std::cmp::Ordering;
 use std::ops::Deref;
 use std::vec::Vec;
 
+#[inline]
+fn forward_if_referent_marked(heap: &mut [HeapCellValue], h: usize) {
+    read_heap_cell!(heap[h],
+        (HeapCellValueTag::Str
+         | HeapCellValueTag::Lis
+         | HeapCellValueTag::AttrVar
+         | HeapCellValueTag::Var
+         | HeapCellValueTag::PStrLoc, vh) => {
+            if heap[vh].get_mark_bit() {
+                heap[h].set_forwarding_bit(true);
+            }
+        }
+        _ => {}
+    )
+}
+
+#[bitfield]
+#[repr(u64)]
+#[derive(Clone, Copy, Debug)]
+pub struct IterStackLoc {
+    value: B63,
+    m: bool,
+}
+
+impl IterStackLoc {
+    #[inline]
+    pub fn iterable_heap_loc(h: usize) -> Self {
+        IterStackLoc::new().with_m(false).with_value(h as u64)
+    }
+
+    #[inline]
+    pub fn mark_heap_loc(h: usize) -> Self {
+        IterStackLoc::new().with_m(true).with_value(h as u64)
+    }
+}
+
 #[derive(Debug)]
-pub(crate) struct HCPreOrderIterator<'a> {
-    pub(crate) machine_st: &'a MachineState,
-    pub(crate) state_stack: Vec<Addr>,
+pub struct StackfulPreOrderHeapIter<'a> {
+    pub heap: &'a mut Vec<HeapCellValue>,
+    stack: Vec<IterStackLoc>,
+    h: usize,
+}
+
+impl<'a> Drop for StackfulPreOrderHeapIter<'a> {
+    fn drop(&mut self) {
+        while let Some(h) = self.stack.pop() {
+            let h = h.value() as usize;
+
+            self.heap[h].set_forwarding_bit(false);
+            self.heap[h].set_mark_bit(false);
+        }
+
+        self.heap.pop();
+    }
 }
 
-impl<'a> HCPreOrderIterator<'a> {
-    pub(crate) fn new(machine_st: &'a MachineState, a: Addr) -> Self {
-        HCPreOrderIterator {
-            machine_st,
-            state_stack: vec![a],
+impl<'a> StackfulPreOrderHeapIter<'a> {
+    #[inline]
+    fn new(heap: &'a mut Vec<HeapCellValue>, cell: HeapCellValue) -> Self {
+        let h = heap.len();
+        heap.push(cell);
+
+        Self {
+            heap,
+            h,
+            stack: vec![IterStackLoc::iterable_heap_loc(h)],
         }
     }
 
     #[inline]
-    pub(crate) fn machine_st(&self) -> &MachineState {
-        &self.machine_st
+    pub fn push_stack(&mut self, h: usize) {
+        self.stack.push(IterStackLoc::iterable_heap_loc(h));
     }
 
-    fn follow_heap(&mut self, h: usize) -> Addr {
-        match &self.machine_st.heap[h] {
-            &HeapCellValue::NamedStr(arity, _, _) => {
-                for idx in (1..arity + 1).rev() {
-                    self.state_stack.push(Addr::HeapCell(h + idx));
-                }
+    #[inline]
+    pub fn stack_last(&self) -> Option<usize> {
+        for h in self.stack.iter().rev() {
+            let is_readable_marked = h.m();
+            let h = h.value() as usize;
+            let cell = self.heap[h];
 
-                Addr::Str(h)
+            if cell.get_forwarding_bit() == Some(true) {
+                return Some(h);
+            } else if cell.get_mark_bit() && !is_readable_marked {
+                continue;
             }
-            &HeapCellValue::Addr(a) => self.follow(a),
-            HeapCellValue::PartialString(..) => self.follow(Addr::PStrLocation(h, 0)),
-            HeapCellValue::Atom(..)
-            | HeapCellValue::DBRef(_)
-            | HeapCellValue::Integer(_)
-            | HeapCellValue::Rational(_) => Addr::Con(h),
-            HeapCellValue::LoadStatePayload(_) => Addr::LoadStatePayload(h),
-            HeapCellValue::Stream(_) => Addr::Stream(h),
-            HeapCellValue::TcpListener(_) => Addr::TcpListener(h),
+
+            return Some(h);
         }
+
+        None
     }
 
-    // called under the assumption that the location at r is about to
-    // be visited, and so any follow up states need to be added to
-    // state_stack. returns the dereferenced Addr from Ref.
-    fn follow(&mut self, addr: Addr) -> Addr {
-        let da = self.machine_st.store(self.machine_st.deref(addr));
+    #[inline]
+    pub fn stack_is_empty(&self) -> bool {
+        self.stack.is_empty()
+    }
 
-        match da {
-            Addr::Lis(a) => {
-                self.state_stack.push(Addr::HeapCell(a + 1));
-                self.state_stack.push(Addr::HeapCell(a));
+    #[inline]
+    pub fn focus(&self) -> usize {
+        self.h
+    }
 
-                da
-            }
-            Addr::PStrLocation(h, n) => {
-                if let &HeapCellValue::PartialString(ref pstr, has_tail) = &self.machine_st.heap[h]
-                {
-                    if let Some(c) = pstr.range_from(n..).next() {
-                        if !pstr.at_end(n + c.len_utf8()) {
-                            self.state_stack
-                                .push(Addr::PStrLocation(h, n + c.len_utf8()));
-                        } else if has_tail {
-                            self.state_stack.push(Addr::HeapCell(h + 1));
-                        } else {
-                            self.state_stack.push(Addr::EmptyList);
-                        }
+    #[inline]
+    pub fn pop_stack(&mut self) -> Option<HeapCellValue> {
+        while let Some(h) = self.stack.pop() {
+            let is_readable_marked = h.m();
+            let h = h.value() as usize;
+            self.h = h;
 
-                        self.state_stack.push(Addr::Char(c));
-                    } else if has_tail {
-                        return self.follow(Addr::HeapCell(h + 1));
-                    }
-                } else {
-                    unreachable!()
-                }
+            let cell = &mut self.heap[h];
 
-                Addr::PStrLocation(h, n)
-            }
-            Addr::Str(s) => {
-                self.follow_heap(s) // record terms of structure.
+            if cell.get_forwarding_bit() == Some(true) {
+                cell.set_forwarding_bit(false);
+            } else if cell.get_mark_bit() && !is_readable_marked {
+                cell.set_mark_bit(false);
+                continue;
             }
-            Addr::Con(h) => {
-                if let &HeapCellValue::PartialString(ref pstr, has_tail) = &self.machine_st.heap[h]
-                {
-                    if let Some(c) = pstr.range_from(0..).next() {
-                        self.state_stack.push(Addr::PStrLocation(h, c.len_utf8()));
-                        self.state_stack.push(Addr::Char(c));
-
-                        Addr::PStrLocation(h, 0)
-                    } else if has_tail {
-                        self.follow(Addr::HeapCell(h + 1))
-                    } else {
-                        Addr::EmptyList
-                    }
-                } else {
-                    Addr::Con(h)
-                }
+
+            return Some(*cell);
+        }
+
+        None
+    }
+
+    fn push_if_unmarked(&mut self, h: usize) {
+        if !self.heap[h].get_mark_bit() {
+            self.heap[h].set_mark_bit(true);
+            self.stack.push(IterStackLoc::iterable_heap_loc(h));
+        }
+    }
+
+    fn follow(&mut self) -> Option<HeapCellValue> {
+        while let Some(h) = self.stack.pop() {
+            let is_readable_marked = h.m();
+            let h = h.value() as usize;
+
+            self.h = h;
+            let cell = &mut self.heap[h];
+
+            if cell.get_forwarding_bit() == Some(true) {
+                let copy = *cell;
+                cell.set_forwarding_bit(false);
+                return Some(copy);
+            } else if cell.get_mark_bit() && !is_readable_marked {
+                cell.set_mark_bit(false);
+                continue;
             }
-            da => da,
+
+            read_heap_cell!(*cell,
+               (HeapCellValueTag::Str, vh) => {
+                   self.stack.push(IterStackLoc::iterable_heap_loc(vh));
+               }
+               (HeapCellValueTag::Lis, vh) => {
+                   self.push_if_unmarked(vh);
+
+                   self.stack.push(IterStackLoc::iterable_heap_loc(vh + 1));
+                   forward_if_referent_marked(&mut self.heap, vh + 1);
+
+                   self.stack.push(IterStackLoc::mark_heap_loc(vh));
+                   forward_if_referent_marked(&mut self.heap, vh);
+
+                   return Some(self.heap[h]);
+               }
+               (HeapCellValueTag::AttrVar | HeapCellValueTag::PStrLoc | HeapCellValueTag::Var, vh) => {
+                   self.push_if_unmarked(h);
+                   self.stack.push(IterStackLoc::iterable_heap_loc(vh));
+                   forward_if_referent_marked(&mut self.heap, vh);
+               }
+               (HeapCellValueTag::PStrOffset, offset) => {
+                   self.push_if_unmarked(offset);
+                   self.stack.push(IterStackLoc::iterable_heap_loc(h+1));
+
+                   return Some(self.heap[h]);
+               }
+               (HeapCellValueTag::PStr) => {
+                   self.push_if_unmarked(h);
+
+                   forward_if_referent_marked(&mut self.heap, h+1);
+                   self.stack.push(IterStackLoc::iterable_heap_loc(h+1));
+
+                   return Some(self.heap[h]);
+               }
+               (HeapCellValueTag::Atom, (_name, arity)) => {
+                   if arity > 0 {
+                       self.push_if_unmarked(h);
+                   }
+
+                   for h in (h + 1 .. h + arity + 1).rev() {
+                       forward_if_referent_marked(&mut self.heap, h);
+                       self.stack.push(IterStackLoc::iterable_heap_loc(h));
+                   }
+
+                   return Some(self.heap[h]);
+               }
+               // (HeapCellValueTag::StackVar) => {
+               //     unreachable!() // TODO: here, crashing.
+               // }
+               _ => {
+                   return Some(*cell);
+               }
+            )
         }
+
+        None
     }
 }
 
-impl<'a> Iterator for HCPreOrderIterator<'a> {
-    type Item = Addr;
+impl<'a> Iterator for StackfulPreOrderHeapIter<'a> {
+    type Item = HeapCellValue;
 
+    #[inline]
     fn next(&mut self) -> Option<Self::Item> {
-        self.state_stack.pop().map(|a| self.follow(a))
+        self.follow()
     }
 }
 
-pub(crate) trait MutStackHCIterator<'b>
-where
-    Self: Iterator,
-{
-    type MutStack;
+#[inline(always)]
+pub(crate) fn stackless_preorder_iter(
+    heap: &mut Vec<HeapCellValue>,
+    cell: HeapCellValue,
+) -> StacklessPreOrderHeapIter<IteratorUMP> {
+    StacklessPreOrderHeapIter::new(heap, cell)
+}
 
-    fn stack(&'b mut self) -> Self::MutStack;
+#[inline(always)]
+pub(crate) fn stackful_preorder_iter(
+    heap: &mut Vec<HeapCellValue>,
+    cell: HeapCellValue,
+) -> StackfulPreOrderHeapIter {
+    StackfulPreOrderHeapIter::new(heap, cell)
 }
 
 #[derive(Debug)]
-pub(crate) struct HCPostOrderIterator<'a> {
-    base_iter: HCPreOrderIterator<'a>,
-    parent_stack: Vec<(usize, Addr)>, // number of children, parent node.
+pub(crate) struct PostOrderIterator<Iter: Iterator<Item = HeapCellValue>> {
+    base_iter: Iter,
+    base_iter_valid: bool,
+    parent_stack: Vec<(usize, HeapCellValue)>, // number of children, parent node.
 }
 
-impl<'a> Deref for HCPostOrderIterator<'a> {
-    type Target = HCPreOrderIterator<'a>;
+impl<Iter: Iterator<Item = HeapCellValue>> Deref for PostOrderIterator<Iter> {
+    type Target = Iter;
 
     fn deref(&self) -> &Self::Target {
         &self.base_iter
     }
 }
 
-impl<'a> HCPostOrderIterator<'a> {
-    pub(crate) fn new(base_iter: HCPreOrderIterator<'a>) -> Self {
-        HCPostOrderIterator {
+impl<Iter: Iterator<Item = HeapCellValue>> PostOrderIterator<Iter> {
+    pub(crate) fn new(base_iter: Iter) -> Self {
+        PostOrderIterator {
             base_iter,
+            base_iter_valid: true,
             parent_stack: vec![],
         }
     }
 }
 
-impl<'a> Iterator for HCPostOrderIterator<'a> {
-    type Item = Addr;
+impl<Iter: Iterator<Item = HeapCellValue>> Iterator for PostOrderIterator<Iter> {
+    type Item = HeapCellValue;
 
     fn next(&mut self) -> Option<Self::Item> {
         loop {
@@ -161,179 +280,2317 @@ impl<'a> Iterator for HCPostOrderIterator<'a> {
                 self.parent_stack.push((child_count - 1, node));
             }
 
-            if let Some(item) = self.base_iter.next() {
-                match self.base_iter.machine_st.heap.index_addr(&item).as_ref() {
-                    &HeapCellValue::NamedStr(arity, ..) => {
-                        self.parent_stack.push((arity, item));
-                    }
-                    &HeapCellValue::Addr(Addr::Lis(a)) => {
-                        self.parent_stack.push((2, Addr::Lis(a)));
-                    }
-                    &HeapCellValue::Addr(Addr::PStrLocation(h, n)) => {
-                        match &self.machine_st.heap[h] {
-                            &HeapCellValue::PartialString(..) => {
-                                // ref pstr, _) => {
-                                /*
-                                let c = pstr.range_from(n ..).next().unwrap();
-                                let next_n = n + c.len_utf8();
-
-                                if !pstr.at_end(next_n) {
-                                */
-                                // self.parent_stack.push((2, Addr::PStrLocation(h, next_n)));
-                                // }
-
-                                self.parent_stack.push((2, Addr::PStrLocation(h, n)));
-                            }
-                            _ => {
-                                unreachable!()
-                            }
+            if self.base_iter_valid {
+                if let Some(item) = self.base_iter.next() {
+                    read_heap_cell!(item,
+                        (HeapCellValueTag::Atom, (_name, arity)) => {
+                            self.parent_stack.push((arity, item));
                         }
-                    }
-                    _ => {
-                        return Some(item);
-                    }
+                        (HeapCellValueTag::Lis) => {
+                            self.parent_stack.push((2, item));
+                        }
+                        (HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset) => {
+                            self.parent_stack.push((1, item));
+                        }
+                        _ => {
+                            return Some(item);
+                        }
+                    );
+
+                    continue;
+                } else {
+                    self.base_iter_valid = false;
                 }
-            } else {
+            }
+
+            if self.parent_stack.is_empty() {
                 return None;
             }
         }
     }
 }
 
-impl MachineState {
-    pub(crate) fn pre_order_iter<'a>(&'a self, a: Addr) -> HCPreOrderIterator<'a> {
-        HCPreOrderIterator::new(self, a)
-    }
+pub(crate) type LeftistPostOrderHeapIter<'a> = PostOrderIterator<StackfulPreOrderHeapIter<'a>>;
 
-    pub(crate) fn post_order_iter<'a>(&'a self, a: Addr) -> HCPostOrderIterator<'a> {
-        HCPostOrderIterator::new(HCPreOrderIterator::new(self, a))
-    }
+impl<'a> LeftistPostOrderHeapIter<'a> {
+    #[inline]
+    pub fn pop_stack(&mut self) {
+        if let Some((child_count, _)) = self.parent_stack.last() {
+            for _ in 0 .. *child_count {
+                self.base_iter.pop_stack();
+            }
 
-    pub(crate) fn acyclic_pre_order_iter<'a>(&'a self, a: Addr) -> HCAcyclicIterator<'a> {
-        HCAcyclicIterator::new(HCPreOrderIterator::new(self, a))
+            self.parent_stack.pop();
+        }
     }
 
-    pub(crate) fn zipped_acyclic_pre_order_iter<'a>(
-        &'a self,
-        a1: Addr,
-        a2: Addr,
-    ) -> HCZippedAcyclicIterator<'a> {
-        HCZippedAcyclicIterator::new(
-            HCPreOrderIterator::new(self, a1),
-            HCPreOrderIterator::new(self, a2),
-        )
+    #[inline]
+    pub fn parent_stack_len(&self) -> usize {
+        self.parent_stack.len()
     }
 }
 
-impl<'b, 'a: 'b> MutStackHCIterator<'b> for HCPreOrderIterator<'a> {
-    type MutStack = &'b mut Vec<Addr>;
-
-    fn stack(&'b mut self) -> Self::MutStack {
-        &mut self.state_stack
-    }
+#[inline]
+pub(crate) fn stackful_post_order_iter<'a>(
+    heap: &'a mut Heap,
+    cell: HeapCellValue,
+) -> LeftistPostOrderHeapIter<'a> {
+    PostOrderIterator::new(StackfulPreOrderHeapIter::new(heap, cell))
 }
 
-#[derive(Debug)]
-pub(crate) struct HCAcyclicIterator<'a> {
-    iter: HCPreOrderIterator<'a>,
-    seen: IndexSet<Addr>,
+pub(crate) type RightistPostOrderHeapIter<'a> =
+    PostOrderIterator<StacklessPreOrderHeapIter<'a, IteratorUMP>>;
+
+#[inline]
+pub(crate) fn stackless_post_order_iter<'a>(
+    heap: &'a mut Heap,
+    cell: HeapCellValue,
+) -> RightistPostOrderHeapIter<'a> {
+    PostOrderIterator::new(stackless_preorder_iter(heap, cell))
 }
 
-impl<'a> HCAcyclicIterator<'a> {
-    pub(crate) fn new(iter: HCPreOrderIterator<'a>) -> Self {
-        HCAcyclicIterator {
-            iter,
-            seen: IndexSet::new(),
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::machine::mock_wam::*;
+
+    #[test]
+    fn heap_stackless_iter_tests() {
+        let mut wam = MockWAM::new();
+
+        let f_atom = atom!("f");
+        let a_atom = atom!("a");
+        let b_atom = atom!("b");
+
+        wam.machine_st
+           .heap
+           .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)]));
+
+        {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 2)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom, 0)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom, 0)
+            );
+
+            assert_eq!(iter.next(), None);
         }
-    }
-}
 
-impl<'a> Deref for HCAcyclicIterator<'a> {
-    type Target = HCPreOrderIterator<'a>;
+        all_cells_unmarked(&wam.machine_st.heap);
 
-    fn deref(&self) -> &Self::Target {
-        &self.iter
-    }
-}
+        wam.machine_st.heap.clear();
 
-impl<'b, 'a: 'b> MutStackHCIterator<'b> for HCAcyclicIterator<'a> {
-    type MutStack = &'b mut Vec<Addr>;
+        wam.machine_st.heap.extend(functor!(
+            f_atom,
+            [
+                atom(a_atom),
+                atom(b_atom),
+                atom(a_atom),
+                cell(str_loc_as_cell!(0))
+            ]
+        ));
 
-    fn stack(&'b mut self) -> Self::MutStack {
-        self.iter.stack()
-    }
-}
+        for _ in 0..20 {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0));
 
-impl<'a> Iterator for HCAcyclicIterator<'a> {
-    type Item = Addr;
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 4)
+            );
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(0));
 
-    fn next(&mut self) -> Option<Self::Item> {
-        while let Some(addr) = self.iter.stack().pop() {
-            if !self.seen.contains(&addr) {
-                self.iter.stack().push(addr.clone());
-                self.seen.insert(addr);
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
 
-                break;
-            }
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            assert_eq!(iter.next(), None);
         }
 
-        self.iter.next()
-    }
-}
+        all_cells_unmarked(&wam.machine_st.heap);
 
-#[derive(Debug)]
-pub(crate) struct HCZippedAcyclicIterator<'a> {
-    i1: HCPreOrderIterator<'a>,
-    i2: HCPreOrderIterator<'a>,
-    seen: IndexSet<(Addr, Addr)>,
-    pub(crate) first_to_expire: Ordering,
-}
+        wam.machine_st.heap.clear();
 
-impl<'b, 'a: 'b> MutStackHCIterator<'b> for HCZippedAcyclicIterator<'a> {
-    type MutStack = (&'b mut Vec<Addr>, &'b mut Vec<Addr>);
+        wam.machine_st.heap.push(str_loc_as_cell!(1));
 
-    fn stack(&'b mut self) -> Self::MutStack {
-        (self.i1.stack(), self.i2.stack())
-    }
-}
+        wam.machine_st.heap.extend(functor!(
+            f_atom,
+            [
+                atom(a_atom),
+                atom(b_atom),
+                atom(a_atom),
+                cell(str_loc_as_cell!(1))
+            ]
+        ));
 
-impl<'a> HCZippedAcyclicIterator<'a> {
-    pub(crate) fn new(i1: HCPreOrderIterator<'a>, i2: HCPreOrderIterator<'a>) -> Self {
-        HCZippedAcyclicIterator {
-            i1,
-            i2,
-            seen: IndexSet::new(),
-            first_to_expire: Ordering::Equal,
+        for _ in 0..2 { //00000 {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 4)
+            );
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(1));
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            assert_eq!(iter.next(), None);
         }
-    }
-}
 
-impl<'a> Iterator for HCZippedAcyclicIterator<'a> {
-    type Item = (Addr, Addr);
+        all_cells_unmarked(&wam.machine_st.heap);
 
-    fn next(&mut self) -> Option<Self::Item> {
-        while let (Some(a1), Some(a2)) = (self.i1.stack().pop(), self.i2.stack().pop()) {
-            if !self.seen.contains(&(a1.clone(), a2.clone())) {
-                self.i1.stack().push(a1.clone());
-                self.i2.stack().push(a2.clone());
+        wam.machine_st.heap.clear();
 
-                self.seen.insert((a1, a2));
+        {
+            wam.machine_st.heap.push(heap_loc_as_cell!(0));
 
-                break;
-            }
+            let mut iter = stackless_preorder_iter(
+                &mut wam.machine_st.heap,
+                heap_loc_as_cell!(0),
+            );
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), heap_loc_as_cell!(0));
+            assert_eq!(iter.next(), None);
         }
 
-        match (self.i1.next(), self.i2.next()) {
-            (Some(v1), Some(v2)) => Some((v1, v2)),
-            (Some(_), None) => {
-                self.first_to_expire = Ordering::Greater;
-                None
-            }
-            (None, Some(_)) => {
-                self.first_to_expire = Ordering::Less;
-                None
-            }
-            _ => None,
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        // term  is: [a, b]
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(atom_as_cell!(a_atom));
+        wam.machine_st.heap.push(list_loc_as_cell!(3));
+        wam.machine_st.heap.push(atom_as_cell!(b_atom));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        {
+            let mut iter = stackless_preorder_iter(
+                &mut wam.machine_st.heap,
+                heap_loc_as_cell!(0),
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                empty_list_as_cell!()
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.pop();
+
+        // now make the list cyclic.
+        wam.machine_st.heap.push(heap_loc_as_cell!(0));
+
+        {
+            let mut iter = stackless_preorder_iter(
+                &mut wam.machine_st.heap,
+                heap_loc_as_cell!(0),
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(0)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        // first a 'dangling' partial string, later modified to be a two-part complete string,
+        // then a three-part cyclic string involving an uncompacted list of chars.
+        let pstr_var_cell = put_partial_string(&mut wam.machine_st.heap, "abc ", &mut wam.machine_st.atom_tbl);
+        let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize];
+
+        {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(1),
+            );
+
+            assert_eq!(iter.next(), None);
         }
+
+        assert_eq!(wam.machine_st.heap[0], pstr_cell);
+        assert_eq!(wam.machine_st.heap[1], heap_loc_as_cell!(1));
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(pstr_loc_as_cell!(2));
+
+        let pstr_second_var_cell = put_partial_string(
+            &mut wam.machine_st.heap,
+            "def",
+            &mut wam.machine_st.atom_tbl,
+        );
+
+        let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize];
+
+        {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(3),
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        assert_eq!(wam.machine_st.heap[0], pstr_cell);
+        assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(2));
+        assert_eq!(wam.machine_st.heap[2], pstr_second_cell);
+        assert_eq!(wam.machine_st.heap[3], heap_loc_as_cell!(3));
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(pstr_loc_as_cell!(4));
+        wam.machine_st.heap.push(pstr_offset_as_cell!(0));
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(2)));
+
+        {
+            let mut iter = stackless_preorder_iter(
+                &mut wam.machine_st.heap,
+                pstr_loc_as_cell!(4),
+            );
+
+            let pstr_offset_cell = pstr_offset_as_cell!(0);
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+
+            assert_eq!(iter.next(), None);
+        }
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_loc_as_cell!(2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_offset_as_cell!(0));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), fixnum_as_cell!(Fixnum::build_with(2)));
+
+        wam.machine_st.heap.truncate(4);
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(pstr_loc_as_cell!(wam.machine_st.heap.len() + 1));
+
+        wam.machine_st.heap.push(pstr_offset_as_cell!(0));
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0i64)));
+
+        {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+            let pstr_offset_cell = pstr_offset_as_cell!(0);
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell);
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(1i64)));
+
+        {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0));
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0));
+
+            assert_eq!(iter.next(), None);
+
+            assert_eq!(iter.heap[4], pstr_offset_as_cell!(0));
+            assert_eq!(iter.heap[5], fixnum_as_cell!(Fixnum::build_with(1i64)));
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        let functor = functor!(f_atom, [atom(a_atom), atom(b_atom), atom(b_atom)]);
+
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(str_loc_as_cell!(5));
+        wam.machine_st.heap.push(list_loc_as_cell!(3));
+        wam.machine_st.heap.push(str_loc_as_cell!(5));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        wam.machine_st.heap.extend(functor);
+
+        {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                empty_list_as_cell!()
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                empty_list_as_cell!()
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            // drop the iterator before the iteration is complete to test
+            // that modified heap cells are restored to their
+            // pre-traversal state by the stackless iterator's Drop
+            // instance.
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        assert_eq!(wam.machine_st.heap[0], list_loc_as_cell!(1));
+        assert_eq!(wam.machine_st.heap[1], str_loc_as_cell!(5));
+        assert_eq!(wam.machine_st.heap[2], list_loc_as_cell!(3));
+        assert_eq!(wam.machine_st.heap[3], str_loc_as_cell!(5));
+        assert_eq!(wam.machine_st.heap[4], empty_list_as_cell!());
+
+        {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                empty_list_as_cell!()
+            );
+
+            // drop the iterator before the iteration is complete to test
+            // that modified heap cells are restored to their
+            // pre-traversal state by the stackless iterator's Drop
+            // instance.
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        assert_eq!(wam.machine_st.heap[0], list_loc_as_cell!(1));
+        assert_eq!(wam.machine_st.heap[1], str_loc_as_cell!(5));
+        assert_eq!(wam.machine_st.heap[2], list_loc_as_cell!(3));
+        assert_eq!(wam.machine_st.heap[3], str_loc_as_cell!(5));
+        assert_eq!(wam.machine_st.heap[4], empty_list_as_cell!());
+
+        wam.machine_st.heap[4] = list_loc_as_cell!(1);
+
+        {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1),
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(heap_loc_as_cell!(1));
+        wam.machine_st.heap.push(heap_loc_as_cell!(2));
+        wam.machine_st.heap.push(heap_loc_as_cell!(3));
+        wam.machine_st.heap.push(heap_loc_as_cell!(3));
+
+        {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(iter.next().unwrap(), heap_loc_as_cell!(3));
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), heap_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), heap_loc_as_cell!(2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), heap_loc_as_cell!(3));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), heap_loc_as_cell!(3));
+
+        wam.machine_st.heap.clear();
+
+        // print L = [L|L].
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+
+        {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1));
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+
+            assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1));
+            // this is what happens! this next line! We would like it not to happen though.
+            assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1));
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), list_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), list_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), list_loc_as_cell!(1));
+
+        wam.machine_st.heap.clear();
+
+        // term is [X,f(Y),Z].
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(heap_loc_as_cell!(1));
+        wam.machine_st.heap.push(heap_loc_as_cell!(3)); // 2
+        wam.machine_st.heap.push(list_loc_as_cell!(4)); // 3
+        wam.machine_st.heap.push(str_loc_as_cell!(6)); // 4
+        wam.machine_st.heap.push(heap_loc_as_cell!(8));
+        wam.machine_st.heap.push(atom_as_cell!(f_atom, 1)); // 6
+        wam.machine_st.heap.push(heap_loc_as_cell!(11)); // 7
+        wam.machine_st.heap.push(list_loc_as_cell!(9));
+        wam.machine_st.heap.push(heap_loc_as_cell!(9));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        wam.machine_st.heap.push(attr_var_as_cell!(11)); // linked from 7.
+        wam.machine_st.heap.push(heap_loc_as_cell!(12));
+
+        {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(4)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(9)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                empty_list_as_cell!()
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(9)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 1)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                attr_var_as_cell!(11)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(1)
+            );
+            assert_eq!(iter.next(), None);
+        }
+
+        // now populate the attributes list. the iteration must not change.
+        let clpz_atom = atom!("clpz");
+        let p_atom = atom!("p");
+
+        wam.machine_st.heap.pop();
+
+        wam.machine_st.heap.push(heap_loc_as_cell!(13)); // 12
+        wam.machine_st.heap.push(list_loc_as_cell!(14)); // 13
+        wam.machine_st.heap.push(str_loc_as_cell!(16)); // 14
+        wam.machine_st.heap.push(heap_loc_as_cell!(19)); // 15
+        wam.machine_st.heap.push(atom_as_cell!(clpz_atom, 2)); // 16
+        wam.machine_st.heap.push(atom_as_cell!(a_atom)); // 17
+        wam.machine_st.heap.push(atom_as_cell!(b_atom)); // 18
+        wam.machine_st.heap.push(list_loc_as_cell!(20)); // 19
+        wam.machine_st.heap.push(str_loc_as_cell!(22)); // 20
+        wam.machine_st.heap.push(empty_list_as_cell!()); // 21
+        wam.machine_st.heap.push(atom_as_cell!(p_atom, 1)); // 22
+        wam.machine_st.heap.push(heap_loc_as_cell!(23)); // 23
+
+        {
+            let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(4)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(9)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                empty_list_as_cell!()
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(9)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 1)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                attr_var_as_cell!(11)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(1)
+            );
+            assert_eq!(iter.next(), None);
+        }
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), list_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), heap_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), heap_loc_as_cell!(3));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), list_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), str_loc_as_cell!(6));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), heap_loc_as_cell!(8));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[6]), atom_as_cell!(f_atom, 1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[7]), heap_loc_as_cell!(11));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[8]), list_loc_as_cell!(9));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[9]), heap_loc_as_cell!(9));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[10]), empty_list_as_cell!());
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[11]), attr_var_as_cell!(11));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[12]), heap_loc_as_cell!(13));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[13]), list_loc_as_cell!(14));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[14]), str_loc_as_cell!(16));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[15]), heap_loc_as_cell!(19));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[16]), atom_as_cell!(clpz_atom, 2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[17]), atom_as_cell!(a_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[18]), atom_as_cell!(b_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[19]), list_loc_as_cell!(20));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[20]), str_loc_as_cell!(22));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[21]), empty_list_as_cell!());
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[22]), atom_as_cell!(p_atom, 1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[23]), heap_loc_as_cell!(23));
+
+        wam.machine_st.heap.clear();
+
+        {
+            let mut iter = stackless_preorder_iter(
+                &mut wam.machine_st.heap,
+                fixnum_as_cell!(Fixnum::build_with(0))
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                fixnum_as_cell!(Fixnum::build_with(0))
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        assert_eq!(wam.machine_st.heap.len(), 0);
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(str_loc_as_cell!(1));
+
+        wam.machine_st.heap.push(atom_as_cell!(atom!("g"),2));
+        wam.machine_st.heap.push(heap_loc_as_cell!(0));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("y")));
+
+        {
+            let mut iter = stackless_preorder_iter(
+                &mut wam.machine_st.heap,
+                str_loc_as_cell!(1),
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(atom!("g"),2)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(atom!("y"))
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                str_loc_as_cell!(1)
+            );
+
+            assert!(iter.next().is_none());
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(atom_as_cell!(atom!("g"),2));
+        wam.machine_st.heap.push(str_loc_as_cell!(0));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("y")));
+
+        {
+            let mut iter = stackless_preorder_iter(
+                &mut wam.machine_st.heap,
+                str_loc_as_cell!(0),
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(atom!("g"),2)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(atom!("y"))
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                str_loc_as_cell!(0)
+            );
+
+            assert!(iter.next().is_none());
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(str_loc_as_cell!(1));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("g"), 2));
+        wam.machine_st.heap.push(heap_loc_as_cell!(0));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("y")));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("="), 2));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("X")));
+        wam.machine_st.heap.push(heap_loc_as_cell!(0));
+        wam.machine_st.heap.push(list_loc_as_cell!(8));
+        wam.machine_st.heap.push(str_loc_as_cell!(4));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        {
+            let mut iter = stackless_preorder_iter(
+                &mut wam.machine_st.heap,
+                heap_loc_as_cell!(7),
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(8)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                empty_list_as_cell!()
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(atom!("="), 2)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(atom!("g"), 2)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(atom!("y"))
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(0)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(atom!("X"))
+            );
+
+            assert!(iter.next().is_none());
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        assert_eq!(wam.machine_st.heap[0], str_loc_as_cell!(1));
+        assert_eq!(wam.machine_st.heap[1], atom_as_cell!(atom!("g"), 2));
+        assert_eq!(wam.machine_st.heap[2], heap_loc_as_cell!(0));
+        assert_eq!(wam.machine_st.heap[3], atom_as_cell!(atom!("y")));
+        assert_eq!(wam.machine_st.heap[4], atom_as_cell!(atom!("="), 2));
+        assert_eq!(wam.machine_st.heap[5], atom_as_cell!(atom!("X")));
+        assert_eq!(wam.machine_st.heap[6], heap_loc_as_cell!(0));
+        assert_eq!(wam.machine_st.heap[7], list_loc_as_cell!(8));
+        assert_eq!(wam.machine_st.heap[8], str_loc_as_cell!(4));
+        assert_eq!(wam.machine_st.heap[9], empty_list_as_cell!());
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(atom_as_cell!(atom!("f"), 2));
+        wam.machine_st.heap.push(heap_loc_as_cell!(1));
+        wam.machine_st.heap.push(heap_loc_as_cell!(1));
+
+        {
+            let mut iter = stackless_preorder_iter(
+                &mut wam.machine_st.heap,
+                str_loc_as_cell!(0),
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(atom!("f"), 2)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(1)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(1)
+            );
+
+            assert!(iter.next().is_none());
+        }
+
+        assert_eq!(wam.machine_st.heap[0], atom_as_cell!(atom!("f"), 2));
+        assert_eq!(wam.machine_st.heap[1], heap_loc_as_cell!(1));
+        assert_eq!(wam.machine_st.heap[2], heap_loc_as_cell!(1));
+    }
+
+    #[test]
+    fn heap_stackful_iter_tests() {
+        let mut wam = MockWAM::new();
+
+        let f_atom = atom!("f");
+        let a_atom = atom!("a");
+        let b_atom = atom!("b");
+
+        wam.machine_st.heap
+            .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)]));
+
+        {
+            let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 2)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.extend(functor!(
+            f_atom,
+            [
+                atom(a_atom),
+                atom(b_atom),
+                atom(a_atom),
+                cell(str_loc_as_cell!(0))
+            ]
+        ));
+
+        for _ in 0..20 {
+            let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 4)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(0));
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        {
+            wam.machine_st.heap.push(heap_loc_as_cell!(0));
+
+            let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+            let mut var = heap_loc_as_cell!(0);
+
+            // self-referencing variables are copied with their forwarding
+            // and marking bits set to true. it suffices to check only the
+            // forwarding bit to detect cycles of all kinds, including
+            // unbound/self-referencing variables.
+
+            var.set_forwarding_bit(true);
+            var.set_mark_bit(true);
+
+            assert_eq!(iter.next().unwrap(), var);
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        {
+            // mutually referencing variables.
+            wam.machine_st.heap.push(heap_loc_as_cell!(1));
+            wam.machine_st.heap.push(heap_loc_as_cell!(0));
+
+            let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(0)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        // term  is: [a, b]
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(atom_as_cell!(a_atom));
+        wam.machine_st.heap.push(list_loc_as_cell!(3));
+        wam.machine_st.heap.push(atom_as_cell!(b_atom));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        {
+            let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                empty_list_as_cell!()
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.pop();
+
+        // now make the list cyclic.
+        wam.machine_st.heap.push(heap_loc_as_cell!(0));
+
+        {
+            let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            // the cycle will be iterated twice before being detected.
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        {
+            let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            // cut the iteration short to check that all cells are
+            // unmarked and unforwarded by the Drop instance of
+            // StackfulPreOrderHeapIter.
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        assert_eq!(wam.machine_st.heap[0], list_loc_as_cell!(1));
+        assert_eq!(wam.machine_st.heap[1], atom_as_cell!(a_atom));
+        assert_eq!(wam.machine_st.heap[2], list_loc_as_cell!(3));
+        assert_eq!(wam.machine_st.heap[3], atom_as_cell!(b_atom));
+        assert_eq!(wam.machine_st.heap[4], heap_loc_as_cell!(0));
+
+        wam.machine_st.heap.clear();
+
+        // first a 'dangling' partial string, later modified to be a
+        // two-part complete string, then a three-part cyclic string
+        // involving an uncompacted list of chars.
+
+        let pstr_var_cell = put_partial_string(&mut wam.machine_st.heap, "abc ", &mut wam.machine_st.atom_tbl);
+        let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize];
+
+        {
+            let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(1),
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        // here
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(heap_loc_as_cell!(2));
+
+        let pstr_second_var_cell = put_partial_string(&mut wam.machine_st.heap, "def", &mut wam.machine_st.atom_tbl);
+        let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize];
+
+        {
+            let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(3),
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(pstr_loc_as_cell!(wam.machine_st.heap.len() + 1));
+
+        wam.machine_st.heap.push(pstr_offset_as_cell!(0));
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0i64)));
+
+        {
+            let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+            let pstr_offset_cell = pstr_offset_as_cell!(0);
+
+            // pstr_offset_cell.set_forwarding_bit(true);
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell);
+            assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(0i64)));
+
+            assert_eq!(iter.next(), None);
+        }
+
+        /*
+        {
+            let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0);
+            let string: String = iter.chars().collect();
+            assert_eq!(string, "abc def");
+        }
+        */
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(1i64)));
+
+        {
+            let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+            let pstr_offset_cell = pstr_offset_as_cell!(0);
+
+            // pstr_offset_cell.set_forwarding_bit(true);
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell);
+            assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(1i64)));
+
+            let h = iter.focus();
+
+            assert_eq!(h, 5);
+            assert_eq!(unmark_cell_bits!(iter.heap[4]), pstr_offset_as_cell!(0));
+            assert_eq!(unmark_cell_bits!(iter.heap[5]), fixnum_as_cell!(Fixnum::build_with(1i64)));
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        let functor = functor!(f_atom, [atom(a_atom), atom(b_atom), atom(b_atom)]);
+
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(str_loc_as_cell!(5));
+        wam.machine_st.heap.push(list_loc_as_cell!(3));
+        wam.machine_st.heap.push(str_loc_as_cell!(5));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        wam.machine_st.heap.extend(functor);
+
+        {
+            let mut iter = StackfulPreOrderHeapIter::new(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                empty_list_as_cell!()
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap[4] = list_loc_as_cell!(1);
+
+        {
+            let mut iter = stackful_preorder_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+
+            let mut link_back = list_loc_as_cell!(1);
+            link_back.set_forwarding_bit(true);
+
+            assert_eq!(iter.next().unwrap(), link_back);
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+
+        {
+            let mut iter = StackfulPreOrderHeapIter::new(
+                &mut wam.machine_st.heap,
+                heap_loc_as_cell!(0),
+            );
+
+            let mut cyclic_link = list_loc_as_cell!(1);
+            cyclic_link.set_forwarding_bit(true);
+
+            assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1));
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+            assert_eq!(iter.next().unwrap(), cyclic_link);
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(pstr_as_cell!(atom!("a string")));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        {
+            let mut iter = stackful_preorder_iter(
+                &mut wam.machine_st.heap,
+                heap_loc_as_cell!(0),
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                pstr_as_cell!(atom!("a string"))
+            );
+
+            assert_eq!(
+                iter.next().unwrap(),
+                empty_list_as_cell!()
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(str_loc_as_cell!(1));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("g"), 2));
+        wam.machine_st.heap.push(heap_loc_as_cell!(0));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("y")));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("="), 2));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("X")));
+        wam.machine_st.heap.push(heap_loc_as_cell!(0));
+        wam.machine_st.heap.push(list_loc_as_cell!(8));
+        wam.machine_st.heap.push(str_loc_as_cell!(4));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        {
+            let mut iter = stackful_preorder_iter(
+                &mut wam.machine_st.heap,
+                heap_loc_as_cell!(0),
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(atom!("g"), 2)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                str_loc_as_cell!(1)
+            );
+
+            assert_eq!(
+                iter.next().unwrap(),
+                atom_as_cell!(atom!("y"))
+            );
+
+            assert!(iter.next().is_none());
+        }
+    }
+
+    #[test]
+    fn heap_stackful_post_order_iter() {
+        let mut wam = MockWAM::new();
+
+        let f_atom = atom!("f");
+        let a_atom = atom!("a");
+        let b_atom = atom!("b");
+
+        wam.machine_st.heap
+            .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)]));
+
+        {
+            let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 2)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.extend(functor!(
+            f_atom,
+            [
+                atom(a_atom),
+                atom(b_atom),
+                atom(a_atom),
+                cell(str_loc_as_cell!(0))
+            ]
+        ));
+
+        for _ in 0..20 { // 0000 {
+            let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 4)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        {
+            wam.machine_st.heap.push(heap_loc_as_cell!(0));
+
+            let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+            let mut var = heap_loc_as_cell!(0);
+
+            // self-referencing variables are copied with their forwarding
+            // and marking bits set to true. it suffices to check only the
+            // forwarding bit to detect cycles of all kinds, including
+            // unbound/self-referencing variables.
+
+            var.set_forwarding_bit(true);
+            var.set_mark_bit(true);
+
+            assert_eq!(iter.next().unwrap(), var);
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        {
+            // mutually referencing variables.
+            wam.machine_st.heap.push(heap_loc_as_cell!(1));
+            wam.machine_st.heap.push(heap_loc_as_cell!(0));
+
+            let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(0)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        // term  is: [a, b]
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(atom_as_cell!(a_atom));
+        wam.machine_st.heap.push(list_loc_as_cell!(3));
+        wam.machine_st.heap.push(atom_as_cell!(b_atom));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        {
+            let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                empty_list_as_cell!()
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.pop();
+
+        // now make the list cyclic.
+        wam.machine_st.heap.push(heap_loc_as_cell!(0));
+
+        {
+            let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            // the cycle will be iterated twice before being detected.
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        {
+            let mut iter = stackful_post_order_iter(
+                &mut wam.machine_st.heap,
+                heap_loc_as_cell!(0),
+            );
+
+            // cut the iteration short to check that all cells are
+            // unmarked and unforwarded by the Drop instance of
+            // StackfulPreOrderHeapIter.
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        assert_eq!(wam.machine_st.heap[0], list_loc_as_cell!(1));
+        assert_eq!(wam.machine_st.heap[1], atom_as_cell!(a_atom));
+        assert_eq!(wam.machine_st.heap[2], list_loc_as_cell!(3));
+        assert_eq!(wam.machine_st.heap[3], atom_as_cell!(b_atom));
+        assert_eq!(wam.machine_st.heap[4], heap_loc_as_cell!(0));
+
+        wam.machine_st.heap.clear();
+
+        // first a 'dangling' partial string, later modified to be a
+        // two-part complete string, then a three-part cyclic string
+        // involving an uncompacted list of chars.
+
+        let pstr_var_cell = put_partial_string(&mut wam.machine_st.heap, "abc ", &mut wam.machine_st.atom_tbl);
+        let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize];
+
+        {
+            let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(1),
+            );
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(pstr_loc_as_cell!(2));
+
+        let pstr_second_var_cell = put_partial_string(&mut wam.machine_st.heap, "def", &mut wam.machine_st.atom_tbl);
+        let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize];
+
+        {
+            let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(3),
+            );
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(pstr_loc_as_cell!(wam.machine_st.heap.len() + 1));
+
+        wam.machine_st.heap.push(pstr_offset_as_cell!(0));
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0i64)));
+
+        {
+            let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+
+            assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(0i64)));
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0));
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(1i64)));
+
+        {
+            let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+
+            assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(1i64)));
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0));
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        let functor = functor!(f_atom, [atom(a_atom), atom(b_atom), atom(b_atom)]);
+
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(str_loc_as_cell!(5));
+        wam.machine_st.heap.push(list_loc_as_cell!(3));
+        wam.machine_st.heap.push(str_loc_as_cell!(5));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        wam.machine_st.heap.extend(functor);
+
+        {
+            let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                empty_list_as_cell!()
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap[4] = list_loc_as_cell!(1);
+
+        {
+            let mut iter = stackful_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+
+            let mut link_back = list_loc_as_cell!(1);
+            link_back.set_forwarding_bit(true);
+
+            assert_eq!(iter.next().unwrap(), link_back);
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+        wam.machine_st.heap.clear();
+    }
+
+    #[test]
+    fn heap_stackless_post_order_iter() {
+        let mut wam = MockWAM::new();
+
+        let f_atom = atom!("f");
+        let a_atom = atom!("a");
+        let b_atom = atom!("b");
+
+        wam.machine_st.heap.extend(functor!(f_atom, [atom(a_atom), atom(b_atom)]));
+
+        {
+            let mut iter = stackless_post_order_iter(
+                &mut wam.machine_st.heap,
+                str_loc_as_cell!(0),
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 2)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.extend(functor!(
+            f_atom,
+            [
+                atom(a_atom),
+                atom(b_atom),
+                atom(a_atom),
+                cell(str_loc_as_cell!(0))
+            ]
+        ));
+
+        for _ in 0..20 {
+            let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, str_loc_as_cell!(0));
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), str_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 4)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        {
+            wam.machine_st.heap.push(heap_loc_as_cell!(0));
+
+            let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(0)
+            );
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        {
+            // mutually referencing variables.
+            wam.machine_st.heap.push(heap_loc_as_cell!(1));
+            wam.machine_st.heap.push(heap_loc_as_cell!(0));
+
+            let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(0)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        // term  is: [a, b]
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(atom_as_cell!(a_atom));
+        wam.machine_st.heap.push(list_loc_as_cell!(3));
+        wam.machine_st.heap.push(atom_as_cell!(b_atom));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        {
+            let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                empty_list_as_cell!()
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.pop();
+
+        // now make the list cyclic.
+        wam.machine_st.heap.push(heap_loc_as_cell!(0));
+
+        {
+            let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            // the cycle will be iterated twice before being detected.
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(0)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        {
+            let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            // cut the iteration short to check that all cells are
+            // unmarked and unforwarded by the Drop instance of
+            // StacklessPreOrderHeapIter.
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(0)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        assert_eq!(wam.machine_st.heap[0], list_loc_as_cell!(1));
+        assert_eq!(wam.machine_st.heap[1], atom_as_cell!(a_atom));
+        assert_eq!(wam.machine_st.heap[2], list_loc_as_cell!(3));
+        assert_eq!(wam.machine_st.heap[3], atom_as_cell!(b_atom));
+        assert_eq!(wam.machine_st.heap[4], heap_loc_as_cell!(0));
+
+        wam.machine_st.heap.clear();
+
+        // first a 'dangling' partial string, later modified to be a
+        // two-part complete string, then a three-part cyclic string
+        // involving an uncompacted list of chars.
+
+        let pstr_var_cell = put_partial_string(&mut wam.machine_st.heap, "abc ", &mut wam.machine_st.atom_tbl);
+        let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize];
+
+        {
+            let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(1),
+            );
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(pstr_loc_as_cell!(2));
+
+        let pstr_second_var_cell = put_partial_string(
+            &mut wam.machine_st.heap,
+            "def",
+            &mut wam.machine_st.atom_tbl,
+        );
+
+        let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize];
+
+        {
+            let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                heap_loc_as_cell!(3),
+            );
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(pstr_loc_as_cell!(wam.machine_st.heap.len() + 1));
+
+        wam.machine_st.heap.push(pstr_offset_as_cell!(0));
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0)));
+
+        {
+            let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+            let mut pstr_loc_cell = pstr_loc_as_cell!(0);
+
+            pstr_loc_cell.set_forwarding_bit(true);
+
+            // assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(0i64)));
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0));
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0));
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(1)));
+
+        {
+            let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+
+            //assert_eq!(iter.next().unwrap(), fixnum_as_cell!(Fixnum::build_with(1)));
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0));
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_as_cell!(0));
+
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
+            assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
+
+            assert_eq!(iter.next(), None);
+        }
+
+        wam.machine_st.heap.clear();
+
+        let functor = functor!(f_atom, [atom(a_atom), atom(b_atom), atom(b_atom)]);
+
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(str_loc_as_cell!(5));
+        wam.machine_st.heap.push(list_loc_as_cell!(3));
+        wam.machine_st.heap.push(str_loc_as_cell!(5));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        wam.machine_st.heap.extend(functor);
+
+        {
+            let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                empty_list_as_cell!()
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap[4] = list_loc_as_cell!(1);
+
+        {
+            let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(b_atom)
+            );
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(a_atom)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                atom_as_cell!(f_atom, 3)
+            );
+
+            assert_eq!(iter.next().unwrap(), list_loc_as_cell!(1));
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(3)
+            );
+
+            assert_eq!(
+                unmark_cell_bits!(iter.next().unwrap()),
+                list_loc_as_cell!(1)
+            );
+
+            assert_eq!(iter.next(), None);
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
     }
 }
index 09af815fc7b032caaac4ee9eac3a73b48d672c22..7a0c2c5db7bb674716bc11bb4b56f50819d00647 100644 (file)
@@ -1,9 +1,11 @@
-use prolog_parser::ast::*;
-use prolog_parser::{
-    alpha_numeric_char, capital_letter_char, clause_name, cut_char, decimal_digit_char,
-    graphic_token_char, is_fx, is_infix, is_postfix, is_prefix, is_xf, is_xfx, is_xfy, is_yfx,
-    semicolon_char, sign_char, single_quote_char, small_letter_char, solo_char,
-    variable_indicator_char,
+use crate::arena::*;
+use crate::atom_table::*;
+use crate::parser::ast::*;
+use crate::parser::rug::{Integer, Rational};
+use crate::{
+    alpha_numeric_char, capital_letter_char, cut_char, decimal_digit_char, graphic_token_char,
+    is_fx, is_infix, is_postfix, is_prefix, is_xf, is_xfx, is_xfy, is_yfx, semicolon_char,
+    sign_char, single_quote_char, small_letter_char, solo_char, variable_indicator_char,
 };
 
 use crate::clause_types::*;
@@ -11,40 +13,41 @@ use crate::forms::*;
 use crate::heap_iter::*;
 use crate::machine::heap::*;
 use crate::machine::machine_indices::*;
-use crate::machine::machine_state::*;
+use crate::machine::partial_string::*;
 use crate::machine::streams::*;
-use crate::rug::{Integer, Rational};
+use crate::types::*;
+
 use ordered_float::OrderedFloat;
 
 use indexmap::{IndexMap, IndexSet};
 
 use std::cell::Cell;
 use std::convert::TryFrom;
-use std::iter::{once, FromIterator};
+use std::iter::once;
 use std::net::{IpAddr, TcpListener};
 use std::ops::{Range, RangeFrom};
 use std::rc::Rc;
 
 /* contains the location, name, precision and Specifier of the parent op. */
-#[derive(Debug, Clone)]
+#[derive(Debug, Copy, Clone)]
 pub(crate) enum DirectedOp {
-    Left(ClauseName, SharedOpDesc),
-    Right(ClauseName, SharedOpDesc),
+    Left(Atom, OpDesc),
+    Right(Atom, OpDesc),
 }
 
 impl DirectedOp {
     #[inline]
-    fn as_str(&self) -> &str {
+    fn as_atom(&self) -> Atom {
         match self {
-            &DirectedOp::Left(ref name, _) | &DirectedOp::Right(ref name, _) => name.as_str(),
+            &DirectedOp::Left(name, _) | &DirectedOp::Right(name, _) => name,
         }
     }
 
     #[inline]
     fn is_negative_sign(&self) -> bool {
         match self {
-            &DirectedOp::Left(ref name, ref cell) | &DirectedOp::Right(ref name, ref cell) => {
-                name.as_str() == "-" && is_prefix!(cell.assoc())
+            &DirectedOp::Left(name, cell) | &DirectedOp::Right(name, cell) => {
+                name == atom!("-") && is_prefix!(cell.get_spec() as u32)
             }
         }
     }
@@ -59,29 +62,33 @@ impl DirectedOp {
     }
 }
 
-fn needs_bracketing(child_spec: &SharedOpDesc, op: &DirectedOp) -> bool {
+fn needs_bracketing(child_desc: OpDesc, op: &DirectedOp) -> bool {
     match op {
-        &DirectedOp::Left(ref name, ref cell) => {
+        DirectedOp::Left(name, cell) => {
             let (priority, spec) = cell.get();
 
             if name.as_str() == "-" {
-                let child_assoc = child_spec.assoc();
+                let child_assoc = child_desc.get_spec();
                 if is_prefix!(spec) && (is_postfix!(child_assoc) || is_infix!(child_assoc)) {
                     return true;
                 }
             }
 
             let is_strict_right = is_yfx!(spec) || is_xfx!(spec) || is_fx!(spec);
-            child_spec.prec() > priority || (child_spec.prec() == priority && is_strict_right)
+            child_desc.get_prec() > priority
+                || (child_desc.get_prec() == priority && is_strict_right)
         }
-        &DirectedOp::Right(_, ref cell) => {
+        DirectedOp::Right(_, cell) => {
             let (priority, spec) = cell.get();
             let is_strict_left = is_xfx!(spec) || is_xfy!(spec) || is_xf!(spec);
 
-            if child_spec.prec() > priority || (child_spec.prec() == priority && is_strict_left) {
+            if child_desc.get_prec() > priority
+                || (child_desc.get_prec() == priority && is_strict_left)
+            {
                 true
-            } else if (is_postfix!(spec) || is_infix!(spec)) && !is_postfix!(child_spec.assoc()) {
-                !SharedOpDesc::ptr_eq(&cell, &child_spec) && child_spec.prec() == priority
+            } else if (is_postfix!(spec) || is_infix!(spec)) && !is_postfix!(child_desc.get_spec())
+            {
+                *cell == child_desc && child_desc.get_prec() == priority
             } else {
                 false
             }
@@ -89,59 +96,67 @@ fn needs_bracketing(child_spec: &SharedOpDesc, op: &DirectedOp) -> bool {
     }
 }
 
-impl<'a> HCPreOrderIterator<'a> {
+impl<'a> StackfulPreOrderHeapIter<'a> {
     /*
      * descend into the subtree where the iterator is currently parked
      * and check that the leftmost leaf is a number, with every node
      * encountered on the way an infix or postfix operator, unblocked
      * by brackets.
      */
-    fn leftmost_leaf_has_property<P>(&self, property_check: P) -> bool
+    fn leftmost_leaf_has_property<P>(&self, op_dir: &OpDir, property_check: P) -> bool
     where
-        P: Fn(Addr, &Heap) -> bool,
+        P: Fn(HeapCellValue) -> bool,
     {
-        let mut addr = match self.state_stack.last().cloned() {
-            Some(addr) => addr,
+        let mut h = match self.stack_last() {
+            Some(h) => h,
             None => return false,
         };
 
-        let mut parent_spec = DirectedOp::Left(clause_name!("-"), SharedOpDesc::new(200, FY));
+        let mut parent_spec = DirectedOp::Left(atom!("-"), OpDesc::build_with(200, FY as u8));
 
         loop {
-            match self.machine_st.store(self.machine_st.deref(addr)) {
-                Addr::Str(s) => match &self.machine_st.heap[s] {
-                    &HeapCellValue::NamedStr(_, ref name, Some(ref spec))
-                        if is_postfix!(spec.assoc()) || is_infix!(spec.assoc()) =>
-                    {
-                        if needs_bracketing(spec, &parent_spec) {
+            // match self.machine_st.store(self.machine_st.deref(addr)) {
+            read_heap_cell!(self.heap[h],
+                (HeapCellValueTag::Str, s) => {
+                    read_heap_cell!(self.heap[s],
+                        (HeapCellValueTag::Atom, (name, _arity)) => {
+                            if let Some(spec) = fetch_atom_op_spec(name, None, op_dir) {
+                                if is_postfix!(spec.get_spec() as u32) || is_infix!(spec.get_spec() as u32) {
+                                    if needs_bracketing(spec, &parent_spec) {
+                                        return false;
+                                    } else {
+                                        h = s + 1;
+                                        parent_spec = DirectedOp::Right(name, spec);
+                                        continue;
+                                    }
+                                }
+                            }
+
                             return false;
-                        } else {
-                            addr = Addr::HeapCell(s + 1);
-                            parent_spec = DirectedOp::Right(name.clone(), spec.clone());
                         }
-                    }
-                    _ => {
-                        return false;
-                    }
-                },
-                addr => {
-                    return property_check(addr, &self.machine_st.heap);
+                        _ => {
+                            return false;
+                        }
+                    )
                 }
-            }
+                _ => {
+                    return property_check(self.heap[h]);
+                }
+            )
         }
     }
 
     fn immediate_leaf_has_property<P>(&self, property_check: P) -> bool
     where
-        P: Fn(Addr, &Heap) -> bool,
+        P: Fn(HeapCellValue) -> bool,
     {
-        let addr = match self.state_stack.last().cloned() {
-            Some(addr) => addr,
+        let addr = match self.stack_last() {
+            Some(h) => self.heap[h],
             None => return false,
         };
 
-        let addr = self.machine_st.store(self.machine_st.deref(addr));
-        property_check(addr, &self.machine_st.heap)
+        // let addr = self.machine_st.store(self.machine_st.deref(addr));
+        property_check(addr)
     }
 }
 
@@ -168,20 +183,36 @@ fn char_to_string(is_quoted: bool, c: char) -> String {
     }
 }
 
+#[derive(Clone, Copy, Debug)]
+enum NumberFocus {
+    Unfocused(Number),
+    Denominator(TypedArenaPtr<Rational>),
+    Numerator(TypedArenaPtr<Rational>),
+}
+
+impl NumberFocus {
+    fn is_positive(&self) -> bool {
+        match self {
+            NumberFocus::Unfocused(n) => n.is_positive(),
+            NumberFocus::Denominator(r) | NumberFocus::Numerator(r) => **r > 0,
+        }
+    }
+}
+
 #[derive(Debug, Clone)]
 enum TokenOrRedirect {
-    Atom(ClauseName),
+    Atom(Atom),
     BarAsOp,
-    Op(ClauseName, SharedOpDesc),
+    Op(Atom, OpDesc),
     NumberedVar(String),
     CompositeRedirect(usize, DirectedOp),
     FunctorRedirect(usize),
-    IpAddr(IpAddr),
-    Number(Number, Option<DirectedOp>),
+    #[allow(unused)] IpAddr(IpAddr),
+    NumberFocus(NumberFocus, Option<DirectedOp>),
     Open,
     Close,
     Comma,
-    RawPtr(*const u8),
+    RawPtr(*const ArenaHeader),
     Space,
     LeftCurly,
     RightCurly,
@@ -190,7 +221,89 @@ enum TokenOrRedirect {
     HeadTailSeparator,
 }
 
-pub(crate) trait HCValueOutputter {
+pub(crate) fn requires_space(atom: &str, op: &str) -> bool {
+    match atom.chars().last() {
+        Some(ac) => op
+            .chars()
+            .next()
+            .map(|oc| {
+                if ac == '0' {
+                    oc == '\'' || oc == '(' || alpha_numeric_char!(oc)
+                } else if alpha_numeric_char!(ac) {
+                    oc == '(' || alpha_numeric_char!(oc)
+                } else if graphic_token_char!(ac) {
+                    graphic_token_char!(oc)
+                } else if variable_indicator_char!(ac) {
+                    alpha_numeric_char!(oc)
+                } else if capital_letter_char!(ac) {
+                    alpha_numeric_char!(oc)
+                } else if sign_char!(ac) {
+                    sign_char!(oc) || decimal_digit_char!(oc)
+                } else if single_quote_char!(ac) {
+                    single_quote_char!(oc)
+                } else {
+                    false
+                }
+            })
+            .unwrap_or(false),
+        _ => false,
+    }
+}
+
+fn non_quoted_graphic_token<Iter: Iterator<Item = char>>(mut iter: Iter, c: char) -> bool {
+    if c == '/' {
+        return match iter.next() {
+            None => true,
+            Some('*') => false, // if we start with comment token, we must quote.
+            Some(c) => {
+                if graphic_token_char!(c) {
+                    iter.all(|c| graphic_token_char!(c))
+                } else {
+                    false
+                }
+            }
+        };
+    } else if c == '.' {
+        return match iter.next() {
+            None => false,
+            Some(c) => {
+                if graphic_token_char!(c) {
+                    iter.all(|c| graphic_token_char!(c))
+                } else {
+                    false
+                }
+            }
+        };
+    } else {
+        iter.all(|c| graphic_token_char!(c))
+    }
+}
+
+pub(super) fn non_quoted_token<Iter: Iterator<Item = char>>(mut iter: Iter) -> bool {
+    if let Some(c) = iter.next() {
+        if small_letter_char!(c) {
+            iter.all(|c| alpha_numeric_char!(c))
+        } else if graphic_token_char!(c) {
+            non_quoted_graphic_token(iter, c)
+        } else if semicolon_char!(c) {
+            iter.next().is_none()
+        } else if cut_char!(c) {
+            iter.next().is_none()
+        } else if c == '[' {
+            iter.next() == Some(']') && iter.next().is_none()
+        } else if c == '{' {
+            iter.next() == Some('}') && iter.next().is_none()
+        } else if solo_char!(c) {
+            !(c == '(' || c == ')' || c == '}' || c == ']' || c == ',' || c == '%' || c == '|')
+        } else {
+            false
+        }
+    } else {
+        false
+    }
+}
+
+pub trait HCValueOutputter {
     type Output;
 
     fn new() -> Self;
@@ -207,7 +320,7 @@ pub(crate) trait HCValueOutputter {
 }
 
 #[derive(Debug)]
-pub(crate) struct PrinterOutputter {
+pub struct PrinterOutputter {
     contents: String,
 }
 
@@ -273,11 +386,15 @@ fn is_numbered_var(ct: &ClauseType, arity: usize) -> bool {
 }
 
 #[inline]
-fn negated_op_needs_bracketing(iter: &HCPreOrderIterator, op: &Option<DirectedOp>) -> bool {
+fn negated_op_needs_bracketing(
+    iter: &StackfulPreOrderHeapIter,
+    op_dir: &OpDir,
+    op: &Option<DirectedOp>,
+) -> bool {
     if let Some(ref op) = op {
         op.is_negative_sign()
-            && iter.leftmost_leaf_has_property(|addr, heap| match Number::try_from((addr, heap)) {
-                Ok(Number::Fixnum(n)) => n > 0,
+            && iter.leftmost_leaf_has_property(op_dir, |addr| match Number::try_from(addr) {
+                Ok(Number::Fixnum(n)) => n.get_num() > 0,
                 Ok(Number::Float(f)) => f > OrderedFloat(0f64),
                 Ok(Number::Integer(n)) => &*n > &0,
                 Ok(Number::Rational(n)) => &*n > &0,
@@ -288,66 +405,103 @@ fn negated_op_needs_bracketing(iter: &HCPreOrderIterator, op: &Option<DirectedOp
     }
 }
 
-fn numbervar(n: Integer) -> Var {
-    static CHAR_CODES: [char; 26] = [
-        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
-        'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
-    ];
+pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option<String> {
+    fn numbervar(n: Integer) -> String {
+        static CHAR_CODES: [char; 26] = [
+            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
+            'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+        ];
 
-    let i = n.mod_u(26) as usize;
-    let j = n.div_rem_floor(Integer::from(26));
-    let j = <(Integer, Integer)>::from(j).0;
+        let i = n.mod_u(26) as usize;
+        let j = n.div_rem_floor(Integer::from(26));
+        let j = <(Integer, Integer)>::from(j).0;
 
-    if j == 0 {
-        CHAR_CODES[i].to_string()
-    } else {
-        format!("{}{}", CHAR_CODES[i], j)
+        if j == 0 {
+            CHAR_CODES[i].to_string()
+        } else {
+            format!("{}{}", CHAR_CODES[i], j)
+        }
     }
-}
 
-impl MachineState {
-    pub(crate) fn numbervar(&self, offset: &Integer, addr: Addr) -> Option<Var> {
-        let addr = self.store(self.deref(addr));
-
-        match Number::try_from((addr, &self.heap)) {
-            Ok(Number::Fixnum(n)) => {
-                if n >= 0 {
-                    Some(numbervar(Integer::from(offset + Integer::from(n))))
-                } else {
-                    None
-                }
+    match Number::try_from(addr) {
+        Ok(Number::Fixnum(n)) => {
+            if n.get_num() >= 0 {
+                Some(numbervar(offset + Integer::from(n.get_num())))
+            } else {
+                None
             }
-            Ok(Number::Integer(n)) => {
-                if &*n >= &0 {
-                    Some(numbervar(Integer::from(offset + &*n)))
-                } else {
-                    None
-                }
+        }
+        Ok(Number::Integer(n)) => {
+            if &*n >= &0 {
+                Some(numbervar(Integer::from(offset + &*n)))
+            } else {
+                None
             }
-            _ => None,
         }
+        _ => None,
     }
 }
 
-type ReverseHeapVarDict = IndexMap<Addr, Rc<Var>>;
+macro_rules! push_char {
+    ($self:ident, $c:expr) => {{
+        $self.outputter.push_char($c);
+        $self.last_item_idx = $self.outputter.len();
+    }};
+}
+
+macro_rules! append_str {
+    ($self:ident, $s:expr) => {{
+        $self.last_item_idx = $self.outputter.len();
+        $self.outputter.append($s);
+    }};
+}
+
+macro_rules! print_char {
+    ($self:ident, $is_quoted:expr, $c:expr) => {
+        if non_quoted_token(once($c)) {
+            let result = char_to_string(false, $c);
+
+            push_space_if_amb!($self, &result, {
+                append_str!($self, &result);
+            });
+        } else {
+            let mut result = String::new();
+
+            if $self.quoted {
+                result.push('\'');
+                result += &char_to_string($is_quoted, $c);
+                result.push('\'');
+            } else {
+                result += &char_to_string($is_quoted, $c);
+            }
+
+            push_space_if_amb!($self, &result, {
+                append_str!($self, result.as_str());
+            });
+        }
+    };
+}
 
 #[derive(Debug)]
-pub(crate) struct HCPrinter<'a, Outputter> {
+pub struct HCPrinter<'a, Outputter> {
     outputter: Outputter,
-    machine_st: &'a MachineState,
+    iter: StackfulPreOrderHeapIter<'a>,
+    arena: &'a mut Arena,
     op_dir: &'a OpDir,
     state_stack: Vec<TokenOrRedirect>,
     toplevel_spec: Option<DirectedOp>,
-    heap_locs: ReverseHeapVarDict,
+    // heap_locs: ReverseHeapVarDict,
+    printed_vars: IndexSet<HeapCellValue>,
     last_item_idx: usize,
-    cyclic_terms: IndexMap<Addr, usize>,
-    non_cyclic_terms: IndexSet<usize>,
-    pub(crate) var_names: IndexMap<Addr, Var>,
-    pub(crate) numbervars_offset: Integer,
-    pub(crate) numbervars: bool,
-    pub(crate) quoted: bool,
-    pub(crate) ignore_ops: bool,
-    pub(crate) max_depth: usize,
+    // cyclic_terms: IndexMap<HeapCellValue, usize>,
+    // non_cyclic_terms: IndexSet<usize>,
+    pub var_names: IndexMap<HeapCellValue, Rc<String>>,
+    pub numbervars_offset: Integer,
+    pub numbervars: bool,
+    pub quoted: bool,
+    pub ignore_ops: bool,
+    pub print_strings_as_strs: bool,
+    pub max_depth: usize,
 }
 
 macro_rules! push_space_if_amb {
@@ -361,117 +515,29 @@ macro_rules! push_space_if_amb {
     };
 }
 
-pub(crate) fn requires_space(atom: &str, op: &str) -> bool {
-    match atom.chars().last() {
-        Some(ac) => op
-            .chars()
-            .next()
-            .map(|oc| {
-                if ac == '0' {
-                    oc == '\'' || oc == '(' || alpha_numeric_char!(oc)
-                } else if alpha_numeric_char!(ac) {
-                    oc == '(' || alpha_numeric_char!(oc)
-                } else if graphic_token_char!(ac) {
-                    graphic_token_char!(oc)
-                } else if variable_indicator_char!(ac) {
-                    alpha_numeric_char!(oc)
-                } else if capital_letter_char!(ac) {
-                    alpha_numeric_char!(oc)
-                } else if sign_char!(ac) {
-                    sign_char!(oc) || decimal_digit_char!(oc)
-                } else if single_quote_char!(ac) {
-                    single_quote_char!(oc)
-                } else {
-                    false
-                }
-            })
-            .unwrap_or(false),
-        _ => false,
-    }
-}
-
-fn non_quoted_graphic_token<Iter: Iterator<Item = char>>(mut iter: Iter, c: char) -> bool {
-    if c == '/' {
-        return match iter.next() {
-            None => true,
-            Some('*') => false, // if we start with comment token, we must quote.
-            Some(c) => {
-                if graphic_token_char!(c) {
-                    iter.all(|c| graphic_token_char!(c))
-                } else {
-                    false
-                }
-            }
-        };
-    } else if c == '.' {
-        return match iter.next() {
-            None => false,
-            Some(c) => {
-                if graphic_token_char!(c) {
-                    iter.all(|c| graphic_token_char!(c))
-                } else {
-                    false
-                }
-            }
-        };
-    } else {
-        iter.all(|c| graphic_token_char!(c))
-    }
-}
-
-pub(super) fn non_quoted_token<Iter: Iterator<Item = char>>(mut iter: Iter) -> bool {
-    if let Some(c) = iter.next() {
-        if small_letter_char!(c) {
-            iter.all(|c| alpha_numeric_char!(c))
-        } else if graphic_token_char!(c) {
-            non_quoted_graphic_token(iter, c)
-        } else if semicolon_char!(c) {
-            iter.next().is_none()
-        } else if cut_char!(c) {
-            iter.next().is_none()
-        } else if c == '[' {
-            iter.next() == Some(']') && iter.next().is_none()
-        } else if c == '{' {
-            iter.next() == Some('}') && iter.next().is_none()
-        } else if solo_char!(c) {
-            !(c == '(' || c == ')' || c == '}' || c == ']' || c == ',' || c == '%' || c == '|')
-        } else {
-            false
-        }
-    } else {
-        false
-    }
-}
-
-#[inline]
-fn functor_location(addr: &Addr) -> Option<usize> {
-    Some(match addr {
-        &Addr::Lis(l) => l,
-        &Addr::Str(s) => s,
-        &Addr::PStrLocation(h, _) => h,
-        _ => {
-            return None;
-        }
-    })
-}
-
 impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
-    pub(crate) fn new(machine_st: &'a MachineState, op_dir: &'a OpDir, output: Outputter) -> Self {
+    pub fn new(
+        heap: &'a mut Heap,
+        arena: &'a mut Arena,
+        op_dir: &'a OpDir,
+        output: Outputter,
+        cell: HeapCellValue,
+    ) -> Self {
         HCPrinter {
             outputter: output,
-            machine_st,
+            iter: stackful_preorder_iter(heap, cell),
+            arena,
             op_dir,
             state_stack: vec![],
-            heap_locs: ReverseHeapVarDict::new(),
             toplevel_spec: None,
+            printed_vars: IndexSet::new(),
             last_item_idx: 0,
             numbervars: false,
             numbervars_offset: Integer::from(0),
             quoted: false,
             ignore_ops: false,
-            cyclic_terms: IndexMap::new(),
-            non_cyclic_terms: IndexSet::new(),
             var_names: IndexMap::new(),
+            print_strings_as_strs: false,
             max_depth: 0,
         }
     }
@@ -482,87 +548,82 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         requires_space(tail, atom)
     }
 
-    fn enqueue_op(
-        &mut self,
-        iter: &mut HCPreOrderIterator,
-        mut max_depth: usize,
-        ct: ClauseType,
-        spec: SharedOpDesc,
-    ) {
-        if is_postfix!(spec.assoc()) {
+    fn enqueue_op(&mut self, mut max_depth: usize, ct: ClauseType, spec: OpDesc) {
+        let name = ct.name();
+
+        if is_postfix!(spec.get_spec()) {
             if self.check_max_depth(&mut max_depth) {
-                iter.stack().pop();
+                self.iter.pop_stack();
 
-                self.state_stack.push(TokenOrRedirect::Op(ct.name(), spec));
-                self.state_stack
-                    .push(TokenOrRedirect::Atom(clause_name!("...")));
+                self.state_stack.push(TokenOrRedirect::Op(name, spec));
+                self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
 
                 return;
             }
 
-            let right_directed_op = DirectedOp::Right(ct.name(), spec.clone());
+            let right_directed_op = DirectedOp::Right(name, spec);
 
-            self.state_stack.push(TokenOrRedirect::Op(ct.name(), spec));
+            self.state_stack.push(TokenOrRedirect::Op(name, spec));
             self.state_stack.push(TokenOrRedirect::CompositeRedirect(
                 max_depth,
                 right_directed_op,
             ));
-        } else if is_prefix!(spec.assoc()) {
-            match ct.name().as_str() {
-                "-" | "\\" => {
-                    self.format_prefix_op_with_space(iter, max_depth, ct.name(), spec);
+        } else if is_prefix!(spec.get_spec()) {
+            match name {
+                atom!("-") | atom!("\\") => {
+                    self.format_prefix_op_with_space(max_depth, name, spec);
                     return;
                 }
                 _ => {}
             };
 
             if self.check_max_depth(&mut max_depth) {
-                iter.stack().pop();
+                self.iter.pop_stack();
 
-                self.state_stack
-                    .push(TokenOrRedirect::Atom(clause_name!("...")));
-                self.state_stack.push(TokenOrRedirect::Op(ct.name(), spec));
+                self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
+                self.state_stack.push(TokenOrRedirect::Op(name, spec));
 
                 return;
             }
 
-            let left_directed_op = DirectedOp::Left(ct.name(), spec.clone());
+            let left_directed_op = DirectedOp::Left(name, spec);
 
             self.state_stack.push(TokenOrRedirect::CompositeRedirect(
                 max_depth,
                 left_directed_op,
             ));
-            self.state_stack.push(TokenOrRedirect::Op(ct.name(), spec));
+
+            self.state_stack.push(TokenOrRedirect::Op(name, spec));
         } else {
-            match ct.name().as_str() {
+            match name.as_str() {
                 "|" => {
-                    self.format_bar_separator_op(iter, max_depth, ct.name(), spec);
+                    self.format_bar_separator_op(max_depth, name, spec);
                     return;
                 }
                 _ => {}
             };
 
+            let ellipsis_atom = atom!("...");
+
             if self.check_max_depth(&mut max_depth) {
-                iter.stack().pop();
-                iter.stack().pop();
+                self.iter.pop_stack();
+                self.iter.pop_stack();
 
-                self.state_stack
-                    .push(TokenOrRedirect::Atom(clause_name!("...")));
-                self.state_stack.push(TokenOrRedirect::Op(ct.name(), spec));
-                self.state_stack
-                    .push(TokenOrRedirect::Atom(clause_name!("...")));
+                self.state_stack.push(TokenOrRedirect::Atom(ellipsis_atom));
+                self.state_stack.push(TokenOrRedirect::Op(name, spec));
+                self.state_stack.push(TokenOrRedirect::Atom(ellipsis_atom));
 
                 return;
             }
 
-            let left_directed_op = DirectedOp::Left(ct.name(), spec.clone());
-            let right_directed_op = DirectedOp::Right(ct.name(), spec.clone());
+            let left_directed_op = DirectedOp::Left(name, spec);
+            let right_directed_op = DirectedOp::Right(name, spec);
 
             self.state_stack.push(TokenOrRedirect::CompositeRedirect(
                 max_depth,
                 left_directed_op,
             ));
-            self.state_stack.push(TokenOrRedirect::Op(ct.name(), spec));
+            self.state_stack.push(TokenOrRedirect::Op(name, spec));
             self.state_stack.push(TokenOrRedirect::CompositeRedirect(
                 max_depth,
                 right_directed_op,
@@ -570,21 +631,14 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         }
     }
 
-    fn format_struct(
-        &mut self,
-        iter: &mut HCPreOrderIterator,
-        mut max_depth: usize,
-        arity: usize,
-        name: ClauseName,
-    ) -> bool {
+    fn format_struct(&mut self, mut max_depth: usize, arity: usize, name: Atom) -> bool {
         if self.check_max_depth(&mut max_depth) {
             for _ in 0..arity {
-                iter.stack().pop();
+                self.iter.pop_stack();
             }
 
             self.state_stack.push(TokenOrRedirect::Close);
-            self.state_stack
-                .push(TokenOrRedirect::Atom(clause_name!("...")));
+            self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
             self.state_stack.push(TokenOrRedirect::Open);
 
             self.state_stack.push(TokenOrRedirect::Atom(name));
@@ -608,25 +662,18 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         true
     }
 
-    fn format_prefix_op_with_space(
-        &mut self,
-        iter: &mut HCPreOrderIterator,
-        mut max_depth: usize,
-        name: ClauseName,
-        spec: SharedOpDesc,
-    ) {
+    fn format_prefix_op_with_space(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) {
         if self.check_max_depth(&mut max_depth) {
-            iter.stack().pop();
+            self.iter.pop_stack();
 
-            self.state_stack
-                .push(TokenOrRedirect::Atom(clause_name!("...")));
+            self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
             self.state_stack.push(TokenOrRedirect::Space);
             self.state_stack.push(TokenOrRedirect::Atom(name));
 
             return;
         }
 
-        let op = DirectedOp::Left(name.clone(), spec);
+        let op = DirectedOp::Left(name, spec);
 
         self.state_stack
             .push(TokenOrRedirect::CompositeRedirect(max_depth, op));
@@ -634,46 +681,43 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         self.state_stack.push(TokenOrRedirect::Atom(name));
     }
 
-    fn format_bar_separator_op(
-        &mut self,
-        iter: &mut HCPreOrderIterator,
-        mut max_depth: usize,
-        name: ClauseName,
-        spec: SharedOpDesc,
-    ) {
+    fn format_bar_separator_op(&mut self, mut max_depth: usize, name: Atom, spec: OpDesc) {
         if self.check_max_depth(&mut max_depth) {
-            iter.stack().pop();
+            self.iter.pop_stack();
 
-            self.state_stack
-                .push(TokenOrRedirect::Atom(clause_name!("...")));
+            let ellipsis_atom = atom!("...");
+
+            self.state_stack.push(TokenOrRedirect::Atom(ellipsis_atom));
             self.state_stack.push(TokenOrRedirect::BarAsOp);
-            self.state_stack
-                .push(TokenOrRedirect::Atom(clause_name!("...")));
+            self.state_stack.push(TokenOrRedirect::Atom(ellipsis_atom));
 
             return;
         }
 
-        let left_directed_op = DirectedOp::Left(name.clone(), spec.clone());
-        let right_directed_op = DirectedOp::Right(name.clone(), spec.clone());
+        let left_directed_op = DirectedOp::Left(name, spec);
+        let right_directed_op = DirectedOp::Right(name, spec);
 
         self.state_stack.push(TokenOrRedirect::CompositeRedirect(
             max_depth,
             left_directed_op,
         ));
+
         self.state_stack.push(TokenOrRedirect::BarAsOp);
+
         self.state_stack.push(TokenOrRedirect::CompositeRedirect(
             max_depth,
             right_directed_op,
         ));
     }
 
-    fn format_curly_braces(&mut self, iter: &mut HCPreOrderIterator, mut max_depth: usize) -> bool {
+    fn format_curly_braces(&mut self, mut max_depth: usize) -> bool {
         if self.check_max_depth(&mut max_depth) {
-            iter.stack().pop();
+            self.iter.pop_stack();
+
+            let ellipsis_atom = atom!("...");
 
             self.state_stack.push(TokenOrRedirect::RightCurly);
-            self.state_stack
-                .push(TokenOrRedirect::Atom(clause_name!("...")));
+            self.state_stack.push(TokenOrRedirect::Atom(ellipsis_atom));
             self.state_stack.push(TokenOrRedirect::LeftCurly);
 
             return false;
@@ -687,12 +731,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         true
     }
 
-    fn format_numbered_vars(&mut self, iter: &mut HCPreOrderIterator) -> bool {
-        let addr = iter.stack().last().cloned().unwrap();
+    fn format_numbered_vars(&mut self) -> bool {
+        let h = self.iter.stack_last().unwrap();
 
         // 7.10.4
-        if let Some(var) = iter.machine_st().numbervar(&self.numbervars_offset, addr) {
-            iter.stack().pop();
+        if let Some(var) = numbervar(&self.numbervars_offset, self.iter.heap[h]) {
+            self.iter.pop_stack();
             self.state_stack.push(TokenOrRedirect::NumberedVar(var));
             return true;
         }
@@ -702,198 +746,134 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
 
     fn format_clause(
         &mut self,
-        iter: &mut HCPreOrderIterator,
         max_depth: usize,
         arity: usize,
         ct: ClauseType,
+        op_desc: Option<OpDesc>,
     ) -> bool {
         if self.numbervars && is_numbered_var(&ct, arity) {
-            if self.format_numbered_vars(iter) {
+            if self.format_numbered_vars() {
                 return true;
             }
         }
 
-        if let Some(spec) = ct.spec() {
-            if "." == ct.name().as_str() && is_infix!(spec.assoc()) {
+        let dot_atom = atom!(".");
+        let name = ct.name();
+
+        if let Some(spec) = op_desc {
+            if dot_atom == name && is_infix!(spec.get_spec()) {
                 if !self.ignore_ops {
-                    self.push_list(iter, max_depth);
+                    self.push_list(max_depth);
                     return true;
                 }
             }
 
-            if !self.ignore_ops && spec.prec() > 0 {
-                self.enqueue_op(iter, max_depth, ct, spec);
+            if !self.ignore_ops && spec.get_prec() > 0 {
+                self.enqueue_op(max_depth, ct, spec);
                 return true;
             }
         }
 
-        return match (ct.name().as_str(), arity) {
-            ("{}", 1) if !self.ignore_ops => self.format_curly_braces(iter, max_depth),
-            _ => self.format_struct(iter, max_depth, arity, ct.name()),
+        return match (name.as_str(), arity) {
+            ("{}", 1) if !self.ignore_ops => self.format_curly_braces(max_depth),
+            _ => self.format_struct(max_depth, arity, name),
         };
     }
 
-    #[inline]
-    fn push_char(&mut self, c: char) {
-        self.outputter.push_char(c);
-        self.last_item_idx = self.outputter.len();
-    }
-
-    #[inline]
-    fn append_str(&mut self, s: &str) {
-        self.last_item_idx = self.outputter.len();
-        self.outputter.append(s);
-    }
+    fn offset_as_string(&mut self, h: usize) -> Option<String> {
+        let addr = self.iter.heap[h];
 
-    fn offset_as_string(&mut self, iter: &mut HCPreOrderIterator, addr: Addr) -> Option<Var> {
         if let Some(var) = self.var_names.get(&addr) {
-            if addr.as_var().is_some() {
-                return Some(format!("{}", var));
-            } else {
-                iter.stack().push(addr);
-                return None;
-            }
+            read_heap_cell!(addr,
+               (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
+                   return Some(format!("{}", var.as_str()));
+               }
+               _ => {
+                   self.iter.push_stack(h);
+                   return None;
+               }
+            );
         }
 
-        match addr {
-            Addr::Lis(h) | Addr::Str(h) => Some(format!("{}", h)),
-            _ => {
-                if let Some(r) = addr.as_var() {
-                    match r {
-                        Ref::StackCell(fr, sc) => Some(format!("_s_{}_{}", fr, sc)),
-                        Ref::HeapCell(h) | Ref::AttrVar(h) => Some(format!("_{}", h)),
-                    }
-                } else {
-                    None
-                }
+        read_heap_cell!(addr,
+            (HeapCellValueTag::Lis | HeapCellValueTag::Str, h) => {
+                Some(format!("{}", h))
             }
-        }
-    }
-
-    fn record_children_as_non_cyclic(&mut self, addr: &Addr) {
-        match addr {
-            &Addr::Lis(l) => {
-                let c1 = self
-                    .machine_st
-                    .store(self.machine_st.deref(Addr::HeapCell(l)));
-                let c2 = self
-                    .machine_st
-                    .store(self.machine_st.deref(Addr::HeapCell(l + 1)));
-
-                if let Some(c) = functor_location(&c1) {
-                    self.non_cyclic_terms.insert(c);
-                }
-
-                if let Some(c) = functor_location(&c2) {
-                    self.non_cyclic_terms.insert(c);
-                }
+            (HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => {
+                Some(format!("_{}", h))
             }
-            &Addr::Str(s) => {
-                let arity = match &self.machine_st.heap[s] {
-                    HeapCellValue::NamedStr(arity, ..) => arity,
-                    _ => {
-                        unreachable!()
-                    }
-                };
-
-                for i in 1..arity + 1 {
-                    let c = self
-                        .machine_st
-                        .store(self.machine_st.deref(Addr::HeapCell(s + i)));
-
-                    if let Some(c) = functor_location(&c) {
-                        self.non_cyclic_terms.insert(c);
-                    }
-                }
+            (HeapCellValueTag::StackVar, h) => {
+                Some(format!("_s_{}", h))
             }
-            &Addr::PStrLocation(h, _) => {
-                let tail = self.machine_st.heap[h + 1].as_addr(h + 1);
-                let tail = self.machine_st.store(self.machine_st.deref(tail));
-
-                if let Some(c) = functor_location(&tail) {
-                    self.non_cyclic_terms.insert(c);
-                }
+            _ => {
+                None
             }
-            _ => {}
-        }
+        )
     }
 
-    fn check_for_seen(&mut self, iter: &mut HCPreOrderIterator) -> Option<Addr> {
-        iter.stack().last().cloned().and_then(|addr| {
-            let addr = self.machine_st.store(self.machine_st.deref(addr));
+    fn check_for_seen(&mut self) -> Option<HeapCellValue> {
+        if let Some(addr) = self.iter.next() {
+            let is_cyclic = addr.is_forwarded();
 
-            if let Some(var) = self.var_names.get(&addr) {
-                self.heap_locs.insert(addr.clone(), Rc::new(var.clone()));
-            }
+            let addr = heap_bound_store(
+                self.iter.heap,
+                heap_bound_deref(self.iter.heap, addr),
+            );
+
+            let addr = unmark_cell_bits!(addr);
 
-            match self.heap_locs.get(&addr).cloned() {
-                Some(var) if addr.is_ref() => {
-                    iter.stack().pop();
+            match self.var_names.get(&addr).cloned() {
+                Some(var) if addr.is_var() => {
+                    // If addr is an unbound variable and maps to
+                    // a name via heap_locs, append the name to
+                    // the current output, and return None. None
+                    // short-circuits handle_heap_term.
+                    // self.iter.pop_stack();
 
-                    push_space_if_amb!(self, &var, {
-                        self.append_str(&var);
+                    let var_str = var.as_str();
+
+                    push_space_if_amb!(self, var_str, {
+                        append_str!(self, var_str);
                     });
 
-                    return None;
+                    None
                 }
                 var_opt => {
-                    let offset = match functor_location(&addr) {
-                        Some(offset) => offset,
-                        None => {
-                            return iter.next();
-                        }
-                    };
-
-                    if !self.non_cyclic_terms.contains(&offset) {
-                        if let Some(reps) = self.cyclic_terms.get(&addr).cloned() {
-                            if reps > 0 {
-                                self.cyclic_terms.insert(addr, reps - 1);
-                            } else {
-                                match var_opt {
-                                    Some(var) => {
-                                        push_space_if_amb!(self, &var, {
-                                            self.append_str(&var);
-                                        });
-
-                                        iter.stack().pop();
-                                    }
-                                    None => {
-                                        push_space_if_amb!(self, "...", {
-                                            self.append_str("...");
-                                        });
-
-                                        iter.stack().pop();
-                                        self.cyclic_terms.insert(addr, 2);
-                                    }
-                                }
-
-                                return None;
+                    if is_cyclic && addr.is_compound() {
+                        // self-referential variables are marked "cyclic".
+                        match var_opt {
+                            Some(var) => {
+                                // If the term is bound to a named variable,
+                                // print the variable's name to output.
+                                push_space_if_amb!(self, &var, {
+                                    append_str!(self, &var);
+                                });
                             }
-                        } else if self.machine_st.is_cyclic_term(addr.clone()) {
-                            if var_opt.is_some() {
-                                self.cyclic_terms.insert(addr, 0);
-                            } else {
-                                self.cyclic_terms.insert(addr, 2);
+                            None => {
+                                // otherwise, contract it to an ellipsis.
+                                push_space_if_amb!(self, "...", {
+                                    append_str!(self, "...");
+                                });
                             }
-                        } else {
-                            self.record_children_as_non_cyclic(&addr);
-                            self.non_cyclic_terms.insert(offset);
                         }
-                    } else {
-                        self.record_children_as_non_cyclic(&addr);
+
+                        return None;
                     }
 
-                    iter.next()
+                    Some(addr)
                 }
             }
-        })
+        } else {
+            while let Some(_) = self.iter.pop_stack() {}
+            None
+        }
     }
 
-    fn print_atom(&mut self, atom: &ClauseName) {
+    fn print_atom(&mut self, atom: Atom) {
         let result = self.print_op_addendum(atom.as_str());
 
         push_space_if_amb!(self, result.as_str(), {
-            self.append_str(&result);
+            append_str!(self, &result);
         });
     }
 
@@ -929,23 +909,23 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         };
 
         push_space_if_amb!(self, &result, {
-            self.append_str(&result);
+            append_str!(self, &result);
         });
     }
 
     #[inline]
     fn print_ip_addr(&mut self, ip: IpAddr) {
-        self.push_char('\'');
-        self.append_str(&format!("{}", ip));
-        self.push_char('\'');
+        push_char!(self, '\'');
+        append_str!(self, &format!("{}", ip));
+        push_char!(self, '\'');
     }
 
     #[inline]
-    fn print_raw_ptr(&mut self, ptr: *const u8) {
-        self.append_str(&format!("0x{:x}", ptr as usize));
+    fn print_raw_ptr(&mut self, ptr: *const ArenaHeader) {
+        append_str!(self, &format!("0x{:x}", ptr as *const u8 as usize));
     }
 
-    fn print_number(&mut self, n: Number, op: &Option<DirectedOp>) {
+    fn print_number(&mut self, n: NumberFocus, op: &Option<DirectedOp>) {
         let add_brackets = if let Some(op) = op {
             op.is_negative_sign() && n.is_positive()
         } else {
@@ -953,86 +933,106 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         };
 
         if add_brackets {
-            self.push_char('(');
+            push_char!(self, '(');
         }
 
         match n {
-            Number::Float(fl) => {
-                if &fl == &OrderedFloat(0f64) {
-                    push_space_if_amb!(self, "0.0", {
-                        self.append_str("0.0");
-                    });
-                } else {
-                    let OrderedFloat(fl) = fl;
-                    let output_str = format!("{0:<20?}", fl);
+            NumberFocus::Unfocused(n) => match n {
+                Number::Float(fl) => {
+                    if &fl == &OrderedFloat(0f64) {
+                        push_space_if_amb!(self, "0.0", {
+                            append_str!(self, "0.0");
+                        });
+                    } else {
+                        let OrderedFloat(fl) = fl;
+                        let output_str = format!("{0:<20?}", fl);
+
+                        push_space_if_amb!(self, &output_str, {
+                            append_str!(self, &output_str.trim());
+                        });
+                    }
+                }
+                Number::Rational(r) => {
+                    self.print_rational(r, add_brackets);
+                    return;
+                }
+                n => {
+                    let output_str = format!("{}", n);
 
                     push_space_if_amb!(self, &output_str, {
-                        self.append_str(&output_str.trim());
+                        append_str!(self, &output_str);
                     });
                 }
+            },
+            NumberFocus::Denominator(r) => {
+                let output_str = format!("{}", r.denom());
+
+                push_space_if_amb!(self, &output_str, {
+                    append_str!(self, &output_str);
+                });
             }
-            Number::Rational(r) => {
-                self.print_rational(&r, add_brackets);
-                return;
-            }
-            n => {
-                let output_str = format!("{}", n);
+            NumberFocus::Numerator(r) => {
+                let output_str = format!("{}", r.numer());
 
                 push_space_if_amb!(self, &output_str, {
-                    self.append_str(&output_str);
+                    append_str!(self, &output_str);
                 });
             }
         }
 
         if add_brackets {
-            self.push_char(')');
+            push_char!(self, ')');
         }
     }
 
-    fn print_rational(&mut self, r: &Rational, add_brackets: bool) {
-        match self.op_dir.get(&(clause_name!("rdiv"), Fixity::In)) {
-            Some(OpDirValue(ref spec)) => {
+    fn print_rational(&mut self, r: TypedArenaPtr<Rational>, add_brackets: bool) {
+        match self.op_dir.get(&(atom!("rdiv"), Fixity::In)) {
+            Some(op_desc) => {
                 if add_brackets {
                     self.state_stack.push(TokenOrRedirect::Close);
                 }
 
-                let rdiv_ct = clause_name!("rdiv");
+                let rdiv_ct = atom!("rdiv");
 
-                let left_directed_op = if spec.prec() > 0 {
-                    Some(DirectedOp::Left(rdiv_ct.clone(), spec.clone()))
+                let left_directed_op = if op_desc.get_prec() > 0 {
+                    Some(DirectedOp::Left(rdiv_ct, *op_desc))
                 } else {
                     None
                 };
 
-                let right_directed_op = if spec.prec() > 0 {
-                    Some(DirectedOp::Right(rdiv_ct.clone(), spec.clone()))
+                let right_directed_op = if op_desc.get_prec() > 0 {
+                    Some(DirectedOp::Right(rdiv_ct, *op_desc))
                 } else {
                     None
                 };
 
-                if spec.prec() > 0 {
-                    self.state_stack.push(TokenOrRedirect::Number(
-                        Number::from(r.denom()),
+                if op_desc.get_prec() > 0 {
+                    self.state_stack.push(TokenOrRedirect::NumberFocus(
+                        NumberFocus::Denominator(r),
                         left_directed_op,
                     ));
 
                     self.state_stack
-                        .push(TokenOrRedirect::Op(rdiv_ct, spec.clone()));
+                        .push(TokenOrRedirect::Op(rdiv_ct, *op_desc));
 
-                    self.state_stack.push(TokenOrRedirect::Number(
-                        Number::from(r.numer()),
+                    self.state_stack.push(TokenOrRedirect::NumberFocus(
+                        NumberFocus::Numerator(r),
                         right_directed_op,
                     ));
                 } else {
                     self.state_stack.push(TokenOrRedirect::Close);
 
-                    self.state_stack
-                        .push(TokenOrRedirect::Number(Number::from(r.denom()), None));
+                    self.state_stack.push(TokenOrRedirect::NumberFocus(
+                        NumberFocus::Denominator(r),
+                        None,
+                    ));
 
                     self.state_stack.push(TokenOrRedirect::Comma);
 
-                    self.state_stack
-                        .push(TokenOrRedirect::Number(Number::from(r.numer()), None));
+                    self.state_stack.push(TokenOrRedirect::NumberFocus(
+                        NumberFocus::Numerator(r),
+                        None,
+                    ));
 
                     self.state_stack.push(TokenOrRedirect::Open);
                     self.state_stack.push(TokenOrRedirect::Atom(rdiv_ct));
@@ -1046,157 +1046,164 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         }
     }
 
-    fn print_char(&mut self, is_quoted: bool, c: char) {
-        if non_quoted_token(once(c)) {
-            let c = char_to_string(false, c);
+    // returns true if max_depth limit is reached and ellipsis is printed.
+    fn print_string_as_functor(&mut self, focus: usize, max_depth: usize) -> bool {
+        let iter = HeapPStrIter::new(self.iter.heap, focus);
 
-            push_space_if_amb!(self, &c, {
-                self.append_str(c.as_str());
-            });
-        } else {
-            let mut result = String::new();
+        for (char_count, c) in iter.chars().enumerate() {
+            append_str!(self, "'.'");
+            push_char!(self, '(');
 
-            if self.quoted {
-                result.push('\'');
-                result += &char_to_string(is_quoted, c);
-                result.push('\'');
-            } else {
-                result += &char_to_string(is_quoted, c);
-            }
+            print_char!(self, self.quoted, c);
+            push_char!(self, ',');
 
-            push_space_if_amb!(self, &result, {
-                self.append_str(result.as_str());
-            });
+            self.state_stack.push(TokenOrRedirect::Close);
+
+            if max_depth >= char_count + 1 {
+                self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
+                return true;
+            }
         }
+
+        false
     }
 
-    fn print_proper_string(&mut self, buf: String, max_depth: usize) {
-        self.push_char('"');
+    fn print_proper_string(&mut self, focus: usize, max_depth: usize) {
+        push_char!(self, '"');
 
-        let buf = if max_depth == 0 {
-            String::from_iter(buf.chars().map(|c| char_to_string(self.quoted, c)))
+        let iter = HeapPStrIter::new(self.iter.heap, focus);
+
+        if max_depth == 0 {
+            for c in iter.chars() {
+                for c in char_to_string(self.quoted, c).chars() {
+                    push_char!(self, c);
+                }
+            }
         } else {
             let mut char_count = 0;
-            let mut buf = String::from_iter(buf.chars().take(max_depth).map(|c| {
+
+            for c in iter.chars().take(max_depth) {
                 char_count += 1;
-                char_to_string(self.quoted, c)
-            }));
+
+                for c in char_to_string(self.quoted, c).chars() {
+                    push_char!(self, c);
+                }
+            }
 
             if char_count == max_depth {
-                buf += " ...";
+                append_str!(self, " ...");
             }
+        }
 
-            buf
-        };
+        push_char!(self, '"');
+    }
 
-        self.append_str(&buf);
-        self.push_char('"');
+    fn remove_list_children(&mut self, h: usize) {
+        match self.iter.heap[h].get_tag() {
+            HeapCellValueTag::Lis => {
+                self.iter.pop_stack();
+                self.iter.pop_stack();
+            }
+            HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset => {
+                self.iter.pop_stack();
+            }
+            HeapCellValueTag::CStr => {
+            }
+            _ => {
+                unreachable!();
+            }
+        }
     }
 
-    fn print_list_like(&mut self, iter: &mut HCPreOrderIterator, addr: Addr, mut max_depth: usize) {
-        if self.check_max_depth(&mut max_depth) {
-            iter.stack().pop();
-            iter.stack().pop();
+    fn print_list_like(&mut self, mut max_depth: usize) {
+        let focus = self.iter.focus();
+        let mut heap_pstr_iter = HeapPStrIter::new(self.iter.heap, focus);
 
-            self.state_stack
-                .push(TokenOrRedirect::Atom(clause_name!("...")));
-            return;
+        if heap_pstr_iter.next().is_some() {
+            while let Some(_) = heap_pstr_iter.next() {}
+        } else {
+            return self.push_list(max_depth);
         }
 
-        let mut heap_pstr_iter = self.machine_st.heap_pstr_iter(addr);
+        let end_h = heap_pstr_iter.focus();
+        let end_cell = self.iter.heap[end_h];
 
-        let buf = heap_pstr_iter.to_string();
+        self.remove_list_children(focus);
 
-        if buf.is_empty() {
-            self.push_list(iter, max_depth);
+        if self.check_max_depth(&mut max_depth) {
+            self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
             return;
         }
 
-        iter.stack().pop();
-        iter.stack().pop();
-
-        let end_addr = heap_pstr_iter.focus();
-
         let at_cdr = self.at_cdr(",");
 
-        if !at_cdr && Addr::EmptyList == end_addr {
-            if !self.ignore_ops {
-                self.print_proper_string(buf, max_depth);
-                return;
-            }
+        if !at_cdr && !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) {
+            return self.print_proper_string(focus, max_depth);
         }
 
-        let buf_len = buf.len();
-
-        let buf_iter: Box<dyn Iterator<Item = char>> = if self.max_depth == 0 {
-            Box::new(buf.chars())
-        } else {
-            Box::new(buf.chars().take(max_depth))
-        };
-
-        let mut byte_len = 0;
-
         if self.ignore_ops {
-            let mut char_count = 0;
-
-            for c in buf_iter {
-                self.append_str("'.'");
-                self.push_char('(');
-
-                self.print_char(self.quoted, c);
-                self.push_char(',');
-
-                char_count += 1;
-                byte_len += c.len_utf8();
-            }
-
-            for _ in 0..char_count {
-                self.state_stack.push(TokenOrRedirect::Close);
-            }
+            if !self.print_string_as_functor(end_h, max_depth) {
+                if end_cell.get_tag() == HeapCellValueTag::CStr {
+                    append_str!(self, "[]");
+                } else {
+                    if self.outputter.ends_with(",") {
+                        self.outputter.truncate(self.outputter.len() - ','.len_utf8());
+                    }
 
-            if self.max_depth > 0 && buf_len > byte_len {
-                self.state_stack
-                    .push(TokenOrRedirect::Atom(clause_name!("...")));
-            } else {
-                self.state_stack
-                    .push(TokenOrRedirect::FunctorRedirect(max_depth));
-                iter.stack().push(end_addr);
+                    self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth));
+                    self.iter.push_stack(end_h);
+                }
             }
         } else {
             let switch = if !at_cdr {
-                self.push_char('[');
+                push_char!(self, '[');
                 true
             } else {
                 false
             };
 
-            for c in buf_iter {
-                self.print_char(self.quoted, c);
-                self.push_char(',');
+            let heap_pstr_iter = HeapPStrIter::new(self.iter.heap, focus);
+
+            let mut iter = heap_pstr_iter.chars();
+            let mut char_count = 0;
+
+            while let Some(c) = iter.next() {
+                print_char!(self, self.quoted, c);
+                push_char!(self, ',');
+
+                char_count += 1;
 
-                byte_len += c.len_utf8();
+                if max_depth > 0 && max_depth <= char_count {
+                    break;
+                }
             }
 
-            self.state_stack
-                .push(TokenOrRedirect::CloseList(Rc::new(Cell::new((switch, 0)))));
+            self.state_stack.push(TokenOrRedirect::CloseList(Rc::new(Cell::new((switch, 0)))));
 
-            if self.max_depth > 0 && buf_len > byte_len {
-                self.state_stack
-                    .push(TokenOrRedirect::Atom(clause_name!("...")));
+            if self.max_depth > 0 && iter.next().is_some() {
+                self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
             } else {
-                self.outputter
-                    .truncate(self.outputter.len() - ','.len_utf8());
-                self.state_stack
-                    .push(TokenOrRedirect::FunctorRedirect(max_depth));
+                if iter.cycle_detected() {
+                    self.iter.heap[end_h].set_forwarding_bit(true);
+                }
 
-                iter.stack().push(end_addr);
+                if end_cell.get_tag() == HeapCellValueTag::CStr {
+                    self.state_stack.push(TokenOrRedirect::Atom(atom!("[]")));
+                } else {
+                    self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth));
+                    self.iter.push_stack(end_h);
+                }
             }
 
             self.state_stack.push(TokenOrRedirect::HeadTailSeparator);
+
+            if self.outputter.ends_with(",") {
+                self.outputter.truncate(self.outputter.len() - ','.len_utf8());
+            }
         }
     }
 
-    fn check_max_depth(&mut self, max_depth: &mut usize) -> bool {
+    fn check_max_depth(&self, max_depth: &mut usize) -> bool {
         if self.max_depth > 0 && *max_depth == 0 {
             return true;
         }
@@ -1208,17 +1215,15 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         false
     }
 
-    fn push_list(&mut self, iter: &mut HCPreOrderIterator, mut max_depth: usize) {
+    fn push_list(&mut self, mut max_depth: usize) {
         if self.check_max_depth(&mut max_depth) {
-            iter.stack().pop();
-            iter.stack().pop();
+            self.iter.pop_stack();
+            self.iter.pop_stack();
 
             let cell = Rc::new(Cell::new((true, 0)));
 
-            self.state_stack
-                .push(TokenOrRedirect::CloseList(cell.clone()));
-            self.state_stack
-                .push(TokenOrRedirect::Atom(clause_name!("...")));
+            self.state_stack.push(TokenOrRedirect::CloseList(cell.clone()));
+            self.state_stack.push(TokenOrRedirect::Atom(atom!("...")));
             self.state_stack.push(TokenOrRedirect::OpenList(cell));
 
             return;
@@ -1226,26 +1231,22 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
 
         let cell = Rc::new(Cell::new((true, max_depth)));
 
-        self.state_stack
-            .push(TokenOrRedirect::CloseList(cell.clone()));
+        self.state_stack.push(TokenOrRedirect::CloseList(cell.clone()));
 
-        self.state_stack
-            .push(TokenOrRedirect::FunctorRedirect(max_depth));
+        self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth));
         self.state_stack.push(TokenOrRedirect::HeadTailSeparator); // bar
-        self.state_stack
-            .push(TokenOrRedirect::FunctorRedirect(max_depth));
+        self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth));
 
         self.state_stack.push(TokenOrRedirect::OpenList(cell));
     }
 
     fn handle_op_as_struct(
         &mut self,
-        name: ClauseName,
+        name: Atom,
         arity: usize,
-        iter: &mut HCPreOrderIterator,
         op: &Option<DirectedOp>,
         is_functor_redirect: bool,
-        spec: SharedOpDesc,
+        op_desc: OpDesc,
         negated_operand: bool,
         max_depth: usize,
     ) {
@@ -1253,20 +1254,20 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
             negated_operand
                 || if let Some(ref op) = op {
                     if self.numbervars && arity == 1 && name.as_str() == "$VAR" {
-                        !iter.immediate_leaf_has_property(|addr, heap| {
-                            match heap.index_addr(&addr).as_ref() {
-                                &HeapCellValue::Integer(ref n) => &**n >= &0,
-                                &HeapCellValue::Addr(Addr::Fixnum(n)) => n >= 0,
-                                &HeapCellValue::Addr(Addr::Float(f)) => f >= OrderedFloat(0f64),
-                                &HeapCellValue::Rational(ref r) => &**r >= &0,
+                        !self.iter.immediate_leaf_has_property(|addr| {
+                            match Number::try_from(addr) {
+                                Ok(Number::Integer(n)) => &*n >= &0,
+                                Ok(Number::Fixnum(n)) => n.get_num() >= 0,
+                                Ok(Number::Float(f)) => f >= OrderedFloat(0f64),
+                                Ok(Number::Rational(r)) => &*r >= &0,
                                 _ => false,
                             }
-                        }) && needs_bracketing(&spec, op)
+                        }) && needs_bracketing(op_desc, op)
                     } else {
-                        needs_bracketing(&spec, op)
+                        needs_bracketing(op_desc, op)
                     }
                 } else {
-                    is_functor_redirect && spec.prec() >= 1000
+                    is_functor_redirect && op_desc.get_prec() >= 1000
                 }
         } else {
             false
@@ -1276,44 +1277,46 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
             self.state_stack.push(TokenOrRedirect::Close);
         }
 
-        let ct = ClauseType::from(name.clone(), arity, Some(spec));
+        let ct = ClauseType::from(name, arity);
 
-        if self.format_clause(iter, max_depth, arity, ct) {
-            if add_brackets {
-                self.state_stack.push(TokenOrRedirect::Open);
+        if self.format_clause(max_depth, arity, ct, Some(op_desc)) && add_brackets {
+            self.state_stack.push(TokenOrRedirect::Open);
 
-                if let Some(ref op) = &op {
-                    if op.is_left() && requires_space(op.as_str(), "(") {
-                        self.state_stack.push(TokenOrRedirect::Space);
-                    }
+            if let Some(ref op) = &op {
+                if op.is_left() && requires_space(op.as_atom().as_str(), "(") {
+                    self.state_stack.push(TokenOrRedirect::Space);
                 }
             }
         }
     }
 
-    fn print_tcp_listener(
-        &mut self,
-        iter: &mut HCPreOrderIterator,
-        tcp_listener: &TcpListener,
-        max_depth: usize,
-    ) {
+    #[allow(dead_code)]
+    fn print_tcp_listener(&mut self, tcp_listener: &TcpListener, max_depth: usize) {
         let (ip, port) = if let Some(addr) = tcp_listener.local_addr().ok() {
-            (addr.ip(), Number::from(addr.port() as isize))
+            (
+                addr.ip(),
+                Number::arena_from(addr.port() as usize, self.arena),
+            )
         } else {
-            let disconnected_atom = clause_name!("$disconnected_tcp_listener");
+            let disconnected_atom = atom!("$disconnected_tcp_listener");
             self.state_stack
                 .push(TokenOrRedirect::Atom(disconnected_atom));
 
             return;
         };
 
-        if self.format_struct(iter, max_depth, 1, clause_name!("$tcp_listener")) {
+        let tcp_listener_atom = atom!("$tcp_listener");
+
+        if self.format_struct(max_depth, 1, tcp_listener_atom) {
             let atom = self.state_stack.pop().unwrap();
 
             self.state_stack.pop();
             self.state_stack.pop();
 
-            self.state_stack.push(TokenOrRedirect::Number(port, None));
+            self.state_stack.push(TokenOrRedirect::NumberFocus(
+                NumberFocus::Unfocused(port),
+                None,
+            ));
             self.state_stack.push(TokenOrRedirect::Comma);
             self.state_stack.push(TokenOrRedirect::IpAddr(ip));
 
@@ -1322,13 +1325,15 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         }
     }
 
-    fn print_stream(&mut self, iter: &mut HCPreOrderIterator, stream: &Stream, max_depth: usize) {
-        if let Some(alias) = &stream.options().alias {
+    fn print_stream(&mut self, stream: Stream, max_depth: usize) {
+        if let Some(alias) = stream.options().get_alias() {
             self.print_atom(alias);
         } else {
-            if self.format_struct(iter, max_depth, 1, clause_name!("$stream")) {
-                let atom = if stream.is_stdout() || stream.is_stdin() || stream.is_stderr() {
-                    TokenOrRedirect::Atom(clause_name!("user"))
+            let stream_atom = atom!("$stream");
+
+            if self.format_struct(max_depth, 1, stream_atom) {
+                let atom = if stream.is_stdout() || stream.is_stdin() {
+                    TokenOrRedirect::Atom(atom!("user"))
                 } else {
                     TokenOrRedirect::RawPtr(stream.as_ptr())
                 };
@@ -1347,121 +1352,144 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
 
     fn handle_heap_term(
         &mut self,
-        iter: &mut HCPreOrderIterator,
         op: Option<DirectedOp>,
         is_functor_redirect: bool,
         max_depth: usize,
     ) {
-        let negated_operand = negated_op_needs_bracketing(iter, &op);
+        let negated_operand = negated_op_needs_bracketing(&self.iter, self.op_dir, &op);
 
-        let addr = match self.check_for_seen(iter) {
+        let addr = match self.check_for_seen() {
             Some(addr) => addr,
             None => return,
         };
 
-        match self.machine_st.heap.index_addr(&addr).as_ref() {
-            &HeapCellValue::NamedStr(arity, ref name, ref spec) => {
-                let spec =
-                    fetch_op_spec_from_existing(name.clone(), arity, spec.clone(), self.op_dir);
-
-                if let Some(spec) = spec {
-                    self.handle_op_as_struct(
-                        name.clone(),
-                        arity,
-                        iter,
-                        &op,
-                        is_functor_redirect,
-                        spec.clone(),
-                        negated_operand,
-                        max_depth,
-                    );
-                } else {
-                    push_space_if_amb!(self, name.as_str(), {
-                        let ct = ClauseType::from(name.clone(), arity, spec);
-                        self.format_clause(iter, max_depth, arity, ct);
-                    });
-                }
-            }
-            &HeapCellValue::Atom(ref atom, ref spec) => {
-                if let Some(_) = fetch_atom_op_spec(atom.clone(), spec.clone(), self.op_dir) {
+        read_heap_cell!(addr,
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                if name == atom!("[]") {
+                    if !self.at_cdr("") {
+                        append_str!(self, "[]");
+                    }
+                } else if arity > 0 {
+                    if let Some(spec) = fetch_op_spec(name, arity, self.op_dir) {
+                        self.handle_op_as_struct(
+                            name,
+                            arity,
+                            &op,
+                            is_functor_redirect,
+                            spec,
+                            negated_operand,
+                            max_depth,
+                        );
+                    } else {
+                        push_space_if_amb!(self, name.as_str(), {
+                            let ct = ClauseType::from(name, arity);
+                             self.format_clause(max_depth, arity, ct, None);
+                        });
+                    }
+                } else if fetch_op_spec(name, arity, self.op_dir).is_some() {
                     let mut result = String::new();
 
                     if let Some(ref op) = op {
-                        if self.outputter.ends_with(&format!(" {}", op.as_str())) {
+                        if self.outputter.ends_with(&format!(" {}", op.as_atom().as_str())) {
                             result.push(' ');
                         }
 
                         result.push('(');
                     }
 
-                    result += &self.print_op_addendum(atom.as_str());
+                    result += &self.print_op_addendum(name.as_str());
 
                     if op.is_some() {
                         result.push(')');
                     }
 
                     push_space_if_amb!(self, &result, {
-                        self.append_str(&result);
+                        append_str!(self, &result);
                     });
                 } else {
-                    push_space_if_amb!(self, atom.as_str(), {
-                        self.print_atom(&atom);
+                    push_space_if_amb!(self, name.as_str(), {
+                        self.print_atom(name);
                     });
                 }
             }
-            &HeapCellValue::Addr(Addr::Char(c)) => {
-                self.print_char(self.quoted, c);
-            }
-            &HeapCellValue::Addr(Addr::CutPoint(b)) => {
-                self.append_str(&format!("{}", b));
-            }
-            &HeapCellValue::Addr(Addr::EmptyList) => {
-                if !self.at_cdr("") {
-                    self.append_str("[]");
+            (HeapCellValueTag::Str, s) => {
+                let (name, arity) = cell_as_atom_cell!(self.iter.heap[s])
+                    .get_name_and_arity();
+
+                if let Some(spec) = fetch_op_spec(name, arity, self.op_dir) {
+                        self.handle_op_as_struct(
+                            name,
+                            arity,
+                            &op,
+                            is_functor_redirect,
+                            spec,
+                            negated_operand,
+                            max_depth,
+                        );
+                } else {
+                    push_space_if_amb!(self, name.as_str(), {
+                        let ct = ClauseType::from(name, arity);
+                        self.format_clause(max_depth, arity, ct, None);
+                    });
                 }
             }
-            &HeapCellValue::Addr(Addr::Float(n)) => {
-                self.print_number(Number::Float(n), &op);
+            (HeapCellValueTag::Fixnum, n) => {
+                append_str!(self, &format!("{}", n.get_num()));
             }
-            &HeapCellValue::Addr(Addr::Fixnum(n)) => {
-                self.print_number(Number::Fixnum(n), &op);
+            (HeapCellValueTag::F64, f) => {
+                self.print_number(NumberFocus::Unfocused(Number::Float(**f)), &op);
             }
-            &HeapCellValue::Addr(Addr::Usize(u)) => {
-                self.append_str(&format!("{}", u));
+            (HeapCellValueTag::PStrOffset) => {
+                self.print_list_like(max_depth);
             }
-            &HeapCellValue::Addr(Addr::PStrLocation(h, n)) => {
-                self.print_list_like(iter, Addr::PStrLocation(h, n), max_depth);
+            (HeapCellValueTag::PStr | HeapCellValueTag::CStr) => {
+                self.print_list_like(max_depth);
             }
-            &HeapCellValue::Addr(Addr::Lis(l)) => {
+            (HeapCellValueTag::Lis) => {
                 if self.ignore_ops {
-                    self.format_struct(iter, max_depth, 2, clause_name!("."));
+                    let period_atom = atom!(".");
+                    self.format_struct(max_depth, 2, period_atom);
                 } else {
-                    self.print_list_like(iter, Addr::Lis(l), max_depth);
+                    self.print_list_like(max_depth);
                 }
             }
-            &HeapCellValue::Addr(addr) => {
-                if let Some(offset_str) = self.offset_as_string(iter, addr) {
+            (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
+                let h = self.iter.focus();
+
+                if let Some(offset_str) = self.offset_as_string(h) {
                     push_space_if_amb!(self, &offset_str, {
-                        self.append_str(offset_str.as_str());
+                        append_str!(self, offset_str.as_str());
                     })
                 }
             }
-            &HeapCellValue::Integer(ref n) => {
-                self.print_number(Number::Integer(n.clone()), &op);
-            }
-            &HeapCellValue::Rational(ref n) => {
-                self.print_number(Number::Rational(n.clone()), &op);
-            }
-            &HeapCellValue::Stream(ref stream) => {
-                self.print_stream(iter, stream, max_depth);
+            (HeapCellValueTag::Char, c) => {
+                print_char!(self, self.quoted, c);
             }
-            &HeapCellValue::TcpListener(ref tcp_listener) => {
-                self.print_tcp_listener(iter, tcp_listener, max_depth);
+            (HeapCellValueTag::Cons, c) => {
+                match_untyped_arena_ptr!(c,
+                    (ArenaHeaderTag::F64, f) => {
+                        self.print_number(NumberFocus::Unfocused(Number::Float(*f)), &op);
+                    }
+                    (ArenaHeaderTag::Integer, n) => {
+                        self.print_number(NumberFocus::Unfocused(Number::Integer(n)), &op);
+                    }
+                    (ArenaHeaderTag::Rational, r) => {
+                        self.print_number(NumberFocus::Unfocused(Number::Rational(r)), &op);
+                    }
+                    (ArenaHeaderTag::Stream, stream) => {
+                        self.print_stream(stream, max_depth);
+                    }
+                    (ArenaHeaderTag::OssifiedOpDir, _op_dir) => {
+                        append_str!(self, "$ossified_op_dir");
+                    }
+                    _ => {
+                    }
+                );
             }
             _ => {
                 unreachable!()
             }
-        }
+        );
     }
 
     fn at_cdr(&mut self, tr: &str) -> bool {
@@ -1469,7 +1497,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
 
         if self.outputter.ends_with("|") {
             self.outputter.truncate(len - "|".len());
-            self.append_str(tr);
+            append_str!(self, tr);
 
             true
         } else {
@@ -1477,29 +1505,27 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         }
     }
 
-    pub(crate) fn print(mut self, addr: Addr) -> Outputter {
-        let mut iter = self.machine_st.pre_order_iter(addr);
-
+    pub fn print(mut self) -> Outputter {
         loop {
             if let Some(loc_data) = self.state_stack.pop() {
                 match loc_data {
-                    TokenOrRedirect::Atom(atom) => self.print_atom(&atom),
-                    TokenOrRedirect::BarAsOp => self.append_str(" | "),
+                    TokenOrRedirect::Atom(atom) => self.print_atom(atom),
+                    TokenOrRedirect::BarAsOp => append_str!(self, " | "),
                     TokenOrRedirect::Op(atom, _) => self.print_op(atom.as_str()),
-                    TokenOrRedirect::NumberedVar(num_var) => self.append_str(num_var.as_str()),
+                    TokenOrRedirect::NumberedVar(num_var) => append_str!(self, &num_var),
                     TokenOrRedirect::CompositeRedirect(max_depth, op) => {
-                        self.handle_heap_term(&mut iter, Some(op), false, max_depth)
+                        self.handle_heap_term(Some(op), false, max_depth)
                     }
                     TokenOrRedirect::FunctorRedirect(max_depth) => {
-                        self.handle_heap_term(&mut iter, None, true, max_depth)
+                        self.handle_heap_term(None, true, max_depth)
                     }
-                    TokenOrRedirect::Close => self.push_char(')'),
+                    TokenOrRedirect::Close => push_char!(self, ')'),
                     TokenOrRedirect::IpAddr(ip) => self.print_ip_addr(ip),
                     TokenOrRedirect::RawPtr(ptr) => self.print_raw_ptr(ptr),
-                    TokenOrRedirect::Open => self.push_char('('),
+                    TokenOrRedirect::Open => push_char!(self, '('),
                     TokenOrRedirect::OpenList(delimit) => {
                         if !self.at_cdr(",") {
-                            self.push_char('[');
+                            push_char!(self, '[');
                         } else {
                             let (_, max_depth) = delimit.get();
                             delimit.set((false, max_depth));
@@ -1507,19 +1533,19 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
                     }
                     TokenOrRedirect::CloseList(delimit) => {
                         if delimit.get().0 {
-                            self.push_char(']');
+                            push_char!(self, ']');
                         }
                     }
-                    TokenOrRedirect::HeadTailSeparator => self.append_str("|"),
-                    TokenOrRedirect::Number(n, op) => self.print_number(n, &op),
-                    TokenOrRedirect::Comma => self.append_str(","),
-                    TokenOrRedirect::Space => self.push_char(' '),
-                    TokenOrRedirect::LeftCurly => self.push_char('{'),
-                    TokenOrRedirect::RightCurly => self.push_char('}'),
+                    TokenOrRedirect::HeadTailSeparator => append_str!(self, "|"),
+                    TokenOrRedirect::NumberFocus(n, op) => self.print_number(n, &op),
+                    TokenOrRedirect::Comma => append_str!(self, ","),
+                    TokenOrRedirect::Space => push_char!(self, ' '),
+                    TokenOrRedirect::LeftCurly => push_char!(self, '{'),
+                    TokenOrRedirect::RightCurly => push_char!(self, '}'),
                 }
-            } else if !iter.stack().is_empty() {
+            } else if !self.iter.stack_is_empty() {
                 let spec = self.toplevel_spec.take();
-                self.handle_heap_term(&mut iter, spec, false, self.max_depth);
+                self.handle_heap_term(spec, false, self.max_depth);
             } else {
                 break;
             }
@@ -1528,3 +1554,273 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
         self.outputter
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use crate::machine::mock_wam::*;
+
+    #[test]
+    fn term_printing_tests() {
+        let mut wam = MockWAM::new();
+
+        let f_atom = atom!("f");
+        let a_atom = atom!("a");
+        let b_atom = atom!("b");
+        let c_atom = atom!("c");
+
+        wam.machine_st.heap.extend(functor!(f_atom, [atom(a_atom), atom(b_atom)]));
+
+        {
+            let printer = HCPrinter::new(
+                &mut wam.machine_st.heap,
+                &mut wam.machine_st.arena,
+                &wam.op_dir,
+                PrinterOutputter::new(),
+                heap_loc_as_cell!(0)
+            );
+
+            let output = printer.print();
+
+            assert_eq!(output.result(), "f(a,b)");
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.extend(functor!(
+            f_atom,
+            [
+                atom(a_atom),
+                atom(b_atom),
+                atom(a_atom),
+                cell(str_loc_as_cell!(0))
+            ]
+        ));
+
+        {
+            let printer = HCPrinter::new(
+                &mut wam.machine_st.heap,
+                &mut wam.machine_st.arena,
+                &wam.op_dir,
+                PrinterOutputter::new(),
+                heap_loc_as_cell!(0)
+            );
+
+            let output = printer.print();
+
+            assert_eq!(output.result(), "f(a,b,a,...)");
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        // print L = [L|L].
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+
+        {
+            let printer = HCPrinter::new(
+                &mut wam.machine_st.heap,
+                &mut wam.machine_st.arena,
+                &wam.op_dir,
+                PrinterOutputter::new(),
+                heap_loc_as_cell!(0)
+            );
+
+            let output = printer.print();
+
+            assert_eq!(output.result(), "[...|...]");
+
+            let mut printer = HCPrinter::new(
+                &mut wam.machine_st.heap,
+                &mut wam.machine_st.arena,
+                &wam.op_dir,
+                PrinterOutputter::new(),
+                heap_loc_as_cell!(0)
+            );
+
+            printer
+                .var_names
+                .insert(list_loc_as_cell!(1), Rc::new("L".to_string()));
+
+            let output = printer.print();
+
+            assert_eq!(output.result(), "[L|L]");
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        let functor = functor!(f_atom, [atom(a_atom), atom(b_atom), atom(b_atom)]);
+
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(str_loc_as_cell!(5));
+        wam.machine_st.heap.push(list_loc_as_cell!(3));
+        wam.machine_st.heap.push(str_loc_as_cell!(5));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        wam.machine_st.heap.extend(functor);
+
+        {
+            let printer = HCPrinter::new(
+                &mut wam.machine_st.heap,
+                &mut wam.machine_st.arena,
+                &wam.op_dir,
+                PrinterOutputter::new(),
+                heap_loc_as_cell!(0),
+            );
+
+            let output = printer.print();
+
+            assert_eq!(output.result(), "[f(a,b,b),f(a,b,b)]");
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap[4] = list_loc_as_cell!(1);
+
+        {
+            let printer = HCPrinter::new(
+                &mut wam.machine_st.heap,
+                &mut wam.machine_st.arena,
+                &wam.op_dir,
+                PrinterOutputter::new(),
+                heap_loc_as_cell!(0),
+            );
+
+            let output = printer.print();
+
+            assert_eq!(output.result(), "[f(a,b,b),f(a,b,b)|...]");
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        {
+            let mut printer = HCPrinter::new(
+                &mut wam.machine_st.heap,
+                &mut wam.machine_st.arena,
+                &wam.op_dir,
+                PrinterOutputter::new(),
+                heap_loc_as_cell!(0)
+            );
+
+            printer
+                .var_names
+                .insert(list_loc_as_cell!(1), Rc::new("L".to_string()));
+
+            let output = printer.print();
+
+            assert_eq!(output.result(), "[f(a,b,b),f(a,b,b)|L]");
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        // issue #382
+        wam.machine_st.heap.clear();
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+
+        for idx in 0..3000 {
+            wam.machine_st.heap.push(heap_loc_as_cell!(2 * idx + 1));
+            wam.machine_st.heap.push(list_loc_as_cell!(2 * idx + 2 + 1));
+        }
+
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        {
+            let mut printer = HCPrinter::new(
+                &mut wam.machine_st.heap,
+                &mut wam.machine_st.arena,
+                &wam.op_dir,
+                PrinterOutputter::new(),
+                heap_loc_as_cell!(0)
+            );
+
+            printer.max_depth = 5;
+
+            let output = printer.print();
+
+            assert_eq!(output.result(), "[_1,_3,_5,_7,_9,...]");
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        put_partial_string(&mut wam.machine_st.heap, "abc", &mut wam.machine_st.atom_tbl);
+
+        {
+            let printer = HCPrinter::new(
+                &mut wam.machine_st.heap,
+                &mut wam.machine_st.arena,
+                &wam.op_dir,
+                PrinterOutputter::new(),
+                pstr_loc_as_cell!(0)
+            );
+
+            let output = printer.print();
+
+            assert_eq!(output.result(), "[a,b,c|_1]");
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.pop();
+
+        wam.machine_st.heap.push(list_loc_as_cell!(2));
+
+        wam.machine_st.heap.push(atom_as_cell!(a_atom));
+        wam.machine_st.heap.push(list_loc_as_cell!(4));
+        wam.machine_st.heap.push(atom_as_cell!(b_atom));
+        wam.machine_st.heap.push(list_loc_as_cell!(6));
+        wam.machine_st.heap.push(atom_as_cell!(c_atom));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        {
+            let printer = HCPrinter::new(
+                &mut wam.machine_st.heap,
+                &mut wam.machine_st.arena,
+                &wam.op_dir,
+                PrinterOutputter::new(),
+                heap_loc_as_cell!(0),
+            );
+
+            let output = printer.print();
+
+            assert_eq!(output.result(), "\"abcabc\"");
+        }
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        assert_eq!(
+            &wam.parse_and_print_term("=(X,[a,b,c|X]).").unwrap(),
+            "=(X,[a,b,c|X])"
+        );
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        assert_eq!(
+            &wam.parse_and_print_term("[a,b,\"a\",[a,b,c]].").unwrap(),
+            "[a,b,\"a\",\"abc\"]"
+        );
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        assert_eq!(
+            &wam.parse_and_print_term("[\"abc\",e,f,[g,e,h,Y,v|[X,Y]]].")
+                .unwrap(),
+            "[\"abc\",e,f,[g,e,h,Y,v,X,Y]]"
+        );
+
+        all_cells_unmarked(&wam.machine_st.heap);
+
+        assert_eq!(&wam.parse_and_print_term("f((a,b)).").unwrap(), "f((a,b))");
+    }
+}
index a71ab01f9d866058b4a32b3cfc7a15ebae41a685..6d63ff794e1be88d8adc5a3068900560ae1ba55d 100644 (file)
@@ -1,33 +1,20 @@
-use prolog_parser::ast::*;
-use prolog_parser::clause_name;
-use prolog_parser::tabled_rc::*;
+use crate::atom_table::*;
+use crate::parser::ast::*;
 
 use crate::forms::*;
 use crate::instructions::*;
 
-use crate::rug::Integer;
 use indexmap::IndexMap;
-
 use slice_deque::{sdeq, SliceDeque};
 
-use std::convert::TryFrom;
 use std::hash::Hash;
 use std::iter::once;
 use std::mem;
-use std::rc::Rc;
-
-#[derive(Debug, Clone, Copy)]
-pub(crate) 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.
-}
 
 #[derive(Debug, Clone, Copy)]
 enum OptArgIndexKeyType {
     Structure,
-    Constant,
+    Literal,
     // List,
 }
 
@@ -35,7 +22,7 @@ impl OptArgIndexKey {
     #[inline]
     fn has_key_type(&self, key_type: OptArgIndexKeyType) -> bool {
         match (self, key_type) {
-            (OptArgIndexKey::Constant(..), OptArgIndexKeyType::Constant)
+            (OptArgIndexKey::Literal(..), OptArgIndexKeyType::Literal)
             | (OptArgIndexKey::Structure(..), OptArgIndexKeyType::Structure)
             // | (OptArgIndexKey::List(..), OptArgIndexKeyType::List)
             => true,
@@ -45,11 +32,12 @@ impl OptArgIndexKey {
 }
 
 #[inline]
-fn search_skeleton_for_first_key_type(
-    skeleton: &[ClauseIndexInfo],
+fn search_skeleton_for_first_key_type<'a>(
+    skeleton: &'a [ClauseIndexInfo],
+    retracted_dynamic_clauses: &'a Option<Vec<ClauseIndexInfo>>,
     key_type: OptArgIndexKeyType,
     append_or_prepend: AppendOrPrepend,
-) -> Option<&OptArgIndexKey> {
+) -> Option<&'a OptArgIndexKey> {
     if append_or_prepend.is_append() {
         for clause_index_info in skeleton.iter().rev() {
             if clause_index_info.opt_arg_index_key.has_key_type(key_type) {
@@ -64,11 +52,20 @@ fn search_skeleton_for_first_key_type(
         }
     }
 
+    if let Some(retracted_clauses) = retracted_dynamic_clauses {
+        for clause_index_info in retracted_clauses.iter().rev() {
+            if clause_index_info.opt_arg_index_key.has_key_type(key_type) {
+                return Some(&clause_index_info.opt_arg_index_key);
+            }
+        }
+    }
+
     None
 }
 
 struct IndexingCodeMergingPtr<'a> {
     skeleton: &'a mut [ClauseIndexInfo],
+    retracted_dynamic_clauses: &'a Option<Vec<ClauseIndexInfo>>,
     indexing_code: &'a mut Vec<IndexingLine>,
     offset: usize,
     append_or_prepend: AppendOrPrepend,
@@ -79,22 +76,22 @@ impl<'a> IndexingCodeMergingPtr<'a> {
     #[inline]
     fn new(
         skeleton: &'a mut [ClauseIndexInfo],
+        retracted_dynamic_clauses: &'a Option<Vec<ClauseIndexInfo>>,
         indexing_code: &'a mut Vec<IndexingLine>,
         append_or_prepend: AppendOrPrepend,
     ) -> Self {
         let is_dynamic = match &indexing_code[0] {
-            IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, v, ..)) => {
-                match v {
-                    IndexingCodePtr::External(_) => false,
-                    IndexingCodePtr::DynamicExternal(_) => true,
-                    _ => unreachable!()
-                }
-            }
-            _ => unreachable!()
+            IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, v, ..)) => match v {
+                IndexingCodePtr::External(_) => false,
+                IndexingCodePtr::DynamicExternal(_) => true,
+                _ => unreachable!(),
+            },
+            _ => unreachable!(),
         };
 
         Self {
             skeleton,
+            retracted_dynamic_clauses,
             indexing_code,
             offset: 0,
             append_or_prepend,
@@ -105,15 +102,16 @@ impl<'a> IndexingCodeMergingPtr<'a> {
     fn internalize_constant(&mut self, constant_ptr: IndexingCodePtr) {
         let constant_key = search_skeleton_for_first_key_type(
             self.skeleton,
-            OptArgIndexKeyType::Constant,
+            self.retracted_dynamic_clauses,
+            OptArgIndexKeyType::Literal,
             self.append_or_prepend,
         );
 
         let mut constants = IndexMap::new();
 
         match constant_key {
-            Some(OptArgIndexKey::Constant(_, _, ref constant, _)) => {
-                constants.insert(constant.clone(), constant_ptr);
+            Some(OptArgIndexKey::Literal(_, _, constant, _)) => {
+                constants.insert(*constant, constant_ptr);
             }
             _ => {
                 if let IndexingCodePtr::DynamicExternal(_) = constant_ptr {
@@ -145,7 +143,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
     fn add_static_indexed_choice_for_constant(
         &mut self,
         external: usize,
-        constant: Constant,
+        constant: Literal,
         index: usize,
     ) {
         let third_level_index = if self.append_or_prepend.is_append() {
@@ -179,7 +177,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
     fn add_dynamic_indexed_choice_for_constant(
         &mut self,
         external: usize,
-        constant: Constant,
+        constant: Literal,
         index: usize,
     ) {
         let third_level_index = if self.append_or_prepend.is_append() {
@@ -232,8 +230,8 @@ impl<'a> IndexingCodeMergingPtr<'a> {
 
     fn index_overlapping_constant(
         &mut self,
-        orig_constant: &Constant,
-        overlapping_constant: Constant,
+        orig_constant: Literal,
+        overlapping_constant: Literal,
         index: usize,
     ) {
         loop {
@@ -252,7 +250,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
                         }
                         IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => {
                             let mut constants = IndexMap::new();
-                            constants.insert(orig_constant.clone(), *c);
+                            constants.insert(orig_constant, *c);
 
                             *c = IndexingCodePtr::Internal(indexing_code_len);
 
@@ -282,10 +280,18 @@ impl<'a> IndexingCodeMergingPtr<'a> {
                             );
                         }
                         Some(IndexingCodePtr::DynamicExternal(o)) => {
-                            self.add_dynamic_indexed_choice_for_constant(o, overlapping_constant, index);
+                            self.add_dynamic_indexed_choice_for_constant(
+                                o,
+                                overlapping_constant,
+                                index,
+                            );
                         }
                         Some(IndexingCodePtr::External(o)) => {
-                            self.add_static_indexed_choice_for_constant(o, overlapping_constant, index);
+                            self.add_static_indexed_choice_for_constant(
+                                o,
+                                overlapping_constant,
+                                index,
+                            );
                         }
                         Some(IndexingCodePtr::Internal(o)) => {
                             self.offset += o;
@@ -307,7 +313,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
         }
     }
 
-    fn index_constant(&mut self, constant: Constant, index: usize) {
+    fn index_constant(&mut self, constant: Literal, index: usize) {
         loop {
             let indexing_code_len = self.indexing_code.len();
 
@@ -338,10 +344,16 @@ impl<'a> IndexingCodeMergingPtr<'a> {
                 IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(constants)) => {
                     match constants.get(&constant).cloned() {
                         None | Some(IndexingCodePtr::Fail) if self.is_dynamic => {
-                            constants.insert(constant, IndexingCodePtr::DynamicExternal(index));
+                            constants.insert(
+                                constant,
+                                IndexingCodePtr::DynamicExternal(index),
+                            );
                         }
                         None | Some(IndexingCodePtr::Fail) => {
-                            constants.insert(constant, IndexingCodePtr::External(index));
+                            constants.insert(
+                                constant,
+                                IndexingCodePtr::External(index),
+                            );
                         }
                         Some(IndexingCodePtr::DynamicExternal(o)) => {
                             self.add_dynamic_indexed_choice_for_constant(o, constant, index);
@@ -372,6 +384,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
     fn internalize_structure(&mut self, structure_ptr: IndexingCodePtr) {
         let structure_key = search_skeleton_for_first_key_type(
             self.skeleton,
+            self.retracted_dynamic_clauses,
             OptArgIndexKeyType::Structure,
             self.append_or_prepend,
         );
@@ -379,8 +392,8 @@ impl<'a> IndexingCodeMergingPtr<'a> {
         let mut structures = IndexMap::new();
 
         match structure_key {
-            Some(OptArgIndexKey::Structure(_, _, ref name, ref arity)) => {
-                structures.insert((name.clone(), *arity), structure_ptr);
+            Some(OptArgIndexKey::Structure(_, _, name, arity)) => {
+                structures.insert((*name, *arity), structure_ptr);
             }
             _ => {
                 if let IndexingCodePtr::DynamicExternal(_) = structure_ptr {
@@ -428,8 +441,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
         };
 
         let indexing_code_len = self.indexing_code.len();
-        self.indexing_code
-            .push(IndexingLine::IndexedChoice(third_level_index));
+        self.indexing_code.push(IndexingLine::IndexedChoice(third_level_index));
 
         match &mut self.indexing_code[self.offset] {
             IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => {
@@ -599,6 +611,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
 pub(crate) fn merge_clause_index(
     target_indexing_code: &mut Vec<IndexingLine>,
     skeleton: &mut [ClauseIndexInfo], // the clause to be merged is the last element in the skeleton.
+    retracted_clauses: &Option<Vec<ClauseIndexInfo>>,
     new_clause_loc: usize,            // the absolute location of the new clause in the code vector.
     append_or_prepend: AppendOrPrepend,
 ) {
@@ -609,27 +622,28 @@ pub(crate) fn merge_clause_index(
 
     let mut merging_ptr = IndexingCodeMergingPtr::new(
         skeleton,
+        retracted_clauses,
         target_indexing_code,
         append_or_prepend,
     );
 
     match &opt_arg_index_key {
-        OptArgIndexKey::Constant(_, index_loc, ref constant, ref overlapping_constants) => {
+        OptArgIndexKey::Literal(_, index_loc, constant, ref overlapping_constants) => {
             let offset = new_clause_loc - index_loc + 1;
-            merging_ptr.index_constant(constant.clone(), offset);
+            merging_ptr.index_constant(*constant, offset);
 
             for overlapping_constant in overlapping_constants {
                 merging_ptr.offset = 0;
 
                 merging_ptr.index_overlapping_constant(
-                    constant,
-                    overlapping_constant.clone(),
+                    *constant,
+                    *overlapping_constant,
                     offset,
                 );
             }
         }
-        OptArgIndexKey::Structure(_, index_loc, ref name, ref arity) => {
-            merging_ptr.index_structure((name.clone(), *arity), new_clause_loc - index_loc + 1);
+        OptArgIndexKey::Structure(_, index_loc, name, arity) => {
+            merging_ptr.index_structure((*name, *arity), new_clause_loc - index_loc + 1);
         }
         OptArgIndexKey::List(_, index_loc) => {
             merging_ptr.index_list(new_clause_loc - index_loc + 1);
@@ -650,13 +664,13 @@ pub(crate) fn merge_clause_index(
 }
 
 pub(crate) fn remove_constant_indices(
-    constant: &Constant,
-    overlapping_constants: &[Constant],
+    constant: Literal,
+    overlapping_constants: &[Literal],
     indexing_code: &mut Vec<IndexingLine>,
     offset: usize,
 ) {
     let mut index = 0;
-    let iter = once(constant).chain(overlapping_constants.iter());
+    let iter = once(&constant).chain(overlapping_constants.iter());
 
     match &mut indexing_code[index] {
         IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, ref mut c, ..)) => {
@@ -688,11 +702,13 @@ pub(crate) fn remove_constant_indices(
                 )) => {
                     constants_index = index;
 
-                    match constants.get(constant).cloned() {
-                        Some(IndexingCodePtr::DynamicExternal(_)) |
-                        Some(IndexingCodePtr::External(_)) |
-                        Some(IndexingCodePtr::Fail) => {
-                            constants.remove(constant);
+                    let constant = *constant;
+
+                    match constants.get(&constant).cloned() {
+                        Some(IndexingCodePtr::DynamicExternal(_))
+                        | Some(IndexingCodePtr::External(_))
+                        | Some(IndexingCodePtr::Fail) => {
+                            constants.remove(&constant);
                             break;
                         }
                         Some(IndexingCodePtr::Internal(o)) => {
@@ -704,13 +720,14 @@ pub(crate) fn remove_constant_indices(
                     }
                 }
                 IndexingLine::IndexedChoice(ref mut indexed_choice_instrs) => {
-                    StaticCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset);
+                    StaticCodeIndices::remove_instruction_with_offset(
+                        indexed_choice_instrs,
+                        offset,
+                    );
 
                     if indexed_choice_instrs.len() == 1 {
                         if let Some(indexed_choice_instr) = indexed_choice_instrs.pop_back() {
-                            let ext = IndexingCodePtr::External(
-                                indexed_choice_instr.offset()
-                            );
+                            let ext = IndexingCodePtr::External(indexed_choice_instr.offset());
 
                             match &mut indexing_code[constants_index] {
                                 IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
@@ -724,7 +741,7 @@ pub(crate) fn remove_constant_indices(
                                 IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(
                                     ref mut constants,
                                 )) => {
-                                    constants.insert(constant.clone(), ext);
+                                    constants.insert(*constant, ext);
                                 }
                                 _ => {
                                     unreachable!()
@@ -736,7 +753,10 @@ pub(crate) fn remove_constant_indices(
                     break;
                 }
                 IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs) => {
-                    DynamicCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset);
+                    DynamicCodeIndices::remove_instruction_with_offset(
+                        indexed_choice_instrs,
+                        offset,
+                    );
 
                     if indexed_choice_instrs.len() == 1 {
                         if let Some(indexed_choice_instr) = indexed_choice_instrs.pop_back() {
@@ -754,7 +774,7 @@ pub(crate) fn remove_constant_indices(
                                 IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(
                                     ref mut constants,
                                 )) => {
-                                    constants.insert(constant.clone(), ext);
+                                    constants.insert(*constant, ext);
                                 }
                                 _ => {
                                     unreachable!()
@@ -790,7 +810,7 @@ pub(crate) fn remove_constant_indices(
 }
 
 pub(crate) fn remove_structure_index(
-    name: &ClauseName,
+    name: Atom,
     arity: usize,
     indexing_code: &mut Vec<IndexingLine>,
     offset: usize,
@@ -825,7 +845,8 @@ pub(crate) fn remove_structure_index(
                 structures_index = index;
 
                 match structures.get(&(name.clone(), arity)).cloned() {
-                    Some(IndexingCodePtr::DynamicExternal(_)) | Some(IndexingCodePtr::External(_)) => {
+                    Some(IndexingCodePtr::DynamicExternal(_))
+                    | Some(IndexingCodePtr::External(_)) => {
                         structures.remove(&(name.clone(), arity));
                         break;
                     }
@@ -1012,27 +1033,14 @@ pub(crate) fn remove_index(
     clause_loc: usize,
 ) {
     match opt_arg_index_key {
-        OptArgIndexKey::Constant(_, _, ref constant, ref overlapping_constants) => {
-            remove_constant_indices(
-                constant,
-                overlapping_constants,
-                indexing_code,
-                clause_loc,
-            );
-        }
-        OptArgIndexKey::Structure(_, _, ref name, ref arity) => {
-            remove_structure_index(
-                name,
-                *arity,
-                indexing_code,
-                clause_loc,
-            );
+        OptArgIndexKey::Literal(_, _, constant, ref overlapping_constants) => {
+            remove_constant_indices(*constant, overlapping_constants, indexing_code, clause_loc);
+        }
+        OptArgIndexKey::Structure(_, _, name, arity) => {
+            remove_structure_index(*name, *arity, indexing_code, clause_loc);
         }
         OptArgIndexKey::List(..) => {
-            remove_list_index(
-                indexing_code,
-                clause_loc,
-            );
+            remove_list_index(indexing_code, clause_loc);
         }
         OptArgIndexKey::None => {
             unreachable!()
@@ -1076,49 +1084,52 @@ fn uncap_choice_seq_with_try(prelude: &mut [IndexedChoiceInstruction]) {
     });
 }
 
-pub(crate) fn constant_key_alternatives(constant: &Constant, atom_tbl: TabledData<Atom>) -> Vec<Constant> {
+pub(crate) fn constant_key_alternatives(
+    constant: Literal,
+    atom_tbl: &mut AtomTable,
+    // arena: &mut Arena,
+) -> Vec<Literal> {
     let mut constants = vec![];
 
     match constant {
-        Constant::Atom(ref name, ref op) => {
-            if name.is_char() {
-                let c = name.as_str().chars().next().unwrap();
-                constants.push(Constant::Char(c));
-            }
-
-            if op.is_some() {
-                constants.push(Constant::Atom(name.clone(), None));
+        Literal::Atom(ref name) => {
+            if let Some(c) = name.as_char() {
+                constants.push(Literal::Char(c));
             }
         }
-        Constant::Char(c) => {
-            let atom = clause_name!(c.to_string(), atom_tbl);
-            constants.push(Constant::Atom(atom, None));
+        Literal::Char(c) => {
+            let atom = atom_tbl.build_with(&c.to_string());
+            constants.push(Literal::Atom(atom));
         }
-        Constant::Fixnum(ref n) => {
-            constants.push(Constant::Integer(Rc::new(Integer::from(*n))));
+        /*
+        Literal::Fixnum(ref n) => {
+            constants.push(Literal::Integer(arena_alloc!(n, arena))); //Rc::new(Integer::from(*n))));
 
+            /*
             if *n >= 0 {
                 if let Ok(n) = usize::try_from(*n) {
-                    constants.push(Constant::Usize(n));
+                    constants.push(Literal::Usize(n));
                 }
             }
+            */
         }
-        Constant::Integer(ref n) => {
+        */
+        Literal::Integer(ref n) => {
             if let Some(n) = n.to_isize() {
-                constants.push(Constant::Fixnum(n));
-            }
-
-            if let Some(n) = n.to_usize() {
-                constants.push(Constant::Usize(n));
+                Fixnum::build_with_checked(n as i64).map(|n| {
+                    constants.push(Literal::Fixnum(n));
+                }).unwrap();
             }
         }
-        Constant::Usize(n) => {
-            constants.push(Constant::Integer(Rc::new(Integer::from(*n))));
+        /*
+        Literal::Usize(n) => {
+            constants.push(Literal::Integer(Rc::new(Integer::from(*n))));
 
             if let Ok(n) = isize::try_from(*n) {
-                constants.push(Constant::Fixnum(n));
+                constants.push(Literal::Fixnum(n));
             }
         }
+        */
         _ => {}
     }
 
@@ -1127,16 +1138,16 @@ pub(crate) fn constant_key_alternatives(constant: &Constant, atom_tbl: TabledDat
 
 #[derive(Debug)]
 pub(crate) struct StaticCodeIndices {
-    constants: IndexMap<Constant, SliceDeque<IndexedChoiceInstruction>>,
+    constants: IndexMap<Literal, SliceDeque<IndexedChoiceInstruction>>,
     lists: SliceDeque<IndexedChoiceInstruction>,
-    structures: IndexMap<(ClauseName, usize), SliceDeque<IndexedChoiceInstruction>>,
+    structures: IndexMap<(Atom, usize), SliceDeque<IndexedChoiceInstruction>>,
 }
 
 #[derive(Debug)]
 pub(crate) struct DynamicCodeIndices {
-    constants: IndexMap<Constant, SliceDeque<usize>>,
+    constants: IndexMap<Literal, SliceDeque<usize>>,
     lists: SliceDeque<usize>,
-    structures: IndexMap<(ClauseName, usize), SliceDeque<usize>>,
+    structures: IndexMap<(Atom, usize), SliceDeque<usize>>,
 }
 
 pub(crate) trait Indexer {
@@ -1144,9 +1155,9 @@ pub(crate) trait Indexer {
 
     fn new() -> Self;
 
-    fn constants(&mut self) -> &mut IndexMap<Constant, SliceDeque<Self::ThirdLevelIndex>>;
+    fn constants(&mut self) -> &mut IndexMap<Literal, SliceDeque<Self::ThirdLevelIndex>>;
     fn lists(&mut self) -> &mut SliceDeque<Self::ThirdLevelIndex>;
-    fn structures(&mut self) -> &mut IndexMap<(ClauseName, usize), SliceDeque<Self::ThirdLevelIndex>>;
+    fn structures(&mut self) -> &mut IndexMap<(Atom, usize), SliceDeque<Self::ThirdLevelIndex>>;
 
     fn compute_index(is_initial_index: bool, index: usize) -> Self::ThirdLevelIndex;
 
@@ -1166,10 +1177,7 @@ pub(crate) trait Indexer {
         prelude: &mut SliceDeque<IndexingLine>,
     ) -> IndexingCodePtr;
 
-    fn remove_instruction_with_offset(
-        code: &mut SliceDeque<Self::ThirdLevelIndex>,
-        offset: usize,
-    );
+    fn remove_instruction_with_offset(code: &mut SliceDeque<Self::ThirdLevelIndex>, offset: usize);
 
     fn var_offset_wrapper(var_offset: usize) -> IndexingCodePtr;
 }
@@ -1187,7 +1195,7 @@ impl Indexer for StaticCodeIndices {
     }
 
     #[inline]
-    fn constants(&mut self) -> &mut IndexMap<Constant, SliceDeque<IndexedChoiceInstruction>> {
+    fn constants(&mut self) -> &mut IndexMap<Literal, SliceDeque<IndexedChoiceInstruction>> {
         &mut self.constants
     }
 
@@ -1197,7 +1205,7 @@ impl Indexer for StaticCodeIndices {
     }
 
     #[inline]
-    fn structures(&mut self) -> &mut IndexMap<(ClauseName, usize), SliceDeque<IndexedChoiceInstruction>> {
+    fn structures(&mut self) -> &mut IndexMap<(Atom, usize), SliceDeque<IndexedChoiceInstruction>> {
         &mut self.structures
     }
 
@@ -1271,7 +1279,10 @@ impl Indexer for StaticCodeIndices {
     }
 
     #[inline]
-    fn remove_instruction_with_offset(code: &mut SliceDeque<IndexedChoiceInstruction>, offset: usize) {
+    fn remove_instruction_with_offset(
+        code: &mut SliceDeque<IndexedChoiceInstruction>,
+        offset: usize,
+    ) {
         for (index, line) in code.iter().enumerate() {
             if offset == line.offset() {
                 code.remove(index);
@@ -1300,7 +1311,7 @@ impl Indexer for DynamicCodeIndices {
     }
 
     #[inline]
-    fn constants(&mut self) -> &mut IndexMap<Constant, SliceDeque<usize>> {
+    fn constants(&mut self) -> &mut IndexMap<Literal, SliceDeque<usize>> {
         &mut self.constants
     }
 
@@ -1310,7 +1321,7 @@ impl Indexer for DynamicCodeIndices {
     }
 
     #[inline]
-    fn structures(&mut self) -> &mut IndexMap<(ClauseName, usize), SliceDeque<usize>> {
+    fn structures(&mut self) -> &mut IndexMap<(Atom, usize), SliceDeque<usize>> {
         &mut self.structures
     }
 
@@ -1395,19 +1406,13 @@ impl Indexer for DynamicCodeIndices {
 
 #[derive(Debug)]
 pub(crate) struct CodeOffsets<I: Indexer> {
-    atom_tbl: TabledData<Atom>,
     indices: I,
     optimal_index: usize,
 }
 
 impl<I: Indexer> CodeOffsets<I> {
-    pub(crate) fn new(
-        atom_tbl: TabledData<Atom>,
-        indices: I,
-        optimal_index: usize,
-    ) -> Self {
+    pub(crate) fn new(indices: I, optimal_index: usize) -> Self {
         CodeOffsets {
-            atom_tbl,
             indices,
             optimal_index,
         }
@@ -1419,15 +1424,24 @@ impl<I: Indexer> CodeOffsets<I> {
         self.indices.lists().push_back(index);
     }
 
-    fn index_constant(&mut self, constant: &Constant, index: usize) -> Vec<Constant> {
-        let overlapping_constants = constant_key_alternatives(constant, self.atom_tbl.clone());
-        let code = self.indices.constants().entry(constant.clone()).or_insert(sdeq![]);
+    fn index_constant(
+        &mut self,
+        atom_tbl: &mut AtomTable,
+        constant: Literal,
+        index: usize,
+    ) -> Vec<Literal> {
+        let overlapping_constants = constant_key_alternatives(constant, atom_tbl);
+        let code = self.indices.constants().entry(constant).or_insert(sdeq![]);
 
         let is_initial_index = code.is_empty();
         code.push_back(I::compute_index(is_initial_index, index));
 
         for constant in &overlapping_constants {
-            let code = self.indices.constants().entry(constant.clone()).or_insert(sdeq![]);
+            let code = self
+                .indices
+                .constants()
+                .entry(*constant)
+                .or_insert(sdeq![]);
 
             let is_initial_index = code.is_empty();
             let index = I::compute_index(is_initial_index, index);
@@ -1438,8 +1452,9 @@ impl<I: Indexer> CodeOffsets<I> {
         overlapping_constants
     }
 
-    fn index_structure(&mut self, name: &ClauseName, arity: usize, index: usize) -> usize {
-        let code = self.indices
+    fn index_structure(&mut self, name: Atom, arity: usize, index: usize) -> usize {
+        let code = self
+            .indices
             .structures()
             .entry((name.clone(), arity))
             .or_insert(sdeq![]);
@@ -1456,28 +1471,25 @@ impl<I: Indexer> CodeOffsets<I> {
         optimal_arg: &Term,
         index: usize,
         clause_index_info: &mut ClauseIndexInfo,
+        atom_tbl: &mut AtomTable,
     ) {
         match optimal_arg {
-            &Term::Clause(_, ref name, ref terms, _) => {
+            &Term::Clause(_, name, ref terms) => {
                 clause_index_info.opt_arg_index_key =
                     OptArgIndexKey::Structure(self.optimal_index, 0, name.clone(), terms.len());
 
                 self.index_structure(name, terms.len(), index);
             }
-            &Term::Cons(..) | &Term::Constant(_, Constant::String(_)) => {
+            &Term::Cons(..) | &Term::Literal(_, Literal::String(_)) | &Term::PartialString(..) => {
                 clause_index_info.opt_arg_index_key = OptArgIndexKey::List(self.optimal_index, 0);
 
                 self.index_list(index);
             }
-            &Term::Constant(_, ref constant) => {
-                let overlapping_constants = self.index_constant(constant, index);
-
-                clause_index_info.opt_arg_index_key = OptArgIndexKey::Constant(
-                    self.optimal_index,
-                    0,
-                    constant.clone(),
-                    overlapping_constants,
-                );
+            &Term::Literal(_, constant) => {
+                let overlapping_constants = self.index_constant(atom_tbl, constant, index);
+
+                clause_index_info.opt_arg_index_key =
+                    OptArgIndexKey::Literal(self.optimal_index, 0, constant, overlapping_constants);
             }
             _ => {}
         }
index 90cbeeab02ec4275a777e9c424376d840262b1f6..76c9c76cf8319df105ae74e1e3b189ccdefac8af 100644 (file)
@@ -1,46 +1,42 @@
-use prolog_parser::ast::*;
-use prolog_parser::clause_name;
+use crate::arena::*;
+use crate::atom_table::*;
+use crate::parser::ast::*;
 
 use crate::clause_types::*;
 use crate::forms::*;
-use crate::indexing::IndexingCodePtr;
+use crate::types::*;
 use crate::machine::heap::*;
 use crate::machine::machine_errors::MachineStub;
-use crate::machine::machine_indices::*;
-use crate::rug::Integer;
 
 use indexmap::IndexMap;
-
 use slice_deque::SliceDeque;
 
-use std::rc::Rc;
-
 fn reg_type_into_functor(r: RegType) -> MachineStub {
     match r {
-        RegType::Temp(r) => functor!("x", [integer(r)]),
-        RegType::Perm(r) => functor!("y", [integer(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!("level", [atom("root")]),
-            Level::Shallow => functor!("level", [atom("shallow")]),
-            Level::Deep => functor!("level", [atom("deep")]),
+            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) -> MachineStub {
+    fn into_functor(&self, arena: &mut Arena) -> MachineStub {
         match self {
             &ArithmeticTerm::Reg(r) => reg_type_into_functor(r),
             &ArithmeticTerm::Interm(i) => {
-                functor!("intermediate", [integer(i)])
+                functor!(atom!("intermediate"), [fixnum(i)])
             }
-            &ArithmeticTerm::Number(ref n) => {
-                vec![n.clone().into()]
+            &ArithmeticTerm::Number(n) => {
+                vec![HeapCellValue::from((n, arena))]
             }
         }
     }
@@ -87,33 +83,30 @@ impl ChoiceInstruction {
                 match (death, next_or_fail) {
                     (Death::Infinity, NextOrFail::Next(i)) => {
                         functor!(
-                            "dynamic_else",
-                            [integer(birth), atom("inf"), integer(i)]
+                            atom!("dynamic_else"),
+                            [fixnum(birth), atom(atom!("inf")), fixnum(i)]
                         )
                     }
                     (Death::Infinity, NextOrFail::Fail(i)) => {
-                        let next_functor = functor!("fail", [integer(i)]);
+                        let next_functor = functor!(atom!("fail"), [fixnum(i)]);
 
                         functor!(
-                            "dynamic_else",
-                            [integer(birth), atom("inf"), aux(h, 0)],
+                            atom!("dynamic_else"),
+                            [fixnum(birth), atom(atom!("inf")), str(h, 0)],
                             [next_functor]
                         )
                     }
                     (Death::Finite(d), NextOrFail::Fail(i)) => {
-                        let next_functor = functor!("fail", [integer(i)]);
+                        let next_functor = functor!(atom!("fail"), [fixnum(i)]);
 
                         functor!(
-                            "dynamic_else",
-                            [integer(birth), integer(d), aux(h, 0)],
+                            atom!("dynamic_else"),
+                            [fixnum(birth), fixnum(d), str(h, 0)],
                             [next_functor]
                         )
                     }
                     (Death::Finite(d), NextOrFail::Next(i)) => {
-                        functor!(
-                            "dynamic_else",
-                            [integer(birth), integer(d), integer(i)]
-                        )
+                        functor!(atom!("dynamic_else"), [fixnum(birth), fixnum(d), fixnum(i)])
                     }
                 }
             }
@@ -121,50 +114,50 @@ impl ChoiceInstruction {
                 match (death, next_or_fail) {
                     (Death::Infinity, NextOrFail::Next(i)) => {
                         functor!(
-                            "dynamic_internal_else",
-                            [integer(birth), atom("inf"), integer(i)]
+                            atom!("dynamic_internal_else"),
+                            [fixnum(birth), atom(atom!("inf")), fixnum(i)]
                         )
                     }
                     (Death::Infinity, NextOrFail::Fail(i)) => {
-                        let next_functor = functor!("fail", [integer(i)]);
+                        let next_functor = functor!(atom!("fail"), [fixnum(i)]);
 
                         functor!(
-                            "dynamic_internal_else",
-                            [integer(birth), atom("inf"), aux(h, 0)],
+                            atom!("dynamic_internal_else"),
+                            [fixnum(birth), atom(atom!("inf")), str(h, 0)],
                             [next_functor]
                         )
                     }
                     (Death::Finite(d), NextOrFail::Fail(i)) => {
-                        let next_functor = functor!("fail", [integer(i)]);
+                        let next_functor = functor!(atom!("fail"), [fixnum(i)]);
 
                         functor!(
-                            "dynamic_internal_else",
-                            [integer(birth), integer(d), aux(h, 0)],
+                            atom!("dynamic_internal_else"),
+                            [fixnum(birth), fixnum(d), str(h, 0)],
                             [next_functor]
                         )
                     }
                     (Death::Finite(d), NextOrFail::Next(i)) => {
                         functor!(
-                            "dynamic_internal_else",
-                            [integer(birth), integer(d), integer(i)]
+                            atom!("dynamic_internal_else"),
+                            [fixnum(birth), fixnum(d), fixnum(i)]
                         )
                     }
                 }
             }
             &ChoiceInstruction::TryMeElse(offset) => {
-                functor!("try_me_else", [integer(offset)])
+                functor!(atom!("try_me_else"), [fixnum(offset)])
             }
             &ChoiceInstruction::RetryMeElse(offset) => {
-                functor!("retry_me_else", [integer(offset)])
+                functor!(atom!("retry_me_else"), [fixnum(offset)])
             }
             &ChoiceInstruction::TrustMe(offset) => {
-                functor!("trust_me", [integer(offset)])
+                functor!(atom!("trust_me"), [fixnum(offset)])
             }
             &ChoiceInstruction::DefaultRetryMeElse(offset) => {
-                functor!("default_retry_me_else", [integer(offset)])
+                functor!(atom!("default_retry_me_else"), [fixnum(offset)])
             }
             &ChoiceInstruction::DefaultTrustMe(offset) => {
-                functor!("default_trust_me", [integer(offset)])
+                functor!(atom!("default_trust_me"), [fixnum(offset)])
             }
         }
     }
@@ -183,18 +176,18 @@ impl CutInstruction {
         match self {
             &CutInstruction::Cut(r) => {
                 let rt_stub = reg_type_into_functor(r);
-                functor!("cut", [aux(h, 0)], [rt_stub])
+                functor!(atom!("cut"), [str(h, 0)], [rt_stub])
             }
             &CutInstruction::GetLevel(r) => {
                 let rt_stub = reg_type_into_functor(r);
-                functor!("get_level", [aux(h, 0)], [rt_stub])
+                functor!(atom!("get_level"), [str(h, 0)], [rt_stub])
             }
             &CutInstruction::GetLevelAndUnify(r) => {
                 let rt_stub = reg_type_into_functor(r);
-                functor!("get_level_and_unify", [aux(h, 0)], [rt_stub])
+                functor!(atom!("get_level_and_unify"), [str(h, 0)], [rt_stub])
             }
             &CutInstruction::NeckCut => {
-                functor!("neck_cut")
+                functor!(atom!("neck_cut"))
             }
         }
     }
@@ -219,13 +212,13 @@ impl IndexedChoiceInstruction {
     pub(crate) fn to_functor(&self) -> MachineStub {
         match self {
             &IndexedChoiceInstruction::Try(offset) => {
-                functor!("try", [integer(offset)])
+                functor!(atom!("try"), [fixnum(offset)])
             }
             &IndexedChoiceInstruction::Trust(offset) => {
-                functor!("trust", [integer(offset)])
+                functor!(atom!("trust"), [fixnum(offset)])
             }
             &IndexedChoiceInstruction::Retry(offset) => {
-                functor!("retry", [integer(offset)])
+                functor!(atom!("retry"), [fixnum(offset)])
             }
         }
     }
@@ -276,9 +269,16 @@ impl Line {
         }
     }
 
-    pub(crate) fn enqueue_functors(&self, mut h: usize, functors: &mut Vec<MachineStub>) {
+    pub(crate) fn enqueue_functors(
+        &self,
+        mut h: usize,
+        arena: &mut Arena,
+        functors: &mut Vec<MachineStub>,
+    ) {
         match self {
-            &Line::Arithmetic(ref arith_instr) => functors.push(arith_instr.to_functor(h)),
+            &Line::Arithmetic(ref arith_instr) => {
+                functors.push(arith_instr.to_functor(h, arena))
+            }
             &Line::Choice(ref choice_instr) => functors.push(choice_instr.to_functor(h)),
             &Line::Control(ref control_instr) => functors.push(control_instr.to_functor()),
             &Line::Cut(ref cut_instr) => functors.push(cut_instr.to_functor(h)),
@@ -300,7 +300,11 @@ impl Line {
                         }
                         IndexingLine::DynamicIndexedChoice(indexed_choice_instrs) => {
                             for indexed_choice_instr in indexed_choice_instrs {
-                                let section = functor!("dynamic", [integer(*indexed_choice_instr)]);
+                                let section = functor!(
+                                    atom!("dynamic"),
+                                    [fixnum(*indexed_choice_instr)]
+                                );
+
                                 h += section.len();
                                 functors.push(section);
                             }
@@ -312,7 +316,7 @@ impl Line {
                 functors.push(indexed_choice_instr.to_functor())
             }
             &Line::DynamicIndexedChoice(ref indexed_choice_instr) => {
-                functors.push(functor!("dynamic", [integer(*indexed_choice_instr)]));
+                functors.push(functor!(atom!("dynamic"), [fixnum(*indexed_choice_instr)]));
             }
             &Line::Query(ref query_instr) => functors.push(query_instr.to_functor(h)),
         }
@@ -336,7 +340,7 @@ pub(crate) fn to_indexing_line(line: &Line) -> Option<&Vec<IndexingLine>> {
 }
 
 #[derive(Debug, Clone)]
-pub(crate) enum ArithmeticInstruction {
+pub enum ArithmeticInstruction {
     Add(ArithmeticTerm, ArithmeticTerm, usize),
     Sub(ArithmeticTerm, ArithmeticTerm, usize),
     Mul(ArithmeticTerm, ArithmeticTerm, usize),
@@ -380,132 +384,175 @@ pub(crate) enum ArithmeticInstruction {
 
 fn arith_instr_unary_functor(
     h: usize,
-    name: &'static str,
+    name: Atom,
+    arena: &mut Arena,
     at: &ArithmeticTerm,
     t: usize,
 ) -> MachineStub {
-    let at_stub = at.into_functor();
-
-    functor!(name, [aux(h, 0), integer(t)], [at_stub])
+    let at_stub = at.into_functor(arena);
+    functor!(name, [str(h, 0), fixnum(t)], [at_stub])
 }
 
 fn arith_instr_bin_functor(
     h: usize,
-    name: &'static str,
+    name: Atom,
+    arena: &mut Arena,
     at_1: &ArithmeticTerm,
     at_2: &ArithmeticTerm,
     t: usize,
 ) -> MachineStub {
-    let at_1_stub = at_1.into_functor();
-    let at_2_stub = at_2.into_functor();
+    let at_1_stub = at_1.into_functor(arena);
+    let at_2_stub = at_2.into_functor(arena);
 
     functor!(
         name,
-        [aux(h, 0), aux(h, 1), integer(t)],
+        [str(h, 0), str(h, 1), fixnum(t)],
         [at_1_stub, at_2_stub]
     )
 }
 
 impl ArithmeticInstruction {
-    pub(crate) fn to_functor(&self, h: usize) -> MachineStub {
+    pub(crate) fn to_functor(
+        &self,
+        h: usize,
+        arena: &mut Arena,
+    ) -> MachineStub {
         match self {
             &ArithmeticInstruction::Add(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "add", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("add"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::Sub(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "sub", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("sub"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::Mul(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "mul", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("mul"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::IntPow(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "int_pow", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("int_pow"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::Pow(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "pow", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("pow"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::IDiv(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "idiv", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("idiv"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::Max(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "max", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("max"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::Min(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "min", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("min"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::IntFloorDiv(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "int_floor_div", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("int_floor_div"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::RDiv(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "rdiv", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("rdiv"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::Div(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "div", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("div"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::Shl(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "shl", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("shl"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::Shr(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "shr", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("shr"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::Xor(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "xor", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("xor"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::And(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "and", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("and"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::Or(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "or", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("or"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::Mod(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "mod", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("mod"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::Rem(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "rem", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("rem"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::ATan2(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "rem", at_1, at_2, t)
+                arith_instr_bin_functor(h, atom!("rem"), arena, at_1, at_2, t)
             }
             &ArithmeticInstruction::Gcd(ref at_1, ref at_2, t) => {
-                arith_instr_bin_functor(h, "gcd", at_1, at_2, t)
-            }
-            &ArithmeticInstruction::Sign(ref at, t) => arith_instr_unary_functor(h, "sign", at, t),
-            &ArithmeticInstruction::Cos(ref at, t) => arith_instr_unary_functor(h, "cos", at, t),
-            &ArithmeticInstruction::Sin(ref at, t) => arith_instr_unary_functor(h, "sin", at, t),
-            &ArithmeticInstruction::Tan(ref at, t) => arith_instr_unary_functor(h, "tan", at, t),
-            &ArithmeticInstruction::Log(ref at, t) => arith_instr_unary_functor(h, "log", at, t),
-            &ArithmeticInstruction::Exp(ref at, t) => arith_instr_unary_functor(h, "exp", at, t),
-            &ArithmeticInstruction::ACos(ref at, t) => arith_instr_unary_functor(h, "acos", at, t),
-            &ArithmeticInstruction::ASin(ref at, t) => arith_instr_unary_functor(h, "asin", at, t),
-            &ArithmeticInstruction::ATan(ref at, t) => arith_instr_unary_functor(h, "atan", at, t),
-            &ArithmeticInstruction::Sqrt(ref at, t) => arith_instr_unary_functor(h, "sqrt", at, t),
-            &ArithmeticInstruction::Abs(ref at, t) => arith_instr_unary_functor(h, "abs", at, t),
+                arith_instr_bin_functor(h, atom!("gcd"), arena, at_1, at_2, t)
+            }
+            &ArithmeticInstruction::Sign(ref at, t) => {
+                arith_instr_unary_functor(h, atom!("sign"), arena, at, t)
+            }
+            &ArithmeticInstruction::Cos(ref at, t) => {
+                arith_instr_unary_functor(h, atom!("cos"), arena, at, t)
+            }
+            &ArithmeticInstruction::Sin(ref at, t) => {
+                arith_instr_unary_functor(h, atom!("sin"), arena, at, t)
+            }
+            &ArithmeticInstruction::Tan(ref at, t) => {
+                arith_instr_unary_functor(h, atom!("tan"), arena, at, t)
+            }
+            &ArithmeticInstruction::Log(ref at, t) => {
+                arith_instr_unary_functor(h, atom!("log"), arena, at, t)
+            }
+            &ArithmeticInstruction::Exp(ref at, t) => {
+                arith_instr_unary_functor(h, atom!("exp"), arena, at, t)
+            }
+            &ArithmeticInstruction::ACos(ref at, t) => {
+                arith_instr_unary_functor(h, atom!("acos"), arena, at, t)
+            }
+            &ArithmeticInstruction::ASin(ref at, t) => {
+                arith_instr_unary_functor(h, atom!("asin"), arena, at, t)
+            }
+            &ArithmeticInstruction::ATan(ref at, t) => {
+                arith_instr_unary_functor(h, atom!("atan"), arena, at, t)
+            }
+            &ArithmeticInstruction::Sqrt(ref at, t) => {
+                arith_instr_unary_functor(h, atom!("sqrt"), arena, at, t)
+            }
+            &ArithmeticInstruction::Abs(ref at, t) => {
+                arith_instr_unary_functor(h, atom!("abs"), arena, at, t)
+            }
             &ArithmeticInstruction::Float(ref at, t) => {
-                arith_instr_unary_functor(h, "float", at, t)
+                arith_instr_unary_functor(h, atom!("float"), arena, at, t)
             }
             &ArithmeticInstruction::Truncate(ref at, t) => {
-                arith_instr_unary_functor(h, "truncate", at, t)
+                arith_instr_unary_functor(h, atom!("truncate"), arena, at, t)
             }
             &ArithmeticInstruction::Round(ref at, t) => {
-                arith_instr_unary_functor(h, "round", at, t)
+                arith_instr_unary_functor(h, atom!("round"), arena, at, t)
             }
             &ArithmeticInstruction::Ceiling(ref at, t) => {
-                arith_instr_unary_functor(h, "ceiling", at, t)
+                arith_instr_unary_functor(h, atom!("ceiling"), arena, at, t)
             }
             &ArithmeticInstruction::Floor(ref at, t) => {
-                arith_instr_unary_functor(h, "floor", at, t)
-            }
-            &ArithmeticInstruction::Neg(ref at, t) => arith_instr_unary_functor(h, "-", at, t),
-            &ArithmeticInstruction::Plus(ref at, t) => arith_instr_unary_functor(h, "+", at, t),
-            &ArithmeticInstruction::BitwiseComplement(ref at, t) => {
-                arith_instr_unary_functor(h, "\\", at, t)
-            }
+                arith_instr_unary_functor(h, atom!("floor"), arena, at, t)
+            }
+            &ArithmeticInstruction::Neg(ref at, t) => arith_instr_unary_functor(
+                h,
+                atom!("-"),
+                arena,
+                at,
+                t,
+            ),
+            &ArithmeticInstruction::Plus(ref at, t) => arith_instr_unary_functor(
+                h,
+                atom!("+"),
+                arena,
+                at,
+                t,
+            ),
+            &ArithmeticInstruction::BitwiseComplement(ref at, t) => arith_instr_unary_functor(
+                h,
+                atom!("\\"),
+                arena,
+                at,
+                t,
+            ),
         }
     }
 }
 
 #[derive(Debug)]
-pub(crate) enum ControlInstruction {
+pub enum ControlInstruction {
     Allocate(usize), // num_frames.
     // name, arity, perm_vars after threshold, last call, use default call policy.
     CallClause(ClauseType, usize, usize, bool, bool),
@@ -529,25 +576,25 @@ impl ControlInstruction {
     pub(crate) fn to_functor(&self) -> MachineStub {
         match self {
             &ControlInstruction::Allocate(num_frames) => {
-                functor!("allocate", [integer(num_frames)])
+                functor!(atom!("allocate"), [fixnum(num_frames)])
             }
             &ControlInstruction::CallClause(ref ct, arity, _, false, _) => {
-                functor!("call", [clause_name(ct.name()), integer(arity)])
+                functor!(atom!("call"), [atom(ct.name()), fixnum(arity)])
             }
             &ControlInstruction::CallClause(ref ct, arity, _, true, _) => {
-                functor!("execute", [clause_name(ct.name()), integer(arity)])
+                functor!(atom!("execute"), [atom(ct.name()), fixnum(arity)])
             }
             &ControlInstruction::Deallocate => {
-                functor!("deallocate")
+                functor!(atom!("deallocate"))
             }
             &ControlInstruction::JmpBy(_, offset, ..) => {
-                functor!("jmp_by", [integer(offset)])
+                functor!(atom!("jmp_by"), [fixnum(offset)])
             }
             &ControlInstruction::RevJmpBy(offset) => {
-                functor!("rev_jmp_by", [integer(offset)])
+                functor!(atom!("rev_jmp_by"), [fixnum(offset)])
             }
             &ControlInstruction::Proceed => {
-                functor!("proceed")
+                functor!(atom!("proceed"))
             }
         }
     }
@@ -564,8 +611,22 @@ pub(crate) enum IndexingInstruction {
         IndexingCodePtr,
         IndexingCodePtr,
     ),
-    SwitchOnConstant(IndexMap<Constant, IndexingCodePtr>),
-    SwitchOnStructure(IndexMap<(ClauseName, usize), IndexingCodePtr>),
+    SwitchOnConstant(IndexMap<Literal, IndexingCodePtr>),
+    SwitchOnStructure(IndexMap<(Atom, usize), IndexingCodePtr>),
+}
+
+impl IndexingCodePtr {
+    #[allow(dead_code)]
+    pub(crate) 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 {
@@ -573,9 +634,9 @@ impl IndexingInstruction {
         match self {
             &IndexingInstruction::SwitchOnTerm(arg, vars, constants, lists, structures) => {
                 functor!(
-                    "switch_on_term",
+                    atom!("switch_on_term"),
                     [
-                        integer(arg),
+                        fixnum(arg),
                         indexing_code_ptr(h, vars),
                         indexing_code_ptr(h, constants),
                         indexing_code_ptr(h, lists),
@@ -591,26 +652,23 @@ impl IndexingInstruction {
 
                 for (c, ptr) in constants.iter() {
                     let key_value_pair = functor!(
-                        ":",
-                        SharedOpDesc::new(600, XFY),
-                        [constant(c), indexing_code_ptr(h + 3, *ptr)]
+                        atom!(":"),
+                        [literal(*c), indexing_code_ptr(h + 3, *ptr)]
                     );
 
-                    key_value_list_stub.push(HeapCellValue::Addr(Addr::Lis(h + 1)));
-                    key_value_list_stub.push(HeapCellValue::Addr(Addr::Str(h + 3)));
-                    key_value_list_stub.push(HeapCellValue::Addr(Addr::HeapCell(
-                        h + 3 + key_value_pair.len(),
-                    )));
+                    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(HeapCellValue::Addr(Addr::EmptyList));
+                key_value_list_stub.push(empty_list_as_cell!());
 
                 functor!(
-                    "switch_on_constant",
-                    [aux(orig_h, 0)],
+                    atom!("switch_on_constant"),
+                    [str(orig_h, 0)],
                     [key_value_list_stub]
                 )
             }
@@ -622,33 +680,29 @@ impl IndexingInstruction {
 
                 for ((name, arity), ptr) in structures.iter() {
                     let predicate_indicator_stub = functor!(
-                        "/",
-                        SharedOpDesc::new(400, YFX),
-                        [clause_name(name.clone()), integer(*arity)]
+                        atom!("/"),
+                        [atom(name), fixnum(*arity)]
                     );
 
                     let key_value_pair = functor!(
-                        ":",
-                        SharedOpDesc::new(600, XFY),
-                        [aux(h + 3, 0), indexing_code_ptr(h + 3, *ptr)],
+                        atom!(":"),
+                        [str(h + 3, 0), indexing_code_ptr(h + 3, *ptr)],
                         [predicate_indicator_stub]
                     );
 
-                    key_value_list_stub.push(HeapCellValue::Addr(Addr::Lis(h + 1)));
-                    key_value_list_stub.push(HeapCellValue::Addr(Addr::Str(h + 3)));
-                    key_value_list_stub.push(HeapCellValue::Addr(Addr::HeapCell(
-                        h + 3 + key_value_pair.len(),
-                    )));
+                    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(HeapCellValue::Addr(Addr::EmptyList));
+                key_value_list_stub.push(empty_list_as_cell!());
 
                 functor!(
-                    "switch_on_structure",
-                    [aux(orig_h, 0)],
+                    atom!("switch_on_structure"),
+                    [str(orig_h, 0)],
                     [key_value_list_stub]
                 )
             }
@@ -657,14 +711,14 @@ impl IndexingInstruction {
 }
 
 #[derive(Debug, Clone)]
-pub(crate) enum FactInstruction {
-    GetConstant(Level, Constant, RegType),
+pub enum FactInstruction {
+    GetConstant(Level, HeapCellValue, RegType),
     GetList(Level, RegType),
-    GetPartialString(Level, String, RegType, bool),
+    GetPartialString(Level, Atom, RegType, bool),
     GetStructure(ClauseType, usize, RegType),
     GetValue(RegType, usize),
     GetVariable(RegType, usize),
-    UnifyConstant(Constant),
+    UnifyConstant(HeapCellValue),
     UnifyLocalValue(RegType),
     UnifyVariable(RegType),
     UnifyValue(RegType),
@@ -674,13 +728,13 @@ pub(crate) enum FactInstruction {
 impl FactInstruction {
     pub(crate) fn to_functor(&self, h: usize) -> MachineStub {
         match self {
-            &FactInstruction::GetConstant(lvl, ref c, r) => {
+            &FactInstruction::GetConstant(lvl, c, r) => {
                 let lvl_stub = lvl.into_functor();
                 let rt_stub = reg_type_into_functor(r);
 
                 functor!(
-                    "get_constant",
-                    [aux(h, 0), constant(h, c), aux(h, 1)],
+                    atom!("get_constant"),
+                    [str(h, 0), cell(c), str(h, 1)],
                     [lvl_stub, rt_stub]
                 )
             }
@@ -688,15 +742,24 @@ impl FactInstruction {
                 let lvl_stub = lvl.into_functor();
                 let rt_stub = reg_type_into_functor(r);
 
-                functor!("get_list", [aux(h, 0), aux(h, 1)], [lvl_stub, rt_stub])
+                functor!(
+                    atom!("get_list"),
+                    [str(h, 0), str(h, 1)],
+                    [lvl_stub, rt_stub]
+                )
             }
-            &FactInstruction::GetPartialString(lvl, ref s, r, has_tail) => {
+            &FactInstruction::GetPartialString(lvl, s, r, has_tail) => {
                 let lvl_stub = lvl.into_functor();
                 let rt_stub = reg_type_into_functor(r);
 
                 functor!(
-                    "get_partial_string",
-                    [aux(h, 0), string(h, s), aux(h, 1), boolean(has_tail)],
+                    atom!("get_partial_string"),
+                    [
+                        str(h, 0),
+                        string(h, s),
+                        str(h, 1),
+                        boolean(has_tail)
+                    ],
                     [lvl_stub, rt_stub]
                 )
             }
@@ -704,41 +767,41 @@ impl FactInstruction {
                 let rt_stub = reg_type_into_functor(r);
 
                 functor!(
-                    "get_structure",
-                    [clause_name(ct.name()), integer(arity), aux(h, 0)],
+                    atom!("get_structure"),
+                    [atom(ct.name()), fixnum(arity), str(h, 0)],
                     [rt_stub]
                 )
             }
             &FactInstruction::GetValue(r, arg) => {
                 let rt_stub = reg_type_into_functor(r);
 
-                functor!("get_value", [aux(h, 0), integer(arg)], [rt_stub])
+                functor!(atom!("get_value"), [str(h, 0), fixnum(arg)], [rt_stub])
             }
             &FactInstruction::GetVariable(r, arg) => {
                 let rt_stub = reg_type_into_functor(r);
 
-                functor!("get_variable", [aux(h, 0), integer(arg)], [rt_stub])
+                functor!(atom!("get_variable"), [str(h, 0), fixnum(arg)], [rt_stub])
             }
-            &FactInstruction::UnifyConstant(ref c) => {
-                functor!("unify_constant", [constant(h, c)], [])
+            &FactInstruction::UnifyConstant(c) => {
+                functor!(atom!("unify_constant"), [cell(c)])
             }
             &FactInstruction::UnifyLocalValue(r) => {
                 let rt_stub = reg_type_into_functor(r);
 
-                functor!("unify_local_value", [aux(h, 0)], [rt_stub])
+                functor!(atom!("unify_local_value"), [str(h, 0)], [rt_stub])
             }
             &FactInstruction::UnifyVariable(r) => {
                 let rt_stub = reg_type_into_functor(r);
 
-                functor!("unify_variable", [aux(h, 0)], [rt_stub])
+                functor!(atom!("unify_variable"), [str(h, 0)], [rt_stub])
             }
             &FactInstruction::UnifyValue(r) => {
                 let rt_stub = reg_type_into_functor(r);
 
-                functor!("unify_value", [aux(h, 0)], [rt_stub])
+                functor!(atom!("unify_value"), [str(h, 0)], [rt_stub])
             }
             &FactInstruction::UnifyVoid(vars) => {
-                functor!("unify_void", [integer(vars)])
+                functor!(atom!("unify_void"), [fixnum(vars)])
             }
         }
     }
@@ -747,14 +810,14 @@ impl FactInstruction {
 #[derive(Debug, Clone)]
 pub(crate) enum QueryInstruction {
     GetVariable(RegType, usize),
-    PutConstant(Level, Constant, RegType),
+    PutConstant(Level, HeapCellValue, RegType),
     PutList(Level, RegType),
-    PutPartialString(Level, String, RegType, bool),
+    PutPartialString(Level, Atom, RegType, bool),
     PutStructure(ClauseType, usize, RegType),
     PutUnsafeValue(usize, usize),
     PutValue(RegType, usize),
     PutVariable(RegType, usize),
-    SetConstant(Constant),
+    SetConstant(HeapCellValue),
     SetLocalValue(RegType),
     SetVariable(RegType),
     SetValue(RegType),
@@ -765,15 +828,15 @@ impl QueryInstruction {
     pub(crate) fn to_functor(&self, h: usize) -> MachineStub {
         match self {
             &QueryInstruction::PutUnsafeValue(norm, arg) => {
-                functor!("put_unsafe_value", [integer(norm), integer(arg)])
+                functor!(atom!("put_unsafe_value"), [fixnum(norm), fixnum(arg)])
             }
-            &QueryInstruction::PutConstant(lvl, ref c, r) => {
+            &QueryInstruction::PutConstant(lvl, c, r) => {
                 let lvl_stub = lvl.into_functor();
                 let rt_stub = reg_type_into_functor(r);
 
                 functor!(
-                    "put_constant",
-                    [aux(h, 0), constant(h, c), aux(h, 1)],
+                    atom!("put_constant"),
+                    [str(h, 0), cell(c), str(h, 1)],
                     [lvl_stub, rt_stub]
                 )
             }
@@ -781,15 +844,24 @@ impl QueryInstruction {
                 let lvl_stub = lvl.into_functor();
                 let rt_stub = reg_type_into_functor(r);
 
-                functor!("put_list", [aux(h, 0), aux(h, 1)], [lvl_stub, rt_stub])
+                functor!(
+                    atom!("put_list"),
+                    [str(h, 0), str(h, 1)],
+                    [lvl_stub, rt_stub]
+                )
             }
-            &QueryInstruction::PutPartialString(lvl, ref s, r, has_tail) => {
+            &QueryInstruction::PutPartialString(lvl, s, r, has_tail) => {
                 let lvl_stub = lvl.into_functor();
                 let rt_stub = reg_type_into_functor(r);
 
                 functor!(
-                    "put_partial_string",
-                    [aux(h, 0), string(h, s), aux(h, 1), boolean(has_tail)],
+                    atom!("put_partial_string"),
+                    [
+                        str(h, 0),
+                        string(h, s),
+                        str(h, 1),
+                        boolean(has_tail)
+                    ],
                     [lvl_stub, rt_stub]
                 )
             }
@@ -797,46 +869,46 @@ impl QueryInstruction {
                 let rt_stub = reg_type_into_functor(r);
 
                 functor!(
-                    "put_structure",
-                    [clause_name(ct.name()), integer(arity), aux(h, 0)],
+                    atom!("put_structure"),
+                    [atom(ct.name()), fixnum(arity), str(h, 0)],
                     [rt_stub]
                 )
             }
             &QueryInstruction::PutValue(r, arg) => {
                 let rt_stub = reg_type_into_functor(r);
 
-                functor!("put_value", [aux(h, 0), integer(arg)], [rt_stub])
+                functor!(atom!("put_value"), [str(h, 0), fixnum(arg)], [rt_stub])
             }
             &QueryInstruction::GetVariable(r, arg) => {
                 let rt_stub = reg_type_into_functor(r);
 
-                functor!("get_variable", [aux(h, 0), integer(arg)], [rt_stub])
+                functor!(atom!("get_variable"), [str(h, 0), fixnum(arg)], [rt_stub])
             }
             &QueryInstruction::PutVariable(r, arg) => {
                 let rt_stub = reg_type_into_functor(r);
 
-                functor!("put_variable", [aux(h, 0), integer(arg)], [rt_stub])
+                functor!(atom!("put_variable"), [str(h, 0), fixnum(arg)], [rt_stub])
             }
-            &QueryInstruction::SetConstant(ref c) => {
-                functor!("set_constant", [constant(h, c)], [])
+            &QueryInstruction::SetConstant(c) => {
+                functor!(atom!("set_constant"), [cell(c)], [])
             }
             &QueryInstruction::SetLocalValue(r) => {
                 let rt_stub = reg_type_into_functor(r);
 
-                functor!("set_local_value", [aux(h, 0)], [rt_stub])
+                functor!(atom!("set_local_value"), [str(h, 0)], [rt_stub])
             }
             &QueryInstruction::SetVariable(r) => {
                 let rt_stub = reg_type_into_functor(r);
 
-                functor!("set_variable", [aux(h, 0)], [rt_stub])
+                functor!(atom!("set_variable"), [str(h, 0)], [rt_stub])
             }
             &QueryInstruction::SetValue(r) => {
                 let rt_stub = reg_type_into_functor(r);
 
-                functor!("set_value", [aux(h, 0)], [rt_stub])
+                functor!(atom!("set_value"), [str(h, 0)], [rt_stub])
             }
             &QueryInstruction::SetVoid(vars) => {
-                functor!("set_void", [integer(vars)])
+                functor!(atom!("set_void"), [fixnum(vars)])
             }
         }
     }
index a83c57962d31d17a898582c5a109da69f9776a82..3c6e955c990ff116a0b5a9cc59d2f4da4b900119 100644 (file)
@@ -1,5 +1,5 @@
-use prolog_parser::ast::*;
-use prolog_parser::rc_atom;
+use crate::atom_table::*;
+use crate::parser::ast::*;
 
 use crate::clause_types::*;
 use crate::forms::*;
@@ -16,10 +16,10 @@ use std::vec::Vec;
 pub(crate) enum TermRef<'a> {
     AnonVar(Level),
     Cons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
-    Constant(Level, &'a Cell<RegType>, &'a Constant),
-    Clause(Level, &'a Cell<RegType>, ClauseType, &'a Vec<Box<Term>>),
-    PartialString(Level, &'a Cell<RegType>, String, Option<&'a Term>),
-    Var(Level, &'a Cell<VarReg>, Rc<Var>),
+    Literal(Level, &'a Cell<RegType>, &'a Literal),
+    Clause(Level, &'a Cell<RegType>, ClauseType, &'a Vec<Term>),
+    PartialString(Level, &'a Cell<RegType>, Atom, &'a Option<Box<Term>>),
+    Var(Level, &'a Cell<VarReg>, Rc<String>),
 }
 
 impl<'a> TermRef<'a> {
@@ -27,7 +27,7 @@ impl<'a> TermRef<'a> {
         match self {
             TermRef::AnonVar(lvl)
             | TermRef::Cons(lvl, ..)
-            | TermRef::Constant(lvl, ..)
+            | TermRef::Literal(lvl, ..)
             | TermRef::Var(lvl, ..)
             | TermRef::Clause(lvl, ..) => lvl,
             TermRef::PartialString(lvl, ..) => lvl,
@@ -38,82 +38,31 @@ impl<'a> TermRef<'a> {
 #[derive(Debug)]
 pub(crate) enum TermIterState<'a> {
     AnonVar(Level),
-    Constant(Level, &'a Cell<RegType>, &'a Constant),
-    Clause(
-        Level,
-        usize,
-        &'a Cell<RegType>,
-        ClauseType,
-        &'a Vec<Box<Term>>,
-    ),
+    Literal(Level, &'a Cell<RegType>, &'a Literal),
+    Clause(Level, usize, &'a Cell<RegType>, ClauseType, &'a Vec<Term>),
     InitialCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
     FinalCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
-    PartialString(Level, &'a Cell<RegType>, String, Option<&'a Term>),
-    Var(Level, &'a Cell<VarReg>, Rc<Var>),
-}
-
-fn is_partial_string<'a>(head: &'a Term, mut tail: &'a Term) -> Option<(String, Option<&'a Term>)> {
-    let mut string = match head {
-        &Term::Constant(_, Constant::Atom(ref atom, _)) if atom.is_char() => {
-            atom.as_str().chars().next().unwrap().to_string()
-        }
-        &Term::Constant(_, Constant::Char(c)) => c.to_string(),
-        _ => {
-            return None;
-        }
-    };
-
-    while let Term::Cons(_, ref head, ref succ) = tail {
-        match head.as_ref() {
-            &Term::Constant(_, Constant::Atom(ref atom, _)) if atom.is_char() => {
-                string.push(atom.as_str().chars().next().unwrap());
-            }
-            &Term::Constant(_, Constant::Char(c)) => {
-                string.push(c);
-            }
-            _ => {
-                return None;
-            }
-        };
-
-        tail = succ.as_ref();
-    }
-
-    match tail {
-        Term::AnonVar | Term::Var(..) => {
-            return Some((string, Some(tail)));
-        }
-        Term::Constant(_, Constant::EmptyList) => {
-            return Some((string, None));
-        }
-        Term::Constant(_, Constant::String(tail)) => {
-            string += &tail;
-            return Some((string, None));
-        }
-        _ => {
-            return None;
-        }
-    }
+    InitialPartialString(Level, &'a Cell<RegType>, Atom, &'a Option<Box<Term>>),
+    FinalPartialString(Level, &'a Cell<RegType>, Atom, &'a Option<Box<Term>>),
+    Var(Level, &'a Cell<VarReg>, Rc<String>),
 }
 
 impl<'a> TermIterState<'a> {
     pub(crate) fn subterm_to_state(lvl: Level, term: &'a Term) -> TermIterState<'a> {
         match term {
-            &Term::AnonVar => TermIterState::AnonVar(lvl),
-            &Term::Clause(ref cell, ref name, ref subterms, ref spec) => {
-                let ct = if let Some(spec) = spec {
-                    ClauseType::Op(name.clone(), spec.clone(), CodeIndex::default())
-                } else {
-                    ClauseType::Named(name.clone(), subterms.len(), CodeIndex::default())
-                };
-
+            Term::AnonVar => TermIterState::AnonVar(lvl),
+            Term::Clause(cell, name, subterms) => {
+                let ct = ClauseType::Named(name.clone(), subterms.len(), CodeIndex::default());
                 TermIterState::Clause(lvl, 0, cell, ct, subterms)
             }
-            &Term::Cons(ref cell, ref head, ref tail) => {
+            Term::Cons(cell, head, tail) => {
                 TermIterState::InitialCons(lvl, cell, head.as_ref(), tail.as_ref())
             }
-            &Term::Constant(ref cell, ref constant) => TermIterState::Constant(lvl, cell, constant),
-            &Term::Var(ref cell, ref var) => TermIterState::Var(lvl, cell, var.clone()),
+            Term::Literal(cell, constant) => TermIterState::Literal(lvl, cell, constant),
+            Term::PartialString(cell, string_buf, tail) => {
+                TermIterState::InitialPartialString(lvl, cell, *string_buf, tail)
+            }
+            Term::Var(cell, var) => TermIterState::Var(lvl, cell, var.clone()),
         }
     }
 }
@@ -129,11 +78,11 @@ impl<'a> QueryIterator<'a> {
             .push(TermIterState::subterm_to_state(lvl, term));
     }
 
-    fn from_rule_head_clause(terms: &'a Vec<Box<Term>>) -> Self {
+    fn from_rule_head_clause(terms: &'a Vec<Term>) -> Self {
         let state_stack = terms
             .iter()
             .rev()
-            .map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt.as_ref()))
+            .map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
             .collect();
 
         QueryIterator { state_stack }
@@ -141,29 +90,19 @@ impl<'a> QueryIterator<'a> {
 
     fn from_term(term: &'a Term) -> Self {
         let state = match term {
-            &Term::AnonVar => {
+            Term::AnonVar | Term::Cons(..) | Term::Literal(..) | Term::PartialString(..) => {
                 return QueryIterator {
                     state_stack: vec![],
                 }
             }
-            &Term::Clause(ref r, ref name, ref terms, ref fixity) => TermIterState::Clause(
+            Term::Clause(r, name, terms) => TermIterState::Clause(
                 Level::Root,
                 0,
                 r,
-                ClauseType::from(name.clone(), terms.len(), fixity.clone()),
+                ClauseType::from(*name, terms.len()),
                 terms,
             ),
-            &Term::Cons(..) => {
-                return QueryIterator {
-                    state_stack: vec![],
-                }
-            }
-            &Term::Constant(_, _) => {
-                return QueryIterator {
-                    state_stack: vec![],
-                }
-            }
-            &Term::Var(ref cell, ref var) => TermIterState::Var(Level::Root, cell, (*var).clone()),
+            Term::Var(cell, var) => TermIterState::Var(Level::Root, cell, var.clone()),
         };
 
         QueryIterator {
@@ -186,7 +125,7 @@ impl<'a> QueryIterator<'a> {
                 }
             }
             &QueryTerm::UnblockedCut(ref cell) => {
-                let state = TermIterState::Var(Level::Root, cell, rc_atom!("!"));
+                let state = TermIterState::Var(Level::Root, cell, Rc::new("!".to_string()));
                 QueryIterator {
                     state_stack: vec![state],
                 }
@@ -226,9 +165,9 @@ impl<'a> Iterator for QueryIterator<'a> {
                     if child_num == child_terms.len() {
                         match ct {
                             ClauseType::CallN => {
-                                self.push_subterm(Level::Shallow, child_terms[0].as_ref())
+                                self.push_subterm(Level::Shallow, &child_terms[0]);
                             }
-                            ClauseType::Named(..) | ClauseType::Op(..) => {
+                            ClauseType::Named(..) => {
                                 return match lvl {
                                     Level::Root => None,
                                     lvl => Some(TermRef::Clause(lvl, cell, ct, child_terms)),
@@ -247,33 +186,30 @@ impl<'a> Iterator for QueryIterator<'a> {
                             child_terms,
                         ));
 
-                        self.push_subterm(lvl.child_level(), child_terms[child_num].as_ref());
+                        self.push_subterm(lvl.child_level(), &child_terms[child_num]);
                     }
                 }
                 TermIterState::InitialCons(lvl, cell, head, tail) => {
-                    if let Some((string, tail)) = is_partial_string(head, tail) {
-                        self.state_stack
-                            .push(TermIterState::PartialString(lvl, cell, string, tail));
+                    self.state_stack.push(TermIterState::FinalCons(lvl, cell, head, tail));
 
-                        if let Some(tail) = tail {
-                            self.push_subterm(lvl.child_level(), tail);
-                        }
-                    } else {
-                        self.state_stack
-                            .push(TermIterState::FinalCons(lvl, cell, head, tail));
+                    self.push_subterm(lvl.child_level(), tail);
+                    self.push_subterm(lvl.child_level(), head);
+                }
+                TermIterState::InitialPartialString(lvl, cell, string, tail) => {
+                    self.state_stack.push(TermIterState::FinalPartialString(lvl, cell, string, tail));
 
+                    if let Some(tail) = tail {
                         self.push_subterm(lvl.child_level(), tail);
-                        self.push_subterm(lvl.child_level(), head);
                     }
                 }
-                TermIterState::PartialString(lvl, cell, string, tail) => {
+                TermIterState::FinalPartialString(lvl, cell, string, tail) => {
                     return Some(TermRef::PartialString(lvl, cell, string, tail));
                 }
                 TermIterState::FinalCons(lvl, cell, head, tail) => {
                     return Some(TermRef::Cons(lvl, cell, head, tail));
                 }
-                TermIterState::Constant(lvl, cell, constant) => {
-                    return Some(TermRef::Constant(lvl, cell, constant));
+                TermIterState::Literal(lvl, cell, constant) => {
+                    return Some(TermRef::Literal(lvl, cell, constant));
                 }
                 TermIterState::Var(lvl, cell, var) => {
                     return Some(TermRef::Var(lvl, cell, var));
@@ -297,10 +233,10 @@ impl<'a> FactIterator<'a> {
             .push_back(TermIterState::subterm_to_state(lvl, term));
     }
 
-    pub(crate) fn from_rule_head_clause(terms: &'a Vec<Box<Term>>) -> Self {
+    pub(crate) fn from_rule_head_clause(terms: &'a Vec<Term>) -> Self {
         let state_queue = terms
             .iter()
-            .map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt.as_ref()))
+            .map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
             .collect();
 
         FactIterator {
@@ -311,23 +247,31 @@ impl<'a> FactIterator<'a> {
 
     fn new(term: &'a Term, iterable_root: bool) -> Self {
         let states = match term {
-            &Term::AnonVar => {
+            Term::AnonVar => {
                 vec![TermIterState::AnonVar(Level::Root)]
             }
-            &Term::Clause(ref cell, ref name, ref terms, ref fixity) => {
-                let ct = ClauseType::from(name.clone(), terms.len(), fixity.clone());
+            Term::Clause(cell, name, terms) => {
+                let ct = ClauseType::from(*name, terms.len());
                 vec![TermIterState::Clause(Level::Root, 0, cell, ct, terms)]
             }
-            &Term::Cons(ref cell, ref head, ref tail) => vec![TermIterState::InitialCons(
+            Term::Cons(cell, head, tail) => vec![TermIterState::InitialCons(
                 Level::Root,
                 cell,
                 head.as_ref(),
                 tail.as_ref(),
             )],
-            &Term::Constant(ref cell, ref constant) => {
-                vec![TermIterState::Constant(Level::Root, cell, constant)]
+            Term::PartialString(cell, string_buf, tail_opt) => {
+                vec![TermIterState::InitialPartialString(
+                    Level::Root,
+                    cell,
+                    *string_buf,
+                    tail_opt,
+                )]
             }
-            &Term::Var(ref cell, ref var) => {
+            Term::Literal(cell, constant) => {
+                vec![TermIterState::Literal(Level::Root, cell, constant)]
+            }
+            Term::Var(cell, var) => {
                 vec![TermIterState::Var(Level::Root, cell, var.clone())]
             }
         };
@@ -359,21 +303,20 @@ impl<'a> Iterator for FactIterator<'a> {
                     };
                 }
                 TermIterState::InitialCons(lvl, cell, head, tail) => {
-                    if let Some((string, tail)) = is_partial_string(head, tail) {
-                        if let Some(tail) = tail {
-                            self.push_subterm(Level::Deep, tail);
-                        }
+                    self.push_subterm(Level::Deep, head);
+                    self.push_subterm(Level::Deep, tail);
 
-                        return Some(TermRef::PartialString(lvl, cell, string, tail));
-                    } else {
-                        self.push_subterm(Level::Deep, head);
+                    return Some(TermRef::Cons(lvl, cell, head, tail));
+                }
+                TermIterState::InitialPartialString(lvl, cell, string_buf, tail_opt) => {
+                    if let Some(tail) = tail_opt {
                         self.push_subterm(Level::Deep, tail);
-
-                        return Some(TermRef::Cons(lvl, cell, head, tail));
                     }
+
+                    return Some(TermRef::PartialString(lvl, cell, string_buf, tail_opt));
                 }
-                TermIterState::Constant(lvl, cell, constant) => {
-                    return Some(TermRef::Constant(lvl, cell, constant))
+                TermIterState::Literal(lvl, cell, constant) => {
+                    return Some(TermRef::Literal(lvl, cell, constant))
                 }
                 TermIterState::Var(lvl, cell, var) => {
                     return Some(TermRef::Var(lvl, cell, var));
@@ -386,17 +329,17 @@ impl<'a> Iterator for FactIterator<'a> {
     }
 }
 
-pub(crate) fn post_order_iter(term: &Term) -> QueryIterator {
+pub(crate) fn post_order_iter<'a>(term: &'a Term) -> QueryIterator<'a> {
     QueryIterator::from_term(term)
 }
 
-pub(crate) fn breadth_first_iter(term: &Term, iterable_root: bool) -> FactIterator {
+pub(crate) fn breadth_first_iter<'a>(term: &'a Term, iterable_root: bool) -> FactIterator<'a> {
     FactIterator::new(term, iterable_root)
 }
 
 #[derive(Debug)]
 pub(crate) enum ChunkedTerm<'a> {
-    HeadClause(ClauseName, &'a Vec<Box<Term>>),
+    HeadClause(Atom, &'a Vec<Term>),
     BodyTerm(&'a QueryTerm),
 }
 
@@ -407,7 +350,7 @@ pub(crate) fn query_term_post_order_iter<'a>(query_term: &'a QueryTerm) -> Query
 impl<'a> ChunkedTerm<'a> {
     pub(crate) fn post_order_iter(&self) -> QueryIterator<'a> {
         match self {
-            &ChunkedTerm::BodyTerm(ref qt) => QueryIterator::new(qt),
+            &ChunkedTerm::BodyTerm(qt) => QueryIterator::new(qt),
             &ChunkedTerm::HeadClause(_, terms) => QueryIterator::from_rule_head_clause(terms),
         }
     }
@@ -517,7 +460,7 @@ impl<'a> ChunkedIterator<'a> {
         while let Some(term) = item {
             match term {
                 ChunkedTerm::HeadClause(_, terms) => {
-                    if contains_cut_var(terms.iter().map(|t| t.as_ref())) {
+                    if contains_cut_var(terms.iter()) {
                         self.cut_var_in_head = true;
                     }
 
@@ -547,7 +490,10 @@ impl<'a> ChunkedIterator<'a> {
                     arity = 1;
                     break;
                 }
-                ChunkedTerm::BodyTerm(&QueryTerm::UnblockedCut(..)) => result.push(term),
+                ChunkedTerm::BodyTerm(&QueryTerm::UnblockedCut(..)) => {
+                    self.deep_cut_encountered = true;
+                    result.push(term);
+                }
                 ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::Inlined(_), ..)) => {
                     result.push(term)
                 }
index 1fd4a4fb4d208bb8fd6692656d08b9e9f79ff17a..458ad485a24db88f3b7ff0ea818c66d1b1fbdb75 100644 (file)
@@ -1,25 +1,29 @@
-#[cfg(feature = "num-rug-adapter")]
-use num_rug_adapter as rug;
-#[cfg(feature = "rug")]
-use rug;
+#[macro_use]
+extern crate static_assertions;
 
 #[macro_use]
-mod macros;
+pub mod macros;
+#[macro_use]
+pub mod atom_table;
+#[macro_use]
+pub mod arena;
+#[macro_use]
+pub mod parser;
 mod allocator;
 mod arithmetic;
 mod clause_types;
-mod codegen;
+pub mod codegen;
 mod debray_allocator;
 mod fixtures;
 mod forms;
 mod heap_iter;
-mod heap_print;
+pub mod heap_print;
 mod indexing;
 mod instructions;
 mod iterators;
 pub mod machine;
+mod raw_block;
 pub mod read;
 mod targets;
-mod write;
-
-use machine::*;
+pub mod types;
+pub mod write;
index 5906466b530c9d99a259847163a95e2627ce1642..a9369426e889841bd6a888bac737ed70d367e90a 100644 (file)
     '$absent_from_list'(Ls, Attr).
 
 '$absent_from_list'(X, Attr) :-
-    (  var(X) -> true
-    ;  X = [L|Ls], L \= Attr -> '$absent_from_list'(Ls, Attr)
+    (  var(X) ->
+       true
+    ;  X = [L|Ls],
+       L \= Attr ->
+       '$absent_from_list'(Ls, Attr)
     ).
 
 '$get_attr'(V, Attr) :-
-    '$get_attr_list'(V, Ls), nonvar(Ls), '$get_from_list'(Ls, V, Attr).
+    '$get_attr_list'(V, Ls),
+    nonvar(Ls),
+    '$get_from_list'(Ls, V, Attr).
 
 '$get_from_list'([L|Ls], V, Attr) :-
     nonvar(L),
-    (  L \= Attr -> nonvar(Ls), '$get_from_list'(Ls, V, Attr)
-    ;  L = Attr, '$enqueue_attr_var'(V)
+    (  L \= Attr ->
+       nonvar(Ls),
+       '$get_from_list'(Ls, V, Attr)
+    ;  L = Attr,
+       '$enqueue_attr_var'(V)
     ).
 
 '$put_attr'(V, Attr) :-
-    '$get_attr_list'(V, Ls), '$add_to_list'(Ls, V, Attr).
+    '$get_attr_list'(V, Ls),
+    '$add_to_list'(Ls, V, Attr).
 
 '$add_to_list'(Ls, V, Attr) :-
-    ( var(Ls) ->
-      Ls = [Attr | _], '$enqueue_attr_var'(V)
-    ; Ls = [_ | Ls0], '$add_to_list'(Ls0, V, Attr)
+    (  var(Ls) ->
+       Ls = [Attr | _],
+       '$enqueue_attr_var'(V)
+    ;  Ls = [_ | Ls0],
+       '$add_to_list'(Ls0, V, Attr)
     ).
 
 '$del_attr'(Ls0, _, _) :-
-    var(Ls0), !.
+    var(Ls0),
+    !.
 '$del_attr'(Ls0, V, Attr) :-
     Ls0 = [Att | Ls1],
     nonvar(Att),
@@ -134,22 +146,22 @@ put_attr(Name, Arity, Module) -->
     [(put_atts(V, +Attr) :-
           !,
           functor(Attr, Head, Arity),
-                 functor(AttrForm, Head, Arity),
-                 '$get_attr_list'(V, Ls),
-                 atts:'$del_attr'(Ls, V, Module:AttrForm),
-                 atts:'$put_attr'(V, Module:Attr)),
+             functor(AttrForm, Head, Arity),
+             '$get_attr_list'(V, Ls),
+             atts:'$del_attr'(Ls, V, Module:AttrForm),
+             atts:'$put_attr'(V, Module:Attr)),
      (put_atts(V,  Attr) :-
           !,
           functor(Attr, Head, Arity),
-                 functor(AttrForm, Head, Arity),
-                 '$get_attr_list'(V, Ls),
-                 atts:'$del_attr'(Ls, V, Module:AttrForm),
-                 atts:'$put_attr'(V, Module:Attr)),
+             functor(AttrForm, Head, Arity),
+             '$get_attr_list'(V, Ls),
+             atts:'$del_attr'(Ls, V, Module:AttrForm),
+             atts:'$put_attr'(V, Module:Attr)),
      (put_atts(V, -Attr) :-
           !,
           functor(Attr, _, _),
-                 '$get_attr_list'(V, Ls),
-                 atts:'$del_attr'(Ls, V, Module:Attr))].
+             '$get_attr_list'(V, Ls),
+             atts:'$del_attr'(Ls, V, Module:Attr))].
 
 get_attr(Name, Arity, Module) -->
     { functor(Attr, Name, Arity) },
index 5a0d5babea1115b264e713dd6908e03695527c5b..b162e0234ccfb7024dfc3f6815509388e82b9e8f 100644 (file)
@@ -12,10 +12,22 @@ between(Lower, Upper, X) :-
     (   nonvar(X) ->
         Lower =< X,
         X =< Upper
-    ;   compare(Ord, Lower, Upper),
-        between_(Ord, Lower, Upper, X)
+    ;   % compare(Ord, Lower, Upper),
+        % between_(Ord, Lower, Upper, X)
+        Lower =< Upper,
+        between_(Lower, Upper, X)
     ).
 
+between_(Lower, Lower, Lower) :- !.
+between_(Lower, Upper, Lower1) :-
+    (  Lower < Upper,
+       (  Lower1 = Lower
+       ;  Lower0 is Lower + 1,
+          between_(Lower0, Upper, Lower1)
+       )
+    ).
+
+/*
 between_(<, Lower0, Upper, X) :-
     (   X = Lower0
     ;   Lower1 is Lower0 + 1,
@@ -23,6 +35,7 @@ between_(<, Lower0, Upper, X) :-
         between_(Ord, Lower1, Upper, X)
     ).
 between_(=, Upper, Upper, Upper).
+*/
 
 enumerate_nats(I, I).
 enumerate_nats(I0, N) :-
index 00a26238a84d0a96643fda70750a8da8cebca9d4..0c9d33f9b78f83b9811ee384cfd870942521ebf6 100644 (file)
@@ -347,11 +347,11 @@ call_or_cut_interp((G1 -> G2), B) :-
 :- non_counted_backtracking univ_errors/3.
 univ_errors(Term, List, N) :-
     '$skip_max_list'(N, -1, List, R),
-    ( var(R)        ->
-      (  var(Term),
-         throw(error(instantiation_error, (=..)/2))      % 8.5.3.3 a)
-      ;  true
-      )
+    (  var(R)        ->
+       (  var(Term),
+          throw(error(instantiation_error, (=..)/2))      % 8.5.3.3 a)
+       ;  true
+       )
     ;  R \== []     ->
        throw(error(type_error(list, List), (=..)/2))                % 8.5.3.3 b)
     ;  List = [H|T] ->
@@ -388,9 +388,10 @@ univ_worker(Term, List, _) :-
     !,
     '$call_with_default_policy'(List = [Term]).
 univ_worker(Term, [Name|Args], N) :-
-    var(Term), !,
+    var(Term),
+    !,
     '$call_with_default_policy'(Arity is N-1),
-    '$call_with_default_policy'(functor(Term, Name, Arity)),
+    '$call_with_default_policy'(functor(Term, Name, Arity)), % Term = {var}, Name = nonvar, Arity = 0.
     '$call_with_default_policy'(get_args(Args, Term, 1, Arity)).
 univ_worker(Term, List, _) :-
     '$call_with_default_policy'(functor(Term, Name, Arity)),
@@ -679,9 +680,9 @@ set_difference([], _, []) :- !.
 set_difference(Xs, [], Xs).
 
 group_by_variant([V2-S2 | Pairs], V1-S1, [S2 | Solutions], Pairs0) :-
-    iso_ext:variant(V1, V2),
+    V1 = V2, % \+ \+ (V1 = V2), % (2) % iso_ext:variant(V1, V2), % (1)
     !,
-    V1 = V2,
+    % V1 = V2, % (3)
     group_by_variant(Pairs, V2-S2, Solutions, Pairs0).
 group_by_variant(Pairs, _, [], Pairs).
 
@@ -1075,17 +1076,16 @@ abolish(Pred) :-
     ;  throw(error(type_error(predicate_indicator, Pred), abolish/1))
     ).
 
-'$iterate_db_refs'(Ref, Name/Arity) :-
-    '$lookup_db_ref'(Ref, Name, Arity).
-'$iterate_db_refs'(Ref, Name/Arity) :-
-    '$get_next_db_ref'(Ref, NextRef),
-    '$iterate_db_refs'(NextRef, Name/Arity).
-
+'$iterate_db_refs'(Name, Arity, Name/Arity). % :-
+%   '$lookup_db_ref'(Ref, Name, Arity).
+'$iterate_db_refs'(RName, RArity, Name/Arity) :-
+    '$get_next_db_ref'(RName, RArity, RRName, RRArity),
+    '$iterate_db_refs'(RRName, RRArity, Name/Arity).
 
 current_predicate(Pred) :-
     (  var(Pred) ->
-       '$get_next_db_ref'(Ref, _),
-       '$iterate_db_refs'(Ref, Pred)
+       '$get_next_db_ref'(RN, RA, _, _),
+       '$iterate_db_refs'(RN, RA, Pred)
     ;  Pred \= _/_ ->
        throw(error(type_error(predicate_indicator, Pred), current_predicate/1))
     ;  Pred = Name/Arity,
@@ -1094,15 +1094,14 @@ current_predicate(Pred) :-
        ;  integer(Arity), Arity < 0
        ) ->
        throw(error(type_error(predicate_indicator, Pred), current_predicate/1))
-    ;  '$get_next_db_ref'(Ref, _),
-       '$iterate_db_refs'(Ref, Pred)
+    ;  '$get_next_db_ref'(RN, RA, _, _),
+       '$iterate_db_refs'(RN, RA, Pred)
     ).
 
-'$iterate_op_db_refs'(Ref, Priority, Spec, Op) :-
-    '$lookup_op_db_ref'(Ref, Priority, Spec, Op).
-'$iterate_op_db_refs'(Ref, Priority, Spec, Op) :-
-    '$get_next_op_db_ref'(Ref, NextRef),
-    '$iterate_op_db_refs'(NextRef, Priority, Spec, Op).
+'$iterate_op_db_refs'(RPriority, RSpec, ROp, _, RPriority, RSpec, ROp).
+'$iterate_op_db_refs'(RPriority, RSpec, ROp, OssifiedOpDir, Priority, Spec, Op) :-
+    '$get_next_op_db_ref'(RPriority, RSpec, ROp, OssifiedOpDir, RRPriority, RRSpec, RROp),
+    '$iterate_op_db_refs'(RRPriority, RRSpec, RROp, OssifiedOpDir, Priority, Spec, Op).
 
 can_be_op_priority(Priority) :- var(Priority).
 can_be_op_priority(Priority) :- op_priority(Priority).
@@ -1114,8 +1113,8 @@ current_op(Priority, Spec, Op) :-
     (  can_be_op_priority(Priority),
        can_be_op_specifier(Spec),
        error:can_be(atom, Op) ->
-       '$get_next_op_db_ref'(Ref, _),
-       '$iterate_op_db_refs'(Ref, Priority, Spec, Op)
+       '$get_next_op_db_ref'(RPriority, RSpec, ROp, OssifiedOpDir, _, _, Op),
+       '$iterate_op_db_refs'(RPriority, RSpec, ROp, OssifiedOpDir, Priority, Spec, Op)
     ).
 
 list_of_op_atoms(Var) :-
index 378d495a991de0da64fefb9c22e1dabebffc6e1a..f48af1d29f53a3f8145a746e584b4bc4ffe83d65 100644 (file)
@@ -14,7 +14,7 @@
                     partial_string_tail/2,
                     setup_call_cleanup/3,
                     call_nth/2,
-                    variant/2,
+%                   variant/2,
                     copy_term_nat/2]).
 
 :- use_module(library(error), [can_be/2,
@@ -22,6 +22,7 @@
                                instantiation_error/1,
                                type_error/3]).
 
+:- use_module(library(lists), [maplist/3]).
 
 :- meta_predicate(call_cleanup(0, 0)).
 
@@ -160,14 +161,12 @@ call_with_inference_limit(_, _, R, Bb, B) :-
     '$erase_ball',
     '$call_with_default_policy'(handle_ile(B, Ball, R)).
 
-variant(X, Y) :- '$variant'(X, Y).
-
 partial_string(String, L, L0) :-
     (  String == [] ->
        L = L0
     ;  catch(atom_chars(Atom, String),
-         error(E, _),
-         throw(error(E, partial_string/3))),
+             error(E, _),
+             throw(error(E, partial_string/3))),
        '$create_partial_string'(Atom, L, L0)
     ).
 
index 20631588b53b0ddb9f09450f2cac9debe71d3c51..a64c3ad365c5a73c364bcba7ad0f3e64ece3da67 100644 (file)
@@ -3,7 +3,7 @@
                          maplist/3, maplist/4, maplist/5, maplist/6,
                          maplist/7, maplist/8, maplist/9, same_length/2, nth0/3,
                          sum_list/2, transpose/2, list_to_set/2, list_max/2,
-                          list_min/2, permutation/2]).
+                  list_min/2, permutation/2]).
 
 /*  Author:        Mark Thom, Jan Wielemaker, and Richard O'Keefe
     Copyright (c)  2018-2021, Mark Thom
index d5c1c31b8970fad396bec9ad91949c5691664fed..037e7aa0a570d6b78341fa86a6d93412682e0232 100644 (file)
@@ -9,26 +9,26 @@
    syntaxes. The DCGs are presented in the order they appear in the RFC.
    While some DCGs below use `char_type/2`, the most common ones are defined
    manually in order to take advantage of Prolog's first-argument indexing.
-   
+
    BSD 3-Clause License
-   
+
    Copyright (c) 2021, Aram Panasenco
    All rights reserved.
-   
+
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
-   
+
    * Redistributions of source code must retain the above copyright notice, this
      list of conditions and the following disclaimer.
-   
+
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
-   
+
    * Neither the name of the copyright holder nor the names of its
      contributors may be used to endorse or promote products derived from
      this software without specific prior written permission.
-   
+
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
index e1bebe114780c6042e45284276d07e0089b114f6..dac2788261660adf825bf28108e140b827a7fa22 100644 (file)
@@ -1,29 +1,29 @@
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Written Apr 2021 by Aram Panasenco ([email protected])
    Part of Scryer Prolog.
-   
+
    `json_chars//1` can be used with [`phrase_from_file/2`](src/lib/pio.pl)
    or [`phrase/2`](src/lib/dcgs.pl) to parse and generate [JSON](https://www.json.org/json-en.html).
-   
+
    BSD 3-Clause License
-   
+
    Copyright (c) 2021, Aram Panasenco
    All rights reserved.
-   
+
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
-   
+
    * Redistributions of source code must retain the above copyright notice, this
      list of conditions and the following disclaimer.
-   
+
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
-   
+
    * Neither the name of the copyright holder nor the names of its
      contributors may be used to endorse or promote products derived from
      this software without specific prior written permission.
-   
+
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -44,7 +44,7 @@
 :- use_module(library(dif)).
 :- use_module(library(lists)).
 
-/*  The DCGs are written to match the McKeeman form presented on the right side of https://www.json.org/json-en.html 
+/*  The DCGs are written to match the McKeeman form presented on the right side of https://www.json.org/json-en.html
     as closely as possible. Note that the names in the McKeeman form conflict with the pictures on the site. */
 json_chars(Internal) --> json_element(Internal).
 
index affa3b68cab8fba63301235e1edabd97218b0fb9..3f89db4b2b005526888913ad197d69575370c374 100644 (file)
@@ -4,9 +4,9 @@
    This library provides reasoning about UUID (only version 4 right now).
    There are three predicates:
     * uuidv4/1, to generate a new UUIDv4
-    * uuidv4_string/1, to generate a new UUIDv4 in string hex representation 
+    * uuidv4_string/1, to generate a new UUIDv4 in string hex representation
     * uuid_string/2, to converte between UUID list of bytes and UUID hex representation
-   
+
    Examples:
    ?- uuidv4(X).
       X = [42,147,248,242,117,196,79,2,129,159|...].
@@ -30,7 +30,7 @@
 :- use_module(library(dcgs)).
 :- use_module(library(lists)).
 
-/* 
+/*
 An UUID is made of 16 bytes, composed of 5 sections:
 time_low - 4
 time_mid - 2
index 531730cc51889eea7429b7b9f7a0dcede15c60e2..722d413a96bb97e4c86cea981769b8e5e8ee9dfc 100644 (file)
@@ -1,4 +1,3 @@
-
 :- module(loader, [consult/1,
                    expand_goal/3,
                    expand_term/2,
@@ -508,7 +507,8 @@ open_file(Path, Stream) :-
     ;  catch(open(Path, read, Stream),
              error(existence_error(source_sink, _), _),
              ( atom_concat(Path, '.pl', ExtendedPath),
-               open(ExtendedPath, read, Stream) )
+               open(ExtendedPath, read, Stream)
+             )
             )
     ).
 
index 4e3f832af794771e89736ebe027a0182940211e3..85b6cbc3717497ea5ae254479611a7168d2127dd 100644 (file)
@@ -1,37 +1,98 @@
 use divrem::*;
 
-use prolog_parser::ast::*;
-use prolog_parser::clause_name;
-
+use crate::arena::*;
 use crate::arithmetic::*;
+use crate::atom_table::*;
 use crate::clause_types::*;
 use crate::forms::*;
+use crate::heap_iter::*;
 use crate::machine::machine_errors::*;
-use crate::machine::machine_indices::*;
 use crate::machine::machine_state::*;
-use crate::rug::{Integer, Rational};
+use crate::parser::ast::*;
+use crate::parser::rug::{Integer, Rational};
+use crate::types::*;
+
+use crate::fixnum;
+
 use ordered_float::*;
 
 use std::cmp;
 use std::convert::TryFrom;
 use std::f64;
 use std::mem;
-use std::rc::Rc;
 
 #[macro_export]
 macro_rules! try_numeric_result {
-    ($s: ident, $e: expr, $caller: expr) => {
+    ($e: expr, $stub_gen: expr) => {
         match $e {
             Ok(val) => Ok(val),
-            Err(e) => {
-                let caller_copy = $caller.iter().map(|v| v.context_free_clone()).collect();
+            Err(e) => Err(Box::new(move |machine_st: &mut MachineState| {
+                let stub = $stub_gen();
+                let evaluation_error = machine_st.evaluation_error(e);
+
+                machine_st.error_form(evaluation_error, stub)
+            }) as Box<dyn Fn(&mut MachineState) -> MachineStub>),
+        }
+    };
+}
 
-                Err($s.error_form(MachineError::evaluation_error(e), caller_copy))
+macro_rules! drop_iter_on_err {
+    ($self:expr, $iter: expr, $result: expr) => {
+        match $result {
+            Ok(val) => val,
+            Err(stub_gen) => {
+                std::mem::drop($iter);
+                return Err(stub_gen($self));
             }
         }
     };
 }
 
+fn zero_divisor_eval_error(
+    stub_gen: impl Fn() -> FunctorStub + 'static,
+) -> MachineStubGen {
+    Box::new(move |machine_st| {
+        let eval_error = machine_st.evaluation_error(EvalError::ZeroDivisor);
+        let stub = stub_gen();
+
+        machine_st.error_form(eval_error, stub)
+    })
+}
+
+fn undefined_eval_error(
+    stub_gen: impl Fn() -> FunctorStub + 'static,
+) -> MachineStubGen {
+    Box::new(move |machine_st| {
+        let eval_error = machine_st.evaluation_error(EvalError::Undefined);
+        let stub = stub_gen();
+
+        machine_st.error_form(eval_error, stub)
+    })
+}
+
+fn numerical_type_error(
+    valid_type: ValidType,
+    n: Number,
+    stub_gen: impl Fn() -> FunctorStub + 'static,
+) -> MachineStubGen {
+    Box::new(move |machine_st| {
+        let type_error = machine_st.type_error(valid_type, n);
+        let stub = stub_gen();
+
+        machine_st.error_form(type_error, stub)
+    })
+}
+
+pub(crate) fn sign(n: Number) -> Number {
+    if n.is_positive() {
+        Number::Fixnum(Fixnum::build_with(1))
+    } else if n.is_negative() {
+        Number::Fixnum(Fixnum::build_with(-1))
+    } else {
+        Number::Fixnum(Fixnum::build_with(0))
+    }
+}
+
 fn isize_gcd(n1: isize, n2: isize) -> Option<isize> {
     if n1 == 0 {
         return n2.checked_abs().map(|n| n as isize);
@@ -80,935 +141,1290 @@ fn isize_gcd(n1: isize, n2: isize) -> Option<isize> {
     Some(n1 << shift as isize)
 }
 
-impl MachineState {
-    pub(crate) fn get_number(&mut self, at: &ArithmeticTerm) -> Result<Number, MachineStub> {
-        match at {
-            &ArithmeticTerm::Reg(r) => self.arith_eval_by_metacall(r),
-            &ArithmeticTerm::Interm(i) => {
-                Ok(mem::replace(&mut self.interms[i - 1], Number::Fixnum(0)))
-            }
-            &ArithmeticTerm::Number(ref n) => Ok(n.clone()),
+pub(crate) fn add(lhs: Number, rhs: Number, arena: &mut Arena) -> Result<Number, EvalError> {
+    match (lhs, rhs) {
+        (Number::Fixnum(n1), Number::Fixnum(n2)) => Ok(
+            if let Some(result) = n1.get_num().checked_add(n2.get_num()) {
+                fixnum!(Number, result, arena)
+            } else {
+                Number::arena_from(
+                    Integer::from(n1.get_num()) + Integer::from(n2.get_num()),
+                    arena,
+                )
+            },
+        ),
+        (Number::Fixnum(n1), Number::Integer(n2)) | (Number::Integer(n2), Number::Fixnum(n1)) => {
+            Ok(Number::arena_from(
+                Integer::from(n1.get_num()) + &*n2,
+                arena,
+            ))
+        }
+        (Number::Fixnum(n1), Number::Rational(n2)) | (Number::Rational(n2), Number::Fixnum(n1)) => {
+            Ok(Number::arena_from(
+                Rational::from(n1.get_num()) + &*n2,
+                arena,
+            ))
+        }
+        (Number::Fixnum(n1), Number::Float(OrderedFloat(n2)))
+        | (Number::Float(OrderedFloat(n2)), Number::Fixnum(n1)) => {
+            Ok(Number::Float(add_f(float_fn_to_f(n1.get_num())?, n2)?))
+        }
+        (Number::Integer(n1), Number::Integer(n2)) => {
+            Ok(Number::arena_from(Integer::from(&*n1) + &*n2, arena)) // add_i
+        }
+        (Number::Integer(n1), Number::Float(OrderedFloat(n2)))
+        | (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
+            Ok(Number::Float(add_f(float_i_to_f(&n1)?, n2)?))
+        }
+        (Number::Integer(n1), Number::Rational(n2))
+        | (Number::Rational(n2), Number::Integer(n1)) => {
+            Ok(Number::arena_from(Rational::from(&*n1) + &*n2, arena))
+        }
+        (Number::Rational(n1), Number::Float(OrderedFloat(n2)))
+        | (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
+            Ok(Number::Float(add_f(float_r_to_f(&n1)?, n2)?))
+        }
+        (Number::Float(OrderedFloat(f1)), Number::Float(OrderedFloat(f2))) => {
+            Ok(Number::Float(add_f(f1, f2)?))
+        }
+        (Number::Rational(r1), Number::Rational(r2)) => {
+            Ok(Number::arena_from(Rational::from(&*r1) + &*r2, arena))
         }
     }
+}
 
-    pub(super) fn rational_from_number(&self, n: Number) -> Result<Rc<Rational>, MachineError> {
-        match n {
-            Number::Fixnum(n) => Ok(Rc::new(Rational::from(n))),
-            Number::Rational(r) => Ok(r),
-            Number::Float(OrderedFloat(f)) => match Rational::from_f64(f) {
-                Some(r) => Ok(Rc::new(r)),
-                None => Err(MachineError::instantiation_error()),
-            },
-            Number::Integer(n) => Ok(Rc::new(Rational::from(&*n))),
+pub(crate) fn neg(n: Number, arena: &mut Arena) -> Number {
+    match n {
+        Number::Fixnum(n) => {
+            if let Some(n) = n.get_num().checked_neg() {
+                fixnum!(Number, n, arena)
+            } else {
+                Number::arena_from(-Integer::from(n.get_num()), arena)
+            }
         }
+        Number::Integer(n) => Number::arena_from(-Integer::from(&*n), arena),
+        Number::Float(OrderedFloat(f)) => Number::Float(OrderedFloat(-f)),
+        Number::Rational(r) => Number::arena_from(-Rational::from(&*r), arena),
     }
+}
 
-    pub(crate) fn get_rational(
-        &mut self,
-        at: &ArithmeticTerm,
-        caller: MachineStub,
-    ) -> Result<(Rc<Rational>, MachineStub), MachineStub> {
-        let n = self.get_number(at)?;
-
-        match self.rational_from_number(n) {
-            Ok(r) => Ok((r, caller)),
-            Err(e) => Err(self.error_form(e, caller)),
-        }
-    }
-
-    pub(crate) fn arith_eval_by_metacall(&self, r: RegType) -> Result<Number, MachineStub> {
-        let caller = MachineError::functor_stub(clause_name!("is"), 2);
-        let mut interms: Vec<Number> = Vec::with_capacity(64);
-
-        for addr in self.post_order_iter(self[r]) {
-            match self.heap.index_addr(&addr).as_ref() {
-                &HeapCellValue::NamedStr(2, ref name, _) => {
-                    let a2 = interms.pop().unwrap();
-                    let a1 = interms.pop().unwrap();
-
-                    match name.as_str() {
-                        "+" => interms.push(try_numeric_result!(self, a1 + a2, caller)?),
-                        "-" => interms.push(try_numeric_result!(self, a1 - a2, caller)?),
-                        "*" => interms.push(try_numeric_result!(self, a1 * a2, caller)?),
-                        "/" => interms.push(self.div(a1, a2)?),
-                        "**" => interms.push(self.pow(a1, a2, "is")?),
-                        "^" => interms.push(self.int_pow(a1, a2)?),
-                        "max" => interms.push(self.max(a1, a2)?),
-                        "min" => interms.push(self.min(a1, a2)?),
-                        "rdiv" => {
-                            let r1 = self.rational_from_number(a1);
-                            let r2 =
-                                r1.and_then(|r1| self.rational_from_number(a2).map(|r2| (r1, r2)));
-
-                            match r2 {
-                                Ok((r1, r2)) => {
-                                    let result = Number::Rational(Rc::new(self.rdiv(r1, r2)?));
-                                    interms.push(result);
-                                }
-                                Err(e) => {
-                                    return Err(self.error_form(e, caller));
-                                }
-                            }
-                        }
-                        "//" => interms.push(self.idiv(a1, a2)?),
-                        "div" => interms.push(self.int_floor_div(a1, a2)?),
-                        ">>" => interms.push(self.shr(a1, a2)?),
-                        "<<" => interms.push(self.shl(a1, a2)?),
-                        "/\\" => interms.push(self.and(a1, a2)?),
-                        "\\/" => interms.push(self.or(a1, a2)?),
-                        "xor" => interms.push(self.xor(a1, a2)?),
-                        "mod" => interms.push(self.modulus(a1, a2)?),
-                        "rem" => interms.push(self.remainder(a1, a2)?),
-                        "atan2" => interms.push(Number::Float(OrderedFloat(self.atan2(a1, a2)?))),
-                        "gcd" => interms.push(self.gcd(a1, a2)?),
-                        _ => {
-                            let evaluable_stub = MachineError::functor_stub(name.clone(), 2);
-
-                            return Err(self.error_form(
-                                MachineError::type_error(
-                                    self.heap.h(),
-                                    ValidType::Evaluable,
-                                    evaluable_stub,
-                                ),
-                                caller,
-                            ));
-                        }
-                    }
-                }
-                &HeapCellValue::NamedStr(1, ref name, _) => {
-                    let a1 = interms.pop().unwrap();
-
-                    match name.as_str() {
-                        "-" => interms.push(-a1),
-                        "+" => interms.push(a1),
-                        "cos" => interms.push(Number::Float(OrderedFloat(self.cos(a1)?))),
-                        "sin" => interms.push(Number::Float(OrderedFloat(self.sin(a1)?))),
-                        "tan" => interms.push(Number::Float(OrderedFloat(self.tan(a1)?))),
-                        "sqrt" => interms.push(Number::Float(OrderedFloat(self.sqrt(a1)?))),
-                        "log" => interms.push(Number::Float(OrderedFloat(self.log(a1)?))),
-                        "exp" => interms.push(Number::Float(OrderedFloat(self.exp(a1)?))),
-                        "acos" => interms.push(Number::Float(OrderedFloat(self.acos(a1)?))),
-                        "asin" => interms.push(Number::Float(OrderedFloat(self.asin(a1)?))),
-                        "atan" => interms.push(Number::Float(OrderedFloat(self.atan(a1)?))),
-                        "abs" => interms.push(a1.abs()),
-                        "float" => interms.push(Number::Float(OrderedFloat(self.float(a1)?))),
-                        "truncate" => interms.push(self.truncate(a1)),
-                        "round" => interms.push(self.round(a1)?),
-                        "ceiling" => interms.push(self.ceiling(a1)),
-                        "floor" => interms.push(self.floor(a1)),
-                        "\\" => interms.push(self.bitwise_complement(a1)?),
-                        "sign" => interms.push(self.sign(a1)),
-                        _ => {
-                            let evaluable_stub = MachineError::functor_stub(name.clone(), 1);
-
-                            return Err(self.error_form(
-                                MachineError::type_error(
-                                    self.heap.h(),
-                                    ValidType::Evaluable,
-                                    evaluable_stub,
-                                ),
-                                caller,
-                            ));
-                        }
-                    }
-                }
-                &HeapCellValue::Addr(Addr::Fixnum(n)) => {
-                    interms.push(Number::Fixnum(n));
-                }
-                &HeapCellValue::Addr(Addr::Float(n)) => interms.push(Number::Float(n)),
-                &HeapCellValue::Integer(ref n) => interms.push(Number::Integer(n.clone())),
-                &HeapCellValue::Addr(Addr::Usize(n)) => {
-                    interms.push(Number::Integer(Rc::new(Integer::from(n))));
-                }
-                &HeapCellValue::Rational(ref n) => interms.push(Number::Rational(n.clone())),
-                &HeapCellValue::Atom(ref name, _) if name.as_str() == "pi" => {
-                    interms.push(Number::Float(OrderedFloat(f64::consts::PI)))
-                }
-                &HeapCellValue::Atom(ref name, _) if name.as_str() == "e" => {
-                    interms.push(Number::Float(OrderedFloat(f64::consts::E)))
-                }
-                &HeapCellValue::Atom(ref name, _) if name.as_str() == "epsilon" => {
-                    interms.push(Number::Float(OrderedFloat(f64::EPSILON)))
-                }
-                &HeapCellValue::NamedStr(arity, ref name, _) => {
-                    let evaluable_stub = MachineError::functor_stub(name.clone(), arity);
-
-                    return Err(self.error_form(
-                        MachineError::type_error(
-                            self.heap.h(),
-                            ValidType::Evaluable,
-                            evaluable_stub,
-                        ),
-                        caller,
-                    ));
-                }
-                &HeapCellValue::Atom(ref name, _) => {
-                    let evaluable_stub = MachineError::functor_stub(name.clone(), 0);
-
-                    return Err(self.error_form(
-                        MachineError::type_error(
-                            self.heap.h(),
-                            ValidType::Evaluable,
-                            evaluable_stub,
-                        ),
-                        caller,
-                    ));
-                }
-                &HeapCellValue::Addr(addr) if addr.is_ref() => {
-                    return Err(self.error_form(MachineError::instantiation_error(), caller));
-                }
-                val => {
-                    return Err(self.type_error(
-                        ValidType::Number,
-                        val.context_free_clone(),
-                        clause_name!("is"),
-                        2,
-                    ));
-                }
+pub(crate) fn abs(n: Number, arena: &mut Arena) -> Number {
+    match n {
+        Number::Fixnum(n) => {
+            if let Some(n) = n.get_num().checked_abs() {
+                fixnum!(Number, n, arena)
+            } else {
+                Number::arena_from(Integer::from(n.get_num()).abs(), arena)
             }
         }
-
-        Ok(interms.pop().unwrap())
+        Number::Integer(n) => Number::arena_from(Integer::from(n.abs_ref()), arena),
+        Number::Float(f) => Number::Float(f.abs()),
+        Number::Rational(r) => Number::arena_from(Rational::from(r.abs_ref()), arena),
     }
+}
+
+#[inline]
+pub(crate) fn sub(lhs: Number, rhs: Number, arena: &mut Arena) -> Result<Number, EvalError> {
+    let neg_result = neg(rhs, arena);
+    add(lhs, neg_result, arena)
+}
 
-    pub(crate) fn rdiv(&self, r1: Rc<Rational>, r2: Rc<Rational>) -> Result<Rational, MachineStub> {
-        if &*r2 == &0 {
-            let stub = MachineError::functor_stub(clause_name!("(rdiv)"), 2);
-            Err(self.error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
-        } else {
-            Ok(Rational::from(&*r1 / &*r2))
+pub(crate) fn mul(lhs: Number, rhs: Number, arena: &mut Arena) -> Result<Number, EvalError> {
+    match (lhs, rhs) {
+        (Number::Fixnum(n1), Number::Fixnum(n2)) => Ok(
+            if let Some(result) = n1.get_num().checked_mul(n2.get_num()) {
+                fixnum!(Number, result, arena)
+            } else {
+                Number::arena_from(
+                    Integer::from(n1.get_num()) * Integer::from(n2.get_num()),
+                    arena,
+                )
+            },
+        ),
+        (Number::Fixnum(n1), Number::Integer(n2)) | (Number::Integer(n2), Number::Fixnum(n1)) => {
+            Ok(Number::arena_from(
+                Integer::from(n1.get_num()) * &*n2,
+                arena,
+            ))
+        }
+        (Number::Fixnum(n1), Number::Rational(n2)) | (Number::Rational(n2), Number::Fixnum(n1)) => {
+            Ok(Number::arena_from(
+                Rational::from(n1.get_num()) * &*n2,
+                arena,
+            ))
+        }
+        (Number::Fixnum(n1), Number::Float(OrderedFloat(n2)))
+        | (Number::Float(OrderedFloat(n2)), Number::Fixnum(n1)) => {
+            Ok(Number::Float(mul_f(float_fn_to_f(n1.get_num())?, n2)?))
+        }
+        (Number::Integer(n1), Number::Integer(n2)) => {
+            Ok(Number::arena_from(Integer::from(&*n1) * &*n2, arena)) // mul_i
+        }
+        (Number::Integer(n1), Number::Float(OrderedFloat(n2)))
+        | (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
+            Ok(Number::Float(mul_f(float_i_to_f(&n1)?, n2)?))
+        }
+        (Number::Integer(n1), Number::Rational(n2))
+        | (Number::Rational(n2), Number::Integer(n1)) => {
+            Ok(Number::arena_from(Rational::from(&*n1) * &*n2, arena))
+        }
+        (Number::Rational(n1), Number::Float(OrderedFloat(n2)))
+        | (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
+            Ok(Number::Float(mul_f(float_r_to_f(&n1)?, n2)?))
+        }
+        (Number::Float(OrderedFloat(f1)), Number::Float(OrderedFloat(f2))) => {
+            Ok(Number::Float(mul_f(f1, f2)?))
+        }
+        (Number::Rational(r1), Number::Rational(r2)) => {
+            Ok(Number::arena_from(Rational::from(&*r1) * &*r2, arena))
         }
     }
+}
 
-    pub(crate) fn int_floor_div(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(div)"), 2);
-        let modulus = self.modulus(n1.clone(), n2.clone())?;
+pub(crate) fn div(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
+    let stub_gen = || functor_stub(atom!("/"), 2);
 
-        self.idiv(try_numeric_result!(self, n1 - modulus, stub)?, n2)
+    if n2.is_zero() {
+        Err(zero_divisor_eval_error(stub_gen))
+    } else {
+        try_numeric_result!(n1 / n2, stub_gen)
     }
+}
 
-    pub(crate) fn idiv(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        match (n1, n2) {
-            (Number::Fixnum(n1), Number::Fixnum(n2)) => {
-                if n2 == 0 {
-                    let stub = MachineError::functor_stub(clause_name!("(//)"), 2);
+pub(crate) fn float_pow(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
+    let f1 = result_f(&n1, rnd_f);
+    let f2 = result_f(&n2, rnd_f);
 
-                    Err(self
-                        .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
-                } else {
-                    if let Some(result) = n1.checked_div(n2) {
-                        Ok(Number::from(result))
-                    } else {
-                        let n1 = Integer::from(n1);
-                        let n2 = Integer::from(n2);
+    let stub_gen = || {
+        let pow_atom = atom!("**");
+        functor_stub(pow_atom, 2)
+    };
+
+    let f1 = try_numeric_result!(f1, stub_gen)?;
+    let f2 = try_numeric_result!(f2, stub_gen)?;
 
-                        Ok(Number::from(n1 / n2))
+    let result = result_f(&Number::Float(OrderedFloat(f1.powf(f2))), rnd_f);
+
+    Ok(Number::Float(OrderedFloat(try_numeric_result!(
+        result, stub_gen
+    )?)))
+}
+
+pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
+    if n1.is_zero() && n2.is_negative() {
+        let stub_gen = || {
+            let is_atom = atom!("is");
+            functor_stub(is_atom, 2)
+        };
+
+        return Err(undefined_eval_error(stub_gen));
+    }
+
+    let stub_gen = || {
+        let caret_atom = atom!("^");
+        functor_stub(caret_atom, 2)
+    };
+
+    match (n1, n2) {
+        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
+            let n1_i = n1.get_num();
+            let n2_i = n2.get_num();
+
+            if !(n1_i == 1 || n1_i == 0 || n1_i == -1) && n2_i < 0 {
+                let n = Number::Fixnum(n1);
+                Err(numerical_type_error(ValidType::Float, n, stub_gen))
+            } else {
+                if let Ok(n2_u) = u32::try_from(n2_i) {
+                    if let Some(result) = n1_i.checked_pow(n2_u) {
+                        return Ok(Number::arena_from(result, arena));
                     }
                 }
-            }
-            (Number::Fixnum(n1), Number::Integer(n2)) => {
-                if &*n2 == &0 {
-                    let stub = MachineError::functor_stub(clause_name!("(//)"), 2);
 
-                    Err(self
-                        .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
-                } else {
-                    Ok(Number::from(Integer::from(n1) / &*n2))
-                }
-            }
-            (Number::Integer(n2), Number::Fixnum(n1)) => {
-                if n1 == 0 {
-                    let stub = MachineError::functor_stub(clause_name!("(//)"), 2);
+                let n1 = Integer::from(n1_i);
+                let n2 = Integer::from(n2_i);
 
-                    Err(self
-                        .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
-                } else {
-                    Ok(Number::from(&*n2 / Integer::from(n1)))
-                }
+                Ok(Number::arena_from(binary_pow(n1, &n2), arena))
             }
-            (Number::Integer(n1), Number::Integer(n2)) => {
-                if &*n2 == &0 {
-                    let stub = MachineError::functor_stub(clause_name!("(//)"), 2);
-
-                    Err(self
-                        .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
-                } else {
-                    Ok(Number::from(
-                        <(Integer, Integer)>::from(n1.div_rem_ref(&*n2)).0,
-                    ))
-                }
+        }
+        (Number::Fixnum(n1), Number::Integer(n2)) => {
+            let n1_i = n1.get_num();
+
+            if !(n1_i == 1 || n1_i == 0 || n1_i == -1) && &*n2 < &0 {
+                let n = Number::Fixnum(n1);
+                Err(numerical_type_error(ValidType::Float, n, stub_gen))
+            } else {
+                let n1 = Integer::from(n1_i);
+                Ok(Number::arena_from(binary_pow(n1, &*n2), arena))
             }
-            (Number::Fixnum(_), n2) | (Number::Integer(_), n2) => {
-                let stub = MachineError::functor_stub(clause_name!("(//)"), 2);
-
-                Err(self.error_form(
-                    MachineError::type_error(self.heap.h(), ValidType::Integer, n2),
-                    stub,
-                ))
+        }
+        (Number::Integer(n1), Number::Fixnum(n2)) => {
+            let n2_i = n2.get_num();
+
+            if !(&*n1 == &1 || &*n1 == &0 || &*n1 == &-1) && n2_i < 0 {
+                let n = Number::Integer(n1);
+                Err(numerical_type_error(ValidType::Float, n, stub_gen))
+            } else {
+                let n2 = Integer::from(n2_i);
+                Ok(Number::arena_from(binary_pow((*n1).clone(), &n2), arena))
             }
-            (n1, _) => {
-                let stub = MachineError::functor_stub(clause_name!("(//)"), 2);
-
-                Err(self.error_form(
-                    MachineError::type_error(self.heap.h(), ValidType::Integer, n1),
-                    stub,
-                ))
+        }
+        (Number::Integer(n1), Number::Integer(n2)) => {
+            if !(&*n1 == &1 || &*n1 == &0 || &*n1 == &-1) && &*n2 < &0 {
+                let n = Number::Integer(n1);
+                Err(numerical_type_error(ValidType::Float, n, stub_gen))
+            } else {
+                Ok(Number::arena_from(binary_pow((*n1).clone(), &*n2), arena))
             }
         }
-    }
+        (n1, Number::Integer(n2)) => {
+            let f1 = float(n1)?;
+            let f2 = float(Number::Integer(n2))?;
 
-    pub(crate) fn div(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(/)"), 2);
-
-        if n2.is_zero() {
-            Err(self.error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
-        } else {
-            try_numeric_result!(self, n1 / n2, stub)
+            unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.powf(f2))
+                .map(|f| Number::Float(OrderedFloat(f)))
         }
-    }
+        (n1, n2) => {
+            let f2 = float(n2)?;
 
-    pub(crate) fn atan2(&self, n1: Number, n2: Number) -> Result<f64, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("is"), 2);
+            if n1.is_negative() && f2 != f2.floor() {
+                return Err(undefined_eval_error(stub_gen));
+            }
 
-        if n1.is_zero() && n2.is_zero() {
-            Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub))
-        } else {
-            let f1 = self.float(n1)?;
-            let f2 = self.float(n2)?;
+            let f1 = float(n1)?;
 
-            self.unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.atan2(f2))
+            unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.powf(f2))
+                .map(|f| Number::Float(OrderedFloat(f)))
         }
     }
+}
 
-    pub(crate) fn int_pow(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        if n1.is_zero() && n2.is_negative() {
-            let stub = MachineError::functor_stub(clause_name!("is"), 2);
-            return Err(self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub));
-        }
+pub(crate) fn pow(n1: Number, n2: Number, culprit: Atom) -> Result<Number, MachineStubGen> {
+    if n2.is_negative() && n1.is_zero() {
+        let stub_gen = move || functor_stub(culprit, 2);
 
-        match (n1, n2) {
-            (Number::Fixnum(n1), Number::Fixnum(n2)) => {
-                if !(n1 == 1 || n1 == 0 || n1 == -1) && n2 < 0 {
-                    let n = Number::from(n1);
-                    let stub = MachineError::functor_stub(clause_name!("^"), 2);
+        return Err(undefined_eval_error(stub_gen));
+    }
 
-                    Err(self.error_form(
-                        MachineError::type_error(self.heap.h(), ValidType::Float, n),
-                        stub,
-                    ))
-                } else {
-                    if let Ok(n2) = u32::try_from(n2) {
-                        if let Some(result) = n1.checked_pow(n2) {
-                            return Ok(Number::from(result));
-                        }
-                    }
+    float_pow(n1, n2)
+}
 
-                    let n1 = Integer::from(n1);
-                    let n2 = Integer::from(n2);
+#[inline]
+pub(crate) fn float(n: Number) -> Result<f64, MachineStubGen> {
+    let stub_gen = || {
+        let is_atom = atom!("is");
+        functor_stub(is_atom, 2)
+    };
 
-                    Ok(Number::from(binary_pow(n1, &n2)))
-                }
-            }
-            (Number::Fixnum(n1), Number::Integer(n2)) => {
-                if !(n1 == 1 || n1 == 0 || n1 == -1) && &*n2 < &0 {
-                    let n = Number::from(n1);
-                    let stub = MachineError::functor_stub(clause_name!("^"), 2);
+    try_numeric_result!(result_f(&n, rnd_f), stub_gen)
+}
 
-                    Err(self.error_form(
-                        MachineError::type_error(self.heap.h(), ValidType::Float, n),
-                        stub,
-                    ))
-                } else {
-                    let n1 = Integer::from(n1);
-                    Ok(Number::from(binary_pow(n1, n2.as_ref())))
-                }
-            }
-            (Number::Integer(n1), Number::Fixnum(n2)) => {
-                if !(&*n1 == &1 || &*n1 == &0 || &*n1 == &-1) && n2 < 0 {
-                    let n = Number::Integer(n1);
-                    let stub = MachineError::functor_stub(clause_name!("^"), 2);
+#[inline]
+pub(crate) fn unary_float_fn_template<FloatFn>(
+    n1: Number,
+    f: FloatFn,
+) -> Result<f64, MachineStubGen>
+where
+    FloatFn: Fn(f64) -> f64,
+{
+    let stub_gen = || {
+        let is_atom = atom!("is");
+        functor_stub(is_atom, 2)
+    };
 
-                    Err(self.error_form(
-                        MachineError::type_error(self.heap.h(), ValidType::Float, n),
-                        stub,
-                    ))
-                } else {
-                    let n2 = Integer::from(n2);
-                    Ok(Number::from(binary_pow(n1.as_ref().clone(), &n2)))
-                }
-            }
-            (Number::Integer(n1), Number::Integer(n2)) => {
-                if !(&*n1 == &1 || &*n1 == &0 || &*n1 == &-1) && &*n2 < &0 {
-                    let n = Number::Integer(n1);
-                    let stub = MachineError::functor_stub(clause_name!("^"), 2);
+    let f1 = try_numeric_result!(result_f(&n1, rnd_f), stub_gen)?;
+    let f1 = result_f(&Number::Float(OrderedFloat(f(f1))), rnd_f);
 
-                    Err(self.error_form(
-                        MachineError::type_error(self.heap.h(), ValidType::Float, n),
-                        stub,
-                    ))
-                } else {
-                    Ok(Number::from(binary_pow(n1.as_ref().clone(), n2.as_ref())))
-                }
-            }
-            (n1, Number::Integer(n2)) => {
-                let f1 = self.float(n1)?;
-                let f2 = self.float(Number::Integer(n2))?;
+    try_numeric_result!(f1, stub_gen)
+}
 
-                self.unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.powf(f2))
-                    .map(|f| Number::Float(OrderedFloat(f)))
+pub(crate) fn max(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
+    match (n1, n2) {
+        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
+            if n1.get_num() > n2.get_num() {
+                Ok(Number::Fixnum(n1))
+            } else {
+                Ok(Number::Fixnum(n2))
+            }
+        }
+        (Number::Fixnum(n1), Number::Integer(n2)) => {
+            if &*n2 > &n1.get_num() {
+                Ok(Number::Integer(n2))
+            } else {
+                Ok(Number::Fixnum(n1))
             }
-            (n1, n2) => {
-                let f2 = self.float(n2)?;
+        }
+        (Number::Integer(n1), Number::Fixnum(n2)) => {
+            if &*n1 > &n2.get_num() {
+                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_gen = || {
+                let max_atom = atom!("max");
+                functor_stub(max_atom, 2)
+            };
 
-                if n1.is_negative() && f2 != f2.floor() {
-                    let stub = MachineError::functor_stub(clause_name!("is"), 2);
-                    return Err(
-                        self.error_form(MachineError::evaluation_error(EvalError::Undefined), stub)
-                    );
-                }
+            let f1 = try_numeric_result!(result_f(&n1, rnd_f), stub_gen)?;
+            let f2 = try_numeric_result!(result_f(&n2, rnd_f), stub_gen)?;
 
-                let f1 = self.float(n1)?;
-                self.unary_float_fn_template(Number::Float(OrderedFloat(f1)), |f| f.powf(f2))
-                    .map(|f| Number::Float(OrderedFloat(f)))
-            }
+            Ok(Number::Float(cmp::max(OrderedFloat(f1), OrderedFloat(f2))))
         }
     }
+}
 
-    pub(crate) fn gcd(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        match (n1, n2) {
-            (Number::Fixnum(n1), Number::Fixnum(n2)) => {
-                if let Some(result) = isize_gcd(n1, n2) {
-                    Ok(Number::Fixnum(result))
-                } else {
-                    Ok(Number::from(Integer::from(n1).gcd(&Integer::from(n2))))
-                }
+pub(crate) fn min(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
+    match (n1, n2) {
+        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
+            if n1.get_num() < n2.get_num() {
+                Ok(Number::Fixnum(n1))
+            } else {
+                Ok(Number::Fixnum(n2))
             }
-            (Number::Fixnum(n1), Number::Integer(n2))
-            | (Number::Integer(n2), Number::Fixnum(n1)) => {
-                let n1 = Integer::from(n1);
-                Ok(Number::from(Integer::from(n2.gcd_ref(&n1))))
+        }
+        (Number::Fixnum(n1), Number::Integer(n2)) => {
+            if &*n2 < &n1.get_num() {
+                Ok(Number::Integer(n2))
+            } else {
+                Ok(Number::Fixnum(n1))
             }
-            (Number::Integer(n1), Number::Integer(n2)) => {
-                Ok(Number::from(Integer::from(n1.gcd_ref(&n2))))
+        }
+        (Number::Integer(n1), Number::Fixnum(n2)) => {
+            if &*n1 < &n2.get_num() {
+                Ok(Number::Integer(n1))
+            } else {
+                Ok(Number::Fixnum(n2))
             }
-            (Number::Float(f), _) | (_, Number::Float(f)) => {
-                let n = Number::Float(f);
-                let stub = MachineError::functor_stub(clause_name!("gcd"), 2);
-
-                Err(self.error_form(
-                    MachineError::type_error(self.heap.h(), ValidType::Integer, n),
-                    stub,
-                ))
+        }
+        (Number::Integer(n1), Number::Integer(n2)) => {
+            if n1 < n2 {
+                Ok(Number::Integer(n1))
+            } else {
+                Ok(Number::Integer(n2))
             }
-            (Number::Rational(r), _) | (_, Number::Rational(r)) => {
-                let n = Number::Rational(r);
-                let stub = MachineError::functor_stub(clause_name!("gcd"), 2);
+        }
+        (n1, n2) => {
+            let stub_gen = || {
+                let min_atom = atom!("min");
+                functor_stub(min_atom, 2)
+            };
 
-                Err(self.error_form(
-                    MachineError::type_error(self.heap.h(), ValidType::Integer, n),
-                    stub,
-                ))
-            }
+            let f1 = try_numeric_result!(result_f(&n1, rnd_f), stub_gen)?;
+            let f2 = try_numeric_result!(result_f(&n2, rnd_f), stub_gen)?;
+
+            Ok(Number::Float(cmp::min(OrderedFloat(f1), OrderedFloat(f2))))
         }
     }
+}
 
-    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);
-
-        let stub = MachineError::functor_stub(clause_name!("(**)"), 2);
+pub fn rational_from_number(
+    n: Number,
+    stub_gen: impl Fn() -> FunctorStub + 'static,
+    arena: &mut Arena,
+) -> Result<TypedArenaPtr<Rational>, MachineStubGen> {
+    match n {
+        Number::Fixnum(n) => Ok(arena_alloc!(Rational::from(n.get_num()), arena)),
+        Number::Rational(r) => Ok(r),
+        Number::Float(OrderedFloat(f)) => match Rational::from_f64(f) {
+            Some(r) => Ok(arena_alloc!(r, arena)),
+            None => Err(Box::new(move |machine_st| {
+                let instantiation_error = machine_st.instantiation_error();
+                let stub = stub_gen();
+
+                machine_st.error_form(instantiation_error, stub)
+            })),
+        },
+        Number::Integer(n) => Ok(arena_alloc!(Rational::from(&*n), arena)),
+    }
+}
 
-        let f1 = try_numeric_result!(self, f1, stub)?;
-        let f2 = try_numeric_result!(self, f2, stub)?;
+pub(crate) fn rdiv(
+    r1: TypedArenaPtr<Rational>,
+    r2: TypedArenaPtr<Rational>,
+) -> Result<Rational, MachineStubGen> {
+    if &*r2 == &0 {
+        let stub_gen = || {
+            let rdiv_atom = atom!("rdiv");
+            functor_stub(rdiv_atom, 2)
+        };
+
+        Err(zero_divisor_eval_error(stub_gen))
+    } else {
+        Ok(Rational::from(&*r1 / &*r2))
+    }
+}
 
-        let result = result_f(&Number::Float(OrderedFloat(f1.powf(f2))), rnd_f);
+pub(crate) fn idiv(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
+    let stub_gen = || {
+        let idiv_atom = atom!("//");
+        functor_stub(idiv_atom, 2)
+    };
 
-        Ok(Number::Float(OrderedFloat(try_numeric_result!(
-            self, result, stub
-        )?)))
-    }
+    match (n1, n2) {
+        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
+            if n2.get_num() == 0 {
+                Err(zero_divisor_eval_error(stub_gen))
+            } else {
+                if let Some(result) = n1.get_num().checked_div(n2.get_num()) {
+                    Ok(Number::arena_from(result, arena))
+                } else {
+                    let n1 = Integer::from(n1.get_num());
+                    let n2 = Integer::from(n2.get_num());
 
-    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));
+                    Ok(Number::arena_from(n1 / n2, arena))
+                }
+            }
         }
-
-        self.float_pow(n1, n2)
+        (Number::Fixnum(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(n1)) => {
+            if n1.get_num() == 0 {
+                Err(zero_divisor_eval_error(stub_gen))
+            } else {
+                Ok(Number::arena_from(&*n2 / Integer::from(n1), arena))
+            }
+        }
+        (Number::Integer(n1), Number::Integer(n2)) => {
+            if &*n2 == &0 {
+                Err(zero_divisor_eval_error(stub_gen))
+            } else {
+                Ok(Number::arena_from(
+                    <(Integer, Integer)>::from(n1.div_rem_ref(&*n2)).0,
+                    arena,
+                ))
+            }
+        }
+        (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)),
     }
+}
 
-    #[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);
+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 f1 = try_numeric_result!(self, result_f(&n1, rnd_f), stub)?;
-        let f1 = result_f(&Number::Float(OrderedFloat(f(f1))), rnd_f);
+    let modulus = modulus(n1, n2, arena)?;
+    let n1 = try_numeric_result!(sub(n1, modulus, arena), stub_gen)?;
 
-        try_numeric_result!(self, f1, stub)
-    }
+    idiv(n1, n2, arena)
+}
 
-    #[inline]
-    pub(crate) fn sin(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.sin())
-    }
+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)
+    };
 
-    #[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)) => {
+            let n1_i = n1.get_num();
+            let n2_i = n2.get_num();
 
-    #[inline]
-    pub(crate) fn tan(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.tan())
-    }
+            let n1 = Integer::from(n1_i);
 
-    #[inline]
-    pub(crate) fn log(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.log(f64::consts::E))
-    }
+            if let Ok(n2) = u32::try_from(n2_i) {
+                return Ok(Number::arena_from(n1 >> n2, arena));
+            } else {
+                return Ok(Number::arena_from(n1 >> u32::max_value(), arena));
+            }
+        }
+        (Number::Fixnum(n1), Number::Integer(n2)) => {
+            let n1 = Integer::from(n1.get_num());
 
-    #[inline]
-    pub(crate) fn exp(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.exp())
+            match n2.to_u32() {
+                Some(n2) => Ok(Number::arena_from(n1 >> n2, arena)),
+                _ => Ok(Number::arena_from(n1 >> u32::max_value(), arena)),
+            }
+        }
+        (Number::Integer(n1), Number::Fixnum(n2)) => match u32::try_from(n2.get_num()) {
+            Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
+            _ => Ok(Number::arena_from(
+                Integer::from(&*n1 >> u32::max_value()),
+                arena,
+            )),
+        },
+        (Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() {
+            Some(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
+            _ => Ok(Number::arena_from(
+                Integer::from(&*n1 >> u32::max_value()),
+                arena,
+            )),
+        },
+        (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 asin(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.asin())
-    }
+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)
+    };
 
-    #[inline]
-    pub(crate) fn acos(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.acos())
-    }
+    match (n1, n2) {
+        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
+            let n1_i = n1.get_num();
+            let n2_i = n2.get_num();
 
-    #[inline]
-    pub(crate) fn atan(&self, n1: Number) -> Result<f64, MachineStub> {
-        self.unary_float_fn_template(n1, |f| f.atan())
-    }
+            let n1 = Integer::from(n1_i);
 
-    #[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 let Ok(n2) = u32::try_from(n2_i) {
+                return Ok(Number::arena_from(n1 << n2, arena));
+            } else {
+                return Ok(Number::arena_from(n1 << u32::max_value(), arena));
+            }
         }
+        (Number::Fixnum(n1), Number::Integer(n2)) => {
+            let n1 = Integer::from(n1.get_num());
 
-        self.unary_float_fn_template(n1, |f| f.sqrt())
+            match n2.to_u32() {
+                Some(n2) => Ok(Number::arena_from(n1 << n2, arena)),
+                _ => Ok(Number::arena_from(n1 << u32::max_value(), arena)),
+            }
+        }
+        (Number::Integer(n1), Number::Fixnum(n2)) => match u32::try_from(n2.get_num()) {
+            Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
+            _ => Ok(Number::arena_from(
+                Integer::from(&*n1 << u32::max_value()),
+                arena,
+            )),
+        },
+        (Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() {
+            Some(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
+            _ => Ok(Number::arena_from(
+                Integer::from(&*n1 << u32::max_value()),
+                arena,
+            )),
+        },
+        (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 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 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 floor(&self, n1: Number) -> Number {
-        rnd_i(&n1).to_owned()
+    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 ceiling(&self, n1: Number) -> Number {
-        -self.floor(-n1)
-    }
+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 truncate(&self, n: Number) -> Number {
-        if n.is_negative() {
-            -self.floor(n.abs())
-        } else {
-            self.floor(n)
+    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)),
     }
+}
 
-    pub(crate) fn round(&self, n: Number) -> Result<Number, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("is"), 2);
-
-        let result = n + Number::Float(OrderedFloat(0.5f64));
-        let result = try_numeric_result!(self, result, stub)?;
+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)
+    };
 
-        Ok(self.floor(result))
+    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))
+        }
+        _ => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
     }
+}
 
-    pub(crate) fn shr(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(>>)"), 2);
+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)
+    };
 
-        match (n1, n2) {
-            (Number::Fixnum(n1), Number::Fixnum(n2)) => {
-                let n1 = Integer::from(n1);
+    match (x, y) {
+        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
+            let n2_i = n2.get_num();
 
-                if let Ok(n2) = u32::try_from(n2) {
-                    return Ok(Number::from(n1 >> n2));
-                } else {
-                    return Ok(Number::from(n1 >> u32::max_value()));
-                }
+            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))
             }
-            (Number::Fixnum(n1), Number::Integer(n2)) => {
-                let n1 = Integer::from(n1);
-
-                match n2.to_u32() {
-                    Some(n2) => Ok(Number::from(n1 >> n2)),
-                    _ => Ok(Number::from(n1 >> u32::max_value())),
-                }
+        }
+        (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)) => 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,
-            )),
         }
+        (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)),
     }
+}
 
-    pub(crate) fn shl(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(<<)"), 2);
+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 (n1, n2) {
-            (Number::Fixnum(n1), Number::Fixnum(n2)) => {
-                let n1 = Integer::from(n1);
+    match (x, y) {
+        (Number::Fixnum(n1), Number::Fixnum(n2)) => {
+            let n2_i = n2.get_num();
 
-                if let Ok(n2) = u32::try_from(n2) {
-                    return Ok(Number::from(n1 << n2));
-                } else {
-                    return Ok(Number::from(n1 << u32::max_value()));
-                }
+            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)) => {
-                let n1 = Integer::from(n1);
-
-                match n2.to_u32() {
-                    Some(n2) => Ok(Number::from(n1 << n2)),
-                    _ => Ok(Number::from(n1 << u32::max_value())),
-                }
+        }
+        (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)) => 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,
-            )),
         }
+        (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)),
     }
+}
 
-    pub(crate) fn bitwise_complement(&self, n1: Number) -> Result<Number, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(\\)"), 2);
+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 {
-            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,
-            )),
+    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))
         }
     }
+}
 
-    pub(crate) fn xor(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(xor)"), 2);
+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)
+        };
 
-        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,
-            )),
-        }
+        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))
     }
+}
 
-    pub(crate) fn and(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(/\\)"), 2);
+#[inline]
+pub(crate) fn sin(n1: Number) -> Result<f64, MachineStubGen> {
+    unary_float_fn_template(n1, |f| f.sin())
+}
 
-        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,
-            )),
-        }
+#[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 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 or(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(\\/)"), 2);
+    unary_float_fn_template(n1, |f| f.sqrt())
+}
 
-        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,
-            )),
+#[inline]
+pub(crate) fn floor(n1: Number, arena: &mut Arena) -> Number {
+    rnd_i(&n1, arena)
+}
+
+#[inline]
+pub(crate) fn ceiling(n1: Number, arena: &mut Arena) -> Number {
+    let n1 = neg(n1, arena);
+    let n1 = floor(n1, arena);
+
+    neg(n1, arena)
+}
+
+#[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 round(n: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
+    let stub_gen = || {
+        let is_atom = atom!("is");
+        functor_stub(is_atom, 2)
+    };
+
+    let result = add(n, Number::Float(OrderedFloat(0.5f64)), arena);
+    let result = try_numeric_result!(result, stub_gen)?;
+
+    Ok(floor(result, arena))
+}
+
+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))
         }
     }
+}
 
-    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)))
+                match Number::try_from(value) {
+                    Ok(n) => Ok(n),
+                    Err(_) => self.arith_eval_by_metacall(value),
                 }
             }
-            (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,
-                    ))
-                }
-            }
-            (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 remainder(&self, n1: Number, n2: Number) -> Result<Number, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("(rem)"), 2);
+    pub fn get_rational(
+        &mut self,
+        at: &ArithmeticTerm,
+        caller: impl Fn() -> FunctorStub + 'static,
+    ) -> Result<TypedArenaPtr<Rational>, MachineStub> {
+        let n = self.get_number(at)?;
 
-        match (n1, n2) {
-            (Number::Fixnum(n1), Number::Fixnum(n2)) => {
-                if n2 == 0 {
-                    Err(self
-                        .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
-                } else {
-                    Ok(Number::from(n1 % 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(n1 % &*n2))
-                }
-            }
-            (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(&*n1 % n2))
-                }
-            }
-            (Number::Integer(n1), Number::Integer(n2)) => {
-                if &*n2 == &0 {
-                    Err(self
-                        .error_form(MachineError::evaluation_error(EvalError::ZeroDivisor), stub))
-                } else {
-                    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,
-            )),
+        match rational_from_number(n, caller, &mut self.arena) {
+            Ok(r) => Ok(r),
+            Err(e_gen) => Err(e_gen(self))
         }
     }
 
-    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 = stackless_post_order_iter(&mut self.heap, value);
+
+        while let Some(value) = iter.next() {
+            if value.is_forwarded() {
+                let (name, arity) = read_heap_cell!(value,
+                     (HeapCellValueTag::Atom, (name, arity)) => {
+                         (name, arity)
+                     }
+                     (HeapCellValueTag::Lis | HeapCellValueTag::PStr) => {
+                         (atom!("."), 2)
+                     }
+                     _ => {
+                         unreachable!()
+                     }
+                );
+
+                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))
-                }
-            }
-            (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)?;
+            read_heap_cell!(value,
+                (HeapCellValueTag::Atom, (name, arity)) => {
+                    if arity == 2 {
+                        let a1 = self.interms.pop().unwrap();
+                        let a2 = 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)),
+                                    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!("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(sign(a1)),
+                            _ => {
+                                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 f39a2ac8fa470e565d04c97b3618f445d8cf47f0..34cdd12ef5b85e58f40b4f374cce7f598c71ff9b 100644 (file)
@@ -1,13 +1,15 @@
 use crate::heap_iter::*;
 use crate::machine::*;
-use prolog_parser::temp_v;
+use crate::parser::ast::*;
+use crate::temp_v;
+use crate::types::*;
 
 use indexmap::IndexSet;
 
 use std::cmp::Ordering;
 use std::vec::IntoIter;
 
-pub(super) type Bindings = Vec<(usize, Addr)>;
+pub(super) type Bindings = Vec<(usize, HeapCellValue)>;
 
 #[derive(Debug)]
 pub(super) struct AttrVarInitializer {
@@ -37,7 +39,7 @@ impl AttrVarInitializer {
 }
 
 impl MachineState {
-    pub(super) fn push_attr_var_binding(&mut self, h: usize, addr: Addr) {
+    pub(super) fn push_attr_var_binding(&mut self, h: usize, addr: HeapCellValue) {
         if self.attr_var_init.bindings.is_empty() {
             self.attr_var_init.instigating_p = self.p.local();
 
@@ -53,28 +55,24 @@ impl MachineState {
         self.attr_var_init.bindings.push((h, addr));
     }
 
-    fn populate_var_and_value_lists(&mut self) -> (Addr, Addr) {
+    fn populate_var_and_value_lists(&mut self) -> (HeapCellValue, HeapCellValue) {
         let iter = self
             .attr_var_init
             .bindings
             .iter()
-            .map(|(ref h, _)| HeapCellValue::Addr(Addr::AttrVar(*h)));
+            .map(|(ref h, _)| attr_var_as_cell!(*h));
 
-        let var_list_addr = Addr::HeapCell(self.heap.to_list(iter));
+        let var_list_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, iter));
 
-        let iter = self
-            .attr_var_init
-            .bindings
-            .drain(0..)
-            .map(|(_, addr)| HeapCellValue::Addr(addr));
+        let iter = self.attr_var_init.bindings.drain(0..).map(|(_, ref v)| *v);
 
-        let value_list_addr = Addr::HeapCell(self.heap.to_list(iter));
+        let value_list_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, iter));
         (var_list_addr, value_list_addr)
     }
 
     fn verify_attributes(&mut self) {
         for (h, _) in &self.attr_var_init.bindings {
-            self.heap[*h] = HeapCellValue::Addr(Addr::AttrVar(*h));
+            self.heap[*h] = attr_var_as_cell!(*h);
         }
 
         let (var_list_addr, value_list_addr) = self.populate_var_and_value_lists();
@@ -83,19 +81,26 @@ impl MachineState {
         self[temp_v!(2)] = value_list_addr;
     }
 
-    pub(super) fn gather_attr_vars_created_since(&self, b: usize) -> IntoIter<Addr> {
+    pub(super) fn gather_attr_vars_created_since(&mut self, b: usize) -> IntoIter<HeapCellValue> {
         let mut attr_vars: Vec<_> = self.attr_var_init.attr_var_queue[b..]
             .iter()
-            .filter_map(|h| match self.store(self.deref(Addr::HeapCell(*h))) {
-                Addr::AttrVar(h) => Some(Addr::AttrVar(h)),
-                _ => None,
+            .filter_map(|h| {
+                read_heap_cell!(self.store(self.deref(heap_loc_as_cell!(*h))), //Addr::HeapCell(*h))) {
+                    (HeapCellValueTag::AttrVar, h) => {
+                        Some(attr_var_as_cell!(h))
+                    }
+                    _ => {
+                        None
+                    }
+                )
             })
             .collect();
 
-        attr_vars
-            .sort_unstable_by(|a1, a2| self.compare_term_test(a1, a2).unwrap_or(Ordering::Less));
+        attr_vars.sort_unstable_by(|a1, a2| {
+           compare_term_test!(self, *a1, *a2).unwrap_or(Ordering::Less)
+       });
 
-        self.term_dedup(&mut attr_vars);
+       attr_vars.dedup();
         attr_vars.into_iter()
     }
 
@@ -109,8 +114,10 @@ impl MachineState {
             self.stack.index_and_frame_mut(e)[i] = self[RegType::Temp(i)];
         }
 
-        self.stack.index_and_frame_mut(e)[self.num_of_args + 1] = Addr::CutPoint(self.b0);
-        self.stack.index_and_frame_mut(e)[self.num_of_args + 2] = Addr::Usize(self.num_of_args);
+        self.stack.index_and_frame_mut(e)[self.num_of_args + 1] =
+            fixnum_as_cell!(Fixnum::build_with(self.b0 as i64));
+        self.stack.index_and_frame_mut(e)[self.num_of_args + 2] =
+            fixnum_as_cell!(Fixnum::build_with(self.num_of_args as i64));
 
         self.verify_attributes();
 
@@ -119,33 +126,51 @@ impl MachineState {
         self.p = CodePtr::Local(LocalCodePtr::DirEntry(p));
     }
 
-    pub(super) fn attr_vars_of_term(&self, addr: Addr) -> Vec<Addr> {
+    pub(super) fn attr_vars_of_term(&mut self, cell: HeapCellValue) -> Vec<HeapCellValue> {
         let mut seen_set = IndexSet::new();
         let mut seen_vars = vec![];
 
-        let mut iter = self.acyclic_pre_order_iter(addr);
-
-        while let Some(addr) = iter.next() {
-            if let HeapCellValue::Addr(Addr::AttrVar(h)) = self.heap.index_addr(&addr).as_ref() {
-                if seen_set.contains(h) {
-                    continue;
-                }
-
-                seen_vars.push(addr);
-                seen_set.insert(*h);
-
-                let mut l = h + 1;
-                let mut list_elements = vec![];
-
-                while let Addr::Lis(elem) = self.store(self.deref(Addr::HeapCell(l))) {
-                    list_elements.push(self.heap[elem].as_addr(elem));
-                    l = elem + 1;
+        let mut iter = stackful_preorder_iter(&mut self.heap, cell);
+
+        while let Some(value) = iter.next() {
+            read_heap_cell!(value,
+                (HeapCellValueTag::AttrVar, h) => {
+                    if seen_set.contains(&h) {
+                        continue;
+                    }
+
+                    seen_vars.push(value);
+                    seen_set.insert(h);
+
+                    let mut l = h + 1;
+                    // let mut list_elements = vec![];
+                    // let iter_stack_len = iter.stack_len();
+
+                    loop {
+                        read_heap_cell!(iter.heap[l],
+                            (HeapCellValueTag::Lis) => {
+                                iter.push_stack(l);
+                                // l = elem + 1;
+                                break;
+                            }
+                            (HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => {
+                                if h == l {
+                                    break;
+                                } else {
+                                    l = h;
+                                }
+                            }
+                            _ => {
+                                break;
+                            }
+                        )
+                    }
+
+                    // iter.stack_slice_from(iter_stack_len ..).reverse();
                 }
-
-                for element in list_elements.into_iter().rev() {
-                    iter.stack().push(element);
+                _ => {
                 }
-            }
+            );
         }
 
         seen_vars
index bb21462e2f3b5feb6ddc6349510aa7e160e69af1..54db570f69ee4855c1d2ec4b6b432a20ff45e837 100644 (file)
@@ -3,7 +3,7 @@ use crate::instructions::*;
 use crate::machine::machine_indices::*;
 
 #[derive(Debug)]
-pub(crate) struct CodeRepo {
+pub struct CodeRepo {
     pub(super) code: Code,
 }
 
index ef99df341db96e0ad13f0d64414c2704633270ba..72ec57c4dbe80266e2c992d6ee1b57c0cc096e7b 100644 (file)
@@ -1,18 +1,22 @@
-use prolog_parser::clause_name;
-
+use crate::atom_table::*;
 use crate::codegen::*;
 use crate::debray_allocator::*;
-use crate::indexing::{merge_clause_index, remove_index, IndexingCodePtr};
+use crate::forms::*;
+use crate::indexing::{merge_clause_index, remove_index};
+use crate::instructions::*;
 use crate::machine::load_state::*;
 use crate::machine::loader::*;
+use crate::machine::machine_errors::*;
 use crate::machine::preprocessor::*;
 use crate::machine::term_stream::*;
 use crate::machine::*;
+use crate::parser::ast::*;
 
 use slice_deque::{sdeq, SliceDeque};
 
 use std::cell::Cell;
 use std::collections::VecDeque;
+use std::mem;
 use std::ops::Range;
 
 struct StandaloneCompileResult {
@@ -25,17 +29,21 @@ pub(super) fn bootstrapping_compile(
     wam: &mut Machine,
     listing_src: ListingSource,
 ) -> Result<(), SessionError> {
-    let stream = &mut parsing_stream(stream)?;
-    let term_stream = BootstrappingTermStream::from_prolog_stream(
+    let (wam_prelude, machine_st) = wam.prelude_view_and_machine_st();
+
+    let term_stream = BootstrappingTermStream::from_char_reader(
         stream,
-        wam.machine_st.atom_tbl.clone(),
-        wam.machine_st.flags,
+        machine_st,
         listing_src,
     );
 
-    let loader = Loader::new(term_stream, wam);
-    loader.load()?;
+    let payload = BootstrappingLoadState(
+        LoadStatePayload::new(wam_prelude.code_repo.code.len(), term_stream)
+    );
 
+    let loader: Loader<'_, BootstrappingLoadState> = Loader { payload, wam_prelude };
+
+    loader.load()?;
     Ok(())
 }
 
@@ -57,7 +65,7 @@ pub(super) fn compile_appendix(
     mut queue: VecDeque<TopLevel>,
     jmp_by_locs: Vec<usize>,
     non_counted_bt: bool,
-    atom_tbl: TabledData<Atom>,
+    atom_tbl: &mut AtomTable,
 ) -> Result<(), CompilationError> {
     let mut jmp_by_locs = VecDeque::from(jmp_by_locs);
 
@@ -80,7 +88,7 @@ pub(super) fn compile_appendix(
             non_counted_bt,
         };
 
-        let mut cg = CodeGenerator::<DebrayAllocator>::new(atom_tbl.clone(), settings);
+        let mut cg = CodeGenerator::<DebrayAllocator>::new(atom_tbl, settings);
 
         let tl = queue.pop_front().unwrap();
         let decl_code = compile_relation(&mut cg, &tl)?;
@@ -164,6 +172,7 @@ fn merge_indices(
     target_index_loc: usize,
     index_range: Range<usize>,
     skeleton: &mut [ClauseIndexInfo],
+    retracted_dynamic_clauses: &Option<Vec<ClauseIndexInfo>>,
     retraction_info: &mut RetractionInfo,
 ) {
     for clause_index in index_range {
@@ -183,6 +192,7 @@ fn merge_indices(
             merge_clause_index(
                 target_indexing_line,
                 &mut skeleton[0..clause_index + 1],
+                retracted_dynamic_clauses,
                 clause_loc,
                 AppendOrPrepend::Append,
             );
@@ -422,13 +432,24 @@ fn delete_from_skeleton(
         skeleton.core.clause_assert_margin -= 1;
     }
 
-    retraction_info.push_record(RetractionRecord::RemovedSkeletonClause(
-        compilation_target,
-        key,
-        target_pos,
-        clause_index_info,
-        clause_clause_loc,
-    ));
+    if skeleton.core.is_dynamic {
+        skeleton.core.add_retracted_dynamic_clause_info(clause_index_info);
+
+        retraction_info.push_record(RetractionRecord::RemovedDynamicSkeletonClause(
+            compilation_target,
+            key,
+            target_pos,
+            clause_clause_loc,
+        ));
+    } else {
+        retraction_info.push_record(RetractionRecord::RemovedSkeletonClause(
+            compilation_target,
+            key,
+            target_pos,
+            clause_index_info,
+            clause_clause_loc,
+        ));
+    }
 
     clause_clause_loc
 }
@@ -617,7 +638,6 @@ fn internalize_choice_instr_at(
         }
         Line::Choice(ChoiceInstruction::TryMeElse(0)) => {
             retraction_info.push_record(RetractionRecord::ModifiedTryMeElse(instr_loc, 0));
-
             code[instr_loc] = Line::Choice(ChoiceInstruction::TrustMe(0));
         }
         Line::Choice(ChoiceInstruction::TryMeElse(o)) => {
@@ -804,8 +824,8 @@ fn finalize_retract(
     retraction_info: &mut RetractionInfo,
 ) -> usize {
     let clause_clause_loc = delete_from_skeleton(
-        compilation_target.clone(),
-        key.clone(),
+        compilation_target,
+        key,
         skeleton,
         target_pos,
         retraction_info,
@@ -920,7 +940,7 @@ fn prepend_compiled_clause(
 
                 retraction_info.push_record(RetractionRecord::SkeletonClauseStartReplaced(
                     compilation_target,
-                    key.clone(),
+                    key,
                     1,
                     skeleton.clauses[1].clause_start,
                 ));
@@ -999,6 +1019,7 @@ fn prepend_compiled_clause(
                 merge_clause_index(
                     target_indexing_line,
                     &mut skeleton.clauses,
+                    &skeleton.core.retracted_dynamic_clauses,
                     clause_loc + 2, // == skeleton.clauses[0].clause_start
                     AppendOrPrepend::Prepend,
                 );
@@ -1202,6 +1223,7 @@ fn append_compiled_clause(
             merge_clause_index(
                 target_indexing_line,
                 &mut skeleton.clauses[lower_bound..],
+                &skeleton.core.retracted_dynamic_clauses,
                 clause_loc,
                 AppendOrPrepend::Append,
             );
@@ -1322,21 +1344,20 @@ fn print_overwrite_warning(
         _ => {}
     }
 
-    println!("Warning: overwriting {}/{}", key.0, key.1);
+    println!("Warning: overwriting {}/{}", key.0.as_str(), key.1);
 }
 
-impl<'a> LoadState<'a> {
-    pub(super) fn listing_src_file_name(&self) -> Option<ClauseName> {
-        if let Some(load_context) = self.wam.load_contexts.last() {
+impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
+    pub(super) fn listing_src_file_name(&mut self) -> Option<Atom> {
+        if let Some(load_context) = self.wam_prelude.load_contexts.last() {
             if !load_context.path.is_file() {
                 return None;
             }
 
             if let Some(path_str) = load_context.path.to_str() {
                 if !path_str.is_empty() {
-                    return Some(clause_name!(
-                        path_str.to_string(),
-                        self.wam.machine_st.atom_tbl
+                    return Some(LS::machine_st(&mut self.payload).atom_tbl.build_with(
+                        path_str
                     ));
                 }
             }
@@ -1349,14 +1370,13 @@ impl<'a> LoadState<'a> {
         &mut self,
         term: Term,
         settings: CodeGenSettings,
-        atom_tbl: TabledData<Atom>,
     ) -> Result<StandaloneCompileResult, SessionError> {
-        let mut preprocessor = Preprocessor::new();
-        let mut cg = CodeGenerator::<DebrayAllocator>::new(atom_tbl.clone(), settings);
+        let mut preprocessor = Preprocessor::new(LS::machine_st(&mut self.payload).flags);
 
         let clause = self.try_term_to_tl(term, &mut preprocessor)?;
         let queue = preprocessor.parse_queue(self)?;
 
+        let mut cg = CodeGenerator::<DebrayAllocator>::new(&mut LS::machine_st(&mut self.payload).atom_tbl, settings);
         let mut clause_code = cg.compile_predicate(&vec![clause])?;
 
         compile_appendix(
@@ -1364,7 +1384,7 @@ impl<'a> LoadState<'a> {
             queue,
             cg.jmp_by_locs,
             settings.non_counted_bt,
-            atom_tbl,
+            cg.atom_tbl,
         )?;
 
         Ok(StandaloneCompileResult {
@@ -1376,26 +1396,28 @@ impl<'a> LoadState<'a> {
     fn compile(
         &mut self,
         key: PredicateKey,
-        predicates: &mut PredicateQueue,
+        mut predicates: PredicateQueue,
         settings: CodeGenSettings,
     ) -> Result<CodeIndex, SessionError> {
-        let code_index =
-            self.get_or_insert_code_index(key.clone(), predicates.compilation_target.clone());
+        let code_index = self.get_or_insert_code_index(key, predicates.compilation_target);
 
-        let code_len = self.wam.code_repo.code.len();
+        let code_len = self.wam_prelude.code_repo.code.len();
         let mut code_ptr = code_len;
 
-        let mut cg =
-            CodeGenerator::<DebrayAllocator>::new(self.wam.machine_st.atom_tbl.clone(), settings);
-
         let mut clauses = vec![];
-        let mut preprocessor = Preprocessor::new();
+        let mut preprocessor = Preprocessor::new(LS::machine_st(&mut self.payload).flags);
 
         for term in predicates.predicates.drain(0..) {
             clauses.push(self.try_term_to_tl(term, &mut preprocessor)?);
         }
 
         let queue = preprocessor.parse_queue(self)?;
+
+        let mut cg = CodeGenerator::<DebrayAllocator>::new(
+            &mut LS::machine_st(&mut self.payload).atom_tbl,
+            settings,
+        );
+
         let mut code = cg.compile_predicate(&clauses)?;
 
         compile_appendix(
@@ -1403,7 +1425,7 @@ impl<'a> LoadState<'a> {
             queue,
             cg.jmp_by_locs,
             settings.non_counted_bt,
-            self.wam.machine_st.atom_tbl.clone(),
+            cg.atom_tbl,
         )?;
 
         if settings.is_extensible {
@@ -1424,34 +1446,38 @@ impl<'a> LoadState<'a> {
             }
 
             match self
-                .wam
+                .wam_prelude
                 .indices
                 .get_predicate_skeleton_mut(&predicates.compilation_target, &key)
             {
                 Some(skeleton) => {
-                    self.retraction_info
-                        .push_record(RetractionRecord::SkeletonClauseTruncateBack(
-                            predicates.compilation_target.clone(),
-                            key.clone(),
-                            skeleton.clauses.len(),
-                        ));
+                    let skeleton_clause_len = skeleton.clauses.len();
 
                     skeleton.clauses.extend(cg.skeleton.clauses.into_iter());
                     skeleton
                         .core
                         .clause_clause_locs
                         .extend_from_slice(&clause_clause_locs[0..]);
+
+                    self.payload.retraction_info
+                        .push_record(RetractionRecord::SkeletonClauseTruncateBack(
+                            predicates.compilation_target,
+                            key,
+                            skeleton_clause_len,
+                        ));
                 }
                 None => {
                     cg.skeleton
-                        .core
-                        .clause_clause_locs
-                        .extend_from_slice(&clause_clause_locs[0..]);
+                      .core
+                      .clause_clause_locs
+                      .extend_from_slice(&clause_clause_locs[0..]);
+
+                    let skeleton = cg.skeleton;
 
                     self.add_extensible_predicate(
-                        key.clone(),
-                        cg.skeleton,
-                        predicates.compilation_target.clone(),
+                        key,
+                        skeleton,
+                        predicates.compilation_target,
                     );
                 }
             };
@@ -1477,14 +1503,14 @@ impl<'a> LoadState<'a> {
         };
 
         set_code_index(
-            &mut self.retraction_info,
+            &mut self.payload.retraction_info,
             &predicates.compilation_target,
             key,
             &code_index,
             index_ptr,
         );
 
-        self.wam.code_repo.code.extend(code.into_iter());
+        self.wam_prelude.code_repo.code.extend(code.into_iter());
         Ok(code_index)
     }
 
@@ -1494,18 +1520,22 @@ impl<'a> LoadState<'a> {
         key: &PredicateKey,
         clause_clause_locs: SliceDeque<usize>,
     ) {
-        match self.wam.indices.get_local_predicate_skeleton_mut(
-            self.compilation_target.clone(),
-            compilation_target.clone(),
-            self.listing_src_file_name(),
-            key.clone(),
+        let listing_src_file_name = self.listing_src_file_name();
+
+        match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
+            self.payload.compilation_target,
+            *compilation_target,
+            listing_src_file_name,
+            *key,
         ) {
             Some(skeleton) => {
-                self.retraction_info.push_record(
+                let payload_compilation_target = self.payload.compilation_target;
+
+                self.payload.retraction_info.push_record(
                     RetractionRecord::SkeletonLocalClauseTruncateBack(
-                        self.compilation_target.clone(),
-                        compilation_target.clone(),
-                        key.clone(),
+                        payload_compilation_target,
+                        *compilation_target,
+                        *key,
                         skeleton.clause_clause_locs.len(),
                     ),
                 );
@@ -1519,8 +1549,8 @@ impl<'a> LoadState<'a> {
                 skeleton.clause_clause_locs = clause_clause_locs;
 
                 self.add_local_extensible_predicate(
-                    compilation_target.clone(),
-                    key.clone(),
+                    *compilation_target,
+                    *key,
                     skeleton,
                 );
             }
@@ -1533,18 +1563,22 @@ impl<'a> LoadState<'a> {
         key: &PredicateKey,
         code_len: usize,
     ) {
-        match self.wam.indices.get_local_predicate_skeleton_mut(
-            self.compilation_target.clone(),
-            compilation_target.clone(),
-            self.listing_src_file_name(),
-            key.clone(),
+        let listing_src_file_name = self.listing_src_file_name();
+
+        match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
+            self.payload.compilation_target,
+            *compilation_target,
+            listing_src_file_name,
+            *key,
         ) {
             Some(skeleton) => {
-                self.retraction_info.push_record(
+                let payload_compilation_target = self.payload.compilation_target;
+
+                self.payload.retraction_info.push_record(
                     RetractionRecord::SkeletonLocalClauseClausePopFront(
-                        self.compilation_target.clone(),
-                        compilation_target.clone(),
-                        key.clone(),
+                        payload_compilation_target,
+                        *compilation_target,
+                        *key,
                     ),
                 );
 
@@ -1555,8 +1589,8 @@ impl<'a> LoadState<'a> {
                 skeleton.clause_clause_locs.push_front(code_len);
 
                 self.add_local_extensible_predicate(
-                    compilation_target.clone(),
-                    key.clone(),
+                    *compilation_target,
+                    *key,
                     skeleton,
                 );
             }
@@ -1569,18 +1603,22 @@ impl<'a> LoadState<'a> {
         key: &PredicateKey,
         code_len: usize,
     ) {
-        match self.wam.indices.get_local_predicate_skeleton_mut(
-            self.compilation_target.clone(),
-            compilation_target.clone(),
-            self.listing_src_file_name(),
-            key.clone(),
+        let listing_src_file_name = self.listing_src_file_name();
+
+        match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
+            self.payload.compilation_target,
+            *compilation_target,
+            listing_src_file_name,
+            *key,
         ) {
             Some(skeleton) => {
-                self.retraction_info.push_record(
+                let payload_compilation_target = self.payload.compilation_target;
+
+                self.payload.retraction_info.push_record(
                     RetractionRecord::SkeletonLocalClauseClausePopBack(
-                        self.compilation_target.clone(),
-                        compilation_target.clone(),
-                        key.clone(),
+                        payload_compilation_target,
+                        *compilation_target,
+                        *key,
                     ),
                 );
 
@@ -1591,8 +1629,8 @@ impl<'a> LoadState<'a> {
                 skeleton.clause_clause_locs.push_back(code_len);
 
                 self.add_local_extensible_predicate(
-                    compilation_target.clone(),
-                    key.clone(),
+                    *compilation_target,
+                    *key,
                     skeleton,
                 );
             }
@@ -1608,13 +1646,13 @@ impl<'a> LoadState<'a> {
         append_or_prepend: AppendOrPrepend,
     ) -> Result<CodeIndex, SessionError> {
         let settings = match self
-            .wam
+            .wam_prelude
             .indices
             .get_predicate_skeleton_mut(&compilation_target, &key)
         {
             Some(skeleton) if !skeleton.clauses.is_empty() => CodeGenSettings {
                 global_clock_tick: if skeleton.core.is_dynamic {
-                    Some(self.wam.machine_st.global_clock)
+                    Some(LS::machine_st(&mut self.payload).global_clock)
                 } else {
                     None
                 },
@@ -1625,7 +1663,7 @@ impl<'a> LoadState<'a> {
                 let settings = CodeGenSettings {
                     global_clock_tick: if let Some(skeleton) = skeleton_opt {
                         if skeleton.core.is_dynamic {
-                            Some(self.wam.machine_st.global_clock)
+                            Some(LS::machine_st(&mut self.payload).global_clock)
                         } else {
                             None
                         }
@@ -1639,21 +1677,19 @@ impl<'a> LoadState<'a> {
                 let mut predicate_queue = predicate_queue![clause];
                 predicate_queue.compilation_target = compilation_target;
 
-                return self.compile(key, &mut predicate_queue, settings);
+                return self.compile(key, predicate_queue, settings);
             }
         };
 
-        let atom_tbl = self.wam.machine_st.atom_tbl.clone();
-
         let StandaloneCompileResult {
             clause_code,
             mut standalone_skeleton,
-        } = self.compile_standalone_clause(clause, settings, atom_tbl)?;
+        } = self.compile_standalone_clause(clause, settings)?;
 
-        let code_len = self.wam.code_repo.code.len();
+        let code_len = self.wam_prelude.code_repo.code.len();
 
         let skeleton = match self
-            .wam
+            .wam_prelude
             .indices
             .get_predicate_skeleton_mut(&compilation_target, &key)
         {
@@ -1668,28 +1704,30 @@ impl<'a> LoadState<'a> {
 
                 skeleton.core.clause_clause_locs.push_back(code_len);
 
-                self.retraction_info
+                self.payload.retraction_info
                     .push_record(RetractionRecord::SkeletonClausePopBack(
-                        compilation_target.clone(),
-                        key.clone(),
+                        compilation_target,
+                        key,
                     ));
 
+                let global_clock = LS::machine_st(&mut self.payload).global_clock;
+
                 let result = append_compiled_clause(
-                    &mut self.wam.code_repo.code,
+                    &mut self.wam_prelude.code_repo.code,
                     clause_code,
                     skeleton,
-                    &mut self.retraction_info,
-                    self.wam.machine_st.global_clock,
+                    &mut self.payload.retraction_info,
+                    global_clock,
                 );
 
                 self.push_back_to_local_predicate_skeleton(&compilation_target, &key, code_len);
 
                 let code_index =
-                    self.get_or_insert_code_index(key.clone(), compilation_target.clone());
+                    self.get_or_insert_code_index(key, compilation_target);
 
                 if let Some(new_code_ptr) = result {
                     set_code_index(
-                        &mut self.retraction_info,
+                        &mut self.payload.retraction_info,
                         &compilation_target,
                         key,
                         &code_index,
@@ -1706,29 +1744,31 @@ impl<'a> LoadState<'a> {
                 skeleton.core.clause_clause_locs.push_front(code_len);
                 skeleton.core.clause_assert_margin += 1;
 
-                self.retraction_info
+                self.payload.retraction_info
                     .push_record(RetractionRecord::SkeletonClausePopFront(
-                        compilation_target.clone(),
-                        key.clone(),
+                        compilation_target,
+                        key,
                     ));
 
+                let global_clock = LS::machine_st(&mut self.payload).global_clock;
+
                 let new_code_ptr = prepend_compiled_clause(
-                    &mut self.wam.code_repo.code,
-                    compilation_target.clone(),
-                    key.clone(),
+                    &mut self.wam_prelude.code_repo.code,
+                    compilation_target,
+                    key,
                     clause_code,
                     skeleton,
-                    &mut self.retraction_info,
-                    self.wam.machine_st.global_clock,
+                    &mut self.payload.retraction_info,
+                    global_clock,
                 );
 
                 self.push_front_to_local_predicate_skeleton(&compilation_target, &key, code_len);
 
                 let code_index =
-                    self.get_or_insert_code_index(key.clone(), compilation_target.clone());
+                    self.get_or_insert_code_index(key, compilation_target);
 
                 set_code_index(
-                    &mut self.retraction_info,
+                    &mut self.payload.retraction_info,
                     &compilation_target,
                     key,
                     &code_index,
@@ -1742,9 +1782,9 @@ impl<'a> LoadState<'a> {
 
     pub(super) fn retract_dynamic_clause(&mut self, key: PredicateKey, target_pos: usize) -> usize {
         let skeleton = match self
-            .wam
+            .wam_prelude
             .indices
-            .get_predicate_skeleton_mut(&self.compilation_target, &key)
+            .get_predicate_skeleton_mut(&self.payload.compilation_target, &key)
         {
             Some(skeleton) => skeleton,
             None => {
@@ -1757,38 +1797,38 @@ impl<'a> LoadState<'a> {
             .switch_on_term_loc()
         {
             Some(index_loc) => find_inner_choice_instr(
-                &self.wam.code_repo.code,
+                &self.wam_prelude.code_repo.code,
                 skeleton.clauses[target_pos].clause_start,
                 index_loc,
             ),
             None => skeleton.clauses[target_pos].clause_start,
         };
 
-        match &mut self.wam.code_repo.code[clause_loc] {
+        match &mut self.wam_prelude.code_repo.code[clause_loc] {
             Line::Choice(ChoiceInstruction::DynamicElse(_, ref mut d, _))
             | Line::Choice(ChoiceInstruction::DynamicInternalElse(_, ref mut d, _)) => {
-                *d = Death::Finite(self.wam.machine_st.global_clock);
+                *d = Death::Finite(LS::machine_st(&mut self.payload).global_clock);
             }
             _ => unreachable!(),
         }
 
         delete_from_skeleton(
-            self.compilation_target.clone(),
+            self.payload.compilation_target,
             key,
             skeleton,
             target_pos,
-            &mut self.retraction_info,
+            &mut self.payload.retraction_info,
         )
     }
 
     pub(super) fn retract_clause(&mut self, key: PredicateKey, target_pos: usize) -> usize {
-        let code_index =
-            self.get_or_insert_code_index(key.clone(), self.compilation_target.clone());
+        let payload_compilation_target = self.payload.compilation_target;
+        let code_index = self.get_or_insert_code_index(key, payload_compilation_target);
 
         let skeleton = match self
-            .wam
+            .wam_prelude
             .indices
-            .get_predicate_skeleton_mut(&self.compilation_target, &key)
+            .get_predicate_skeleton_mut(&payload_compilation_target, &key)
         {
             Some(skeleton) => skeleton,
             None => {
@@ -1796,7 +1836,7 @@ impl<'a> LoadState<'a> {
             }
         };
 
-        let code = &mut self.wam.code_repo.code;
+        let code = &mut self.wam_prelude.code_repo.code;
         let lower_bound = lower_bound_of_target_clause(skeleton, target_pos);
         let lower_bound_is_unindexed = !skeleton.clauses[lower_bound].opt_arg_index_key.is_some();
 
@@ -1818,13 +1858,13 @@ impl<'a> LoadState<'a> {
                         code,
                         &skeleton.clauses[target_pos].opt_arg_index_key,
                         inner_clause_start,
-                        &mut self.retraction_info,
+                        &mut self.payload.retraction_info,
                     );
 
                     match derelictize_try_me_else(
                         code,
                         inner_clause_start,
-                        &mut self.retraction_info,
+                        &mut self.payload.retraction_info,
                     ) {
                         Some(offset) => {
                             let instr_loc = find_inner_choice_instr(
@@ -1836,20 +1876,20 @@ impl<'a> LoadState<'a> {
                             let clause_loc = blunt_leading_choice_instr(
                                 code,
                                 instr_loc,
-                                &mut self.retraction_info,
+                                &mut self.payload.retraction_info,
                             );
 
                             set_switch_var_offset(
                                 code,
                                 index_loc,
                                 clause_loc - index_loc,
-                                &mut self.retraction_info,
+                                &mut self.payload.retraction_info,
                             );
 
-                            self.retraction_info.push_record(
+                            self.payload.retraction_info.push_record(
                                 RetractionRecord::SkeletonClauseStartReplaced(
-                                    self.compilation_target.clone(),
-                                    key.clone(),
+                                    payload_compilation_target,
+                                    key,
                                     target_pos + 1,
                                     skeleton.clauses[target_pos + 1].clause_start,
                                 ),
@@ -1866,12 +1906,12 @@ impl<'a> LoadState<'a> {
 
                             return finalize_retract(
                                 key,
-                                self.compilation_target.clone(),
+                                payload_compilation_target,
                                 skeleton,
                                 code_index,
                                 target_pos,
                                 index_ptr_opt,
-                                &mut self.retraction_info,
+                                &mut self.payload.retraction_info,
                             );
                         }
                         None => {
@@ -1883,24 +1923,24 @@ impl<'a> LoadState<'a> {
                                     code,
                                     preceding_choice_instr_loc,
                                     skeleton.clauses[target_pos].clause_start - 2,
-                                    &mut self.retraction_info,
+                                    &mut self.payload.retraction_info,
                                 )
                             } else {
                                 remove_leading_unindexed_clause(
                                     code,
                                     skeleton.clauses[target_pos].clause_start - 2,
-                                    &mut self.retraction_info,
+                                    &mut self.payload.retraction_info,
                                 )
                             };
 
                             return finalize_retract(
                                 key,
-                                self.compilation_target.clone(),
+                                payload_compilation_target,
                                 skeleton,
                                 code_index,
                                 target_pos,
                                 index_ptr_opt,
-                                &mut self.retraction_info,
+                                &mut self.payload.retraction_info,
                             );
                         }
                     }
@@ -1938,7 +1978,7 @@ impl<'a> LoadState<'a> {
 
                         match target_indexing_line {
                             Line::IndexingCode(indexing_code) => {
-                                self.retraction_info.push_record(
+                                self.payload.retraction_info.push_record(
                                     RetractionRecord::ReplacedIndexingLine(
                                         target_indexing_loc,
                                         indexing_code,
@@ -1953,7 +1993,7 @@ impl<'a> LoadState<'a> {
                             skeleton,
                             lower_bound,
                             target_pos + 1,
-                            &mut self.retraction_info,
+                            &mut self.payload.retraction_info,
                         );
 
                         merge_indices(
@@ -1961,14 +2001,15 @@ impl<'a> LoadState<'a> {
                             later_indexing_loc,
                             0..target_pos - lower_bound,
                             &mut skeleton.clauses[lower_bound..],
-                            &mut self.retraction_info,
+                            &skeleton.core.retracted_dynamic_clauses,
+                            &mut self.payload.retraction_info,
                         );
 
                         set_switch_var_offset(
                             code,
                             later_indexing_loc,
                             lower_bound_clause_start - later_indexing_loc,
-                            &mut self.retraction_info,
+                            &mut self.payload.retraction_info,
                         );
                     }
                     _ => {
@@ -1977,7 +2018,7 @@ impl<'a> LoadState<'a> {
                             skeleton,
                             lower_bound,
                             target_pos + 1,
-                            &mut self.retraction_info,
+                            &mut self.payload.retraction_info,
                         );
 
                         merge_indices(
@@ -1985,14 +2026,15 @@ impl<'a> LoadState<'a> {
                             target_indexing_loc,
                             target_pos + 1 - lower_bound..skeleton.clauses.len() - lower_bound,
                             &mut skeleton.clauses[lower_bound..],
-                            &mut self.retraction_info,
+                            &skeleton.core.retracted_dynamic_clauses,
+                            &mut self.payload.retraction_info,
                         );
 
                         set_switch_var_offset_to_choice_instr(
                             code,
                             target_indexing_loc,
                             lower_bound_clause_start - target_indexing_loc,
-                            &mut self.retraction_info,
+                            &mut self.payload.retraction_info,
                         );
                     }
                 };
@@ -2005,7 +2047,7 @@ impl<'a> LoadState<'a> {
                         code,
                         &skeleton.clauses[target_pos].opt_arg_index_key,
                         skeleton.clauses[target_pos].clause_start,
-                        &mut self.retraction_info,
+                        &mut self.payload.retraction_info,
                     );
 
                     match skeleton.clauses[target_pos]
@@ -2023,7 +2065,7 @@ impl<'a> LoadState<'a> {
                                 code,
                                 preceding_choice_instr_loc,
                                 skeleton.clauses[target_pos].clause_start,
-                                &mut self.retraction_info,
+                                &mut self.payload.retraction_info,
                             );
 
                             match &mut code[preceding_choice_instr_loc] {
@@ -2032,7 +2074,7 @@ impl<'a> LoadState<'a> {
                                         code,
                                         index_loc,
                                         preceding_choice_instr_loc + 1 - index_loc,
-                                        &mut self.retraction_info,
+                                        &mut self.payload.retraction_info,
                                     );
                                 }
                                 _ => {}
@@ -2052,7 +2094,7 @@ impl<'a> LoadState<'a> {
                                 code,
                                 preceding_choice_instr_loc,
                                 skeleton.clauses[target_pos].clause_start,
-                                &mut self.retraction_info,
+                                &mut self.payload.retraction_info,
                             )
                         }
                     }
@@ -2060,7 +2102,7 @@ impl<'a> LoadState<'a> {
                     remove_leading_unindexed_clause(
                         code,
                         skeleton.clauses[target_pos].clause_start,
-                        &mut self.retraction_info,
+                        &mut self.payload.retraction_info,
                     )
                 }
             }
@@ -2068,17 +2110,17 @@ impl<'a> LoadState<'a> {
 
         finalize_retract(
             key,
-            self.compilation_target.clone(),
+            payload_compilation_target,
             skeleton,
             code_index,
             target_pos,
             index_ptr_opt,
-            &mut self.retraction_info,
+            &mut self.payload.retraction_info,
         )
     }
 }
 
-impl<'a, TS: TermStream> Loader<'a, TS> {
+impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
     pub(super) fn compile_clause_clauses<ClauseIter: Iterator<Item = (Term, Term)>>(
         &mut self,
         key: PredicateKey,
@@ -2089,24 +2131,23 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
         let clause_predicates = clause_clauses.map(|(head, body)| {
             Term::Clause(
                 Cell::default(),
-                clause_name!("$clause"),
-                vec![Box::new(head), Box::new(body)],
-                None,
+                atom!("$clause"),
+                vec![head, body],
             )
         });
 
         let clause_clause_compilation_target = match compilation_target {
-            CompilationTarget::User => CompilationTarget::Module(clause_name!("builtins")),
-            _ => compilation_target.clone(),
+            CompilationTarget::User => CompilationTarget::Module(atom!("builtins")),
+            _ => compilation_target,
         };
 
         let mut num_clause_predicates = 0;
 
         for clause_term in clause_predicates {
-            self.load_state.incremental_compile_clause(
-                (clause_name!("$clause"), 2),
+            self.incremental_compile_clause(
+                (atom!("$clause"), 2),
                 clause_term,
-                clause_clause_compilation_target.clone(),
+                clause_clause_compilation_target,
                 false, // non_counted_bt is false.
                 append_or_prepend,
             )?;
@@ -2115,8 +2156,7 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
         }
 
         let locs_vec: Vec<_> = match self
-            .load_state
-            .wam
+            .wam_prelude
             .indices
             .get_predicate_skeleton_mut(&compilation_target, &key)
         {
@@ -2136,9 +2176,9 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
             }
         };
 
-        match self.load_state.wam.indices.get_predicate_skeleton_mut(
+        match self.wam_prelude.indices.get_predicate_skeleton_mut(
             &clause_clause_compilation_target,
-            &(clause_name!("$clause"), 2),
+            &(atom!("$clause"), 2),
         ) {
             Some(skeleton) if append_or_prepend.is_append() => {
                 for _ in 0..num_clause_predicates {
@@ -2168,6 +2208,7 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
 
     pub(super) fn compile_and_submit(&mut self) -> Result<(), SessionError> {
         let key = self
+            .payload
             .predicates
             .first()
             .and_then(|cl| {
@@ -2176,23 +2217,24 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
             })
             .ok_or(SessionError::NamelessEntry)?;
 
+        let listing_src_file_name = self.listing_src_file_name();
+        let payload_compilation_target = self.payload.compilation_target;
+
         let mut predicate_info = self
-            .load_state
-            .wam
+            .wam_prelude
             .indices
-            .get_predicate_skeleton(&self.predicates.compilation_target, &key)
+            .get_predicate_skeleton(&self.payload.predicates.compilation_target, &key)
             .map(|skeleton| skeleton.predicate_info())
             .unwrap_or_default();
 
         let local_predicate_info = self
-            .load_state
-            .wam
+            .wam_prelude
             .indices
             .get_local_predicate_skeleton(
-                self.load_state.compilation_target.clone(),
-                self.predicates.compilation_target.clone(),
-                self.load_state.listing_src_file_name(),
-                key.clone(),
+                payload_compilation_target,
+                self.payload.predicates.compilation_target,
+                listing_src_file_name,
+                key,
             )
             .map(|skeleton| skeleton.predicate_info())
             .unwrap_or_default();
@@ -2202,54 +2244,55 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
         }
 
         let do_incremental_compile =
-            if self.load_state.compilation_target == self.predicates.compilation_target {
+            if payload_compilation_target == self.payload.predicates.compilation_target {
                 predicate_info.compile_incrementally()
             } else {
                 local_predicate_info.is_multifile && predicate_info.compile_incrementally()
             };
 
-        let predicates_len = self.predicates.len();
-        let non_counted_bt = self.non_counted_bt_preds.contains(&key);
+        let predicates_len = self.payload.predicates.len();
+        let non_counted_bt = self.payload.non_counted_bt_preds.contains(&key);
 
         if do_incremental_compile {
-            for term in self.predicates.predicates.drain(0..) {
-                self.load_state.incremental_compile_clause(
-                    key.clone(),
+            let predicates = self.payload.predicates.take();
+
+            for term in predicates.predicates {
+                self.incremental_compile_clause(
+                    key,
                     term,
-                    self.predicates.compilation_target.clone(),
+                    payload_compilation_target,
                     non_counted_bt,
                     AppendOrPrepend::Append,
                 )?;
             }
         } else {
-            if self.load_state.compilation_target != self.predicates.compilation_target {
+            if payload_compilation_target != self.payload.predicates.compilation_target {
                 if !local_predicate_info.is_extensible {
                     if predicate_info.is_multifile {
                         println!(
                             "Warning: overwriting multifile predicate {}:{}/{} because \
                                   it was not locally declared multifile.",
-                            self.predicates.compilation_target, key.0, key.1
+                            self.payload.predicates.compilation_target, key.0.as_str(), key.1
                         );
                     }
 
                     if let Some(skeleton) = self
-                        .load_state
-                        .wam
+                        .wam_prelude
                         .indices
-                        .remove_predicate_skeleton(&self.predicates.compilation_target, &key)
+                        .remove_predicate_skeleton(&self.payload.predicates.compilation_target, &key)
                     {
                         if predicate_info.is_dynamic {
                             let clause_clause_compilation_target =
-                                match &self.predicates.compilation_target {
+                                match self.payload.predicates.compilation_target {
                                     CompilationTarget::User => {
-                                        CompilationTarget::Module(clause_name!("builtins"))
+                                        CompilationTarget::Module(atom!("builtins"))
                                     }
-                                    module => module.clone(),
+                                    module => module,
                                 };
 
-                            self.load_state.retract_local_clauses_by_locs(
+                            self.retract_local_clauses_by_locs(
                                 clause_clause_compilation_target,
-                                (clause_name!("$clause"), 2),
+                                (atom!("$clause"), 2),
                                 (0..skeleton.clauses.len()).map(Some).collect(),
                                 false, // the builtin M:'$clause'/2 is never dynamic.
                             );
@@ -2257,10 +2300,10 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
                             predicate_info.is_dynamic = false;
                         }
 
-                        self.load_state.retraction_info.push_record(
+                        self.payload.retraction_info.push_record(
                             RetractionRecord::RemovedSkeleton(
-                                self.predicates.compilation_target.clone(),
-                                key.clone(),
+                                payload_compilation_target,
+                                key,
                                 skeleton,
                             ),
                         );
@@ -2270,7 +2313,7 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
 
             let settings = CodeGenSettings {
                 global_clock_tick: if predicate_info.is_dynamic {
-                    Some(self.load_state.wam.machine_st.global_clock)
+                    Some(LS::machine_st(&mut self.payload).global_clock)
                 } else {
                     None
                 },
@@ -2278,20 +2321,19 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
                 non_counted_bt,
             };
 
-            let code_index =
-                self.load_state
-                    .compile(key.clone(), &mut self.predicates, settings)?;
+            let predicates = self.payload.predicates.take();
+            let code_index = self.compile(key, predicates, settings)?;
 
-            if let Some(filename) = self.load_state.listing_src_file_name() {
-                match self.load_state.wam.indices.modules.get_mut(&filename) {
+            if let Some(filename) = self.listing_src_file_name() {
+                match self.wam_prelude.indices.modules.get_mut(&filename) {
                     Some(ref mut module) => {
                         let index_ptr = code_index.get();
-                        let code_index = module.code_dir.entry(key.clone()).or_insert(code_index);
+                        let code_index = module.code_dir.entry(key).or_insert(code_index);
 
                         set_code_index(
-                            &mut self.load_state.retraction_info,
+                            &mut self.payload.retraction_info,
                             &CompilationTarget::Module(filename),
-                            key.clone(),
+                            key,
                             &code_index,
                             index_ptr,
                         );
@@ -2302,13 +2344,14 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
         }
 
         if predicate_info.is_dynamic {
-            self.load_state.wam.machine_st.global_clock += 1;
+            LS::machine_st(&mut self.payload).global_clock += 1;
 
-            let clauses_vec: Vec<_> = self.clause_clauses.drain(0..predicates_len).collect();
+            let clauses_vec: Vec<_> = self.payload
+                .clause_clauses.drain(0..predicates_len).collect();
 
             self.compile_clause_clauses(
                 key,
-                self.predicates.compilation_target.clone(),
+                payload_compilation_target,
                 clauses_vec.into_iter(),
                 AppendOrPrepend::Append,
             )?;
index fdd52c96d194b51026916d955ef55bb426a9cba2..bab1d110b12465ed2249dd4dea75acdb240d25d0 100644 (file)
@@ -1,5 +1,6 @@
-use crate::machine::machine_indices::*;
+use crate::atom_table::*;
 use crate::machine::stack::*;
+use crate::types::*;
 
 use std::mem;
 use std::ops::IndexMut;
@@ -7,20 +8,24 @@ use std::ops::IndexMut;
 type Trail = Vec<(Ref, HeapCellValue)>;
 
 #[derive(Debug, Clone, Copy)]
-pub(crate) enum AttrVarPolicy {
+pub enum AttrVarPolicy {
     DeepCopy,
     StripAttributes,
 }
 
-pub(crate) trait CopierTarget: IndexMut<usize, Output = HeapCellValue> {
-    fn deref(&self, val: Addr) -> Addr;
-    fn push(&mut self, val: HeapCellValue);
+pub trait CopierTarget: IndexMut<usize, Output = HeapCellValue> {
+    fn store(&self, value: HeapCellValue) -> HeapCellValue;
+    fn deref(&self, value: HeapCellValue) -> HeapCellValue;
+    fn push(&mut self, value: HeapCellValue);
     fn stack(&mut self) -> &mut Stack;
-    fn store(&self, val: Addr) -> Addr;
     fn threshold(&self) -> usize;
 }
 
-pub(crate) fn copy_term<T: CopierTarget>(target: T, addr: Addr, attr_var_policy: AttrVarPolicy) {
+pub(crate) fn copy_term<T: CopierTarget>(
+    target: T,
+    addr: HeapCellValue,
+    attr_var_policy: AttrVarPolicy,
+) {
     let mut copy_term_state = CopyTermState::new(target, attr_var_policy);
     copy_term_state.copy_term_impl(addr);
 }
@@ -47,50 +52,51 @@ impl<T: CopierTarget> CopyTermState<T> {
 
     #[inline]
     fn value_at_scan(&mut self) -> &mut HeapCellValue {
-        let scan = self.scan;
-        &mut self.target[scan]
+        &mut self.target[self.scan]
     }
 
     fn trail_list_cell(&mut self, addr: usize, threshold: usize) {
-        let trail_item = mem::replace(
-            &mut self.target[addr],
-            HeapCellValue::Addr(Addr::Lis(threshold)),
-        );
-
-        self.trail.push((Ref::HeapCell(addr), trail_item));
+        let trail_item = mem::replace(&mut self.target[addr], list_loc_as_cell!(threshold));
+        self.trail.push((Ref::heap_cell(addr), trail_item));
     }
 
     fn copy_list(&mut self, addr: usize) {
         for offset in 0..2 {
-            if let Addr::Lis(h) = self.target[addr + offset].as_addr(addr + offset) {
-                if h >= self.old_h {
-                    *self.value_at_scan() = HeapCellValue::Addr(Addr::Lis(h));
-                    self.scan += 1;
+            read_heap_cell!(self.target[addr + offset],
+                (HeapCellValueTag::Lis, h) => {
+                    if h >= self.old_h {
+                        *self.value_at_scan() = list_loc_as_cell!(h);
+                        self.scan += 1;
 
-                    return;
+                        return;
+                    }
                 }
-            }
+                _ => {
+                }
+            )
         }
 
         let threshold = self.target.threshold();
 
-        *self.value_at_scan() = HeapCellValue::Addr(Addr::Lis(threshold));
+        *self.value_at_scan() = list_loc_as_cell!(threshold);
 
         for i in 0..2 {
-            let hcv = self.target[addr + i].context_free_clone();
+            let hcv = self.target[addr + i];
             self.target.push(hcv);
         }
 
         let cdr = self
             .target
-            .store(self.target.deref(Addr::HeapCell(addr + 1)));
+            .store(self.target.deref(heap_loc_as_cell!(addr + 1)));
 
-        if !cdr.is_ref() {
+        if !cdr.is_var() {
             self.trail_list_cell(addr + 1, threshold);
         } else {
-            let car = self.target.store(self.target.deref(Addr::HeapCell(addr)));
+            let car = self
+                .target
+                .store(self.target.deref(heap_loc_as_cell!(addr)));
 
-            if !car.is_ref() {
+            if !car.is_var() {
                 self.trail_list_cell(addr, threshold);
             }
         }
@@ -98,187 +104,208 @@ impl<T: CopierTarget> CopyTermState<T> {
         self.scan += 1;
     }
 
-    fn copy_partial_string(&mut self, addr: usize, n: usize) {
-        if let &HeapCellValue::Addr(Addr::PStrLocation(h, _)) = &self.target[addr] {
-            if h >= self.old_h {
-                *self.value_at_scan() = HeapCellValue::Addr(Addr::PStrLocation(h, n));
-                self.scan += 1;
+    fn copy_partial_string(&mut self, scan_tag: HeapCellValueTag, pstr_loc: usize) {
+        read_heap_cell!(self.target[pstr_loc],
+            (HeapCellValueTag::PStrLoc, h) => {
+                if h >= self.old_h {
+                    *self.value_at_scan() = match scan_tag {
+                        HeapCellValueTag::PStrLoc => {
+                            pstr_loc_as_cell!(h)
+                        }
+                        tag => {
+                            debug_assert!(tag == HeapCellValueTag::PStrOffset);
+                            pstr_offset_as_cell!(h)
+                        }
+                    };
 
-                return;
+                    self.scan += 1;
+                    return;
+                }
             }
-        }
+            _ => {}
+        );
 
         let threshold = self.target.threshold();
 
-        *self.value_at_scan() = HeapCellValue::Addr(Addr::PStrLocation(threshold, n));
-
+        *self.value_at_scan() = pstr_loc_as_cell!(threshold);
         self.scan += 1;
 
-        let (pstr, has_tail) = match &self.target[addr] {
-            &HeapCellValue::PartialString(ref pstr, has_tail) => {
-                (pstr.clone_from_offset(0), has_tail)
-            }
-            _ => {
-                unreachable!()
-            }
-        };
-
-        self.target
-            .push(HeapCellValue::PartialString(pstr, has_tail));
-
-        let replacement = HeapCellValue::Addr(Addr::PStrLocation(threshold, n));
-
-        let trail_item = mem::replace(&mut self.target[addr], replacement);
+        self.target.push(self.target[pstr_loc]);
 
-        self.trail.push((Ref::HeapCell(addr), trail_item));
+        let replacement = pstr_loc_as_cell!(threshold);
+        let trail_item = mem::replace(&mut self.target[pstr_loc], replacement);
 
-        if has_tail {
-            let tail_addr = self.target[addr + 1].as_addr(addr + 1);
-            self.target.push(HeapCellValue::Addr(tail_addr));
-        }
+        self.trail.push((Ref::heap_cell(pstr_loc), trail_item));
+        self.target.push(self.target[pstr_loc + 1]);
     }
 
-    fn reinstantiate_var(&mut self, addr: Addr, frontier: usize) {
-        match addr {
-            Addr::HeapCell(h) => {
-                self.target[frontier] = HeapCellValue::Addr(Addr::HeapCell(frontier));
-                self.target[h] = HeapCellValue::Addr(Addr::HeapCell(frontier));
+    fn reinstantiate_var(&mut self, addr: HeapCellValue, frontier: usize) {
+        read_heap_cell!(addr,
+            (HeapCellValueTag::Var, h) => {
+                self.target[frontier] = heap_loc_as_cell!(frontier);
+                self.target[h] = heap_loc_as_cell!(frontier);
 
-                self.trail
-                    .push((Ref::HeapCell(h), HeapCellValue::Addr(Addr::HeapCell(h))));
+                self.trail.push((Ref::heap_cell(h), heap_loc_as_cell!(h)));
             }
-            Addr::StackCell(fr, sc) => {
-                self.target[frontier] = HeapCellValue::Addr(Addr::HeapCell(frontier));
-                self.target.stack().index_and_frame_mut(fr)[sc] = Addr::HeapCell(frontier);
-
-                self.trail.push((
-                    Ref::StackCell(fr, sc),
-                    HeapCellValue::Addr(Addr::StackCell(fr, sc)),
-                ));
+            (HeapCellValueTag::StackVar, s) => {
+                self.target[frontier] = heap_loc_as_cell!(frontier);
+                self.target.stack()[s] = heap_loc_as_cell!(frontier);
+
+                self.trail.push((Ref::stack_cell(s), stack_loc_as_cell!(s)));
             }
-            Addr::AttrVar(h) => {
+            (HeapCellValueTag::AttrVar, h) => {
                 let threshold = if let AttrVarPolicy::DeepCopy = self.attr_var_policy {
                     self.target.threshold()
                 } else {
                     frontier
                 };
 
-                self.target[frontier] = HeapCellValue::Addr(Addr::HeapCell(threshold));
-                self.target[h] = HeapCellValue::Addr(Addr::HeapCell(threshold));
+                self.target[frontier] = heap_loc_as_cell!(threshold);
+                self.target[h] = heap_loc_as_cell!(threshold);
 
-                self.trail
-                    .push((Ref::AttrVar(h), HeapCellValue::Addr(Addr::AttrVar(h))));
+                self.trail.push((Ref::attr_var(h), attr_var_as_cell!(h)));
 
                 if let AttrVarPolicy::DeepCopy = self.attr_var_policy {
                     self.target
-                        .push(HeapCellValue::Addr(Addr::AttrVar(threshold)));
+                        .push(attr_var_as_cell!(threshold));
 
-                    let list_val = self.target[h + 1].context_free_clone();
+                    let list_val = self.target[h + 1];
                     self.target.push(list_val);
                 }
             }
             _ => {
                 unreachable!()
             }
+        );
+    }
+
+    fn copy_var(&mut self, addr: HeapCellValue) {
+        let rd = self.target.deref(addr);
+        let ra = self.target.store(rd);
+
+        read_heap_cell!(ra,
+            (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
+                if h >= self.old_h {
+                    *self.value_at_scan() = rd;
+                    self.scan += 1;
+
+                    return;
+                }
+            }
+            _ => {}
+        );
+
+        if addr == ra {
+            self.reinstantiate_var(addr, self.scan);
+            self.scan += 1;
+        } else {
+            *self.value_at_scan() = ra;
+            // self.copy_compound(rd, ra);
         }
     }
 
-    fn copy_var(&mut self, addr: Addr) {
-        let rd = self.target.store(self.target.deref(addr));
+    /*
+    fn copy_compound(&mut self, rd: HeapCellValue, ra: HeapCellValue) {
+        let h = rd.get_value();
+        let trail_item = self.target[h];
+        let threshold = self.target.threshold();
+
+        self.trail.push((Ref::heap_cell(h), trail_item));
+        self.target[self.scan].set_value(threshold);
+
+        read_heap_cell!(ra,
+            (HeapCellValueTag::Atom, (_name, arity)) => {
+                self.target.push(ra);
+
+                for i in 0..arity {
+                    self.target.push(self.target[h + 1 + i]);
+                }
+
+                self.target[h] = str_loc_as_cell!(self.scan + 1);
+            }
+            (HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset) => {
+                self.target.push(ra);
+                self.target.push(self.target[h + 1]);
 
-        match rd {
-            Addr::AttrVar(h) | Addr::HeapCell(h) if h >= self.old_h => {
-                *self.value_at_scan() = HeapCellValue::Addr(rd);
-                self.scan += 1;
+                self.target[h] = pstr_loc_as_cell!(self.scan + 1);
             }
-            _ if addr == rd => {
-                self.reinstantiate_var(addr, self.scan);
-                self.scan += 1;
+            (HeapCellValueTag::CStr, cstr_atom) => {
+                self.target[h] = atom_as_cstr_cell!(cstr_atom);
+            }
+            (HeapCellValueTag::Str, s) => {
+                self.copy_structure(s);
+                return;
             }
             _ => {
-                *self.value_at_scan() = HeapCellValue::Addr(rd);
+                *self.value_at_scan() = rd;
+                self.trail.pop();
+                return;
             }
-        }
+        );
+
+        self.scan += 1;
     }
+    */
 
     fn copy_structure(&mut self, addr: usize) {
-        match self.target[addr].context_free_clone() {
-            HeapCellValue::NamedStr(arity, name, fixity) => {
+        read_heap_cell!(self.target[addr],
+            (HeapCellValueTag::Atom, (name, arity)) => {
                 let threshold = self.target.threshold();
 
-                *self.value_at_scan() = HeapCellValue::Addr(Addr::Str(threshold));
+                *self.value_at_scan() = str_loc_as_cell!(threshold);
 
                 let trail_item = mem::replace(
                     &mut self.target[addr],
-                    HeapCellValue::Addr(Addr::Str(threshold)),
+                    str_loc_as_cell!(threshold),
                 );
 
-                self.trail.push((Ref::HeapCell(addr), trail_item));
-
-                self.target
-                    .push(HeapCellValue::NamedStr(arity, name, fixity));
+                self.trail.push((Ref::heap_cell(addr), trail_item));
+                self.target.push(atom_as_cell!(name, arity));
 
                 for i in 0..arity {
-                    let hcv = self.target[addr + 1 + i].context_free_clone();
+                    let hcv = self.target[addr + 1 + i];
                     self.target.push(hcv);
                 }
             }
-            HeapCellValue::Addr(Addr::Str(addr)) => {
-                *self.value_at_scan() = HeapCellValue::Addr(Addr::Str(addr))
+            (HeapCellValueTag::Str, h) => {
+                *self.value_at_scan() = str_loc_as_cell!(h);
             }
             _ => {
                 unreachable!()
             }
-        }
+        );
 
         self.scan += 1;
     }
 
-    fn copy_term_impl(&mut self, addr: Addr) {
+    fn copy_term_impl(&mut self, addr: HeapCellValue) {
         self.scan = self.target.threshold();
-        self.target.push(HeapCellValue::Addr(addr));
+        self.target.push(addr);
 
         while self.scan < self.target.threshold() {
-            match self.value_at_scan() {
-                &mut HeapCellValue::Addr(addr) => match addr {
-                    Addr::Con(h) => {
-                        let addr = self.target[h].as_addr(h);
-
-                        if addr == Addr::Con(h) {
-                            *self.value_at_scan() = self.target[h].context_free_clone();
-                        } else {
-                            *self.value_at_scan() = HeapCellValue::Addr(addr);
-                        }
-                    }
-                    Addr::Lis(h) => {
-                        if h >= self.old_h {
-                            self.scan += 1;
-                        } else {
-                            self.copy_list(h);
-                        }
-                    }
-                    addr @ Addr::AttrVar(_)
-                    | addr @ Addr::HeapCell(_)
-                    | addr @ Addr::StackCell(..) => {
-                        self.copy_var(addr);
-                    }
-                    Addr::Str(addr) => {
-                        self.copy_structure(addr);
-                    }
-                    Addr::PStrLocation(addr, n) => {
-                        self.copy_partial_string(addr, n);
-                    }
-                    Addr::Stream(h) => {
-                        *self.value_at_scan() = self.target[h].context_free_clone();
-                    }
-                    _ => {
+            let addr = *self.value_at_scan();
+
+            read_heap_cell!(addr,
+                (HeapCellValueTag::Lis, h) => {
+                    if h >= self.old_h {
                         self.scan += 1;
+                    } else {
+                        self.copy_list(h);
                     }
-                },
+                }
+                (HeapCellValueTag::AttrVar | HeapCellValueTag::Var) => {
+                    self.copy_var(addr);
+                }
+                (HeapCellValueTag::Str, h) => {
+                    self.copy_structure(h);
+                }
+                (HeapCellValueTag::PStrLoc | HeapCellValueTag::PStrOffset, pstr_loc) => {
+                    self.copy_partial_string(addr.get_tag(), pstr_loc);
+                }
                 _ => {
                     self.scan += 1;
                 }
-            }
+            );
         }
 
         self.unwind_trail();
@@ -286,12 +313,117 @@ impl<T: CopierTarget> CopyTermState<T> {
 
     fn unwind_trail(&mut self) {
         for (r, value) in self.trail.drain(0..) {
-            match r {
-                Ref::AttrVar(h) | Ref::HeapCell(h) => self.target[h] = value,
-                Ref::StackCell(fr, sc) => {
-                    self.target.stack().index_and_frame_mut(fr)[sc] = value.as_addr(0)
-                }
+            let index = r.get_value() as usize;
+
+            match r.get_tag() {
+                RefTag::AttrVar | RefTag::HeapCell => self.target[index] = value,
+                RefTag::StackCell => self.target.stack()[index] = value,
             }
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::machine::mock_wam::*;
+
+    #[test]
+    fn copier_tests() {
+        let mut wam = MockWAM::new();
+
+        let f_atom = atom!("f");
+        let a_atom = atom!("a");
+        let b_atom = atom!("b");
+
+        wam.machine_st.heap
+           .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)]));
+
+        assert_eq!(wam.machine_st.heap[0], atom_as_cell!(f_atom, 2));
+        assert_eq!(wam.machine_st.heap[1], atom_as_cell!(a_atom));
+        assert_eq!(wam.machine_st.heap[2], atom_as_cell!(b_atom));
+
+        {
+            let wam = TermCopyingMockWAM { wam: &mut wam };
+            copy_term(wam, str_loc_as_cell!(0), AttrVarPolicy::DeepCopy);
+        }
+
+        // check that the original heap state is still intact.
+        assert_eq!(wam.machine_st.heap[0], atom_as_cell!(f_atom, 2));
+        assert_eq!(wam.machine_st.heap[1], atom_as_cell!(a_atom));
+        assert_eq!(wam.machine_st.heap[2], atom_as_cell!(b_atom));
+
+        assert_eq!(wam.machine_st.heap[3], str_loc_as_cell!(4));
+        assert_eq!(wam.machine_st.heap[4], atom_as_cell!(f_atom, 2));
+        assert_eq!(wam.machine_st.heap[5], atom_as_cell!(a_atom));
+        assert_eq!(wam.machine_st.heap[6], atom_as_cell!(b_atom));
+
+        wam.machine_st.heap.clear();
+
+        let pstr_var_cell = put_partial_string(&mut wam.machine_st.heap, "abc ", &mut wam.machine_st.atom_tbl);
+        let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize];
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(pstr_loc_as_cell!(2));
+
+        let pstr_second_var_cell = put_partial_string(&mut wam.machine_st.heap, "def", &mut wam.machine_st.atom_tbl);
+        let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize];
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(pstr_loc_as_cell!(wam.machine_st.heap.len() + 1));
+
+        wam.machine_st.heap.push(pstr_offset_as_cell!(0));
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0i64)));
+
+        {
+            let wam = TermCopyingMockWAM { wam: &mut wam };
+            copy_term(wam, pstr_loc_as_cell!(0), AttrVarPolicy::DeepCopy);
+        }
+
+        print_heap_terms(wam.machine_st.heap[6..].iter(), 6);
+
+        assert_eq!(wam.machine_st.heap[0], pstr_cell);
+        assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(2));
+        assert_eq!(wam.machine_st.heap[2], pstr_second_cell);
+        assert_eq!(wam.machine_st.heap[3], pstr_loc_as_cell!(4));
+        assert_eq!(wam.machine_st.heap[4], pstr_offset_as_cell!(0));
+        assert_eq!(wam.machine_st.heap[5], fixnum_as_cell!(Fixnum::build_with(0i64)));
+
+        assert_eq!(wam.machine_st.heap[7], pstr_cell);
+        assert_eq!(wam.machine_st.heap[8], pstr_loc_as_cell!(9));
+        assert_eq!(wam.machine_st.heap[9], pstr_second_cell);
+        assert_eq!(wam.machine_st.heap[10], pstr_loc_as_cell!(11));
+        assert_eq!(wam.machine_st.heap[11], pstr_offset_as_cell!(7));
+        assert_eq!(wam.machine_st.heap[12], fixnum_as_cell!(Fixnum::build_with(0i64)));
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.extend(functor!(
+            f_atom,
+            [
+                atom(a_atom),
+                atom(b_atom),
+                atom(a_atom),
+                cell(str_loc_as_cell!(0))
+            ]
+        ));
+
+        {
+            let wam = TermCopyingMockWAM { wam: &mut wam };
+            copy_term(wam, str_loc_as_cell!(0), AttrVarPolicy::DeepCopy);
+        }
+
+        assert_eq!(wam.machine_st.heap[0], atom_as_cell!(f_atom, 4));
+        assert_eq!(wam.machine_st.heap[1], atom_as_cell!(a_atom));
+        assert_eq!(wam.machine_st.heap[2], atom_as_cell!(b_atom));
+        assert_eq!(wam.machine_st.heap[3], atom_as_cell!(a_atom));
+        assert_eq!(wam.machine_st.heap[4], str_loc_as_cell!(0));
+
+        assert_eq!(wam.machine_st.heap[5], str_loc_as_cell!(6));
+        assert_eq!(wam.machine_st.heap[6], atom_as_cell!(f_atom, 4));
+        assert_eq!(wam.machine_st.heap[7], atom_as_cell!(a_atom));
+        assert_eq!(wam.machine_st.heap[8], atom_as_cell!(b_atom));
+        assert_eq!(wam.machine_st.heap[9], atom_as_cell!(a_atom));
+        assert_eq!(wam.machine_st.heap[10], str_loc_as_cell!(6));
+    }
+}
diff --git a/src/machine/gc.rs b/src/machine/gc.rs
new file mode 100644 (file)
index 0000000..1b7f83f
--- /dev/null
@@ -0,0 +1,1101 @@
+use crate::atom_table::*;
+use crate::machine::heap::*;
+use crate::types::*;
+
+use core::marker::PhantomData;
+
+// TODO: rename to 'unmark_if_iter', 'mark_if_gc'
+pub(crate) trait UnmarkPolicy {
+    fn unmark(heap: &mut [HeapCellValue], current: usize) -> bool;
+    fn mark(heap: &mut [HeapCellValue], current: usize);
+    fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter<Self>) -> Option<HeapCellValue>
+    where
+        Self: Sized;
+}
+
+pub(crate) struct IteratorUMP;
+
+impl UnmarkPolicy for IteratorUMP {
+    #[inline(always)]
+    fn unmark(heap: &mut [HeapCellValue], current: usize) -> bool {
+        heap[current].set_mark_bit(false);
+        false
+    }
+
+    #[inline(always)]
+    fn mark(_heap: &mut [HeapCellValue], _current: usize) {}
+
+    #[inline(always)]
+    fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter<Self>) -> Option<HeapCellValue> {
+        iter.forward_var()
+    }
+}
+
+struct MarkerUMP {}
+
+impl UnmarkPolicy for MarkerUMP {
+    #[inline(always)]
+    fn unmark(_heap: &mut [HeapCellValue], _current: usize) -> bool { true }
+
+    #[inline(always)]
+    fn mark(heap: &mut [HeapCellValue], current: usize) {
+        heap[current].set_mark_bit(true);
+    }
+
+    #[inline(always)]
+    fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter<Self>) -> Option<HeapCellValue> {
+        if iter.heap[iter.current + 1].get_mark_bit() {
+            return iter.forward_var();
+        }
+
+        let temp = iter.heap[iter.current].get_value();
+
+        iter.heap[iter.current].set_value(iter.next);
+        iter.current += 1;
+
+        iter.next = iter.heap[iter.current].get_value();
+
+        iter.heap[iter.current].set_value(temp);
+        iter.heap[iter.current].set_mark_bit(true);
+
+        None
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct StacklessPreOrderHeapIter<'a, UMP: UnmarkPolicy> {
+    pub(crate) heap: &'a mut Vec<HeapCellValue>,
+    orig_heap_len: usize,
+    start: usize,
+    current: usize,
+    next: usize,
+    _marker: PhantomData<UMP>,
+}
+
+impl<'a, UMP: UnmarkPolicy> Drop for StacklessPreOrderHeapIter<'a, UMP> {
+    fn drop(&mut self) {
+        if self.current == self.start {
+            self.heap.truncate(self.orig_heap_len);
+            return;
+        }
+
+        while !self.backward() {}
+
+        self.heap.truncate(self.orig_heap_len);
+    }
+}
+
+impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> {
+    pub(crate) fn new(heap: &'a mut Vec<HeapCellValue>, cell: HeapCellValue) -> Self {
+        let orig_heap_len = heap.len();
+        let start = orig_heap_len + 1;
+
+        heap.push(cell);
+        heap.push(heap_loc_as_cell!(orig_heap_len));
+
+        heap[start].set_mark_bit(true);
+        let next = heap[start].get_value();
+
+        Self {
+            heap,
+            orig_heap_len,
+            start,
+            current: start,
+            next,
+            _marker: PhantomData,
+        }
+    }
+
+    fn backward_and_return(&mut self) -> Option<HeapCellValue> {
+        let current = self.current;
+
+        if self.backward() {
+            self.heap[self.current].set_forwarding_bit(true);
+            self.heap[self.current].set_mark_bit(true);
+        }
+
+        Some(self.heap[current])
+    }
+
+    fn forward_var(&mut self) -> Option<HeapCellValue> {
+        if self.heap[self.next].get_mark_bit() {
+            return self.backward_and_return();
+        }
+
+        self.heap[self.current].set_forwarding_bit(true);
+
+        if self.heap[self.next].get_forwarding_bit() == Some(true) {
+            return self.backward_and_return();
+        }
+
+        let temp = self.heap[self.next].get_value();
+
+        self.heap[self.next].set_value(self.current);
+        self.current = self.next;
+        self.next = temp;
+
+        None
+    }
+
+    fn forward(&mut self) -> Option<HeapCellValue> {
+        loop {
+            if self.heap[self.current].get_forwarding_bit() != Some(true) {
+                match self.heap[self.current].get_tag() {
+                    HeapCellValueTag::AttrVar => {
+                        if let Some(cell) = UMP::forward_attr_var(self) { return Some(cell); }
+                    }
+                    HeapCellValueTag::Var => {
+                        if let Some(cell) = self.forward_var() { return Some(cell); }
+                    }
+                    HeapCellValueTag::Str => {
+                        if self.heap[self.next + 1].get_mark_bit() {
+                            return self.backward_and_return();
+                        }
+
+                        let h = self.next;
+                        let cell = self.heap[h];
+
+                        self.heap[h].set_forwarding_bit(true);
+                        self.heap[self.current].set_forwarding_bit(true);
+
+                        let arity = cell_as_atom_cell!(self.heap[h]).get_arity();
+
+                        for cell in &mut self.heap[h + 1 .. h + arity + 1] {
+                            cell.set_mark_bit(true);
+                        }
+
+                        let last_cell_loc = h + arity;
+
+                        self.next = self.heap[last_cell_loc].get_value();
+                        self.heap[last_cell_loc].set_value(self.current);
+                        self.current = last_cell_loc;
+
+                        return Some(cell);
+                    }
+                    HeapCellValueTag::Lis => {
+                        if self.heap[self.next + 1].get_mark_bit() {
+                            return self.backward_and_return();
+                        }
+
+                        self.heap[self.current].set_forwarding_bit(true);
+                        self.heap[self.next+1].set_mark_bit(true);
+
+                        let last_cell_loc = self.next + 1;
+
+                        self.next = self.heap[last_cell_loc].get_value();
+                        self.heap[last_cell_loc].set_value(self.current);
+                        self.current = last_cell_loc;
+
+                        return Some(list_loc_as_cell!(last_cell_loc - 1));
+                    }
+                    HeapCellValueTag::PStrLoc => {
+                        let h = self.next;
+                        let cell = self.heap[h];
+
+                        self.heap[self.current].set_forwarding_bit(true);
+
+                        if self.heap[h+1].get_mark_bit() {
+                            return self.backward_and_return();
+                        }
+
+                        if self.heap[h].get_tag() == HeapCellValueTag::PStr {
+                            self.heap[h+1].set_mark_bit(true);
+
+                            self.next = self.heap[h+1].get_value();
+                            self.heap[h+1].set_value(self.current);
+                            self.heap[h].set_forwarding_bit(true);
+
+                            self.current = h+1;
+                        } else {
+                            debug_assert!(self.heap[h].get_tag() == HeapCellValueTag::PStrOffset);
+
+                            self.next = self.heap[h].get_value();
+                            self.heap[h].set_value(self.current);
+                            self.current = h;
+
+                            if self.heap[h].get_forwarding_bit() == Some(true) {
+                                continue;
+                            }
+                        }
+
+                        return Some(cell);
+                    }
+                    HeapCellValueTag::PStrOffset => {
+                        let h = self.next;
+
+                        UMP::mark(self.heap, self.current+1);
+
+                        if self.heap[h].get_tag() == HeapCellValueTag::PStr {
+                            if self.heap[h+1].get_mark_bit() {
+                                return self.backward_and_return();
+                            }
+
+                            self.heap[h+1].set_mark_bit(true);
+                            self.heap[self.current].set_forwarding_bit(true);
+
+                            self.next = self.heap[h+1].get_value();
+                            self.heap[h+1].set_value(self.current);
+                            self.current = h+1;
+                        } else {
+                            debug_assert!(self.heap[h].get_tag() == HeapCellValueTag::CStr);
+
+                            self.next = self.heap[h].get_value();
+                            self.heap[h].set_value(self.current);
+                            self.current = h;
+                        }
+                    }
+                    HeapCellValueTag::StackVar => {
+                        let cell = self.heap[self.current];
+                        self.heap[self.current].set_forwarding_bit(true);
+                        return Some(cell);
+                    }
+                    _ => {
+                        if self.heap[self.current].get_mark_bit() {
+                            let current = self.current;
+
+                            if self.backward() {
+                                return None;
+                            }
+
+                            return Some(self.heap[current]);
+                        }
+
+                        return self.backward_and_return();
+                    }
+                }
+            } else {
+                if self.backward() {
+                    return None;
+                }
+            }
+        }
+    }
+
+    fn backward(&mut self) -> bool {
+        while !self.heap[self.current].get_mark_bit() {
+            let temp = self.heap[self.current].get_value();
+
+            UMP::mark(self.heap, self.current);
+
+            self.heap[self.current].set_forwarding_bit(false);
+            self.heap[self.current].set_value(self.next);
+
+            self.next = self.current;
+            self.current = temp;
+        }
+
+        self.heap[self.current].set_forwarding_bit(false);
+
+        let unmark_is_no_op = UMP::unmark(self.heap, self.current);
+
+        if self.current == self.start {
+            return true;
+        }
+
+        if unmark_is_no_op { // if true, the marker is running.
+            let cell = self.heap[self.current];
+
+            // a cyclic root must be handled specially when marking.
+            if self.next >= self.orig_heap_len && cell.is_ref() {
+                debug_assert!(cell.get_tag() != HeapCellValueTag::PStrOffset);
+
+                self.heap[self.current].set_mark_bit(false);
+                let prev_current = self.heap[self.current].get_value();
+
+                if !self.heap[prev_current].is_forwarded() {
+                    return self.backward();
+                }
+            }
+        }
+
+        self.current -= 1;
+
+        let temp = self.heap[self.current+1].get_value();
+
+        self.heap[self.current+1].set_value(self.next);
+        self.next = self.heap[self.current].get_value();
+        self.heap[self.current].set_value(temp);
+
+        false
+    }
+}
+
+impl<'a, UMP: UnmarkPolicy> Iterator for StacklessPreOrderHeapIter<'a, UMP> {
+    type Item = HeapCellValue;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        self.forward()
+    }
+}
+
+pub fn mark_cells(heap: &mut Heap, cell: HeapCellValue) {
+    let mut iter = StacklessPreOrderHeapIter::<MarkerUMP>::new(heap, cell);
+    while let Some(_) = iter.forward() {}
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::machine::mock_wam::*;
+
+    #[test]
+    fn heap_marking_tests() {
+        let mut wam = MockWAM::new();
+
+        let f_atom = atom!("f");
+        let a_atom = atom!("a");
+        let b_atom = atom!("b");
+
+        wam.machine_st
+           .heap
+           .extend(functor!(f_atom, [atom(a_atom), atom(b_atom)]));
+
+        mark_cells(&mut wam.machine_st.heap, str_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), atom_as_cell!(f_atom, 2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(a_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), atom_as_cell!(b_atom));
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.extend(functor!(
+            f_atom,
+            [
+                atom(a_atom),
+                atom(b_atom),
+                atom(a_atom),
+                cell(str_loc_as_cell!(0))
+            ]
+        ));
+
+        mark_cells(&mut wam.machine_st.heap, str_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), atom_as_cell!(f_atom, 4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(a_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), atom_as_cell!(b_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), atom_as_cell!(a_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), str_loc_as_cell!(0));
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        // make the structure doubly cyclic.
+        wam.machine_st.heap[2] = str_loc_as_cell!(0);
+
+        mark_cells(&mut wam.machine_st.heap, str_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(str_loc_as_cell!(1));
+
+        wam.machine_st.heap.extend(functor!(
+            f_atom,
+            [
+                atom(a_atom),
+                atom(b_atom),
+                atom(a_atom),
+                cell(str_loc_as_cell!(1))
+            ]
+        ));
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), str_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(f_atom, 4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), atom_as_cell!(a_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), atom_as_cell!(b_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), atom_as_cell!(a_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), str_loc_as_cell!(1));
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(heap_loc_as_cell!(0));
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), heap_loc_as_cell!(0));
+
+        wam.machine_st.heap.clear();
+
+        // term is: [a, b]
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(atom_as_cell!(a_atom));
+        wam.machine_st.heap.push(list_loc_as_cell!(3));
+        wam.machine_st.heap.push(atom_as_cell!(b_atom));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), list_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(a_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), list_loc_as_cell!(3));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), atom_as_cell!(b_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), empty_list_as_cell!());
+
+        wam.machine_st.heap.pop();
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        // now make the list cyclic.
+        wam.machine_st.heap.push(heap_loc_as_cell!(0));
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), list_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(a_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), list_loc_as_cell!(3));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), atom_as_cell!(b_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), heap_loc_as_cell!(0));
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        // make the list doubly cyclic.
+        wam.machine_st.heap[3] = heap_loc_as_cell!(0);
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        // term is: [a, <stream ptr>]
+        let stream = Stream::from_static_string("test", &mut wam.machine_st.arena);
+        let stream_cell =
+            HeapCellValue::from(ConsPtr::build_with(stream.as_ptr(), ConsPtrMaskTag::Cons));
+
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(atom_as_cell!(a_atom));
+        wam.machine_st.heap.push(list_loc_as_cell!(3));
+        wam.machine_st.heap.push(stream_cell);
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), list_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(a_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), list_loc_as_cell!(3));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), stream_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), empty_list_as_cell!());
+
+        wam.machine_st.heap.clear();
+
+        // now a cycle of variables.
+
+        wam.machine_st.heap.push(heap_loc_as_cell!(1));
+        wam.machine_st.heap.push(heap_loc_as_cell!(2));
+        wam.machine_st.heap.push(heap_loc_as_cell!(3));
+        wam.machine_st.heap.push(heap_loc_as_cell!(0));
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), heap_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), heap_loc_as_cell!(2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), heap_loc_as_cell!(3));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), heap_loc_as_cell!(0));
+
+        wam.machine_st.heap.clear();
+
+        // first a 'dangling' partial string, later modified to be a
+        // two-part complete string, then a three-part cyclic string
+        // involving an uncompacted list of chars.
+
+        let pstr_var_cell = put_partial_string(&mut wam.machine_st.heap, "abc ", &mut wam.machine_st.atom_tbl);
+        let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize];
+
+        mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), heap_loc_as_cell!(1));
+
+        wam.machine_st.heap.pop();
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        wam.machine_st.heap.push(pstr_loc_as_cell!(2));
+
+        let pstr_second_var_cell = put_partial_string(&mut wam.machine_st.heap, "def", &mut wam.machine_st.atom_tbl);
+        let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize];
+
+        mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_loc_as_cell!(2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), heap_loc_as_cell!(3));
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(pstr_loc_as_cell!(4));
+        wam.machine_st.heap.push(pstr_offset_as_cell!(0));
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(2)));
+
+        mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(4));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_loc_as_cell!(2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_offset_as_cell!(0));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), fixnum_as_cell!(Fixnum::build_with(2)));
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(3));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_loc_as_cell!(2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_offset_as_cell!(0));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), fixnum_as_cell!(Fixnum::build_with(2)));
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(2));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_loc_as_cell!(2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_offset_as_cell!(0));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), fixnum_as_cell!(Fixnum::build_with(2)));
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(1));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_loc_as_cell!(2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_offset_as_cell!(0));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), fixnum_as_cell!(Fixnum::build_with(2)));
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_loc_as_cell!(2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_offset_as_cell!(0));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), fixnum_as_cell!(Fixnum::build_with(2)));
+
+        wam.machine_st.heap.truncate(4);
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        wam.machine_st.heap.push(atom_as_cell!(atom!("irrelevant stuff")));
+        wam.machine_st.heap.push(pstr_offset_as_cell!(0));
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(2)));
+
+        wam.machine_st.heap[3] = pstr_loc_as_cell!(5);
+
+        mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(5));
+
+        assert!(wam.machine_st.heap[0].get_mark_bit());
+        assert!(wam.machine_st.heap[1].get_mark_bit());
+        assert!(wam.machine_st.heap[2].get_mark_bit());
+        assert!(wam.machine_st.heap[3].get_mark_bit());
+        assert!(!wam.machine_st.heap[4].get_mark_bit());
+        assert!(wam.machine_st.heap[5].get_mark_bit());
+        assert!(wam.machine_st.heap[6].get_mark_bit());
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_loc_as_cell!(2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_second_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), pstr_loc_as_cell!(5));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), pstr_offset_as_cell!(0));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[6]), fixnum_as_cell!(Fixnum::build_with(2)));
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(atom_as_cell!(atom!("irrelevant stuff")));
+        wam.machine_st.heap.push(pstr_cell);
+        wam.machine_st.heap.push(pstr_loc_as_cell!(4));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("irrelevant stuff")));
+        wam.machine_st.heap.push(pstr_second_cell);
+        wam.machine_st.heap.push(pstr_loc_as_cell!(7));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("irrelevant stuff")));
+        wam.machine_st.heap.push(pstr_offset_as_cell!(1));
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(2)));
+
+        mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(7));
+
+        assert!(!wam.machine_st.heap[0].get_mark_bit());
+        assert!(wam.machine_st.heap[1].get_mark_bit());
+        assert!(wam.machine_st.heap[2].get_mark_bit());
+        assert!(!wam.machine_st.heap[3].get_mark_bit());
+        assert!(wam.machine_st.heap[4].get_mark_bit());
+        assert!(wam.machine_st.heap[5].get_mark_bit());
+        assert!(!wam.machine_st.heap[6].get_mark_bit());
+        assert!(wam.machine_st.heap[7].get_mark_bit());
+        assert!(wam.machine_st.heap[8].get_mark_bit());
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_second_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), pstr_loc_as_cell!(7));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[6]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[7]), pstr_offset_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[8]), fixnum_as_cell!(Fixnum::build_with(2)));
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(5));
+
+        assert!(!wam.machine_st.heap[0].get_mark_bit());
+        assert!(wam.machine_st.heap[1].get_mark_bit());
+        assert!(wam.machine_st.heap[2].get_mark_bit());
+        assert!(!wam.machine_st.heap[3].get_mark_bit());
+        assert!(wam.machine_st.heap[4].get_mark_bit());
+        assert!(wam.machine_st.heap[5].get_mark_bit());
+        assert!(!wam.machine_st.heap[6].get_mark_bit());
+        assert!(wam.machine_st.heap[7].get_mark_bit());
+        assert!(wam.machine_st.heap[8].get_mark_bit());
+
+        for cell in &wam.machine_st.heap {
+            assert!(cell.get_forwarding_bit() != Some(true));
+        }
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_second_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), pstr_loc_as_cell!(7));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[6]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[7]), pstr_offset_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[8]), fixnum_as_cell!(Fixnum::build_with(2)));
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(4));
+
+        assert!(!wam.machine_st.heap[0].get_mark_bit());
+        assert!(wam.machine_st.heap[1].get_mark_bit());
+        assert!(wam.machine_st.heap[2].get_mark_bit());
+        assert!(!wam.machine_st.heap[3].get_mark_bit());
+        assert!(wam.machine_st.heap[4].get_mark_bit());
+        assert!(wam.machine_st.heap[5].get_mark_bit());
+        assert!(!wam.machine_st.heap[6].get_mark_bit());
+        assert!(wam.machine_st.heap[7].get_mark_bit());
+        assert!(wam.machine_st.heap[8].get_mark_bit());
+
+        for cell in &wam.machine_st.heap {
+            assert!(cell.get_forwarding_bit() != Some(true));
+        }
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_second_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), pstr_loc_as_cell!(7));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[6]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[7]), pstr_offset_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[8]), fixnum_as_cell!(Fixnum::build_with(2)));
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(2));
+
+        assert!(!wam.machine_st.heap[0].get_mark_bit());
+        assert!(wam.machine_st.heap[1].get_mark_bit());
+        assert!(wam.machine_st.heap[2].get_mark_bit());
+        assert!(!wam.machine_st.heap[3].get_mark_bit());
+        assert!(wam.machine_st.heap[4].get_mark_bit());
+        assert!(wam.machine_st.heap[5].get_mark_bit());
+        assert!(!wam.machine_st.heap[6].get_mark_bit());
+        assert!(wam.machine_st.heap[7].get_mark_bit());
+        assert!(wam.machine_st.heap[8].get_mark_bit());
+
+        for cell in &wam.machine_st.heap {
+            assert!(cell.get_forwarding_bit() != Some(true));
+        }
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_second_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), pstr_loc_as_cell!(7));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[6]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[7]), pstr_offset_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[8]), fixnum_as_cell!(Fixnum::build_with(2)));
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        mark_cells(&mut wam.machine_st.heap, pstr_loc_as_cell!(1));
+
+        assert!(!wam.machine_st.heap[0].get_mark_bit());
+        assert!(wam.machine_st.heap[1].get_mark_bit());
+        assert!(wam.machine_st.heap[2].get_mark_bit());
+        assert!(!wam.machine_st.heap[3].get_mark_bit());
+        assert!(wam.machine_st.heap[4].get_mark_bit());
+        assert!(wam.machine_st.heap[5].get_mark_bit());
+        assert!(!wam.machine_st.heap[6].get_mark_bit());
+        assert!(wam.machine_st.heap[7].get_mark_bit());
+        assert!(wam.machine_st.heap[8].get_mark_bit());
+
+        for cell in &wam.machine_st.heap {
+            assert!(cell.get_forwarding_bit() != Some(true));
+        }
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), pstr_second_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), pstr_loc_as_cell!(7));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[6]), atom_as_cell!(atom!("irrelevant stuff")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[7]), pstr_offset_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[8]), fixnum_as_cell!(Fixnum::build_with(2)));
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        wam.machine_st.heap.clear();
+
+        // embedded cyclic partial string.
+
+        wam.machine_st.heap.push(pstr_cell);
+        wam.machine_st.heap.push(pstr_loc_as_cell!(2));
+        wam.machine_st.heap.push(pstr_offset_as_cell!(0));
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(3)));
+        wam.machine_st.heap.push(list_loc_as_cell!(5));
+        wam.machine_st.heap.push(pstr_loc_as_cell!(0));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(4));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_loc_as_cell!(2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_offset_as_cell!(0));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), fixnum_as_cell!(Fixnum::build_with(3)));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), list_loc_as_cell!(5));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), pstr_loc_as_cell!(0));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[6]), empty_list_as_cell!());
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(pstr_cell);
+        wam.machine_st.heap.push(pstr_loc_as_cell!(2));
+        wam.machine_st.heap.push(pstr_offset_as_cell!(0));
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(3)));
+        wam.machine_st.heap.push(list_loc_as_cell!(5));
+        wam.machine_st.heap.push(pstr_loc_as_cell!(0));
+        wam.machine_st.heap.push(heap_loc_as_cell!(4));
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(4));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+        }
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_cell);
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_loc_as_cell!(2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), pstr_offset_as_cell!(0));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), fixnum_as_cell!(Fixnum::build_with(3)));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), list_loc_as_cell!(5));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), pstr_loc_as_cell!(0));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[6]), heap_loc_as_cell!(4));
+
+        wam.machine_st.heap.clear();
+
+        // a chain of variables, ending in a self-referential variable.
+
+        wam.machine_st.heap.push(heap_loc_as_cell!(1));
+        wam.machine_st.heap.push(heap_loc_as_cell!(2));
+        wam.machine_st.heap.push(heap_loc_as_cell!(3));
+        wam.machine_st.heap.push(heap_loc_as_cell!(3));
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), heap_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), heap_loc_as_cell!(2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), heap_loc_as_cell!(3));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), heap_loc_as_cell!(3));
+
+        wam.machine_st.heap.clear();
+
+        // print L = [L|L].
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), list_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), list_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), list_loc_as_cell!(1));
+
+        wam.machine_st.heap.clear();
+
+        // term is [X,f(Y),Z].
+        // Z is an attributed variable, but has a variable attributes list.
+        wam.machine_st.heap.push(list_loc_as_cell!(1));
+        wam.machine_st.heap.push(heap_loc_as_cell!(1));
+        wam.machine_st.heap.push(heap_loc_as_cell!(3)); // 2
+        wam.machine_st.heap.push(list_loc_as_cell!(4)); // 3
+        wam.machine_st.heap.push(str_loc_as_cell!(6)); // 4
+        wam.machine_st.heap.push(heap_loc_as_cell!(8));
+        wam.machine_st.heap.push(atom_as_cell!(f_atom, 1)); // 6
+        wam.machine_st.heap.push(heap_loc_as_cell!(11)); // 7
+        wam.machine_st.heap.push(list_loc_as_cell!(9));
+        wam.machine_st.heap.push(heap_loc_as_cell!(9));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        wam.machine_st.heap.push(attr_var_as_cell!(11)); // linked from 7.
+        wam.machine_st.heap.push(heap_loc_as_cell!(12));
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), list_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), heap_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), heap_loc_as_cell!(3));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), list_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), str_loc_as_cell!(6));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), heap_loc_as_cell!(8));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[6]), atom_as_cell!(f_atom, 1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[7]), heap_loc_as_cell!(11));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[8]), list_loc_as_cell!(9));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[9]), heap_loc_as_cell!(9));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[10]), empty_list_as_cell!());
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[11]), attr_var_as_cell!(11));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[12]), heap_loc_as_cell!(12));
+
+        // now populate the attributes list.
+        let clpz_atom = atom!("clpz");
+        let p_atom = atom!("p");
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+            cell.set_forwarding_bit(false);
+        }
+
+        wam.machine_st.heap.pop();
+
+        wam.machine_st.heap.push(heap_loc_as_cell!(13)); // 12
+        wam.machine_st.heap.push(list_loc_as_cell!(14)); // 13
+        wam.machine_st.heap.push(str_loc_as_cell!(16)); // 14
+        wam.machine_st.heap.push(heap_loc_as_cell!(19)); // 15
+        wam.machine_st.heap.push(atom_as_cell!(clpz_atom, 2)); // 16
+        wam.machine_st.heap.push(atom_as_cell!(a_atom)); // 17
+        wam.machine_st.heap.push(atom_as_cell!(b_atom)); // 18
+        wam.machine_st.heap.push(list_loc_as_cell!(20)); // 19
+        wam.machine_st.heap.push(str_loc_as_cell!(22)); // 20
+        wam.machine_st.heap.push(empty_list_as_cell!()); // 21
+        wam.machine_st.heap.push(atom_as_cell!(p_atom, 1)); // 22
+        wam.machine_st.heap.push(heap_loc_as_cell!(23)); // 23
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), list_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), heap_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), heap_loc_as_cell!(3));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), list_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), str_loc_as_cell!(6));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), heap_loc_as_cell!(8));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[6]), atom_as_cell!(f_atom, 1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[7]), heap_loc_as_cell!(11));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[8]), list_loc_as_cell!(9));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[9]), heap_loc_as_cell!(9));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[10]), empty_list_as_cell!());
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[11]), attr_var_as_cell!(11));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[12]), heap_loc_as_cell!(13));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[13]), list_loc_as_cell!(14));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[14]), str_loc_as_cell!(16));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[15]), heap_loc_as_cell!(19));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[16]), atom_as_cell!(clpz_atom, 2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[17]), atom_as_cell!(a_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[18]), atom_as_cell!(b_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[19]), list_loc_as_cell!(20));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[20]), str_loc_as_cell!(22));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[21]), empty_list_as_cell!());
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[22]), atom_as_cell!(p_atom, 1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[23]), heap_loc_as_cell!(23));
+
+        for cell in &mut wam.machine_st.heap {
+            cell.set_mark_bit(false);
+            cell.set_forwarding_bit(false);
+        }
+
+        // push some unrelated nonsense cells to the heap and check that they
+        // are unmarked after the marker has finished at 0.
+        wam.machine_st.heap.push(heap_loc_as_cell!(5));
+        wam.machine_st.heap.push(heap_loc_as_cell!(5));
+        wam.machine_st.heap.push(list_loc_as_cell!(5));
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&mut wam.machine_st.heap[0..24]);
+
+        for cell in &wam.machine_st.heap[24..] {
+            assert_eq!(cell.get_mark_bit(), false);
+        }
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), list_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), heap_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), heap_loc_as_cell!(3));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), list_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), str_loc_as_cell!(6));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), heap_loc_as_cell!(8));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[6]), atom_as_cell!(f_atom, 1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[7]), heap_loc_as_cell!(11));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[8]), list_loc_as_cell!(9));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[9]), heap_loc_as_cell!(9));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[10]), empty_list_as_cell!());
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[11]), attr_var_as_cell!(11));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[12]), heap_loc_as_cell!(13));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[13]), list_loc_as_cell!(14));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[14]), str_loc_as_cell!(16));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[15]), heap_loc_as_cell!(19));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[16]), atom_as_cell!(clpz_atom, 2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[17]), atom_as_cell!(a_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[18]), atom_as_cell!(b_atom));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[19]), list_loc_as_cell!(20));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[20]), str_loc_as_cell!(22));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[21]), empty_list_as_cell!());
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[22]), atom_as_cell!(p_atom, 1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[23]), heap_loc_as_cell!(23));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[24]), heap_loc_as_cell!(5));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[25]), heap_loc_as_cell!(5));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[26]), list_loc_as_cell!(5));
+
+        wam.machine_st.heap.clear();
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0)));
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(0));
+
+        assert_eq!(wam.machine_st.heap.len(), 1);
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(str_loc_as_cell!(1));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("g"), 2));
+        wam.machine_st.heap.push(heap_loc_as_cell!(0));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("y")));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("="), 2));
+        wam.machine_st.heap.push(atom_as_cell!(atom!("X")));
+        wam.machine_st.heap.push(heap_loc_as_cell!(0));
+        wam.machine_st.heap.push(list_loc_as_cell!(8));
+        wam.machine_st.heap.push(str_loc_as_cell!(4));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        mark_cells(&mut wam.machine_st.heap, heap_loc_as_cell!(7));
+
+        assert_eq!(wam.machine_st.heap.len(), 10);
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), str_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), atom_as_cell!(atom!("g"), 2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), heap_loc_as_cell!(0));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[3]), atom_as_cell!(atom!("y")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[4]), atom_as_cell!(atom!("="), 2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[5]), atom_as_cell!(atom!("X")));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[6]), heap_loc_as_cell!(0));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[7]), list_loc_as_cell!(8));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[8]), str_loc_as_cell!(4));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[9]), empty_list_as_cell!());
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(atom_as_cell!(atom!("f"), 2));
+        wam.machine_st.heap.push(heap_loc_as_cell!(1));
+        wam.machine_st.heap.push(heap_loc_as_cell!(1));
+
+        mark_cells(&mut wam.machine_st.heap, str_loc_as_cell!(0));
+
+        all_cells_marked_and_unforwarded(&wam.machine_st.heap);
+
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), atom_as_cell!(atom!("f"), 2));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), heap_loc_as_cell!(1));
+        assert_eq!(unmark_cell_bits!(wam.machine_st.heap[2]), heap_loc_as_cell!(1));
+    }
+}
index 067bfa939f955e4249e1cf834208183e1e4f7516..462aacbce705970b2b922e2587a0fc47520144d2 100644 (file)
-use core::marker::PhantomData;
-
-use prolog_parser::ast::Constant;
-
+use crate::arena::*;
+use crate::atom_table::*;
+use crate::forms::*;
 use crate::machine::machine_indices::*;
 use crate::machine::partial_string::*;
-use crate::machine::raw_block::*;
+use crate::parser::ast::*;
+use crate::types::*;
+
+use ordered_float::OrderedFloat;
+use rug::{Integer, Rational};
 
 use std::convert::TryFrom;
-use std::mem;
-use std::ops::{Index, IndexMut};
-use std::ptr;
 
-#[derive(Debug)]
-pub(crate) struct StandardHeapTraits {}
+pub(crate) type Heap = Vec<HeapCellValue>;
 
-impl RawBlockTraits for StandardHeapTraits {
+impl From<Literal> for HeapCellValue {
     #[inline]
-    fn init_size() -> usize {
-        256 * mem::size_of::<HeapCellValue>()
+    fn from(literal: Literal) -> Self {
+        match literal {
+            Literal::Atom(name) => atom_as_cell!(name),
+            Literal::Char(c) => char_as_cell!(c),
+            Literal::Fixnum(n) => fixnum_as_cell!(n),
+            Literal::Integer(bigint_ptr) => {
+                typed_arena_ptr_as_cell!(bigint_ptr)
+            }
+            Literal::Rational(bigint_ptr) => {
+                typed_arena_ptr_as_cell!(bigint_ptr)
+            }
+            Literal::Float(f) => HeapCellValue::from(f),
+            Literal::String(s) => {
+                if s == atom!("") {
+                    empty_list_as_cell!()
+                } else {
+                    string_as_cstr_cell!(s)
+                }
+            }
+        }
     }
+}
 
-    #[inline]
-    fn align() -> usize {
-        mem::align_of::<HeapCellValue>()
+impl TryFrom<HeapCellValue> for Literal {
+    type Error = ();
+
+    fn try_from(value: HeapCellValue) -> Result<Literal, ()> {
+        read_heap_cell!(value,
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                if arity == 0 {
+                    Ok(Literal::Atom(name))
+                } else {
+                    Err(())
+                }
+            }
+            (HeapCellValueTag::Char, c) => {
+                Ok(Literal::Char(c))
+            }
+            (HeapCellValueTag::Fixnum, n) => {
+                Ok(Literal::Fixnum(n))
+            }
+            (HeapCellValueTag::F64, f) => {
+                Ok(Literal::Float(f))
+            }
+            (HeapCellValueTag::Cons, cons_ptr) => {
+                match_untyped_arena_ptr!(cons_ptr,
+                     (ArenaHeaderTag::Integer, n) => {
+                         Ok(Literal::Integer(n))
+                     }
+                     (ArenaHeaderTag::Rational, n) => {
+                         Ok(Literal::Rational(n))
+                     }
+                     _ => {
+                         Err(())
+                     }
+                )
+            }
+            (HeapCellValueTag::CStr, cstr_atom) => {
+                Ok(Literal::String(cstr_atom))
+            }
+            _ => {
+                Err(())
+            }
+        )
     }
 }
 
-#[derive(Debug)]
-pub(crate) struct HeapTemplate<T: RawBlockTraits> {
-    buf: RawBlock<T>,
-    _marker: PhantomData<HeapCellValue>,
-}
+// sometimes we need to dereference variables that are found only in
+// the heap without access to the full WAM (e.g., while detecting
+// cycles in terms), and which therefore may only point other cells in
+// the heap (thanks to the design of the WAM).
+pub fn heap_bound_deref(heap: &[HeapCellValue], mut value: HeapCellValue) -> HeapCellValue {
+    loop {
+        let new_value = read_heap_cell!(value,
+            (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
+                heap[h]
+            }
+            _ => {
+                value
+            }
+        );
 
-pub(crate) type Heap = HeapTemplate<StandardHeapTraits>;
+        if new_value != value && new_value.is_var() {
+            value = new_value;
+            continue;
+        }
 
-impl<T: RawBlockTraits> Drop for HeapTemplate<T> {
-    fn drop(&mut self) {
-        self.clear();
-        self.buf.deallocate();
+        return value;
     }
 }
 
-#[derive(Debug)]
-pub(crate) struct HeapIntoIter<T: RawBlockTraits> {
-    offset: usize,
-    buf: RawBlock<T>,
+pub fn heap_bound_store(heap: &[HeapCellValue], value: HeapCellValue) -> HeapCellValue {
+    read_heap_cell!(value,
+        (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
+            heap[h]
+        }
+        _ => {
+            value
+        }
+    )
 }
 
-impl<T: RawBlockTraits> Drop for HeapIntoIter<T> {
-    fn drop(&mut self) {
-        let mut heap = HeapTemplate {
-            buf: self.buf.take(),
-            _marker: PhantomData,
-        };
-
-        heap.truncate(self.offset / mem::size_of::<HeapCellValue>());
-        heap.buf.deallocate();
+#[allow(dead_code)]
+pub fn print_heap_terms<'a, I: Iterator<Item = &'a HeapCellValue>>(heap: I, h: usize) {
+    for (index, term) in heap.enumerate() {
+        println!("{} : {:?}", h + index, term);
     }
 }
 
-impl<T: RawBlockTraits> Iterator for HeapIntoIter<T> {
-    type Item = HeapCellValue;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let ptr = self.buf.base as usize + self.offset;
-        self.offset += mem::size_of::<HeapCellValue>();
-
-        if ptr < self.buf.top as usize {
-            unsafe { Some(ptr::read(ptr as *const HeapCellValue)) }
-        } else {
-            None
+#[inline]
+pub(crate) fn put_complete_string(
+    heap: &mut Heap,
+    s: &str,
+    atom_tbl: &mut AtomTable,
+) -> HeapCellValue {
+    match allocate_pstr(heap, s, atom_tbl) {
+        Some(h) => {
+            heap.pop(); // pop the trailing variable cell from the heap planted by allocate_pstr.
+
+            if heap.len() == h + 1 {
+                let pstr_atom = cell_as_atom!(heap[h]);
+                heap[h] = atom_as_cstr_cell!(pstr_atom);
+                heap_loc_as_cell!(h)
+            } else {
+                heap.push(empty_list_as_cell!());
+                pstr_loc_as_cell!(h)
+            }
+        }
+        None => {
+            empty_list_as_cell!()
         }
     }
 }
 
-#[derive(Debug)]
-pub(crate) struct HeapIter<'a, T: RawBlockTraits> {
-    offset: usize,
-    buf: &'a RawBlock<T>,
-}
-
-impl<'a, T: RawBlockTraits> HeapIter<'a, T> {
-    pub(crate) fn new(buf: &'a RawBlock<T>, offset: usize) -> Self {
-        HeapIter { buf, offset }
+#[inline]
+pub(crate) fn put_partial_string(
+    heap: &mut Heap,
+    s: &str,
+    atom_tbl: &mut AtomTable,
+) -> HeapCellValue {
+    match allocate_pstr(heap, s, atom_tbl) {
+        Some(h) => {
+            pstr_loc_as_cell!(h)
+        }
+        None => {
+            empty_list_as_cell!()
+        }
     }
 }
 
-impl<'a, T: RawBlockTraits> Iterator for HeapIter<'a, T> {
-    type Item = &'a HeapCellValue;
+#[inline]
+pub(crate) fn allocate_pstr(
+    heap: &mut Heap,
+    mut src: &str,
+    atom_tbl: &mut AtomTable,
+) -> Option<usize> {
+    let orig_h = heap.len();
+
+    loop {
+        if src == "" {
+            return if orig_h == heap.len() {
+                None
+            } else {
+                let tail_h = heap.len() - 1;
+                heap[tail_h] = heap_loc_as_cell!(tail_h);
+
+                Some(orig_h)
+            };
+        }
+
+        let h = heap.len();
+
+        let (pstr, rest_src) = match PartialString::new(src, atom_tbl) {
+            Some(tuple) => tuple,
+            None => {
+                if src.len() > '\u{0}'.len_utf8() {
+                    src = &src['\u{0}'.len_utf8()..];
+                    continue;
+                } else if orig_h == h {
+                    return None;
+                } else {
+                    heap[h - 1] = heap_loc_as_cell!(h - 1);
+                    return Some(orig_h);
+                }
+            }
+        };
 
-    fn next(&mut self) -> Option<Self::Item> {
-        let ptr = self.buf.base as usize + self.offset;
-        self.offset += mem::size_of::<HeapCellValue>();
+        heap.push(string_as_pstr_cell!(pstr));
 
-        if ptr < self.buf.top as usize {
-            unsafe { Some(&*(ptr as *const _)) }
+        if rest_src != "" {
+            heap.push(pstr_loc_as_cell!(h + 2));
+            src = rest_src;
         } else {
-            None
+            heap.push(heap_loc_as_cell!(h + 1));
+            return Some(orig_h);
         }
     }
 }
 
-#[allow(dead_code)]
-pub(crate) fn print_heap_terms<'a, I: Iterator<Item = &'a HeapCellValue>>(heap: I, h: usize) {
-    for (index, term) in heap.enumerate() {
-        println!("{} : {}", h + index, term);
+pub fn filtered_iter_to_heap_list<SrcT: Into<HeapCellValue>>(
+    heap: &mut Heap,
+    values: impl Iterator<Item = SrcT>,
+    filter_fn: impl Fn(&Heap, HeapCellValue) -> bool,
+) -> usize {
+    let head_addr = heap.len();
+    let mut h = head_addr;
+
+    for value in values {
+        let value = value.into();
+
+        if filter_fn(heap, value) {
+            heap.push(list_loc_as_cell!(h + 1));
+            heap.push(value);
+
+            h += 2;
+        }
     }
-}
 
-#[derive(Debug)]
-pub(crate) struct HeapIterMut<'a, T: RawBlockTraits> {
-    offset: usize,
-    buf: &'a mut RawBlock<T>,
+    heap.push(empty_list_as_cell!());
+
+    head_addr
 }
 
-impl<'a, T: RawBlockTraits> HeapIterMut<'a, T> {
-    pub(crate) fn new(buf: &'a mut RawBlock<T>, offset: usize) -> Self {
-        HeapIterMut { buf, offset }
-    }
+#[inline(always)]
+pub fn iter_to_heap_list<Iter, SrcT>(heap: &mut Heap, values: Iter) -> usize
+where
+    Iter: Iterator<Item = SrcT>,
+    SrcT: Into<HeapCellValue>,
+{
+    filtered_iter_to_heap_list(heap, values, |_, _| true)
 }
 
-impl<'a, T: RawBlockTraits> Iterator for HeapIterMut<'a, T> {
-    type Item = &'a mut HeapCellValue;
+pub(crate) fn to_local_code_ptr(heap: &Heap, addr: HeapCellValue) -> Option<LocalCodePtr> {
+    let extract_integer = |s: usize| -> Option<usize> {
+        match Number::try_from(heap[s]) {
+            Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).ok(),
+            Ok(Number::Integer(n)) => n.to_usize(),
+            _ => None,
+        }
+    };
 
-    fn next(&mut self) -> Option<Self::Item> {
-        let ptr = self.buf.base as usize + self.offset;
-        self.offset += mem::size_of::<HeapCellValue>();
+    read_heap_cell!(addr,
+        (HeapCellValueTag::Str, s) => {
+            let (name, arity) = cell_as_atom_cell!(heap[s]).get_name_and_arity();
 
-        if ptr < self.buf.top as usize {
-            unsafe { Some(&mut *(ptr as *mut _)) }
-        } else {
+            if name == atom!("dir_entry") && arity == 1 {
+                extract_integer(s+1).map(LocalCodePtr::DirEntry)
+            } else {
+                panic!(
+                    "to_local_code_ptr crashed with p.i. {}/{}",
+                    name.as_str(),
+                    arity,
+                );
+            }
+        }
+        _ => {
             None
         }
-    }
+    )
 }
 
+/*
 impl<T: RawBlockTraits> HeapTemplate<T> {
     #[inline]
     pub(crate) fn new() -> Self {
@@ -144,67 +286,40 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
         }
     }
 
-    #[inline]
-    pub(crate) fn clone(&self, h: usize) -> HeapCellValue {
-        match &self[h] {
-            &HeapCellValue::Addr(addr) => HeapCellValue::Addr(addr),
-            &HeapCellValue::Atom(ref name, ref op) => HeapCellValue::Atom(name.clone(), op.clone()),
-            &HeapCellValue::DBRef(ref db_ref) => HeapCellValue::DBRef(db_ref.clone()),
-            &HeapCellValue::Integer(ref n) => HeapCellValue::Integer(n.clone()),
-            &HeapCellValue::LoadStatePayload(_) => HeapCellValue::Addr(Addr::LoadStatePayload(h)),
-            &HeapCellValue::NamedStr(arity, ref name, ref op) => {
-                HeapCellValue::NamedStr(arity, name.clone(), op.clone())
-            }
-            &HeapCellValue::PartialString(..) => HeapCellValue::Addr(Addr::PStrLocation(h, 0)),
-            &HeapCellValue::Rational(ref r) => HeapCellValue::Rational(r.clone()),
-            &HeapCellValue::Stream(_) => HeapCellValue::Addr(Addr::Stream(h)),
-            &HeapCellValue::TcpListener(_) => HeapCellValue::Addr(Addr::TcpListener(h)),
-        }
-    }
-
-    #[inline]
-    pub(crate) fn put_complete_string(&mut self, s: &str) -> Addr {
-        if s.is_empty() {
-            return Addr::EmptyList;
-        }
-
-        let addr = self.allocate_pstr(s);
-        self.pop();
-
-        let h = self.h();
-
-        match &mut self[h - 1] {
-            &mut HeapCellValue::PartialString(_, ref mut has_tail) => {
-                *has_tail = false;
-            }
-            _ => {
-                unreachable!()
-            }
-        }
-
-        addr
-    }
-
-    #[inline]
-    pub(crate) fn put_constant(&mut self, c: Constant) -> Addr {
-        match c {
-            Constant::Atom(name, op) => Addr::Con(self.push(HeapCellValue::Atom(name, op))),
-            Constant::Char(c) => Addr::Char(c),
-            Constant::EmptyList => Addr::EmptyList,
-            Constant::Fixnum(n) => Addr::Fixnum(n),
-            Constant::Integer(n) => Addr::Con(self.push(HeapCellValue::Integer(n))),
-            Constant::Rational(r) => Addr::Con(self.push(HeapCellValue::Rational(r))),
-            Constant::Float(f) => Addr::Float(f),
-            Constant::String(s) => {
-                if s.is_empty() {
-                    Addr::EmptyList
-                } else {
-                    self.put_complete_string(&s)
+    /*
+        // TODO: move this to the WAM, then remove the temporary (and by
+        // then, unnecessary and impossible) "arena" argument. OR, remove
+        // this thing totally!  if we can. by that I mean, just convert a
+        // little to a HeapCellValue. don't bother writing to the
+        // heap at all. Each of these data is either already inlinable in a
+        // HeapCellValue or a pointer to an GC'ed location in memory.
+        #[inline]
+        pub(crate) fn put_literal(&mut self, literal: Literal) -> HeapCellValue {
+            match literal {
+                Literal::Atom(name) => atom_as_cell!(name),
+                Literal::Char(c) => char_as_cell!(c),
+                Literal::EmptyList => empty_list_as_cell!(),
+                Literal::Fixnum(n) => fixnum_as_cell!(n),
+                Literal::Integer(bigint_ptr) => {
+                    let h = self.push(typed_arena_ptr_as_cell!(bigint_ptr));
+                    self[h]
+                }
+                Literal::Rational(bigint_ptr) => {
+                    let h = self.push(typed_arena_ptr_as_cell!(bigint_ptr));
+                    self[h]
                 }
+                Literal::Float(f) => typed_arena_ptr_as_cell!(f),
+                Literal::String(s) => {
+                    if s.as_str().is_empty() {
+                        empty_list_as_cell!()
+                    } else {
+                        // TODO: how do we know where the tail is located?? well, there is no tail. separate tag?
+                        untyped_arena_ptr_as_cell!(s) // self.put_complete_string(arena, &s)
+                    }
+                } // Literal::Usize(n) => Addr::Usize(n),
             }
-            Constant::Usize(n) => Addr::Usize(n),
         }
-    }
+    */
 
     #[inline]
     pub(crate) fn is_empty(&self) -> bool {
@@ -225,14 +340,14 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
         let h = self.h();
 
         unsafe {
-            let new_top = self.buf.new_block(mem::size_of::<HeapCellValue>());
-            ptr::write(self.buf.top as *mut _, val);
-            self.buf.top = new_top;
+            let new_ptr = self.buf.alloc(mem::size_of::<HeapCellValue>());
+            ptr::write(new_ptr as *mut _, val);
         }
 
         h
     }
 
+    /*
     #[inline]
     pub(crate) fn atom_at(&self, h: usize) -> bool {
         if let HeapCellValue::Atom(..) = &self[h] {
@@ -265,76 +380,17 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
             val @ HeapCellValue::TcpListener(..) => Addr::TcpListener(self.push(val)),
         }
     }
-
-    #[inline]
-    pub(crate) fn allocate_pstr(&mut self, src: &str) -> Addr {
-        self.write_pstr(src).unwrap_or_else(|| Addr::EmptyList)
-    }
-
-    #[inline]
-    fn write_pstr(&mut self, mut src: &str) -> Option<Addr> {
-        let orig_h = self.h();
-
-        loop {
-            if src == "" {
-                return if orig_h == self.h() {
-                    None
-                } else {
-                    let tail_h = self.h() - 1;
-                    self[tail_h] = HeapCellValue::Addr(Addr::HeapCell(tail_h));
-
-                    Some(Addr::PStrLocation(orig_h, 0))
-                };
-            }
-
-            let h = self.h();
-
-            let (pstr, rest_src) = match PartialString::new(src) {
-                Some(tuple) => tuple,
-                None => {
-                    if src.len() > '\u{0}'.len_utf8() {
-                        src = &src['\u{0}'.len_utf8()..];
-                        continue;
-                    } else if orig_h == h {
-                        return None;
-                    } else {
-                        self[h - 1] = HeapCellValue::Addr(Addr::HeapCell(h - 1));
-                        return Some(Addr::PStrLocation(orig_h, 0));
-                    }
-                }
-            };
-
-            self.push(HeapCellValue::PartialString(pstr, true));
-
-            if rest_src != "" {
-                self.push(HeapCellValue::Addr(Addr::PStrLocation(h + 2, 0)));
-                src = rest_src;
-            } else {
-                self.push(HeapCellValue::Addr(Addr::HeapCell(h + 1)));
-                return Some(Addr::PStrLocation(orig_h, 0));
-            }
-        }
-    }
+    */
 
     #[inline]
     pub(crate) fn truncate(&mut self, h: usize) {
-        let new_top = h * mem::size_of::<HeapCellValue>() + self.buf.base as usize;
-        let mut h = new_top;
-
-        unsafe {
-            while h as *const _ < self.buf.top {
-                let val = h as *mut HeapCellValue;
-                ptr::drop_in_place(val);
-                h += mem::size_of::<HeapCellValue>();
-            }
-        }
-
-        self.buf.top = new_top as *const _;
+        let new_ptr = self.buf.top as usize - h * mem::size_of::<HeapCellValue>();
+        self.buf.ptr = new_ptr as *mut _;
     }
 
     #[inline]
     pub(crate) fn h(&self) -> usize {
-        (self.buf.top as usize - self.buf.base as usize) / mem::size_of::<HeapCellValue>()
+        (self.buf.top as usize - self.buf.ptr as usize) / mem::size_of::<HeapCellValue>()
     }
 
     pub(crate) fn append(&mut self, vals: Vec<HeapCellValue>) {
@@ -350,84 +406,7 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
         }
     }
 
-    pub(crate) fn to_list<Iter, SrcT>(&mut self, values: Iter) -> usize
-    where
-        Iter: Iterator<Item = SrcT>,
-        SrcT: Into<HeapCellValue>,
-    {
-        let head_addr = self.h();
-        let mut h = head_addr;
-
-        for value in values.map(|v| v.into()) {
-            self.push(HeapCellValue::Addr(Addr::Lis(h + 1)));
-            self.push(value);
-
-            h += 2;
-        }
-
-        self.push(HeapCellValue::Addr(Addr::EmptyList));
-
-        head_addr
-    }
-
-    /* Create an iterator starting from the passed offset. */
-    pub(crate) fn iter_from<'a>(&'a self, offset: usize) -> HeapIter<'a, T> {
-        HeapIter::new(&self.buf, offset * mem::size_of::<HeapCellValue>())
-    }
-
-    pub(crate) fn iter_mut_from<'a>(&'a mut self, offset: usize) -> HeapIterMut<'a, T> {
-        HeapIterMut::new(&mut self.buf, offset * mem::size_of::<HeapCellValue>())
-    }
-
-    pub(crate) fn into_iter(mut self) -> HeapIntoIter<T> {
-        HeapIntoIter {
-            buf: self.buf.take(),
-            offset: 0,
-        }
-    }
-
-    pub(crate) fn extend<Iter: Iterator<Item = HeapCellValue>>(&mut self, iter: Iter) {
-        for hcv in iter {
-            self.push(hcv);
-        }
-    }
-
-    pub(crate) fn to_local_code_ptr(&self, addr: &Addr) -> Option<LocalCodePtr> {
-        let extract_integer = |s: usize| -> Option<usize> {
-            match &self[s] {
-                &HeapCellValue::Addr(Addr::Fixnum(n)) => usize::try_from(n).ok(),
-                &HeapCellValue::Integer(ref n) => n.to_usize(),
-                _ => None,
-            }
-        };
-
-        match addr {
-            Addr::Str(s) => {
-                match &self[*s] {
-                    HeapCellValue::NamedStr(arity, ref name, _) => {
-                        match (name.as_str(), *arity) {
-                            ("dir_entry", 1) => extract_integer(s + 1).map(LocalCodePtr::DirEntry),
-                            /*
-                            ("top_level", 2) => {
-                                if let Some(chunk_num) = extract_integer(s+1) {
-                                    if let Some(p) = extract_integer(s+2) {
-                                        return Some(LocalCodePtr::TopLevel(chunk_num, p));
-                                    }
-                                }
-
-                                None
-                            }
-                            */
-                            _ => None,
-                        }
-                    }
-                    _ => unreachable!(),
-                }
-            }
-            _ => None,
-        }
-    }
-
+    /* TODO: get rid of this!!
     #[inline]
     pub(crate) fn index_addr<'a>(&'a self, addr: &Addr) -> RefOrOwned<'a, HeapCellValue> {
         match addr {
@@ -437,6 +416,20 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
             addr => RefOrOwned::Owned(HeapCellValue::Addr(*addr)),
         }
     }
+    */
+}
+
+impl<T: RawBlockTraits> Index<u64> for HeapTemplate<T> {
+    type Output = HeapCellValue;
+
+    #[inline]
+    fn index(&self, index: u64) -> &Self::Output {
+        unsafe {
+            let ptr =
+                self.buf.top as usize - (index as usize + 1) * mem::size_of::<HeapCellValue>();
+            &*(ptr as *const HeapCellValue)
+        }
+    }
 }
 
 impl<T: RawBlockTraits> Index<usize> for HeapTemplate<T> {
@@ -445,7 +438,7 @@ impl<T: RawBlockTraits> Index<usize> for HeapTemplate<T> {
     #[inline]
     fn index(&self, index: usize) -> &Self::Output {
         unsafe {
-            let ptr = self.buf.base as usize + index * mem::size_of::<HeapCellValue>();
+            let ptr = self.buf.top as usize - (index + 1) * mem::size_of::<HeapCellValue>();
             &*(ptr as *const HeapCellValue)
         }
     }
@@ -455,8 +448,9 @@ impl<T: RawBlockTraits> IndexMut<usize> for HeapTemplate<T> {
     #[inline]
     fn index_mut(&mut self, index: usize) -> &mut Self::Output {
         unsafe {
-            let ptr = self.buf.base as usize + index * mem::size_of::<HeapCellValue>();
+            let ptr = self.buf.top as usize - (index + 1) * mem::size_of::<HeapCellValue>();
             &mut *(ptr as *mut HeapCellValue)
         }
     }
 }
+*/
index d0a0d126470fd0f45daede7a24926ebadf32eed8..2b8c0fb51d897ea989d77a0cab8272e0603f82f6 100644 (file)
@@ -1,26 +1,21 @@
+use crate::clause_types::*;
+use crate::forms::*;
+use crate::machine::loader::*;
+use crate::machine::machine_errors::*;
 use crate::machine::machine_indices::*;
 use crate::machine::preprocessor::*;
 use crate::machine::term_stream::*;
 use crate::machine::*;
-
-use prolog_parser::clause_name;
+use crate::parser::ast::*;
 
 use indexmap::IndexSet;
 use ref_thread_local::RefThreadLocal;
 use slice_deque::{sdeq, SliceDeque};
 
-type ModuleOpExports = Vec<(OpDecl, Option<(usize, Specifier)>)>;
-
-/*
- * We will want to borrow these fields from Loader separately, without
- * restricting access to other fields by borrowing them mutably.
- */
-pub(super) struct LoadState<'a> {
-    pub(super) compilation_target: CompilationTarget,
-    pub(super) module_op_exports: ModuleOpExports,
-    pub(super) retraction_info: RetractionInfo,
-    pub(super) wam: &'a mut Machine,
-}
+use std::fs::File;
+use std::mem;
+
+pub(super) type ModuleOpExports = Vec<(OpDecl, Option<OpDesc>)>;
 
 pub(super) fn set_code_index(
     retraction_info: &mut RetractionInfo,
@@ -42,10 +37,10 @@ pub(super) fn set_code_index(
         CompilationTarget::Module(ref module_name) => {
             if IndexPtr::Undefined == code_index.get() {
                 code_index.set(code_ptr);
-                RetractionRecord::AddedModulePredicate(module_name.clone(), key)
+                RetractionRecord::AddedModulePredicate(*module_name, key)
             } else {
                 let replaced = code_index.replace(code_ptr);
-                RetractionRecord::ReplacedModulePredicate(module_name.clone(), key, replaced)
+                RetractionRecord::ReplacedModulePredicate(*module_name, key, replaced)
             }
         }
     };
@@ -69,18 +64,17 @@ fn add_op_decl_as_module_export(
     */
 
     match op_decl.insert_into_op_dir(wam_op_dir) {
-        Some((prec, spec)) => {
+        Some(op_desc) => {
             retraction_info.push_record(RetractionRecord::ReplacedUserOp(
-                op_decl.clone(),
-                prec,
-                spec,
+                *op_decl,
+                op_desc,
             ));
 
-            module_op_exports.push((op_decl.clone(), Some((prec, spec))));
+            module_op_exports.push((*op_decl, Some(op_desc)));
         }
         None => {
-            retraction_info.push_record(RetractionRecord::AddedUserOp(op_decl.clone()));
-            module_op_exports.push((op_decl.clone(), None));
+            retraction_info.push_record(RetractionRecord::AddedUserOp(*op_decl));
+            module_op_exports.push((*op_decl, None));
         }
     }
 
@@ -94,31 +88,29 @@ pub(super) fn add_op_decl(
     op_decl: &OpDecl,
 ) {
     match op_decl.insert_into_op_dir(op_dir) {
-        Some((prec, spec)) => match &compilation_target {
+        Some(op_desc) => match &compilation_target {
             CompilationTarget::User => {
                 retraction_info.push_record(RetractionRecord::ReplacedUserOp(
-                    op_decl.clone(),
-                    prec,
-                    spec,
+                    *op_decl,
+                    op_desc,
                 ));
             }
             CompilationTarget::Module(ref module_name) => {
                 retraction_info.push_record(RetractionRecord::ReplacedModuleOp(
-                    module_name.clone(),
-                    op_decl.clone(),
-                    prec,
-                    spec,
+                    *module_name,
+                    *op_decl,
+                    op_desc,
                 ));
             }
         },
         None => match &compilation_target {
             CompilationTarget::User => {
-                retraction_info.push_record(RetractionRecord::AddedUserOp(op_decl.clone()));
+                retraction_info.push_record(RetractionRecord::AddedUserOp(*op_decl));
             }
             CompilationTarget::Module(ref module_name) => {
                 retraction_info.push_record(RetractionRecord::AddedModuleOp(
-                    module_name.clone(),
-                    op_decl.clone(),
+                    *module_name,
+                    *op_decl,
                 ));
             }
         },
@@ -135,16 +127,16 @@ pub(super) fn import_module_exports(
 ) -> Result<(), SessionError> {
     for export in imported_module.module_decl.exports.iter() {
         match export {
-            ModuleExport::PredicateKey((ref name, arity)) => {
-                let key = (name.clone(), *arity);
+            ModuleExport::PredicateKey((name, arity)) => {
+                let key = (*name, *arity);
 
                 if let Some(meta_specs) = imported_module.meta_predicates.get(&key) {
-                    meta_predicates.insert(key.clone(), meta_specs.clone());
+                    meta_predicates.insert(key, meta_specs.clone());
                 }
 
                 if let Some(src_code_index) = imported_module.code_dir.get(&key) {
                     let target_code_index = code_dir
-                        .entry(key.clone())
+                        .entry(key)
                         .or_insert_with(|| CodeIndex::new(IndexPtr::Undefined))
                         .clone();
 
@@ -157,8 +149,8 @@ pub(super) fn import_module_exports(
                     );
                 } else {
                     return Err(SessionError::ModuleDoesNotContainExport(
-                        imported_module.module_decl.name.clone(),
-                        (name.clone(), *arity),
+                        imported_module.module_decl.name,
+                        key,
                     ));
                 }
             }
@@ -183,8 +175,8 @@ fn import_module_exports_into_module(
 ) -> Result<(), SessionError> {
     for export in imported_module.module_decl.exports.iter() {
         match export {
-            ModuleExport::PredicateKey((ref name, arity)) => {
-                let key = (name.clone(), *arity);
+            ModuleExport::PredicateKey((name, arity)) => {
+                let key = (*name, *arity);
 
                 if let Some(meta_specs) = imported_module.meta_predicates.get(&key) {
                     meta_predicates.insert(key.clone(), meta_specs.clone());
@@ -192,7 +184,7 @@ fn import_module_exports_into_module(
 
                 if let Some(src_code_index) = imported_module.code_dir.get(&key) {
                     let target_code_index = code_dir
-                        .entry(key.clone())
+                        .entry(key)
                         .or_insert_with(|| CodeIndex::new(IndexPtr::Undefined))
                         .clone();
 
@@ -206,7 +198,7 @@ fn import_module_exports_into_module(
                 } else {
                     return Err(SessionError::ModuleDoesNotContainExport(
                         imported_module.module_decl.name.clone(),
-                        (name.clone(), *arity),
+                        (*name, *arity),
                     ));
                 }
             }
@@ -241,8 +233,8 @@ fn import_qualified_module_exports(
         }
 
         match export {
-            ModuleExport::PredicateKey((ref name, arity)) => {
-                let key = (name.clone(), *arity);
+            ModuleExport::PredicateKey((name, arity)) => {
+                let key = (*name, *arity);
 
                 if let Some(meta_specs) = imported_module.meta_predicates.get(&key) {
                     meta_predicates.insert(key.clone(), meta_specs.clone());
@@ -264,7 +256,7 @@ fn import_qualified_module_exports(
                 } else {
                     return Err(SessionError::ModuleDoesNotContainExport(
                         imported_module.module_decl.name.clone(),
-                        (name.clone(), *arity),
+                        (*name, *arity),
                     ));
                 }
             }
@@ -294,16 +286,16 @@ fn import_qualified_module_exports_into_module(
         }
 
         match export {
-            ModuleExport::PredicateKey((ref name, arity)) => {
-                let key = (name.clone(), *arity);
+            ModuleExport::PredicateKey((name, arity)) => {
+                let key = (*name, *arity);
 
                 if let Some(meta_specs) = imported_module.meta_predicates.get(&key) {
-                    meta_predicates.insert(key.clone(), meta_specs.clone());
+                    meta_predicates.insert(key, meta_specs.clone());
                 }
 
                 if let Some(src_code_index) = imported_module.code_dir.get(&key) {
                     let target_code_index = code_dir
-                        .entry(key.clone())
+                        .entry(key)
                         .or_insert_with(|| CodeIndex::new(IndexPtr::Undefined))
                         .clone();
 
@@ -317,7 +309,7 @@ fn import_qualified_module_exports_into_module(
                 } else {
                     return Err(SessionError::ModuleDoesNotContainExport(
                         imported_module.module_decl.name.clone(),
-                        (name.clone(), *arity),
+                        (*name, *arity),
                     ));
                 }
             }
@@ -337,15 +329,15 @@ fn import_qualified_module_exports_into_module(
     Ok(())
 }
 
-impl<'a> LoadState<'a> {
-    pub(super) fn retract_local_clauses(
+impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
+    pub(super) fn retract_local_clauses_impl(
         &mut self,
         compilation_target: CompilationTarget,
         key: PredicateKey,
         clause_locs: &SliceDeque<usize>,
     ) {
         let result_opt = self
-            .wam
+            .wam_prelude
             .indices
             .get_predicate_skeleton(&compilation_target, &key)
             .map(|skeleton| {
@@ -377,15 +369,18 @@ impl<'a> LoadState<'a> {
         mut clause_target_poses: Vec<Option<usize>>,
         is_dynamic: bool,
     ) {
-        let old_compilation_target = mem::replace(&mut self.compilation_target, compilation_target);
+        let old_compilation_target = mem::replace(
+            &mut self.payload.compilation_target,
+            compilation_target,
+        );
 
         while let Some(target_pos_opt) = clause_target_poses.pop() {
             match target_pos_opt {
                 Some(target_pos) if is_dynamic => {
-                    self.retract_dynamic_clause(key.clone(), target_pos);
+                    self.retract_dynamic_clause(key, target_pos);
                 }
                 Some(target_pos) => {
-                    self.retract_clause(key.clone(), target_pos);
+                    self.retract_clause(key, target_pos);
                 }
                 None => {
                     // Here because the clause was been removed
@@ -395,7 +390,7 @@ impl<'a> LoadState<'a> {
             }
         }
 
-        self.compilation_target = old_compilation_target;
+        self.payload.compilation_target = old_compilation_target;
     }
 
     pub(super) fn retract_local_clause_clauses(
@@ -403,20 +398,23 @@ impl<'a> LoadState<'a> {
         clause_clause_compilation_target: CompilationTarget,
         clause_locs: &SliceDeque<usize>,
     ) {
-        let key = (clause_name!("$clause"), 2);
-
-        match self.wam.indices.get_local_predicate_skeleton_mut(
-            self.compilation_target.clone(),
-            clause_clause_compilation_target.clone(),
-            self.listing_src_file_name(),
-            key.clone(),
+        let key = (atom!("$clause"), 2);
+        let listing_src_file_name = self.listing_src_file_name();
+
+        match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
+            self.payload.compilation_target,
+            clause_clause_compilation_target,
+            listing_src_file_name,
+            key,
         ) {
             Some(skeleton) => {
-                self.retraction_info.push_record(
+                let payload_compilation_target = self.payload.compilation_target;
+
+                self.payload.retraction_info.push_record(
                     RetractionRecord::RemovedLocalSkeletonClauseLocations(
-                        self.compilation_target.clone(),
-                        clause_clause_compilation_target.clone(),
-                        key.clone(),
+                        payload_compilation_target,
+                        clause_clause_compilation_target,
+                        key,
                         mem::replace(&mut skeleton.clause_clause_locs, sdeq![]),
                     ),
                 );
@@ -430,7 +428,7 @@ impl<'a> LoadState<'a> {
             }
         };
 
-        self.retract_local_clauses(clause_clause_compilation_target, key, &clause_locs);
+        self.retract_local_clauses_impl(clause_clause_compilation_target, key, &clause_locs);
     }
 
     pub(super) fn try_term_to_tl(
@@ -450,19 +448,18 @@ impl<'a> LoadState<'a> {
 
     #[inline]
     pub(super) fn remove_module_op_exports(&mut self) {
-        for (mut op_decl, record) in self.module_op_exports.drain(0..) {
-            op_decl.remove(&mut self.wam.indices.op_dir);
+        for (mut op_decl, record) in self.payload.module_op_exports.drain(0..) {
+            op_decl.remove(&mut self.wam_prelude.indices.op_dir);
 
-            if let Some((prec, spec)) = record {
-                op_decl.prec = prec;
-                op_decl.spec = spec;
-                op_decl.insert_into_op_dir(&mut self.wam.indices.op_dir);
+            if let Some(op_desc) = record {
+                op_decl.op_desc = op_desc;
+                op_decl.insert_into_op_dir(&mut self.wam_prelude.indices.op_dir);
             }
         }
     }
 
-    pub(super) fn remove_replaced_in_situ_module(&mut self, module_name: ClauseName) {
-        let removed_module = match self.wam.indices.modules.remove(&module_name) {
+    pub(super) fn remove_replaced_in_situ_module(&mut self, module_name: Atom) {
+        let removed_module = match self.wam_prelude.indices.modules.remove(&module_name) {
             Some(module) => module,
             None => return,
         };
@@ -479,7 +476,7 @@ impl<'a> LoadState<'a> {
             if code_index.get() != IndexPtr::Undefined {
                 let old_index_ptr = code_index.replace(IndexPtr::Undefined);
 
-                self.retraction_info
+                self.payload.retraction_info
                     .push_record(RetractionRecord::ReplacedModulePredicate(
                         module_name.clone(),
                         key.clone(),
@@ -488,11 +485,11 @@ impl<'a> LoadState<'a> {
             }
         }
 
-        self.wam.indices.modules.insert(module_name, removed_module);
+        self.wam_prelude.indices.modules.insert(module_name, removed_module);
     }
 
-    pub(super) fn remove_module_exports(&mut self, module_name: ClauseName) {
-        let removed_module = match self.wam.indices.modules.remove(&module_name) {
+    pub(super) fn remove_module_exports(&mut self, module_name: Atom) {
+        let removed_module = match self.wam_prelude.indices.modules.remove(&module_name) {
             Some(module) => module,
             None => return,
         };
@@ -503,7 +500,7 @@ impl<'a> LoadState<'a> {
             op_dir: &mut OpDir,
             retraction_info: &mut RetractionInfo,
             predicate_retractor: impl Fn(PredicateKey, IndexPtr) -> RetractionRecord,
-            op_retractor: impl Fn(OpDecl, usize, Specifier) -> RetractionRecord,
+            op_retractor: impl Fn(OpDecl, OpDesc) -> RetractionRecord,
         ) {
             for export in removed_module.module_decl.exports.iter() {
                 match export {
@@ -513,55 +510,51 @@ impl<'a> LoadState<'a> {
                                 if module_code_index.get() == target_code_index.get() =>
                             {
                                 let old_index_ptr = target_code_index.replace(IndexPtr::Undefined);
-
-                                retraction_info
-                                    .push_record(predicate_retractor(key.clone(), old_index_ptr));
+                                retraction_info.push_record(predicate_retractor(*key, old_index_ptr));
                             }
                             _ => {}
                         }
                     }
                     ModuleExport::OpDecl(op_decl) => {
                         let op_dir_value_opt =
-                            op_dir.remove(&(op_decl.name.clone(), op_decl.fixity()));
-
-                        if let Some(op_dir_value) = op_dir_value_opt {
-                            let (prec, spec) = op_dir_value.shared_op_desc().get();
+                            op_dir.remove(&(op_decl.name, fixity(op_decl.op_desc.get_spec() as u32)));
 
-                            retraction_info.push_record(op_retractor(op_decl.clone(), prec, spec));
+                        if let Some(op_desc) = op_dir_value_opt {
+                            retraction_info.push_record(op_retractor(*op_decl, op_desc));
                         }
                     }
                 }
             }
         }
 
-        match &self.compilation_target {
+        match self.payload.compilation_target {
             CompilationTarget::User => {
                 remove_module_exports(
                     &removed_module,
-                    &mut self.wam.indices.code_dir,
-                    &mut self.wam.indices.op_dir,
-                    &mut self.retraction_info,
+                    &mut self.wam_prelude.indices.code_dir,
+                    &mut self.wam_prelude.indices.op_dir,
+                    &mut self.payload.retraction_info,
                     RetractionRecord::ReplacedUserPredicate,
                     RetractionRecord::ReplacedUserOp,
                 );
             }
-            CompilationTarget::Module(ref target_module_name)
-                if target_module_name.as_str() != module_name.as_str() =>
+            CompilationTarget::Module(target_module_name)
+                if target_module_name != module_name =>
             {
                 let predicate_retractor = |key, index_ptr| {
-                    RetractionRecord::ReplacedModulePredicate(module_name.clone(), key, index_ptr)
+                    RetractionRecord::ReplacedModulePredicate(module_name, key, index_ptr)
                 };
 
-                let op_retractor = |op_decl, prec, spec| {
-                    RetractionRecord::ReplacedModuleOp(module_name.clone(), op_decl, prec, spec)
+                let op_retractor = |op_decl, op_desc| {
+                    RetractionRecord::ReplacedModuleOp(module_name, op_decl, op_desc)
                 };
 
-                if let Some(module) = self.wam.indices.modules.get_mut(target_module_name) {
+                if let Some(module) = self.wam_prelude.indices.modules.get_mut(&target_module_name) {
                     remove_module_exports(
                         &removed_module,
                         &mut module.code_dir,
                         &mut module.op_dir,
-                        &mut self.retraction_info,
+                        &mut self.payload.retraction_info,
                         predicate_retractor,
                         op_retractor,
                     );
@@ -572,24 +565,24 @@ impl<'a> LoadState<'a> {
             CompilationTarget::Module(_) => {}
         };
 
-        self.wam.indices.modules.insert(module_name, removed_module);
+        self.wam_prelude.indices.modules.insert(module_name, removed_module);
     }
 
     fn get_or_insert_local_code_index(
         &mut self,
-        module_name: ClauseName,
+        module_name: Atom,
         key: PredicateKey,
     ) -> CodeIndex {
-        match self.wam.indices.modules.get_mut(&module_name) {
+        match self.wam_prelude.indices.modules.get_mut(&module_name) {
             Some(ref mut module) => module
                 .code_dir
                 .entry(key)
                 .or_insert_with(|| CodeIndex::new(IndexPtr::Undefined))
                 .clone(),
             None => {
-                self.add_dynamically_generated_module(&module_name);
+                self.add_dynamically_generated_module(module_name);
 
-                match self.wam.indices.modules.get_mut(&module_name) {
+                match self.wam_prelude.indices.modules.get_mut(&module_name) {
                     Some(ref mut module) => module
                         .code_dir
                         .entry(key)
@@ -610,7 +603,7 @@ impl<'a> LoadState<'a> {
     ) -> CodeIndex {
         match compilation_target {
             CompilationTarget::User => self
-                .wam
+                .wam_prelude
                 .indices
                 .code_dir
                 .entry(key)
@@ -624,12 +617,12 @@ impl<'a> LoadState<'a> {
 
     pub(super) fn get_or_insert_qualified_code_index(
         &mut self,
-        module_name: ClauseName,
+        module_name: Atom,
         key: PredicateKey,
     ) -> CodeIndex {
-        if module_name.as_str() == "user" {
+        if module_name == atom!("user") {
             return self
-                .wam
+                .wam_prelude
                 .indices
                 .code_dir
                 .entry(key)
@@ -649,18 +642,18 @@ impl<'a> LoadState<'a> {
     ) {
         match compilation_target {
             CompilationTarget::User => {
-                self.wam
+                self.wam_prelude
                     .indices
                     .extensible_predicates
-                    .insert(key.clone(), skeleton);
+                    .insert(key, skeleton);
 
                 let record =
                     RetractionRecord::AddedExtensiblePredicate(CompilationTarget::User, key);
 
-                self.retraction_info.push_record(record);
+                self.payload.retraction_info.push_record(record);
             }
             CompilationTarget::Module(module_name) => {
-                if let Some(module) = self.wam.indices.modules.get_mut(&module_name) {
+                if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) {
                     module.extensible_predicates.insert(key.clone(), skeleton);
 
                     let record = RetractionRecord::AddedExtensiblePredicate(
@@ -668,7 +661,7 @@ impl<'a> LoadState<'a> {
                         key,
                     );
 
-                    self.retraction_info.push_record(record);
+                    self.payload.retraction_info.push_record(record);
                 } else {
                     unreachable!()
                 }
@@ -685,21 +678,21 @@ impl<'a> LoadState<'a> {
     ) {
         let src_compilation_target = match self.listing_src_file_name() {
             Some(filename) => CompilationTarget::Module(filename),
-            None => self.compilation_target.clone(),
+            None => self.payload.compilation_target,
         };
 
         match src_compilation_target {
             CompilationTarget::User => {
-                self.wam
+                self.wam_prelude
                     .indices
                     .local_extensible_predicates
-                    .insert((local_compilation_target.clone(), key.clone()), skeleton);
+                    .insert((local_compilation_target, key), skeleton);
             }
             CompilationTarget::Module(module_name) => {
-                if let Some(module) = self.wam.indices.modules.get_mut(&module_name) {
+                if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) {
                     module
                         .local_extensible_predicates
-                        .insert((local_compilation_target.clone(), key.clone()), skeleton);
+                        .insert((local_compilation_target, key), skeleton);
                 } else {
                     unreachable!()
                 }
@@ -708,10 +701,13 @@ impl<'a> LoadState<'a> {
     }
 
     pub(super) fn add_op_decl(&mut self, op_decl: &OpDecl) {
-        match &self.compilation_target {
+        let listing_src_file_name = self.listing_src_file_name();
+        let payload_compilation_target = self.payload.compilation_target;
+
+        match payload_compilation_target {
             CompilationTarget::User => {
-                if let Some(filename) = self.listing_src_file_name() {
-                    match self.wam.indices.modules.get_mut(&filename) {
+                if let Some(filename) = listing_src_file_name {
+                    match self.wam_prelude.indices.modules.get_mut(&filename) {
                         Some(ref mut module) => {
                             op_decl.insert_into_op_dir(&mut module.op_dir);
                         }
@@ -720,21 +716,23 @@ impl<'a> LoadState<'a> {
                 }
 
                 add_op_decl(
-                    &mut self.retraction_info,
-                    &self.compilation_target,
-                    &mut self.wam.indices.op_dir,
+                    &mut self.payload.retraction_info,
+                    &payload_compilation_target,
+                    &mut self.wam_prelude.indices.op_dir,
                     op_decl,
                 );
             }
             CompilationTarget::Module(ref module_name) => {
-                match self.wam.indices.modules.get_mut(module_name) {
+                match self.wam_prelude.indices.modules.get_mut(module_name) {
                     Some(ref mut module) => {
+                        let payload: &mut LoadStatePayload<_> = &mut self.payload;
+
                         add_op_decl_as_module_export(
                             &mut module.op_dir,
-                            &self.compilation_target,
-                            &mut self.retraction_info,
-                            &mut self.wam.indices.op_dir,
-                            &mut self.module_op_exports,
+                            &payload.compilation_target,
+                            &mut payload.retraction_info,
+                            &mut self.wam_prelude.indices.op_dir,
+                            &mut payload.module_op_exports,
                             op_decl,
                         );
                     }
@@ -746,28 +744,17 @@ impl<'a> LoadState<'a> {
         }
     }
 
-    pub(super) fn get_clause_type(
-        &mut self,
-        name: ClauseName,
-        arity: usize,
-        fixity: Option<SharedOpDesc>,
-    ) -> ClauseType {
-        match ClauseType::from(name, arity, fixity) {
+    pub(super) fn get_clause_type(&mut self, name: Atom, arity: usize) -> ClauseType {
+        match ClauseType::from(name, arity) {
             ClauseType::Named(name, arity, _) => {
-                let idx = self.get_or_insert_code_index(
-                    (name.clone(), arity),
-                    self.compilation_target.clone(),
-                );
+                let payload_compilation_target = self.payload.compilation_target;
 
-                ClauseType::Named(name, arity, idx)
-            }
-            ClauseType::Op(name, fixity, _) => {
                 let idx = self.get_or_insert_code_index(
-                    (name.clone(), arity),
-                    self.compilation_target.clone(),
+                    (name, arity),
+                    payload_compilation_target,
                 );
 
-                ClauseType::Op(name, fixity, idx)
+                ClauseType::Named(name, arity, idx)
             }
             ct => ct,
         }
@@ -775,92 +762,85 @@ impl<'a> LoadState<'a> {
 
     pub(super) fn get_qualified_clause_type(
         &mut self,
-        module_name: ClauseName,
-        name: ClauseName,
+        module_name: Atom,
+        name: Atom,
         arity: usize,
-        fixity: Option<SharedOpDesc>,
     ) -> ClauseType {
-        match ClauseType::from(name, arity, fixity) {
+        match ClauseType::from(name, arity) {
             ClauseType::Named(name, arity, _) => {
-                let key = (name.clone(), arity);
+                let key = (name, arity);
                 let idx = self.get_or_insert_qualified_code_index(module_name, key);
 
                 ClauseType::Named(name, arity, idx)
             }
-            ClauseType::Op(name, fixity, _) => {
-                let key = (name.clone(), arity);
-                let idx = self.get_or_insert_qualified_code_index(module_name, key);
-
-                ClauseType::Op(name, fixity, idx)
-            }
             ct => ct,
         }
     }
 
     pub(super) fn add_meta_predicate_record(
         &mut self,
-        module_name: ClauseName,
-        name: ClauseName,
+        module_name: Atom,
+        name: Atom,
         meta_specs: Vec<MetaSpec>,
     ) {
         let arity = meta_specs.len();
         let key = (name, arity);
 
-        match module_name.as_str() {
-            "user" => {
+        match module_name {
+            atom!("user") => {
                 match self
-                    .wam
+                    .wam_prelude
                     .indices
                     .meta_predicates
-                    .insert(key.clone(), meta_specs)
+                    .insert(key, meta_specs)
                 {
                     Some(old_meta_specs) => {
-                        self.retraction_info
+                        self.payload.retraction_info
                             .push_record(RetractionRecord::ReplacedMetaPredicate(
-                                module_name.clone(),
+                                module_name,
                                 key.0,
                                 old_meta_specs,
                             ));
                     }
                     None => {
-                        self.retraction_info
+                        self.payload.retraction_info
                             .push_record(RetractionRecord::AddedMetaPredicate(
-                                module_name.clone(),
+                                module_name,
                                 key,
                             ));
                     }
                 }
             }
             _ => {
-                match self.wam.indices.modules.get_mut(&module_name) {
+                match self.wam_prelude.indices.modules.get_mut(&module_name) {
                     Some(ref mut module) => {
                         match module.meta_predicates.insert(key.clone(), meta_specs) {
                             Some(old_meta_specs) => {
-                                self.retraction_info.push_record(
+                                self.payload.retraction_info.push_record(
                                     RetractionRecord::ReplacedMetaPredicate(
-                                        module_name.clone(),
+                                        module_name,
                                         key.0,
                                         old_meta_specs,
                                     ),
                                 );
                             }
                             None => {
-                                self.retraction_info.push_record(
-                                    RetractionRecord::AddedMetaPredicate(module_name.clone(), key),
+                                self.payload.retraction_info.push_record(
+                                    RetractionRecord::AddedMetaPredicate(module_name, key),
                                 );
                             }
                         }
                     }
                     None => {
-                        self.add_dynamically_generated_module(&module_name);
+                        self.add_dynamically_generated_module(module_name);
 
-                        if let Some(module) = self.wam.indices.modules.get_mut(&module_name) {
+                        if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) {
                             module.meta_predicates.insert(key.clone(), meta_specs);
                         } else {
                             unreachable!()
                         }
 
-                        self.retraction_info
+                        self.payload.retraction_info
                             .push_record(RetractionRecord::AddedMetaPredicate(
                                 module_name.clone(),
                                 key,
@@ -871,7 +851,7 @@ impl<'a> LoadState<'a> {
         }
     }
 
-    pub(super) fn add_dynamically_generated_module(&mut self, module_name: &ClauseName) {
+    pub(super) fn add_dynamically_generated_module(&mut self, module_name: Atom) {
         let module_decl = ModuleDecl {
             name: module_name.clone(),
             exports: vec![],
@@ -887,28 +867,28 @@ impl<'a> LoadState<'a> {
             &mut module.meta_predicates,
         );
 
-        self.retraction_info
+        self.payload.retraction_info
             .push_record(RetractionRecord::AddedModule(module_name.clone()));
 
-        self.wam.indices.modules.insert(module_name.clone(), module);
+        self.wam_prelude.indices.modules.insert(module_name.clone(), module);
     }
 
     fn import_builtins_in_module(
         &mut self,
-        module_name: ClauseName,
+        module_name: Atom,
         code_dir: &mut CodeDir,
         op_dir: &mut OpDir,
         meta_predicates: &mut MetaPredicateDir,
     ) {
-        if let Some(builtins) = self.wam.indices.modules.get(&clause_name!("builtins")) {
+        if let Some(builtins) = self.wam_prelude.indices.modules.get(&atom!("builtins")) {
             let module_compilation_target = CompilationTarget::Module(module_name);
 
-            if CompilationTarget::Module(clause_name!("builtins")) == self.compilation_target {
+            if CompilationTarget::Module(atom!("builtins")) == self.payload.compilation_target {
                 return;
             }
 
             import_module_exports(
-                &mut self.retraction_info,
+                &mut self.payload.retraction_info,
                 &module_compilation_target,
                 builtins,
                 code_dir,
@@ -929,7 +909,7 @@ impl<'a> LoadState<'a> {
         self.remove_module_exports(module_name.clone());
         self.remove_replaced_in_situ_module(module_name.clone());
 
-        match self.wam.indices.modules.get_mut(&module_name) {
+        match self.wam_prelude.indices.modules.get_mut(&module_name) {
             Some(module) => {
                 let old_module_decl = mem::replace(&mut module.module_decl, module_decl.clone());
 
@@ -939,14 +919,14 @@ impl<'a> LoadState<'a> {
                 );
 
                 for ((compilation_target, key), skeleton) in local_extensible_predicates.iter() {
-                    self.retract_local_clauses(
-                        compilation_target.clone(),
-                        key.clone(),
+                    self.retract_local_clauses_impl(
+                        *compilation_target,
+                        *key,
                         &skeleton.clause_clause_locs,
                     );
 
                     let is_dynamic = self
-                        .wam
+                        .wam_prelude
                         .indices
                         .get_predicate_skeleton(compilation_target, key)
                         .map(|skeleton| skeleton.core.is_dynamic)
@@ -955,7 +935,7 @@ impl<'a> LoadState<'a> {
                     if is_dynamic {
                         let clause_clause_compilation_target = match compilation_target {
                             CompilationTarget::User => {
-                                CompilationTarget::Module(clause_name!("builtins"))
+                                CompilationTarget::Module(atom!("builtins"))
                             }
                             module => module.clone(),
                         };
@@ -967,7 +947,7 @@ impl<'a> LoadState<'a> {
                     }
                 }
 
-                self.retraction_info
+                self.payload.retraction_info
                     .push_record(RetractionRecord::ReplacedModule(
                         old_module_decl,
                         listing_src.clone(),
@@ -980,15 +960,16 @@ impl<'a> LoadState<'a> {
 
     pub(crate) fn add_module(&mut self, module_decl: ModuleDecl, listing_src: ListingSource) {
         self.reset_in_situ_module(module_decl.clone(), &listing_src);
-        let module_name = module_decl.name.clone();
+        let module_name = module_decl.name;
 
-        let mut module = match self.wam.indices.modules.remove(&module_name) {
+        let mut module = match self.wam_prelude.indices.modules.remove(&module_name) {
             Some(mut module) => {
                 module.listing_src = listing_src;
                 module
             }
             None => {
-                self.retraction_info
+                self.payload
+                    .retraction_info
                     .push_record(RetractionRecord::AddedModule(module_name.clone()));
 
                 Module::new(module_decl, listing_src)
@@ -996,7 +977,7 @@ impl<'a> LoadState<'a> {
         };
 
         self.import_builtins_in_module(
-            module_name.clone(),
+            module_name,
             &mut module.code_dir,
             &mut module.op_dir,
             &mut module.meta_predicates,
@@ -1004,62 +985,68 @@ impl<'a> LoadState<'a> {
 
         for export in &module.module_decl.exports {
             if let ModuleExport::OpDecl(ref op_decl) = export {
+                let payload: &mut LoadStatePayload<_> = &mut self.payload;
+
                 add_op_decl_as_module_export(
                     &mut module.op_dir,
-                    &self.compilation_target, // this is a Module.
-                    &mut self.retraction_info,
-                    &mut self.wam.indices.op_dir,
-                    &mut self.module_op_exports,
+                    &payload.compilation_target, // this is a Module.
+                    &mut payload.retraction_info,
+                    &mut self.wam_prelude.indices.op_dir,
+                    &mut payload.module_op_exports,
                     op_decl,
                 );
             }
         }
 
-        if let Some(load_context) = self.wam.load_contexts.last_mut() {
-            load_context.module = module_name.clone();
+        if let Some(load_context) = self.wam_prelude.load_contexts.last_mut() {
+            load_context.module = module_name;
         }
 
-        self.wam.indices.modules.insert(module_name, module);
+        self.wam_prelude.indices.modules.insert(module_name, module);
     }
 
-    pub(super) fn import_module(&mut self, module_name: ClauseName) -> Result<(), SessionError> {
-        if let Some(module) = self.wam.indices.modules.remove(&module_name) {
-            match &self.compilation_target {
+    pub(super) fn import_module(&mut self, module_name: Atom) -> Result<(), SessionError> {
+        if let Some(module) = self.wam_prelude.indices.modules.remove(&module_name) {
+            let payload_compilation_target = self.payload.compilation_target;
+
+            match &payload_compilation_target {
                 CompilationTarget::User => {
                     import_module_exports(
-                        &mut self.retraction_info,
-                        &self.compilation_target,
+                        &mut self.payload.retraction_info,
+                        &payload_compilation_target,
                         &module,
-                        &mut self.wam.indices.code_dir,
-                        &mut self.wam.indices.op_dir,
-                        &mut self.wam.indices.meta_predicates,
+                        &mut self.wam_prelude.indices.code_dir,
+                        &mut self.wam_prelude.indices.op_dir,
+                        &mut self.wam_prelude.indices.meta_predicates,
                     )?;
                 }
                 CompilationTarget::Module(ref defining_module_name) => {
-                    match self.wam.indices.modules.get_mut(defining_module_name) {
+                    match self.wam_prelude.indices.modules.get_mut(defining_module_name) {
                         Some(ref mut target_module) => {
+                            let payload: &mut LoadStatePayload<_> = &mut self.payload;
+
                             import_module_exports_into_module(
-                                &mut self.retraction_info,
-                                &self.compilation_target,
+                                &mut payload.retraction_info,
+                                &payload_compilation_target,
                                 &module,
                                 &mut target_module.code_dir,
                                 &mut target_module.op_dir,
                                 &mut target_module.meta_predicates,
-                                &mut self.wam.indices.op_dir,
-                                &mut self.module_op_exports,
+                                &mut self.wam_prelude.indices.op_dir,
+                                &mut payload.module_op_exports,
                             )?;
                         }
                         None => {
                             // we find ourselves here because we're trying to import
                             // a module into itself as it is being defined.
-                            self.wam.indices.modules.insert(module_name.clone(), module);
+                            self.wam_prelude.indices.modules.insert(module_name, module);
                             return Err(SessionError::ModuleCannotImportSelf(module_name));
                         }
                     }
                 }
             }
 
-            self.wam.indices.modules.insert(module_name, module);
+            self.wam_prelude.indices.modules.insert(module_name, module);
             Ok(())
         } else {
             Err(SessionError::ExistenceError(ExistenceError::Module(
@@ -1070,53 +1057,55 @@ impl<'a> LoadState<'a> {
 
     pub(super) fn import_qualified_module(
         &mut self,
-        module_name: ClauseName,
+        module_name: Atom,
         exports: IndexSet<ModuleExport>,
     ) -> Result<(), SessionError> {
-        if let Some(module) = self.wam.indices.modules.remove(&module_name) {
-            match &self.compilation_target {
+        if let Some(module) = self.wam_prelude.indices.modules.remove(&module_name) {
+            let payload_compilation_target = self.payload.compilation_target;
+
+            match &payload_compilation_target {
                 CompilationTarget::User => {
                     import_qualified_module_exports(
-                        &mut self.retraction_info,
-                        &self.compilation_target,
+                        &mut self.payload.retraction_info,
+                        &payload_compilation_target,
                         &module,
                         &exports,
-                        &mut self.wam.indices.code_dir,
-                        &mut self.wam.indices.op_dir,
-                        &mut self.wam.indices.meta_predicates,
+                        &mut self.wam_prelude.indices.code_dir,
+                        &mut self.wam_prelude.indices.op_dir,
+                        &mut self.wam_prelude.indices.meta_predicates,
                     )?;
                 }
                 CompilationTarget::Module(ref defining_module_name) => {
-                    match self.wam.indices.modules.get_mut(defining_module_name) {
+                    match self.wam_prelude.indices.modules.get_mut(defining_module_name) {
                         Some(ref mut target_module) => {
+                            let payload: &mut LoadStatePayload<_> = &mut self.payload;
+
                             import_qualified_module_exports_into_module(
-                                &mut self.retraction_info,
-                                &self.compilation_target,
+                                &mut payload.retraction_info,
+                                &payload_compilation_target,
                                 &module,
                                 &exports,
                                 &mut target_module.code_dir,
                                 &mut target_module.op_dir,
                                 &mut target_module.meta_predicates,
-                                &mut self.wam.indices.op_dir,
-                                &mut self.module_op_exports,
+                                &mut self.wam_prelude.indices.op_dir,
+                                &mut payload.module_op_exports,
                             )?;
                         }
                         None => {
                             // we find ourselves here because we're trying to import
                             // a module into itself as it is being defined.
-                            self.wam.indices.modules.insert(module_name.clone(), module);
+                            self.wam_prelude.indices.modules.insert(module_name, module);
                             return Err(SessionError::ModuleCannotImportSelf(module_name));
                         }
                     }
                 }
             }
 
-            self.wam.indices.modules.insert(module_name, module);
+            self.wam_prelude.indices.modules.insert(module_name, module);
             Ok(())
         } else {
-            Err(SessionError::ExistenceError(ExistenceError::Module(
-                module_name,
-            )))
+            Err(SessionError::ExistenceError(ExistenceError::Module(module_name)))
         }
     }
 
@@ -1125,23 +1114,30 @@ impl<'a> LoadState<'a> {
             ModuleSource::File(filename) => {
                 let mut path_buf = PathBuf::from(filename.as_str());
                 path_buf.set_extension("pl");
+
                 let file = File::open(&path_buf)?;
 
                 (
-                    Stream::from_file_as_input(filename.clone(), file),
+                    Stream::from_file_as_input(filename, file, &mut LS::machine_st(&mut self.payload).arena),
                     ListingSource::File(filename, path_buf),
                 )
             }
             ModuleSource::Library(library) => match LIBRARIES.borrow().get(library.as_str()) {
                 Some(code) => {
-                    if let Some(ref module) = self.wam.indices.modules.get(&library) {
+                    if let Some(ref module) = self.wam_prelude.indices.modules.get(&library) {
                         if let ListingSource::DynamicallyGenerated = &module.listing_src {
-                            (Stream::from(*code), ListingSource::User)
+                            (
+                                Stream::from_static_string(*code, &mut LS::machine_st(&mut self.payload).arena),
+                                ListingSource::User,
+                            )
                         } else {
                             return self.import_module(library);
                         }
                     } else {
-                        (Stream::from(*code), ListingSource::User)
+                        (
+                            Stream::from_static_string(*code, &mut LS::machine_st(&mut self.payload).arena),
+                            ListingSource::User,
+                        )
                     }
                 }
                 None => {
@@ -1151,16 +1147,23 @@ impl<'a> LoadState<'a> {
         };
 
         let compilation_target = {
-            let stream = &mut parsing_stream(stream)?;
-
-            let ts = BootstrappingTermStream::from_prolog_stream(
+            let term_stream = BootstrappingTermStream::from_char_reader(
                 stream,
-                self.wam.machine_st.atom_tbl.clone(),
-                self.wam.machine_st.flags,
+                LS::machine_st(&mut self.payload),
                 listing_src,
             );
 
-            let subloader = Loader::new(ts, self.wam);
+            let subloader: Loader<'_, BootstrappingLoadState> = Loader {
+                payload: BootstrappingLoadState(
+                    LoadStatePayload::new(self.wam_prelude.code_repo.code.len(), term_stream)
+                ),
+                wam_prelude: MachinePreludeView {
+                    indices: self.wam_prelude.indices,
+                    code_repo: self.wam_prelude.code_repo,
+                    load_contexts: self.wam_prelude.load_contexts,
+                }
+            };
+
             subloader.load()?
         };
 
@@ -1185,16 +1188,19 @@ impl<'a> LoadState<'a> {
                 let file = File::open(&path_buf)?;
 
                 (
-                    Stream::from_file_as_input(filename.clone(), file),
+                    Stream::from_file_as_input(filename, file, &mut LS::machine_st(&mut self.payload).arena),
                     ListingSource::File(filename, path_buf),
                 )
             }
             ModuleSource::Library(library) => match LIBRARIES.borrow().get(library.as_str()) {
                 Some(code) => {
-                    if self.wam.indices.modules.contains_key(&library) {
+                    if self.wam_prelude.indices.modules.contains_key(&library) {
                         return self.import_qualified_module(library, exports);
                     } else {
-                        (Stream::from(*code), ListingSource::User)
+                        (
+                            Stream::from_static_string(*code, &mut LS::machine_st(&mut self.payload).arena),
+                            ListingSource::User,
+                        )
                     }
                 }
                 None => {
@@ -1204,16 +1210,23 @@ impl<'a> LoadState<'a> {
         };
 
         let compilation_target = {
-            let stream = &mut parsing_stream(stream)?;
-
-            let ts = BootstrappingTermStream::from_prolog_stream(
+            let term_stream = BootstrappingTermStream::from_char_reader(
                 stream,
-                self.wam.machine_st.atom_tbl.clone(),
-                self.wam.machine_st.flags,
+                LS::machine_st(&mut self.payload),
                 listing_src,
             );
 
-            let subloader = Loader::new(ts, self.wam);
+            let subloader: Loader<'_, BootstrappingLoadState> = Loader {
+                payload: BootstrappingLoadState(
+                    LoadStatePayload::new(self.wam_prelude.code_repo.code.len(), term_stream),
+                ),
+                wam_prelude: MachinePreludeView {
+                    indices: self.wam_prelude.indices,
+                    code_repo: self.wam_prelude.code_repo,
+                    load_contexts: self.wam_prelude.load_contexts,
+                }
+            };
+
             subloader.load()?
         };
 
@@ -1227,21 +1240,4 @@ impl<'a> LoadState<'a> {
             }
         }
     }
-
-    #[inline]
-    pub(super) fn composite_op_dir(&self) -> CompositeOpDir {
-        match &self.compilation_target {
-            CompilationTarget::User => CompositeOpDir::new(&self.wam.indices.op_dir, None),
-            CompilationTarget::Module(ref module_name) => {
-                match self.wam.indices.modules.get(module_name) {
-                    Some(ref module) => {
-                        CompositeOpDir::new(&self.wam.indices.op_dir, Some(&module.op_dir))
-                    }
-                    None => {
-                        unreachable!()
-                    }
-                }
-            }
-        }
-    }
 }
index 6f48b1d9da12d322cbe7c906e617d99f0a7eabb7..b2f43e543ae5239141aaac60daf7c354d7b4757d 100644 (file)
@@ -1,18 +1,26 @@
-use prolog_parser::ast::*;
-use prolog_parser::{clause_name, temp_v};
-
+use crate::arena::*;
+use crate::atom_table::*;
+use crate::clause_types::*;
 use crate::forms::*;
+use crate::heap_iter::*;
 use crate::indexing::*;
+use crate::instructions::*;
 use crate::machine::load_state::*;
+use crate::machine::machine_errors::*;
 use crate::machine::machine_indices::*;
 use crate::machine::preprocessor::*;
+use crate::machine::term_stream::*;
 use crate::machine::*;
+use crate::parser::ast::*;
+use crate::types::*;
 
 use indexmap::IndexSet;
 use slice_deque::{sdeq, SliceDeque};
 
 use std::cell::Cell;
 use std::convert::TryFrom;
+use std::mem;
+use std::ops::{Deref, DerefMut};
 use std::rc::Rc;
 
 /*
@@ -44,19 +52,19 @@ use std::rc::Rc;
 
 #[derive(Debug)]
 pub(crate) enum RetractionRecord {
-    AddedMetaPredicate(ClauseName, PredicateKey),
-    ReplacedMetaPredicate(ClauseName, ClauseName, Vec<MetaSpec>),
-    AddedModule(ClauseName),
+    AddedMetaPredicate(Atom, PredicateKey),
+    ReplacedMetaPredicate(Atom, Atom, Vec<MetaSpec>),
+    AddedModule(Atom),
     ReplacedModule(ModuleDecl, ListingSource, LocalExtensiblePredicates),
-    AddedModuleOp(ClauseName, OpDecl),
-    ReplacedModuleOp(ClauseName, OpDecl, usize, Specifier),
-    AddedModulePredicate(ClauseName, PredicateKey),
-    ReplacedModulePredicate(ClauseName, PredicateKey, IndexPtr),
+    AddedModuleOp(Atom, OpDecl),
+    ReplacedModuleOp(Atom, OpDecl, OpDesc),
+    AddedModulePredicate(Atom, PredicateKey),
+    ReplacedModulePredicate(Atom, PredicateKey, IndexPtr),
     AddedDiscontiguousPredicate(CompilationTarget, PredicateKey),
     AddedDynamicPredicate(CompilationTarget, PredicateKey),
     AddedMultifilePredicate(CompilationTarget, PredicateKey),
     AddedUserOp(OpDecl),
-    ReplacedUserOp(OpDecl, usize, Specifier),
+    ReplacedUserOp(OpDecl, OpDesc),
     AddedExtensiblePredicate(CompilationTarget, PredicateKey),
     AddedUserPredicate(PredicateKey),
     ReplacedUserPredicate(PredicateKey, IndexPtr),
@@ -75,6 +83,7 @@ pub(crate) enum RetractionRecord {
     SkeletonLocalClauseTruncateBack(CompilationTarget, CompilationTarget, PredicateKey, usize),
     SkeletonClauseTruncateBack(CompilationTarget, PredicateKey, usize),
     SkeletonClauseStartReplaced(CompilationTarget, PredicateKey, usize, usize),
+    RemovedDynamicSkeletonClause(CompilationTarget, PredicateKey, usize, usize),
     RemovedSkeletonClause(
         CompilationTarget,
         PredicateKey,
@@ -113,7 +122,7 @@ impl RetractionInfo {
     pub(super) fn new(orig_code_extent: usize) -> Self {
         Self {
             orig_code_extent,
-            records: vec![], //BTreeMap::new(),
+            records: vec![],
         }
     }
 
@@ -134,16 +143,397 @@ impl RetractionInfo {
     }
 }
 
-impl<'a> Drop for LoadState<'a> {
+impl<'a, LS: LoadState<'a>> Drop for Loader<'a, LS> {
     fn drop(&mut self) {
-        while let Some(record) = self.retraction_info.records.pop() {
+        if LS::should_drop_load_state(self) {
+            LS::reset_machine(self);
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
+pub enum CompilationTarget {
+    Module(Atom),
+    User,
+}
+
+impl Default for CompilationTarget {
+    #[inline]
+    fn default() -> Self {
+        CompilationTarget::User
+    }
+}
+
+impl CompilationTarget {
+    #[inline]
+    pub(crate) fn module_name(&self) -> Atom {
+        match self {
+            CompilationTarget::User => {
+                atom!("user")
+            }
+            CompilationTarget::Module(module_name) => *module_name,
+        }
+    }
+}
+
+pub struct PredicateQueue {
+    pub(super) predicates: Vec<Term>,
+    pub(super) compilation_target: CompilationTarget,
+}
+
+impl PredicateQueue {
+    #[inline]
+    pub(super) fn push(&mut self, clause: Term) {
+        self.predicates.push(clause);
+    }
+
+    #[inline]
+    pub(crate) fn first(&self) -> Option<&Term> {
+        self.predicates.first()
+    }
+
+    #[inline]
+    pub(crate) fn is_empty(&self) -> bool {
+        self.predicates.is_empty()
+    }
+
+    #[inline]
+    pub(super) fn take(&mut self) -> Self {
+        Self {
+            predicates: mem::replace(&mut self.predicates, vec![]),
+            compilation_target: self.compilation_target.clone(),
+        }
+    }
+
+    #[inline]
+    pub(super) fn len(&self) -> usize {
+        self.predicates.len()
+    }
+}
+
+#[macro_export]
+macro_rules! predicate_queue {
+    [$($v:expr),*] => (
+        PredicateQueue {
+            predicates: vec![$($v,)*],
+            compilation_target: CompilationTarget::default(),
+        }
+    )
+}
+
+pub type LiveLoadState = LoadStatePayload<LiveTermStream>;
+
+pub struct BootstrappingLoadState<'a>(
+    pub LoadStatePayload<BootstrappingTermStream<'a>>
+);
+
+impl<'a> Deref for BootstrappingLoadState<'a> {
+    type Target = LoadStatePayload<BootstrappingTermStream<'a>>;
+
+    #[inline(always)]
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<'a> DerefMut for BootstrappingLoadState<'a> {
+    #[inline(always)]
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
+pub trait LoadState<'a>: Sized {
+    type Evacuable;
+    type TS: TermStream;
+    type LoaderFieldType: DerefMut<Target=LoadStatePayload<Self::TS>>;
+
+    fn new(machine_st: &'a mut MachineState, payload: LoadStatePayload<Self::TS>) -> Self::LoaderFieldType;
+    fn evacuate(loader: Loader<'a, Self>) -> Result<Self::Evacuable, SessionError>;
+    fn should_drop_load_state(loader: &Loader<'a, Self>) -> bool;
+    fn reset_machine(loader: &mut Loader<'a, Self>);
+    fn machine_st(loader: &mut Self::LoaderFieldType) -> &mut MachineState;
+}
+
+pub struct LiveLoadAndMachineState<'a> {
+    load_state: TypedArenaPtr<LiveLoadState>,
+    machine_st: &'a mut MachineState,
+}
+
+impl<'a> Deref for LiveLoadAndMachineState<'a> {
+    type Target = LiveLoadState;
+
+    #[inline(always)]
+    fn deref(&self) -> &Self::Target {
+        &self.load_state
+    }
+}
+
+impl<'a> DerefMut for LiveLoadAndMachineState<'a> {
+    #[inline(always)]
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.load_state
+    }
+}
+
+impl<'a> LoadState<'a> for LiveLoadAndMachineState<'a> {
+    type TS = LiveTermStream;
+    type LoaderFieldType = LiveLoadAndMachineState<'a>;
+    type Evacuable = TypedArenaPtr<LiveLoadState>;
+
+    #[inline(always)]
+    fn new(machine_st: &'a mut MachineState, payload: LoadStatePayload<Self::TS>) -> Self::LoaderFieldType {
+        let load_state = arena_alloc!(payload, &mut machine_st.arena);
+        LiveLoadAndMachineState { load_state, machine_st }
+    }
+
+    #[inline(always)]
+    fn evacuate(mut loader: Loader<'a, Self>) -> Result<Self::Evacuable, SessionError> {
+        loader.payload.load_state.set_tag(ArenaHeaderTag::InactiveLoadState);
+        Ok(loader.payload.load_state)
+    }
+
+    #[inline(always)]
+    fn should_drop_load_state(loader: &Loader<'a, Self>) -> bool {
+        loader.payload.load_state.get_tag() == ArenaHeaderTag::LiveLoadState
+    }
+
+    #[inline(always)]
+    fn reset_machine(loader: &mut Loader<'a, Self>) {
+        if loader.payload.load_state.get_tag() != ArenaHeaderTag::Dropped {
+            loader.payload.load_state.set_tag(ArenaHeaderTag::Dropped);
+            loader.reset_machine();
+        }
+    }
+
+    #[inline(always)]
+    fn machine_st(loader: &mut Self::LoaderFieldType) -> &mut MachineState {
+        loader.machine_st
+    }
+}
+
+impl<'a> LoadState<'a> for BootstrappingLoadState<'a> {
+    type TS = BootstrappingTermStream<'a>;
+    type LoaderFieldType = BootstrappingLoadState<'a>;
+    type Evacuable = CompilationTarget;
+
+    #[inline(always)]
+    fn new(_: &'a mut MachineState, payload: LoadStatePayload<Self::TS>) -> Self::LoaderFieldType {
+        BootstrappingLoadState(payload)
+    }
+
+    fn evacuate(mut loader: Loader<'a, Self>) -> Result<Self::Evacuable, SessionError> {
+        if !loader.payload.predicates.is_empty() {
+            loader.compile_and_submit()?;
+        }
+
+        let repo_len = loader.wam_prelude.code_repo.code.len();
+
+        loader
+            .payload
+            .retraction_info
+            .reset(repo_len);
+
+        loader.remove_module_op_exports();
+
+        Ok(loader.payload.compilation_target)
+    }
+
+    #[inline(always)]
+    fn should_drop_load_state(_loader: &Loader<'a, Self>) -> bool {
+        true
+    }
+
+    #[inline(always)]
+    fn reset_machine(loader: &mut Loader<'a, Self>) {
+        loader.reset_machine();
+    }
+
+    #[inline(always)]
+    fn machine_st(loader: &mut Self::LoaderFieldType) -> &mut MachineState {
+        &mut loader.term_stream.parser.lexer.machine_st
+    }
+}
+
+pub struct Loader<'a, LS: LoadState<'a>> {
+    pub(super) payload: LS::LoaderFieldType,
+    pub(super) wam_prelude: MachinePreludeView<'a>,
+}
+
+impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
+    #[inline]
+    pub(super) fn new(wam: &'a mut Machine, term_stream: <LS as LoadState<'a>>::TS) -> Self {
+        let payload = LoadStatePayload::new(wam.code_repo.code.len(), term_stream);
+        let (wam_prelude, machine_st) = wam.prelude_view_and_machine_st();
+
+        Self {
+            payload: LS::new(machine_st, payload),
+            wam_prelude,
+        }
+    }
+
+    pub(crate) fn load(mut self) -> Result<LS::Evacuable, SessionError> {
+        while let Some(decl) = self.dequeue_terms()? {
+            self.load_decl(decl)?;
+        }
+
+        LS::evacuate(self)
+    }
+
+    /*
+    #[inline(always)]
+    pub(crate) fn load_state(&mut self) -> &mut LoadStatePayload<<LS as LoadState<'a>>::TS> {
+        self.payload.as_mut()
+    }
+    */
+
+    fn dequeue_terms(&mut self) -> Result<Option<Declaration>, SessionError> {
+        while !self.payload.term_stream.eof()? {
+            let load_state = &mut self.payload;
+            let compilation_target = &load_state.compilation_target;
+            let composite_op_dir = self.wam_prelude.composite_op_dir(compilation_target);
+
+            let term = load_state.term_stream.next(&composite_op_dir)?;
+
+            // if is_consistent is false, self.predicates is not empty.
+            if !term.is_consistent(&load_state.predicates) {
+                self.compile_and_submit()?;
+            }
+
+            let term = match term {
+                Term::Clause(_, name, terms) if name == atom!(":-") && terms.len() == 1 => {
+                    return Ok(Some(setup_declaration(self, terms)?));
+                }
+                term => term,
+            };
+
+            self.payload.predicates.push(term);
+        }
+
+        Ok(None)
+    }
+
+    pub(super) fn load_decl(&mut self, decl: Declaration) -> Result<(), SessionError> {
+        match decl {
+            Declaration::Dynamic(name, arity) => {
+                let compilation_target = self.payload.compilation_target;
+                self.add_dynamic_predicate(compilation_target, name, arity)?;
+            }
+            Declaration::MetaPredicate(module_name, name, meta_specs) => {
+                self.add_meta_predicate_record(module_name, name, meta_specs);
+            }
+            Declaration::Module(module_decl) => {
+                self.payload.compilation_target = CompilationTarget::Module(module_decl.name);
+                self.payload.predicates.compilation_target = self.payload.compilation_target;
+
+                let listing_src = self.payload.term_stream.listing_src().clone();
+                self.add_module(module_decl, listing_src);
+            }
+            Declaration::NonCountedBacktracking(name, arity) => {
+                self.payload.non_counted_bt_preds.insert((name, arity));
+            }
+            Declaration::Op(op_decl) => {
+                self.add_op_decl(&op_decl);
+            }
+            Declaration::UseModule(module_src) => {
+                self.use_module(module_src)?;
+            }
+            Declaration::UseQualifiedModule(module_src, exports) => {
+                self.use_qualified_module(module_src, exports)?;
+            }
+        }
+
+        Ok(())
+    }
+
+    pub(super) fn read_term_from_heap(&mut self, heap_term_loc: RegType) -> Result<Term, SessionError> {
+        let machine_st = LS::machine_st(&mut self.payload);
+        let term_addr = machine_st[heap_term_loc];
+
+        /*
+        if let Ok(lit) = Literal::try_from(term_addr) {
+            return Ok(Term::Literal(Cell::default(), lit));
+        } else if machine_st.is_cyclic_term(term_addr) {
+            return Err(SessionError::from(CompilationError::CannotParseCyclicTerm));
+        }
+        */
+
+        let mut term_stack = vec![];
+        let mut iter = stackful_post_order_iter(&mut machine_st.heap, term_addr);
+
+        while let Some(addr) = iter.next() {
+            read_heap_cell!(addr,
+                (HeapCellValueTag::Lis) => {
+                    let tail = term_stack.pop().unwrap();
+                    let head = term_stack.pop().unwrap();
+
+                    term_stack.push(Term::Cons(Cell::default(), Box::new(head), Box::new(tail)));
+                }
+                (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar, h) => {
+                    let offset_string = format!("_{}", h);
+                    term_stack.push(Term::Var(Cell::default(), Rc::new(offset_string)));
+                }
+                (HeapCellValueTag::Cons | HeapCellValueTag::CStr | HeapCellValueTag::Fixnum |
+                 HeapCellValueTag::Char | HeapCellValueTag::F64) => {
+                   let addr = unmark_cell_bits!(addr);
+                    term_stack.push(Term::Literal(Cell::default(), Literal::try_from(addr).unwrap()));
+                }
+                (HeapCellValueTag::Atom, (name, arity)) => {
+                    if arity == 0 {
+                        term_stack.push(Term::Literal(Cell::default(), Literal::Atom(name)));
+                    } else {
+                        let subterms = term_stack
+                            .drain(term_stack.len() - arity ..)
+                            .collect();
+
+                        term_stack.push(Term::Clause(Cell::default(), name, subterms));
+                    }
+                }
+                (HeapCellValueTag::PStr, string) => {
+                    let tail = term_stack.pop().unwrap();
+
+                    if let Term::Literal(_, Literal::Atom(atom!("[]"))) = &tail {
+                        term_stack.push(Term::PartialString(
+                            Cell::default(),
+                            string,
+                            None,
+                        ));
+                    } else {
+                        term_stack.push(Term::PartialString(
+                            Cell::default(),
+                            string,
+                            Some(Box::new(tail)),
+                        ));
+                    }
+                }
+                (HeapCellValueTag::PStrOffset, h) => {
+                    let string = cell_as_atom_cell!(iter.heap[h]).get_name();
+                    let tail = term_stack.pop().unwrap();
+
+                    term_stack.push(Term::PartialString(
+                        Cell::default(),
+                        string,
+                        Some(Box::new(tail)),
+                    ));
+                }
+                _ => {
+                }
+            );
+        }
+
+        debug_assert!(term_stack.len() == 1);
+        Ok(term_stack.pop().unwrap())
+    }
+
+    fn reset_machine(&mut self) {
+        while let Some(record) = self.payload.retraction_info.records.pop() {
             match record {
                 RetractionRecord::AddedMetaPredicate(target_module_name, key) => {
-                    match target_module_name.as_str() {
-                        "user" => {
-                            self.wam.indices.meta_predicates.remove(&key);
+                    match target_module_name {
+                        atom!("user") => {
+                            self.wam_prelude.indices.meta_predicates.remove(&key);
                         }
-                        _ => match self.wam.indices.modules.get_mut(&target_module_name) {
+                        _ => match self.wam_prelude.indices.modules.get_mut(&target_module_name) {
                             Some(ref mut module) => {
                                 module.meta_predicates.remove(&key);
                             }
@@ -154,14 +544,14 @@ impl<'a> Drop for LoadState<'a> {
                     }
                 }
                 RetractionRecord::ReplacedMetaPredicate(target_module_name, name, meta_specs) => {
-                    match target_module_name.as_str() {
-                        "user" => {
-                            self.wam
+                    match target_module_name {
+                        atom!("user") => {
+                            self.wam_prelude
                                 .indices
                                 .meta_predicates
                                 .insert((name, meta_specs.len()), meta_specs);
                         }
-                        _ => match self.wam.indices.modules.get_mut(&target_module_name) {
+                        _ => match self.wam_prelude.indices.modules.get_mut(&target_module_name) {
                             Some(ref mut module) => {
                                 module
                                     .meta_predicates
@@ -174,13 +564,13 @@ impl<'a> Drop for LoadState<'a> {
                     }
                 }
                 RetractionRecord::AddedModule(module_name) => {
-                    self.wam.indices.modules.remove(&module_name);
+                    self.wam_prelude.indices.modules.remove(&module_name);
                 }
                 RetractionRecord::ReplacedModule(
                     module_decl,
                     listing_src,
                     local_extensible_predicates,
-                ) => match self.wam.indices.modules.get_mut(&module_decl.name) {
+                ) => match self.wam_prelude.indices.modules.get_mut(&module_decl.name) {
                     Some(ref mut module) => {
                         module.module_decl = module_decl;
                         module.listing_src = listing_src;
@@ -193,7 +583,7 @@ impl<'a> Drop for LoadState<'a> {
                 RetractionRecord::AddedDiscontiguousPredicate(compilation_target, key) => {
                     match compilation_target {
                         CompilationTarget::User => {
-                            self.wam
+                            self.wam_prelude
                                 .indices
                                 .extensible_predicates
                                 .get_mut(&key)
@@ -202,7 +592,7 @@ impl<'a> Drop for LoadState<'a> {
                                 });
                         }
                         CompilationTarget::Module(module_name) => {
-                            match self.wam.indices.modules.get_mut(&module_name) {
+                            match self.wam_prelude.indices.modules.get_mut(&module_name) {
                                 Some(ref mut module) => {
                                     module.extensible_predicates.get_mut(&key).map(|skeleton| {
                                         skeleton.core.is_discontiguous = false;
@@ -216,7 +606,7 @@ impl<'a> Drop for LoadState<'a> {
                 RetractionRecord::AddedDynamicPredicate(compilation_target, key) => {
                     match compilation_target {
                         CompilationTarget::User => {
-                            self.wam
+                            self.wam_prelude
                                 .indices
                                 .extensible_predicates
                                 .get_mut(&key)
@@ -225,10 +615,11 @@ impl<'a> Drop for LoadState<'a> {
                                 });
                         }
                         CompilationTarget::Module(module_name) => {
-                            match self.wam.indices.modules.get_mut(&module_name) {
+                            match self.wam_prelude.indices.modules.get_mut(&module_name) {
                                 Some(ref mut module) => {
                                     module.extensible_predicates.get_mut(&key).map(|skeleton| {
                                         skeleton.core.is_dynamic = false;
+                                        skeleton.core.retracted_dynamic_clauses = None;
                                     });
                                 }
                                 None => {}
@@ -239,7 +630,7 @@ impl<'a> Drop for LoadState<'a> {
                 RetractionRecord::AddedMultifilePredicate(compilation_target, key) => {
                     match compilation_target {
                         CompilationTarget::User => {
-                            self.wam
+                            self.wam_prelude
                                 .indices
                                 .extensible_predicates
                                 .get_mut(&key)
@@ -248,7 +639,7 @@ impl<'a> Drop for LoadState<'a> {
                                 });
                         }
                         CompilationTarget::Module(module_name) => {
-                            match self.wam.indices.modules.get_mut(&module_name) {
+                            match self.wam_prelude.indices.modules.get_mut(&module_name) {
                                 Some(ref mut module) => {
                                     module.extensible_predicates.get_mut(&key).map(|skeleton| {
                                         skeleton.core.is_multifile = false;
@@ -260,25 +651,24 @@ impl<'a> Drop for LoadState<'a> {
                     }
                 }
                 RetractionRecord::AddedModuleOp(module_name, mut op_decl) => {
-                    match self.wam.indices.modules.get_mut(&module_name) {
+                    match self.wam_prelude.indices.modules.get_mut(&module_name) {
                         Some(ref mut module) => {
                             op_decl.remove(&mut module.op_dir);
                         }
                         None => {}
                     }
                 }
-                RetractionRecord::ReplacedModuleOp(module_name, mut op_decl, prec, spec) => {
-                    match self.wam.indices.modules.get_mut(&module_name) {
+                RetractionRecord::ReplacedModuleOp(module_name, mut op_decl, op_desc) => {
+                    match self.wam_prelude.indices.modules.get_mut(&module_name) {
                         Some(ref mut module) => {
-                            op_decl.prec = prec;
-                            op_decl.spec = spec;
+                            op_decl.op_desc = op_desc;
                             op_decl.insert_into_op_dir(&mut module.op_dir);
                         }
                         None => {}
                     }
                 }
                 RetractionRecord::AddedModulePredicate(module_name, key) => {
-                    match self.wam.indices.modules.get_mut(&module_name) {
+                    match self.wam_prelude.indices.modules.get_mut(&module_name) {
                         Some(ref mut module) => {
                             module.code_dir.remove(&key);
                         }
@@ -286,7 +676,7 @@ impl<'a> Drop for LoadState<'a> {
                     }
                 }
                 RetractionRecord::ReplacedModulePredicate(module_name, key, old_code_idx) => {
-                    match self.wam.indices.modules.get_mut(&module_name) {
+                    match self.wam_prelude.indices.modules.get_mut(&module_name) {
                         Some(ref mut module) => {
                             module
                                 .code_dir
@@ -297,23 +687,22 @@ impl<'a> Drop for LoadState<'a> {
                     }
                 }
                 RetractionRecord::AddedExtensiblePredicate(compilation_target, key) => {
-                    self.wam
+                    self.wam_prelude
                         .indices
                         .remove_predicate_skeleton(&compilation_target, &key);
                 }
                 RetractionRecord::AddedUserOp(mut op_decl) => {
-                    op_decl.remove(&mut self.wam.indices.op_dir);
+                    op_decl.remove(&mut self.wam_prelude.indices.op_dir);
                 }
-                RetractionRecord::ReplacedUserOp(mut op_decl, prec, spec) => {
-                    op_decl.prec = prec;
-                    op_decl.spec = spec;
-                    op_decl.insert_into_op_dir(&mut self.wam.indices.op_dir);
+                RetractionRecord::ReplacedUserOp(mut op_decl, op_desc) => {
+                    op_decl.op_desc = op_desc;
+                    op_decl.insert_into_op_dir(&mut self.wam_prelude.indices.op_dir);
                 }
                 RetractionRecord::AddedUserPredicate(key) => {
-                    self.wam.indices.code_dir.remove(&key);
+                    self.wam_prelude.indices.code_dir.remove(&key);
                 }
                 RetractionRecord::ReplacedUserPredicate(key, old_code_idx) => {
-                    self.wam
+                    self.wam_prelude
                         .indices
                         .code_dir
                         .get_mut(&key)
@@ -322,7 +711,7 @@ impl<'a> Drop for LoadState<'a> {
                 RetractionRecord::AddedIndex(index_key, clause_loc) => {
                     // WAS: inner_index_locs) => {
                     if let Some(index_loc) = index_key.switch_on_term_loc() {
-                        let indexing_code = match &mut self.wam.code_repo.code[index_loc] {
+                        let indexing_code = match &mut self.wam_prelude.code_repo.code[index_loc] {
                             Line::IndexingCode(indexing_code) => indexing_code,
                             _ => {
                                 unreachable!()
@@ -330,14 +719,14 @@ impl<'a> Drop for LoadState<'a> {
                         };
 
                         match index_key {
-                            OptArgIndexKey::Constant(
+                            OptArgIndexKey::Literal(
                                 _,
                                 index_loc,
                                 constant,
                                 overlapping_constants,
                             ) => {
                                 remove_constant_indices(
-                                    &constant,
+                                    constant,
                                     &overlapping_constants,
                                     indexing_code,
                                     clause_loc - index_loc, // WAS: &inner_index_locs,
@@ -345,7 +734,7 @@ impl<'a> Drop for LoadState<'a> {
                             }
                             OptArgIndexKey::Structure(_, index_loc, name, arity) => {
                                 remove_structure_index(
-                                    &name,
+                                    name,
                                     arity,
                                     indexing_code,
                                     clause_loc - index_loc, // WAS: &inner_index_locs,
@@ -369,7 +758,7 @@ impl<'a> Drop for LoadState<'a> {
                     // write the retraction logic of this arm.
                 }
                 RetractionRecord::ReplacedChoiceOffset(instr_loc, offset) => {
-                    match &mut self.wam.code_repo.code[instr_loc] {
+                    match self.wam_prelude.code_repo.code[instr_loc] {
                         Line::Choice(ChoiceInstruction::TryMeElse(ref mut o))
                         | Line::Choice(ChoiceInstruction::RetryMeElse(ref mut o))
                         | Line::Choice(ChoiceInstruction::DefaultRetryMeElse(ref mut o)) => {
@@ -381,7 +770,7 @@ impl<'a> Drop for LoadState<'a> {
                     }
                 }
                 RetractionRecord::AppendedTrustMe(instr_loc, offset, is_default) => {
-                    match &mut self.wam.code_repo.code[instr_loc] {
+                    match self.wam_prelude.code_repo.code[instr_loc] {
                         Line::Choice(ref mut choice_instr) => {
                             *choice_instr = if is_default {
                                 ChoiceInstruction::DefaultTrustMe(offset)
@@ -395,7 +784,7 @@ impl<'a> Drop for LoadState<'a> {
                     }
                 }
                 RetractionRecord::ReplacedSwitchOnTermVarIndex(index_loc, old_v) => {
-                    match &mut self.wam.code_repo.code[index_loc] {
+                    match self.wam_prelude.code_repo.code[index_loc] {
                         Line::IndexingCode(ref mut indexing_code) => match &mut indexing_code[0] {
                             IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
                                 _,
@@ -410,20 +799,20 @@ impl<'a> Drop for LoadState<'a> {
                     }
                 }
                 RetractionRecord::ModifiedTryMeElse(instr_loc, o) => {
-                    self.wam.code_repo.code[instr_loc] =
+                    self.wam_prelude.code_repo.code[instr_loc] =
                         Line::Choice(ChoiceInstruction::TryMeElse(o));
                 }
                 RetractionRecord::ModifiedRetryMeElse(instr_loc, o) => {
-                    self.wam.code_repo.code[instr_loc] =
+                    self.wam_prelude.code_repo.code[instr_loc] =
                         Line::Choice(ChoiceInstruction::RetryMeElse(o));
                 }
                 RetractionRecord::ModifiedRevJmpBy(instr_loc, o) => {
-                    self.wam.code_repo.code[instr_loc] =
+                    self.wam_prelude.code_repo.code[instr_loc] =
                         Line::Control(ControlInstruction::RevJmpBy(o));
                 }
                 RetractionRecord::SkeletonClausePopBack(compilation_target, key) => {
                     match self
-                        .wam
+                        .wam_prelude
                         .indices
                         .get_predicate_skeleton_mut(&compilation_target, &key)
                     {
@@ -436,7 +825,7 @@ impl<'a> Drop for LoadState<'a> {
                 }
                 RetractionRecord::SkeletonClausePopFront(compilation_target, key) => {
                     match self
-                        .wam
+                        .wam_prelude
                         .indices
                         .get_predicate_skeleton_mut(&compilation_target, &key)
                     {
@@ -453,10 +842,12 @@ impl<'a> Drop for LoadState<'a> {
                     local_compilation_target,
                     key,
                 ) => {
-                    match self.wam.indices.get_local_predicate_skeleton_mut(
+                    let listing_src_file_name = self.listing_src_file_name();
+
+                    match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
                         src_compilation_target,
                         local_compilation_target,
-                        self.listing_src_file_name(),
+                        listing_src_file_name,
                         key,
                     ) {
                         Some(skeleton) => {
@@ -470,10 +861,12 @@ impl<'a> Drop for LoadState<'a> {
                     local_compilation_target,
                     key,
                 ) => {
-                    match self.wam.indices.get_local_predicate_skeleton_mut(
+                    let listing_src_file_name = self.listing_src_file_name();
+
+                    match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
                         src_compilation_target,
                         local_compilation_target,
-                        self.listing_src_file_name(),
+                        listing_src_file_name,
                         key,
                     ) {
                         Some(skeleton) => {
@@ -488,401 +881,175 @@ impl<'a> Drop for LoadState<'a> {
                     key,
                     len,
                 ) => {
-                    match self.wam.indices.get_local_predicate_skeleton_mut(
-                        src_compilation_target,
-                        local_compilation_target,
-                        self.listing_src_file_name(),
-                        key,
-                    ) {
-                        Some(skeleton) => {
-                            skeleton.clause_clause_locs.truncate_back(len);
-                        }
-                        None => {}
-                    }
-                }
-                RetractionRecord::SkeletonClauseTruncateBack(compilation_target, key, len) => {
-                    match self
-                        .wam
-                        .indices
-                        .get_predicate_skeleton_mut(&compilation_target, &key)
-                    {
-                        Some(skeleton) => {
-                            skeleton.clauses.truncate_back(len);
-                            skeleton.core.clause_clause_locs.truncate_back(len);
-                        }
-                        None => {}
-                    }
-                }
-                RetractionRecord::SkeletonClauseStartReplaced(
-                    compilation_target,
-                    key,
-                    target_pos,
-                    clause_start,
-                ) => {
-                    match self
-                        .wam
-                        .indices
-                        .get_predicate_skeleton_mut(&compilation_target, &key)
-                    {
-                        Some(skeleton) => {
-                            skeleton.clauses[target_pos].clause_start = clause_start;
-                        }
-                        None => {}
-                    }
-                }
-                RetractionRecord::RemovedSkeletonClause(
-                    compilation_target,
-                    key,
-                    target_pos,
-                    clause_index_info,
-                    clause_clause_loc,
-                ) => {
-                    match self
-                        .wam
-                        .indices
-                        .get_predicate_skeleton_mut(&compilation_target, &key)
-                    {
-                        Some(skeleton) => {
-                            skeleton
-                                .core
-                                .clause_clause_locs
-                                .insert(target_pos, clause_clause_loc);
-                            skeleton.clauses.insert(target_pos, clause_index_info);
-                        }
-                        None => {}
-                    }
-                }
-                RetractionRecord::ReplacedIndexingLine(index_loc, indexing_code) => {
-                    self.wam.code_repo.code[index_loc] = Line::IndexingCode(indexing_code);
-                }
-                RetractionRecord::RemovedLocalSkeletonClauseLocations(
-                    compilation_target,
-                    local_compilation_target,
-                    key,
-                    clause_locs,
-                ) => {
-                    match self.wam.indices.get_local_predicate_skeleton_mut(
-                        compilation_target,
-                        local_compilation_target,
-                        self.listing_src_file_name(),
-                        key,
-                    ) {
-                        Some(skeleton) => skeleton.clause_clause_locs = clause_locs,
-                        None => {}
-                    }
-                }
-                RetractionRecord::RemovedSkeleton(compilation_target, key, skeleton) => {
-                    match compilation_target {
-                        CompilationTarget::User => {
-                            self.wam.indices.extensible_predicates.insert(key, skeleton);
-                        }
-                        CompilationTarget::Module(module_name) => {
-                            if let Some(module) = self.wam.indices.modules.get_mut(&module_name) {
-                                module.extensible_predicates.insert(key, skeleton);
-                            }
-                        }
-                    }
-                }
-                RetractionRecord::ReplacedDynamicElseOffset(instr_loc, next) => {
-                    match &mut self.wam.code_repo.code[instr_loc] {
-                        Line::Choice(ChoiceInstruction::DynamicElse(
-                            _,
-                            _,
-                            NextOrFail::Next(ref mut o),
-                        ))
-                        | Line::Choice(ChoiceInstruction::DynamicInternalElse(
-                            _,
-                            _,
-                            NextOrFail::Next(ref mut o),
-                        )) => {
-                            *o = next;
-                        }
-                        _ => {}
-                    }
-                }
-                RetractionRecord::AppendedNextOrFail(instr_loc, fail) => {
-                    match &mut self.wam.code_repo.code[instr_loc] {
-                        Line::Choice(ChoiceInstruction::DynamicElse(
-                            _,
-                            _,
-                            ref mut next_or_fail,
-                        ))
-                        | Line::Choice(ChoiceInstruction::DynamicInternalElse(
-                            _,
-                            _,
-                            ref mut next_or_fail,
-                        )) => {
-                            *next_or_fail = fail;
-                        }
-                        _ => {}
-                    }
-                }
-            }
-        }
-
-        // TODO: necessary? unnecessary?
-        // self.wam.code_repo.code.truncate(self.retraction_info.orig_code_extent);
-    }
-}
-
-#[derive(Debug, Clone, Hash, PartialEq, Eq)]
-pub(crate) enum CompilationTarget {
-    Module(ClauseName),
-    User,
-}
-
-impl Default for CompilationTarget {
-    #[inline]
-    fn default() -> Self {
-        CompilationTarget::User
-    }
-}
-
-impl CompilationTarget {
-    #[inline]
-    pub(super) fn take(&mut self) -> CompilationTarget {
-        mem::replace(self, CompilationTarget::User)
-    }
+                    let listing_src_file_name = self.listing_src_file_name();
 
-    #[inline]
-    pub(crate) fn module_name(&self) -> ClauseName {
-        match self {
-            CompilationTarget::User => {
-                clause_name!("user")
-            }
-            CompilationTarget::Module(ref module_name) => module_name.clone(),
-        }
-    }
-}
-
-pub(crate) struct PredicateQueue {
-    pub(super) predicates: Vec<Term>,
-    pub(super) compilation_target: CompilationTarget,
-}
-
-impl PredicateQueue {
-    #[inline]
-    pub(super) fn push(&mut self, clause: Term) {
-        self.predicates.push(clause);
-    }
-
-    #[inline]
-    pub(crate) fn first(&self) -> Option<&Term> {
-        self.predicates.first()
-    }
-
-    #[inline]
-    pub(crate) fn is_empty(&self) -> bool {
-        self.predicates.is_empty()
-    }
-
-    #[inline]
-    pub(super) fn take(&mut self) -> Self {
-        Self {
-            predicates: mem::replace(&mut self.predicates, vec![]),
-            compilation_target: self.compilation_target.take(),
-        }
-    }
-
-    #[inline]
-    pub(super) fn len(&self) -> usize {
-        self.predicates.len()
-    }
-}
-
-macro_rules! predicate_queue {
-    [$($v:expr),*] => (
-        PredicateQueue {
-            predicates: vec![$($v,)*],
-            compilation_target: CompilationTarget::default(),
-        }
-    )
-}
-
-pub(crate) struct Loader<'a, TermStream> {
-    pub(super) load_state: LoadState<'a>,
-    pub(super) predicates: PredicateQueue,
-    pub(super) clause_clauses: Vec<(Term, Term)>,
-    pub(super) term_stream: TermStream,
-    pub(super) non_counted_bt_preds: IndexSet<PredicateKey>,
-}
-
-impl<'a, TS: TermStream> Loader<'a, TS> {
-    #[inline]
-    pub(super) fn new(term_stream: TS, wam: &'a mut Machine) -> Self {
-        let load_state = LoadState {
-            compilation_target: CompilationTarget::User,
-            module_op_exports: vec![],
-            retraction_info: RetractionInfo::new(wam.code_repo.code.len()),
-            wam,
-        };
-
-        Self {
-            load_state,
-            term_stream,
-            non_counted_bt_preds: IndexSet::new(),
-            predicates: predicate_queue![],
-            clause_clauses: vec![],
-        }
-    }
-
-    pub(crate) fn load(mut self) -> Result<TS::Evacuable, SessionError> {
-        while let Some(decl) = self.dequeue_terms()? {
-            self.load_decl(decl)?;
-        }
-
-        TS::evacuate(self)
-    }
-
-    fn dequeue_terms(&mut self) -> Result<Option<Declaration>, SessionError> {
-        while !self.term_stream.eof()? {
-            let term = self.term_stream.next(&self.load_state.composite_op_dir())?;
-
-            // if is_consistent is false, self.predicates is not empty.
-            if !term.is_consistent(&self.predicates) {
-                self.compile_and_submit()?;
-            }
-
-            let term = match term {
-                Term::Clause(_, name, terms, _) if name.as_str() == ":-" && terms.len() == 1 => {
-                    return Ok(Some(setup_declaration(&self.load_state, terms)?));
-                }
-                term => term,
-            };
-
-            self.predicates.push(term);
-        }
-
-        Ok(None)
-    }
-
-    pub(super) fn load_decl(&mut self, decl: Declaration) -> Result<(), SessionError> {
-        match decl {
-            Declaration::Dynamic(name, arity) => {
-                let compilation_target = self.load_state.compilation_target.clone();
-                self.add_dynamic_predicate(compilation_target, name, arity)?;
-            }
-            Declaration::MetaPredicate(module_name, name, meta_specs) => {
-                self.load_state
-                    .add_meta_predicate_record(module_name, name, meta_specs);
-            }
-            Declaration::Module(module_decl) => {
-                self.load_state.compilation_target =
-                    CompilationTarget::Module(module_decl.name.clone());
-
-                self.predicates.compilation_target = self.load_state.compilation_target.clone();
-
-                self.load_state
-                    .add_module(module_decl, self.term_stream.listing_src().clone());
-            }
-            Declaration::NonCountedBacktracking(name, arity) => {
-                self.non_counted_bt_preds.insert((name, arity));
-            }
-            Declaration::Op(op_decl) => {
-                self.load_state.add_op_decl(&op_decl);
-            }
-            Declaration::UseModule(module_src) => {
-                self.load_state.use_module(module_src)?;
-            }
-            Declaration::UseQualifiedModule(module_src, exports) => {
-                self.load_state.use_qualified_module(module_src, exports)?;
-            }
-        }
-
-        Ok(())
-    }
-
-    pub(super) fn read_term_from_heap(&self, heap_term_loc: RegType) -> Result<Term, SessionError> {
-        let machine_st = &self.load_state.wam.machine_st;
-        let term_addr = machine_st[heap_term_loc];
-
-        if machine_st.is_cyclic_term(term_addr) {
-            return Err(SessionError::from(CompilationError::CannotParseCyclicTerm));
-        }
-
-        let mut term_stack = vec![];
-
-        for addr in machine_st.post_order_iter(term_addr) {
-            match machine_st.heap.index_addr(&addr).as_ref() {
-                HeapCellValue::Addr(Addr::Lis(_)) | HeapCellValue::Addr(Addr::PStrLocation(..)) => {
-                    let tail = term_stack.pop().unwrap();
-                    let head = term_stack.pop().unwrap();
-
-                    term_stack.push(Term::Cons(Cell::default(), Box::new(head), Box::new(tail)));
+                    match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
+                        src_compilation_target,
+                        local_compilation_target,
+                        listing_src_file_name,
+                        key,
+                    ) {
+                        Some(skeleton) => {
+                            skeleton.clause_clause_locs.truncate_back(len);
+                        }
+                        None => {}
+                    }
                 }
-                HeapCellValue::Addr(addr) => {
-                    if let Some(r) = addr.as_var() {
-                        let offset_string = match r {
-                            Ref::HeapCell(h) | Ref::AttrVar(h) => format!("_{}", h),
-                            Ref::StackCell(fr, sc) => format!("_s_{}_{}", fr, sc),
-                        };
+                RetractionRecord::SkeletonClauseTruncateBack(compilation_target, key, len) => {
+                    match self
+                        .wam_prelude
+                        .indices
+                        .get_predicate_skeleton_mut(&compilation_target, &key)
+                    {
+                        Some(skeleton) => {
+                            skeleton.clauses.truncate_back(len);
+                            skeleton.core.clause_clause_locs.truncate_back(len);
+                        }
+                        None => {}
+                    }
+                }
+                RetractionRecord::SkeletonClauseStartReplaced(
+                    compilation_target,
+                    key,
+                    target_pos,
+                    clause_start,
+                ) => {
+                    match self
+                        .wam_prelude
+                        .indices
+                        .get_predicate_skeleton_mut(&compilation_target, &key)
+                    {
+                        Some(skeleton) => {
+                            skeleton.clauses[target_pos].clause_start = clause_start;
+                        }
+                        None => {}
+                    }
+                }
+                RetractionRecord::RemovedDynamicSkeletonClause(
+                    compilation_target,
+                    key,
+                    target_pos,
+                    clause_clause_loc,
+                ) => {
+                    match self
+                        .wam_prelude
+                        .indices
+                        .get_predicate_skeleton_mut(&compilation_target, &key)
+                    {
+                        Some(skeleton) => {
+                            if let Some(removed_clauses) = &mut skeleton.core.retracted_dynamic_clauses {
+                                let clause_index_info = removed_clauses.pop().unwrap();
 
-                        term_stack.push(Term::Var(Cell::default(), Rc::new(offset_string)));
-                    } else {
-                        match addr.as_constant_index(machine_st) {
-                            Some(constant) => {
-                                term_stack.push(Term::Constant(Cell::default(), constant));
-                            }
-                            None => {
-                                return Err(SessionError::from(CompilationError::UnreadableTerm));
+                                skeleton
+                                    .core
+                                    .clause_clause_locs
+                                    .insert(target_pos, clause_clause_loc);
+
+                                skeleton.clauses.insert(target_pos, clause_index_info);
                             }
                         }
+                        None => {}
                     }
                 }
-                HeapCellValue::Atom(ref name, ref shared_op_desc) => {
-                    term_stack.push(Term::Constant(
-                        Cell::default(),
-                        Constant::Atom(name.clone(), shared_op_desc.clone()),
-                    ));
+                RetractionRecord::RemovedSkeletonClause(
+                    compilation_target,
+                    key,
+                    target_pos,
+                    clause_index_info,
+                    clause_clause_loc,
+                ) => {
+                    match self
+                        .wam_prelude
+                        .indices
+                        .get_predicate_skeleton_mut(&compilation_target, &key)
+                    {
+                        Some(skeleton) => {
+                            skeleton
+                                .core
+                                .clause_clause_locs
+                                .insert(target_pos, clause_clause_loc);
+                            skeleton.clauses.insert(target_pos, clause_index_info);
+                        }
+                        None => {}
+                    }
                 }
-                HeapCellValue::Integer(ref integer) => {
-                    term_stack.push(Term::Constant(
-                        Cell::default(),
-                        Constant::Integer(integer.clone()),
-                    ));
+                RetractionRecord::ReplacedIndexingLine(index_loc, indexing_code) => {
+                    self.wam_prelude.code_repo.code[index_loc] = Line::IndexingCode(indexing_code);
                 }
-                HeapCellValue::NamedStr(arity, ref name, ref shared_op_desc) => {
-                    let subterms = term_stack
-                        .drain(term_stack.len() - arity..)
-                        .map(Box::new)
-                        .collect();
+                RetractionRecord::RemovedLocalSkeletonClauseLocations(
+                    compilation_target,
+                    local_compilation_target,
+                    key,
+                    clause_locs,
+                ) => {
+                    let listing_src_file_name = self.listing_src_file_name();
 
-                    term_stack.push(Term::Clause(
-                        Cell::default(),
-                        name.clone(),
-                        subterms,
-                        shared_op_desc.clone(),
-                    ));
+                    match self.wam_prelude.indices.get_local_predicate_skeleton_mut(
+                        compilation_target,
+                        local_compilation_target,
+                        listing_src_file_name,
+                        key,
+                    ) {
+                        Some(skeleton) => skeleton.clause_clause_locs = clause_locs,
+                        None => {}
+                    }
                 }
-                HeapCellValue::PartialString(..) => {
-                    let string = machine_st.heap_pstr_iter(addr).to_string();
-                    term_stack.push(Term::Constant(
-                        Cell::default(),
-                        Constant::String(Rc::new(string)),
-                    ));
+                RetractionRecord::RemovedSkeleton(compilation_target, key, skeleton) => {
+                    match compilation_target {
+                        CompilationTarget::User => {
+                            self.wam_prelude.indices.extensible_predicates.insert(key, skeleton);
+                        }
+                        CompilationTarget::Module(module_name) => {
+                            if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) {
+                                module.extensible_predicates.insert(key, skeleton);
+                            }
+                        }
+                    }
                 }
-                HeapCellValue::Rational(ref rational) => {
-                    term_stack.push(Term::Constant(
-                        Cell::default(),
-                        Constant::Rational(rational.clone()),
-                    ));
+                RetractionRecord::ReplacedDynamicElseOffset(instr_loc, next) => {
+                    match self.wam_prelude.code_repo.code[instr_loc] {
+                        Line::Choice(ChoiceInstruction::DynamicElse(
+                            _,
+                            _,
+                            NextOrFail::Next(ref mut o),
+                        ))
+                        | Line::Choice(ChoiceInstruction::DynamicInternalElse(
+                            _,
+                            _,
+                            NextOrFail::Next(ref mut o),
+                        )) => {
+                            *o = next;
+                        }
+                        _ => {}
+                    }
                 }
-                _ => {
-                    return Err(SessionError::from(CompilationError::UnreadableTerm));
+                RetractionRecord::AppendedNextOrFail(instr_loc, fail) => {
+                    match self.wam_prelude.code_repo.code[instr_loc] {
+                        Line::Choice(ChoiceInstruction::DynamicElse(
+                            _,
+                            _,
+                            ref mut next_or_fail,
+                        ))
+                        | Line::Choice(ChoiceInstruction::DynamicInternalElse(
+                            _,
+                            _,
+                            ref mut next_or_fail,
+                        )) => {
+                            *next_or_fail = fail;
+                        }
+                        _ => {}
+                    }
                 }
             }
         }
-
-        debug_assert!(term_stack.len() == 1);
-        Ok(term_stack.pop().unwrap())
     }
 
     fn extract_module_export_list_from_heap(
-        &self,
+        &mut self,
         r: RegType,
     ) -> Result<IndexSet<ModuleExport>, SessionError> {
         let export_list = self.read_term_from_heap(r)?;
-        let atom_tbl = self.load_state.wam.machine_st.atom_tbl.clone();
+        let atom_tbl = &mut LS::machine_st(&mut self.payload).atom_tbl;
         let export_list = setup_module_export_list(export_list, atom_tbl)?;
 
         Ok(export_list.into_iter().collect())
@@ -890,19 +1057,16 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
 
     fn add_clause_clause(&mut self, term: Term) -> Result<(), CompilationError> {
         match term {
-            Term::Clause(_, turnstile, mut terms, _)
-                if turnstile.as_str() == ":-" && terms.len() == 2 =>
+            Term::Clause(_, atom!(":-"), mut terms) if terms.len() == 2 =>
             {
-                let body = *terms.pop().unwrap();
-                let head = *terms.pop().unwrap();
+                let body = terms.pop().unwrap();
+                let head = terms.pop().unwrap();
 
-                self.clause_clauses.push((head, body));
+                self.payload.clause_clauses.push((head, body));
             }
-            head @ Term::Constant(_, Constant::Atom(..)) | head @ Term::Clause(..) => {
-                let body =
-                    Term::Constant(Cell::default(), Constant::Atom(clause_name!("true"), None));
-
-                self.clause_clauses.push((head, body));
+            head @ Term::Literal(_, Literal::Atom(..)) | head @ Term::Clause(..) => {
+                let body = Term::Literal(Cell::default(), Literal::Atom(atom!("true")));
+                self.payload.clause_clauses.push((head, body));
             }
             _ => {
                 return Err(CompilationError::InadmissibleFact);
@@ -915,7 +1079,7 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
     fn add_extensible_predicate_declaration(
         &mut self,
         compilation_target: CompilationTarget,
-        name: ClauseName,
+        name: Atom,
         arity: usize,
         flag_accessor: impl Fn(&mut LocalPredicateSkeleton) -> &mut bool,
         retraction_fn: impl Fn(CompilationTarget, PredicateKey) -> RetractionRecord,
@@ -926,8 +1090,7 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
         match &compilation_target {
             CompilationTarget::User => {
                 match self
-                    .load_state
-                    .wam
+                    .wam_prelude
                     .indices
                     .extensible_predicates
                     .get_mut(&key)
@@ -936,19 +1099,19 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
                         if !*flag_accessor(&mut skeleton.core) {
                             *flag_accessor(&mut skeleton.core) = true;
 
-                            self.load_state.retraction_info.push_record(retraction_fn(
-                                compilation_target.clone(),
-                                key.clone(),
+                            self.payload.retraction_info.push_record(retraction_fn(
+                                compilation_target,
+                                key,
                             ));
                         }
                     }
                     None => {
-                        if self.load_state.compilation_target == compilation_target {
+                        if self.payload.compilation_target == compilation_target {
                             let mut skeleton = PredicateSkeleton::new();
                             *flag_accessor(&mut skeleton.core) = true;
 
-                            self.load_state.add_extensible_predicate(
-                                key.clone(),
+                            self.add_extensible_predicate(
+                                key,
                                 skeleton,
                                 CompilationTarget::User,
                             );
@@ -959,27 +1122,27 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
                 }
             }
             CompilationTarget::Module(ref module_name) => {
-                match self.load_state.wam.indices.modules.get_mut(module_name) {
+                match self.wam_prelude.indices.modules.get_mut(module_name) {
                     Some(ref mut module) => match module.extensible_predicates.get_mut(&key) {
                         Some(ref mut skeleton) => {
                             if !*flag_accessor(&mut skeleton.core) {
                                 *flag_accessor(&mut skeleton.core) = true;
 
-                                self.load_state.retraction_info.push_record(retraction_fn(
-                                    compilation_target.clone(),
-                                    key.clone(),
+                                self.payload.retraction_info.push_record(retraction_fn(
+                                    compilation_target,
+                                    key,
                                 ));
                             }
                         }
                         None => {
-                            if self.load_state.compilation_target == compilation_target {
+                            if self.payload.compilation_target == compilation_target {
                                 let mut skeleton = PredicateSkeleton::new();
                                 *flag_accessor(&mut skeleton.core) = true;
 
-                                self.load_state.add_extensible_predicate(
-                                    key.clone(),
+                                self.add_extensible_predicate(
+                                    key,
                                     skeleton,
-                                    compilation_target.clone(),
+                                    compilation_target,
                                 );
                             } else {
                                 throw_permission_error = true;
@@ -987,16 +1150,15 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
                         }
                     },
                     None => {
-                        self.load_state
-                            .add_dynamically_generated_module(module_name);
+                        self.add_dynamically_generated_module(*module_name);
 
                         let mut skeleton = PredicateSkeleton::new();
                         *flag_accessor(&mut skeleton.core) = true;
 
-                        self.load_state.add_extensible_predicate(
-                            key.clone(),
+                        self.add_extensible_predicate(
+                            key,
                             skeleton,
-                            compilation_target.clone(),
+                            compilation_target,
                         );
                     }
                 }
@@ -1004,17 +1166,19 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
         }
 
         if !throw_permission_error {
-            match self.load_state.compilation_target.clone() {
+            let listing_src_file_name = self.listing_src_file_name();
+            let payload_compilation_target = self.payload.compilation_target;
+
+            match payload_compilation_target {
                 CompilationTarget::User => {
                     match self
-                        .load_state
-                        .wam
+                        .wam_prelude
                         .indices
                         .get_local_predicate_skeleton_mut(
-                            self.load_state.compilation_target.clone(),
-                            compilation_target.clone(),
-                            self.load_state.listing_src_file_name(),
-                            key.clone(),
+                            payload_compilation_target,
+                            compilation_target,
+                            listing_src_file_name,
+                            key,
                         ) {
                         Some(skeleton) => {
                             if !*flag_accessor(skeleton) {
@@ -1025,19 +1189,19 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
                             let mut skeleton = LocalPredicateSkeleton::new();
                             *flag_accessor(&mut skeleton) = true;
 
-                            self.load_state.add_local_extensible_predicate(
-                                compilation_target.clone(),
-                                key.clone(),
+                            self.add_local_extensible_predicate(
+                                compilation_target,
+                                key,
                                 skeleton,
                             );
                         }
                     }
                 }
                 CompilationTarget::Module(ref module_name) => {
-                    match self.load_state.wam.indices.modules.get_mut(module_name) {
+                    match self.wam_prelude.indices.modules.get_mut(module_name) {
                         Some(module) => match module
                             .local_extensible_predicates
-                            .get_mut(&(compilation_target.clone(), key.clone()))
+                            .get_mut(&(compilation_target, key))
                         {
                             Some(skeleton) => {
                                 if !*flag_accessor(skeleton) {
@@ -1048,23 +1212,22 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
                                 let mut skeleton = LocalPredicateSkeleton::new();
                                 *flag_accessor(&mut skeleton) = true;
 
-                                self.load_state.add_local_extensible_predicate(
-                                    compilation_target.clone(),
-                                    key.clone(),
+                                self.add_local_extensible_predicate(
+                                    compilation_target,
+                                    key,
                                     skeleton,
                                 );
                             }
                         },
                         None => {
-                            self.load_state
-                                .add_dynamically_generated_module(module_name);
+                            self.add_dynamically_generated_module(*module_name);
 
                             let mut skeleton = LocalPredicateSkeleton::new();
                             *flag_accessor(&mut skeleton) = true;
 
-                            self.load_state.add_local_extensible_predicate(
-                                compilation_target.clone(),
-                                key.clone(),
+                            self.add_local_extensible_predicate(
+                                compilation_target,
+                                key,
                                 skeleton,
                             );
                         }
@@ -1072,7 +1235,7 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
                 }
             }
 
-            self.fail_on_undefined(&compilation_target, key);
+            self.fail_on_undefined(compilation_target, key);
 
             Ok(())
         } else {
@@ -1083,20 +1246,18 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
         }
     }
 
-    fn fail_on_undefined(&mut self, compilation_target: &CompilationTarget, key: PredicateKey) {
+    fn fail_on_undefined(&mut self, compilation_target: CompilationTarget, key: PredicateKey) {
         /*
          * DynamicUndefined isn't only applied to dynamic predicates
          * but to multifile and discontiguous predicates as well.
          */
 
-        let code_index = self
-            .load_state
-            .get_or_insert_code_index(key.clone(), compilation_target.clone());
+        let code_index = self.get_or_insert_code_index(key, compilation_target);
 
         if let IndexPtr::Undefined = code_index.get() {
             set_code_index(
-                &mut self.load_state.retraction_info,
-                compilation_target,
+                &mut self.payload.retraction_info,
+                &compilation_target,
                 key,
                 &code_index,
                 IndexPtr::DynamicUndefined,
@@ -1107,7 +1268,7 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
     fn add_discontiguous_predicate(
         &mut self,
         compilation_target: CompilationTarget,
-        name: ClauseName,
+        name: Atom,
         arity: usize,
     ) -> Result<(), SessionError> {
         self.add_extensible_predicate_declaration(
@@ -1122,12 +1283,12 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
     fn add_dynamic_predicate(
         &mut self,
         compilation_target: CompilationTarget,
-        name: ClauseName,
+        name: Atom,
         arity: usize,
     ) -> Result<(), SessionError> {
         self.add_extensible_predicate_declaration(
-            compilation_target.clone(),
-            name.clone(),
+            compilation_target,
+            name,
             arity,
             |skeleton| &mut skeleton.is_dynamic,
             RetractionRecord::AddedDynamicPredicate,
@@ -1137,7 +1298,7 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
     fn add_multifile_predicate(
         &mut self,
         compilation_target: CompilationTarget,
-        name: ClauseName,
+        name: Atom,
         arity: usize,
     ) -> Result<(), SessionError> {
         self.add_extensible_predicate_declaration(
@@ -1152,13 +1313,13 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
     fn add_clause_clause_if_dynamic(&mut self, term: &Term) -> Result<(), SessionError> {
         if let Some(predicate_name) = ClauseInfo::name(term) {
             let arity = ClauseInfo::arity(term);
+            let predicates_compilation_target = self.payload.predicates.compilation_target;
 
             let is_dynamic = self
-                .load_state
-                .wam
+                .wam_prelude
                 .indices
                 .get_predicate_skeleton(
-                    &self.predicates.compilation_target,
+                    &predicates_compilation_target,
                     &(predicate_name, arity),
                 )
                 .map(|skeleton| skeleton.core.is_dynamic)
@@ -1173,15 +1334,18 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
     }
 
     pub(super) fn retract_local_clauses(&mut self, key: &PredicateKey, is_dynamic: bool) {
+        let payload_compilation_target = self.payload.compilation_target;
+        let predicates_compilation_target = self.payload.predicates.compilation_target;
+        let listing_src_file_name = self.listing_src_file_name();
+
         let clause_locs = match self
-            .load_state
-            .wam
+            .wam_prelude
             .indices
             .get_local_predicate_skeleton_mut(
-                self.load_state.compilation_target.clone(),
-                self.predicates.compilation_target.clone(),
-                self.load_state.listing_src_file_name(),
-                key.clone(),
+                payload_compilation_target,
+                predicates_compilation_target,
+                listing_src_file_name,
+                *key,
             ) {
             Some(skeleton) if !skeleton.clause_clause_locs.is_empty() => {
                 mem::replace(&mut skeleton.clause_clause_locs, sdeq![])
@@ -1189,29 +1353,47 @@ impl<'a, TS: TermStream> Loader<'a, TS> {
             _ => return,
         };
 
-        self.load_state.retraction_info.push_record(
+        self.payload.retraction_info.push_record(
             RetractionRecord::RemovedLocalSkeletonClauseLocations(
-                self.load_state.compilation_target.clone(),
-                self.predicates.compilation_target.clone(),
-                key.clone(),
+                payload_compilation_target,
+                predicates_compilation_target,
+                *key,
                 clause_locs.clone(),
             ),
         );
 
-        self.load_state.retract_local_clauses(
-            self.predicates.compilation_target.clone(),
-            key.clone(),
+        self.retract_local_clauses_impl(
+            predicates_compilation_target,
+            *key,
             &clause_locs,
         );
 
         if is_dynamic {
-            let clause_clause_compilation_target = match &self.predicates.compilation_target {
-                CompilationTarget::User => CompilationTarget::Module(clause_name!("builtins")),
-                module_name => module_name.clone(),
+            let clause_clause_compilation_target = match predicates_compilation_target {
+                CompilationTarget::User => CompilationTarget::Module(atom!("builtins")),
+                module_name => module_name,
             };
 
-            self.load_state
-                .retract_local_clause_clauses(clause_clause_compilation_target, &clause_locs);
+            self.retract_local_clause_clauses(clause_clause_compilation_target, &clause_locs);
+        }
+    }
+}
+
+impl<'a> MachinePreludeView<'a> {
+    #[inline]
+    pub(super) fn composite_op_dir(&self, compilation_target: &CompilationTarget) -> CompositeOpDir {
+        match compilation_target {
+            CompilationTarget::User => CompositeOpDir::new(&self.indices.op_dir, None),
+            CompilationTarget::Module(ref module_name) => {
+                match self.indices.modules.get(module_name) {
+                    Some(ref module) => {
+                        CompositeOpDir::new(&self.indices.op_dir, Some(&module.op_dir))
+                    }
+                    None => {
+                        unreachable!()
+                    }
+                }
+            }
         }
     }
 }
@@ -1220,50 +1402,40 @@ impl Machine {
     pub(crate) fn use_module(&mut self) {
         let subevacuable_addr = self
             .machine_st
-            .store(self.machine_st.deref(self.machine_st[temp_v!(2)]));
-
-        let module_src = ModuleSource::Library(match subevacuable_addr {
-            Addr::LoadStatePayload(payload) => match &self.machine_st.heap[payload] {
-                HeapCellValue::LoadStatePayload(payload) => match &payload.compilation_target {
-                    CompilationTarget::Module(ref module_name) => module_name.clone(),
-                    CompilationTarget::User => {
-                        return;
-                    }
-                },
-                _ => {
-                    unreachable!()
+            .store(self.machine_st.deref(self.machine_st.registers[2]));
+
+        let module_src = ModuleSource::Library({
+            let payload = cell_as_load_state_payload!(subevacuable_addr);
+
+            match payload.compilation_target {
+                CompilationTarget::Module(module_name) => module_name,
+                CompilationTarget::User => {
+                    return;
                 }
-            },
-            _ => {
-                unreachable!()
             }
         });
 
-        let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(1));
+        let mut loader = self.loader_from_heap_evacuable(temp_v!(1));
 
         let use_module = || {
             let export_list = loader.extract_module_export_list_from_heap(temp_v!(3))?;
 
             if export_list.is_empty() {
-                loader.load_state.use_module(module_src)?;
+                loader.use_module(module_src)?;
             } else {
-                loader
-                    .load_state
-                    .use_qualified_module(module_src, export_list)?;
+                loader.use_qualified_module(module_src, export_list)?;
             }
 
-            LiveTermStream::evacuate(loader)
+            LiveLoadAndMachineState::evacuate(loader)
         };
 
         let result = use_module();
-        self.restore_load_state_payload(result, evacuable_h);
+        self.restore_load_state_payload(result);
     }
 
     pub(crate) fn load_compiled_library(&mut self) {
-        let library = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(1)]))
+        let library = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
         );
 
         if let Some(module) = self.indices.modules.get(&library) {
@@ -1272,38 +1444,33 @@ impl Machine {
                 return;
             }
 
-            let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(3));
+            let mut loader = self.loader_from_heap_evacuable(temp_v!(3));
 
             let import_module = || {
                 let export_list = loader.extract_module_export_list_from_heap(temp_v!(2))?;
 
                 if export_list.is_empty() {
-                    loader.load_state.import_module(library)?;
+                    loader.import_module(library)?;
                 } else {
-                    loader
-                        .load_state
-                        .import_qualified_module(library, export_list)?;
+                    loader.import_qualified_module(library, export_list)?;
                 }
 
-                LiveTermStream::evacuate(loader)
+                LiveLoadAndMachineState::evacuate(loader)
             };
 
             let result = import_module();
-            self.restore_load_state_payload(result, evacuable_h);
+            self.restore_load_state_payload(result);
         } else {
             self.machine_st.fail = true;
         }
     }
 
     pub(crate) fn declare_module(&mut self) {
-        let module_name = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(1)]))
+        let module_name = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
         );
 
-        // let export_list = self.machine_st.extract_module_export_list(temp_v!(2));
-        let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(3));
+        let mut loader = self.loader_from_heap_evacuable(temp_v!(3));
 
         let declare_module = || {
             // let export_list = export_list?;
@@ -1315,11 +1482,11 @@ impl Machine {
             };
 
             loader.load_decl(Declaration::Module(module_decl))?;
-            LiveTermStream::evacuate(loader)
+            LiveLoadAndMachineState::evacuate(loader)
         };
 
         let result = declare_module();
-        self.restore_load_state_payload(result, evacuable_h);
+        self.restore_load_state_payload(result);
     }
 
     #[inline]
@@ -1351,145 +1518,135 @@ impl Machine {
 
     fn add_extensible_predicate_declaration(
         &mut self,
-        decl_adder: impl Fn(
-            &mut Loader<LiveTermStream>,
+        decl_adder: impl for<'a> Fn(
+            &mut Loader<'a, LiveLoadAndMachineState<'a>>,
             CompilationTarget,
-            ClauseName,
+            Atom,
             usize,
         ) -> Result<(), SessionError>,
     ) {
-        let module_name = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(1)]))
+        let module_name = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
         );
 
-        let compilation_target = match module_name.as_str() {
-            "user" => CompilationTarget::User,
+        let compilation_target = match module_name {
+            atom!("user") => CompilationTarget::User,
             _ => CompilationTarget::Module(module_name),
         };
 
-        let predicate_name = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(2)]))
+        let predicate_name = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2]))
         );
 
         let arity = self
             .machine_st
-            .store(self.machine_st.deref(self.machine_st[temp_v!(3)]));
+            .store(self.machine_st.deref(self.machine_st.registers[3]));
 
-        let arity = match Number::try_from((arity, &self.machine_st.heap)) {
+        let arity = match Number::try_from(arity) {
             Ok(Number::Integer(n)) if &*n >= &0 && &*n <= &MAX_ARITY => Ok(n.to_usize().unwrap()),
-            Ok(Number::Fixnum(n)) if n >= 0 && n <= MAX_ARITY as isize => {
-                Ok(usize::try_from(n).unwrap())
+            Ok(Number::Fixnum(n)) if n.get_num() >= 0 && n.get_num() <= MAX_ARITY as i64 => {
+                Ok(usize::try_from(n.get_num()).unwrap())
             }
             _ => Err(SessionError::from(CompilationError::InvalidRuleHead)),
         };
 
-        let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(4));
+        let mut loader = self.loader_from_heap_evacuable(temp_v!(4));
 
-        let add_predicate_decl = || {
+        let add_predicate_decl = move || {
             decl_adder(&mut loader, compilation_target, predicate_name, arity?)?;
-            LiveTermStream::evacuate(loader)
+            LiveLoadAndMachineState::evacuate(loader)
         };
 
         let result = add_predicate_decl();
-        self.restore_load_state_payload(result, evacuable_h);
+        self.restore_load_state_payload(result);
     }
 
     pub(crate) fn add_term_expansion_clause(&mut self) {
-        let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(2));
+        let mut loader = self.loader_from_heap_evacuable(temp_v!(2));
 
         let add_clause = || {
             let term = loader.read_term_from_heap(temp_v!(1))?;
 
-            loader.load_state.incremental_compile_clause(
-                (clause_name!("term_expansion"), 2),
+            loader.incremental_compile_clause(
+                (atom!("term_expansion"), 2),
                 term,
                 CompilationTarget::User,
                 false,
                 AppendOrPrepend::Append,
             )?;
 
-            LiveTermStream::evacuate(loader)
+            LiveLoadAndMachineState::evacuate(loader)
         };
 
         let result = add_clause();
-        self.restore_load_state_payload(result, evacuable_h);
+        self.restore_load_state_payload(result);
     }
 
     pub(crate) fn add_goal_expansion_clause(&mut self) {
-        let target_module_name = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(1)]))
+        let target_module_name = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
         );
 
-        let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(3));
+        let mut loader = self.loader_from_heap_evacuable(temp_v!(3));
 
-        let compilation_target = match target_module_name.as_str() {
-            "user" => CompilationTarget::User,
+        let compilation_target = match target_module_name {
+            atom!("user") => CompilationTarget::User,
             _ => CompilationTarget::Module(target_module_name),
         };
 
         let add_clause = || {
             let term = loader.read_term_from_heap(temp_v!(2))?;
 
-            loader.load_state.incremental_compile_clause(
-                (clause_name!("goal_expansion"), 2),
+            loader.incremental_compile_clause(
+                (atom!("goal_expansion"), 2),
                 term,
                 compilation_target,
                 false, // backtracking inferences are counted by call_with_inference_limit.
                 AppendOrPrepend::Append,
             )?;
 
-            LiveTermStream::evacuate(loader)
+            LiveLoadAndMachineState::evacuate(loader)
         };
 
         let result = add_clause();
-        self.restore_load_state_payload(result, evacuable_h);
+        self.restore_load_state_payload(result);
     }
 
     pub(crate) fn add_in_situ_filename_module(&mut self) {
-        let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(1));
+        let mut loader = self.loader_from_heap_evacuable(temp_v!(1));
 
         let add_in_situ_filename_module = || {
-            if let Some(filename) = loader.load_state.listing_src_file_name() {
+            if let Some(filename) = loader.listing_src_file_name() {
                 let module_decl = ModuleDecl {
                     name: filename,
                     exports: vec![],
                 };
 
-                let module_name = module_decl.name.clone();
+                let module_name = module_decl.name;
 
                 if !loader
-                    .load_state
-                    .wam
+                    .wam_prelude
                     .indices
                     .modules
                     .contains_key(&module_decl.name)
                 {
                     let module = Module::new_in_situ(module_decl);
                     loader
-                        .load_state
-                        .wam
+                        .wam_prelude
                         .indices
                         .modules
                         .insert(module_name, module);
                 } else {
-                    loader.load_state.reset_in_situ_module(
+                    loader.reset_in_situ_module(
                         module_decl.clone(),
                         &ListingSource::DynamicallyGenerated,
                     );
 
-                    match loader.load_state.wam.indices.modules.get_mut(&module_name) {
+                    match loader.wam_prelude.indices.modules.get_mut(&module_name) {
                         Some(module) => {
                             for (key, value) in module.op_dir.drain(0..) {
-                                let (prec, spec) = value.shared_op_desc().get();
-                                let mut op_decl = OpDecl::new(prec, spec, key.0);
-
-                                op_decl.remove(&mut loader.load_state.wam.indices.op_dir);
+                                let mut op_decl = OpDecl::new(value, key.0);
+                                op_decl.remove(&mut loader.wam_prelude.indices.op_dir);
                             }
                         }
                         None => {}
@@ -1497,83 +1654,78 @@ impl Machine {
                 }
             }
 
-            LiveTermStream::evacuate(loader)
+            LiveLoadAndMachineState::evacuate(loader)
         };
 
         let result = add_in_situ_filename_module();
-        self.restore_load_state_payload(result, evacuable_h);
+        self.restore_load_state_payload(result);
     }
 
-    pub(crate) fn loader_from_heap_evacuable(
-        &mut self,
+    pub(crate) fn loader_from_heap_evacuable<'a>(
+        &'a mut self,
         r: RegType,
-    ) -> (Loader<LiveTermStream>, usize) {
-        let (load_state_payload, evacuable_h) = match self
-            .machine_st
-            .store(self.machine_st.deref(self.machine_st[r]))
-        {
-            Addr::LoadStatePayload(h) => (
-                mem::replace(
-                    &mut self.machine_st.heap[h],
-                    HeapCellValue::Addr(Addr::EmptyList),
-                ),
-                h,
-            ),
-            _ => {
-                unreachable!()
-            }
-        };
+    ) -> Loader<'a, LiveLoadAndMachineState<'a>> {
+        let mut load_state = cell_as_load_state_payload!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st[r]))
+        );
 
-        match load_state_payload {
-            HeapCellValue::LoadStatePayload(payload) => {
-                (Loader::from_load_state_payload(self, payload), evacuable_h)
-            }
-            _ => {
-                unreachable!()
-            }
+        load_state.set_tag(ArenaHeaderTag::LiveLoadState);
+
+        let (wam_prelude, machine_st) = self.prelude_view_and_machine_st();
+
+        Loader {
+            payload: LiveLoadAndMachineState { load_state, machine_st },
+            wam_prelude,
         }
     }
 
     #[inline]
     pub(crate) fn push_load_state_payload(&mut self) {
-        let payload = Box::new(LoadStatePayload::new(self));
-        let addr = Addr::LoadStatePayload(
-            self.machine_st
-                .heap
-                .push(HeapCellValue::LoadStatePayload(payload)),
+        let payload = arena_alloc!(
+            LoadStatePayload::new(
+                self.code_repo.code.len(),
+                LiveTermStream::new(ListingSource::User),
+            ),
+            &mut self.machine_st.arena
         );
 
-        self.machine_st
-            .bind(self.machine_st[temp_v!(1)].as_var().unwrap(), addr);
+        let var = self.machine_st.deref(
+            self.machine_st.registers[1]
+        );
+
+        self.machine_st.bind(
+            var.as_var().unwrap(),
+            typed_arena_ptr_as_cell!(payload),
+        );
     }
 
     #[inline]
     pub(crate) fn pop_load_state_payload(&mut self) {
-        let load_state_payload = match self
-            .machine_st
-            .store(self.machine_st.deref(self.machine_st[temp_v!(1)]))
-        {
-            Addr::LoadStatePayload(h) => mem::replace(
-                &mut self.machine_st.heap[h],
-                HeapCellValue::Addr(Addr::EmptyList),
-            ),
-            _ => {
-                unreachable!()
-            }
-        };
+        let load_state_payload = self.machine_st.store(
+            self.machine_st.deref(self.machine_st.registers[1])
+        );
 
-        match load_state_payload {
-            HeapCellValue::LoadStatePayload(payload) => {
-                Loader::from_load_state_payload(self, payload);
-            }
-            _ => {
-                // unlike in loader_from_heap_evacuable,
-                // pop_load_state_payload is allowed to fail to find a
-                // LoadStatePayload in the heap, as a Rust-side
-                // top-level command may have failed to write the
-                // load state payload back to the heap.
+        // unlike in loader_from_heap_evacuable,
+        // pop_load_state_payload is allowed to fail to find a
+        // LoadStatePayload in the heap, as a Rust-side
+        // top-level command may have failed to write the
+        // load state payload back to the heap.
+
+        read_heap_cell!(load_state_payload,
+            (HeapCellValueTag::Cons, cons_ptr) => {
+                match_untyped_arena_ptr!(cons_ptr,
+                    (ArenaHeaderTag::LiveLoadState, payload) => {
+                        unsafe {
+                            std::ptr::drop_in_place(
+                                payload.as_ptr() as *mut LiveLoadState,
+                            );
+                        }
+                    }
+                    _ => {}
+                );
             }
-        }
+            _ => {}
+        );
     }
 
     #[inline]
@@ -1585,94 +1737,79 @@ impl Machine {
         let stream = try_or_fail!(
             self.machine_st,
             self.machine_st.get_stream_or_alias(
-                self.machine_st[temp_v!(1)],
+                self.machine_st.registers[1],
                 &self.indices.stream_aliases,
-                "$push_load_context",
+                atom!("$push_load_context"),
                 2,
             )
         );
 
-        let path = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(2)]))
+        let path = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[2]))
         );
 
-        self.load_contexts
-            .push(LoadContext::new(path.as_str(), stream));
+        self.load_contexts.push(LoadContext::new(path.as_str(), stream));
     }
 
     pub(crate) fn restore_load_state_payload(
         &mut self,
-        result: Result<LoadStatePayload, SessionError>,
-        evacuable_h: usize,
+        result: Result<TypedArenaPtr<LiveLoadState>, SessionError>,
     ) {
         match result {
-            Ok(payload) => {
-                self.machine_st.heap[evacuable_h] =
-                    HeapCellValue::LoadStatePayload(Box::new(payload));
+            Ok(_payload) => {
             }
             Err(e) => {
-                self.throw_session_error(e, (clause_name!("load"), 1));
+                self.throw_session_error(e, (atom!("load"), 1));
             }
         }
     }
 
     pub(crate) fn scoped_clause_to_evacuable(&mut self) {
-        let module_name = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(1)]))
+        let module_name = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
         );
 
-        let (loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(3));
+        let loader = self.loader_from_heap_evacuable(temp_v!(3));
 
-        let compilation_target = match module_name.as_str() {
-            "user" => CompilationTarget::User,
+        let compilation_target = match module_name {
+            atom!("user") => CompilationTarget::User,
             _ => CompilationTarget::Module(module_name),
         };
 
         let result = loader.read_and_enqueue_term(temp_v!(2), compilation_target);
-        self.restore_load_state_payload(result, evacuable_h);
+        self.restore_load_state_payload(result);
     }
 
     pub(crate) fn clause_to_evacuable(&mut self) {
-        let (loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(2));
-        let compilation_target = loader.load_state.compilation_target.clone();
+        let loader = self.loader_from_heap_evacuable(temp_v!(2));
+        let compilation_target = loader.payload.compilation_target;
 
         let result = loader.read_and_enqueue_term(temp_v!(1), compilation_target);
-        self.restore_load_state_payload(result, evacuable_h);
+        self.restore_load_state_payload(result);
     }
 
     pub(crate) fn conclude_load(&mut self) {
-        let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(1));
+        let mut loader = self.loader_from_heap_evacuable(temp_v!(1));
 
         let compile_final_terms = || {
-            if !loader.predicates.is_empty() {
+            if !loader.payload.predicates.is_empty() {
                 loader.compile_and_submit()?;
             }
 
-            loader.load_state.remove_module_op_exports();
-            LiveTermStream::evacuate(loader)
+            loader.remove_module_op_exports();
+            LiveLoadAndMachineState::evacuate(loader)
         };
 
         let result = compile_final_terms();
-        self.restore_load_state_payload(result, evacuable_h);
+        self.restore_load_state_payload(result);
     }
 
     pub(crate) fn load_context_source(&mut self) {
         if let Some(load_context) = self.load_contexts.last() {
             let path_str = load_context.path.to_str().unwrap();
-            let path_atom = clause_name!(path_str.to_string(), self.machine_st.atom_tbl);
-
-            let path_addr = Addr::Con(
-                self.machine_st
-                    .heap
-                    .push(HeapCellValue::Atom(path_atom, None)),
-            );
+            let path_atom = self.machine_st.atom_tbl.build_with(path_str);
 
-            self.machine_st
-                .unify(path_addr, self.machine_st[temp_v!(1)]);
+            self.machine_st.unify_atom(path_atom, self.machine_st.registers[1]);
         } else {
             self.machine_st.fail = true;
         }
@@ -1683,17 +1820,9 @@ impl Machine {
             match load_context.path.file_name() {
                 Some(file_name) if load_context.path.is_file() => {
                     let file_name_str = file_name.to_str().unwrap();
-                    let file_name_atom =
-                        clause_name!(file_name_str.to_string(), self.machine_st.atom_tbl);
-
-                    let file_name_addr = Addr::Con(
-                        self.machine_st
-                            .heap
-                            .push(HeapCellValue::Atom(file_name_atom, None)),
-                    );
+                    let file_name_atom = self.machine_st.atom_tbl.build_with(file_name_str);
 
-                    self.machine_st
-                        .unify(file_name_addr, self.machine_st[temp_v!(1)]);
+                    self.machine_st.unify_atom(file_name_atom, self.machine_st.registers[1]);
                     return;
                 }
                 _ => {
@@ -1709,18 +1838,9 @@ impl Machine {
         if let Some(load_context) = self.load_contexts.last() {
             if let Some(directory) = load_context.path.parent() {
                 let directory_str = directory.to_str().unwrap();
+                let directory_atom = self.machine_st.atom_tbl.build_with(directory_str);
 
-                let directory_atom =
-                    clause_name!(directory_str.to_string(), self.machine_st.atom_tbl);
-
-                let directory_addr = Addr::Con(
-                    self.machine_st
-                        .heap
-                        .push(HeapCellValue::Atom(directory_atom, None)),
-                );
-
-                self.machine_st
-                    .unify(directory_addr, self.machine_st[temp_v!(1)]);
+                self.machine_st.unify_atom(directory_atom, self.machine_st.registers[1]);
                 return;
             }
         }
@@ -1730,14 +1850,7 @@ impl Machine {
 
     pub(crate) fn load_context_module(&mut self) {
         if let Some(load_context) = self.load_contexts.last() {
-            let module_name_addr = Addr::Con(
-                self.machine_st
-                    .heap
-                    .push(HeapCellValue::Atom(load_context.module.clone(), None)),
-            );
-
-            self.machine_st
-                .unify(module_name_addr, self.machine_st[temp_v!(1)]);
+            self.machine_st.unify_atom(load_context.module, self.machine_st.registers[1]);
         } else {
             self.machine_st.fail = true;
         }
@@ -1745,63 +1858,57 @@ impl Machine {
 
     pub(crate) fn load_context_stream(&mut self) {
         if let Some(load_context) = self.load_contexts.last() {
-            let stream_addr = Addr::Stream(
-                self.machine_st
-                    .heap
-                    .push(HeapCellValue::Stream(load_context.stream.clone())),
+            self.machine_st.unify_constant(
+                UntypedArenaPtr::from(load_context.stream.as_ptr()),
+                self.machine_st.registers[1],
             );
-
-            self.machine_st
-                .unify(stream_addr, self.machine_st[temp_v!(1)]);
         } else {
             self.machine_st.fail = true;
         }
     }
 
-    pub(crate) fn compile_assert(&mut self, append_or_prepend: AppendOrPrepend) {
+    pub(crate) fn compile_assert<'a>(&'a mut self, append_or_prepend: AppendOrPrepend) {
         let key = self
             .machine_st
             .read_predicate_key(self.machine_st[temp_v!(3)], self.machine_st[temp_v!(4)]);
 
-        let module_name = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(5)]))
+        let module_name = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[5]))
         );
 
-        let compilation_target = match module_name.as_str() {
-            "user" => CompilationTarget::User,
+        let compilation_target = match module_name {
+            atom!("user") => CompilationTarget::User,
             _ => CompilationTarget::Module(module_name),
         };
 
-        let compile_assert = || {
-            let mut loader = Loader::new(LiveTermStream::new(ListingSource::User), self);
+        let mut compile_assert = || {
+            let mut loader: Loader<'_, LiveLoadAndMachineState<'_>> =
+                Loader::new(self, LiveTermStream::new(ListingSource::User));
 
-            loader.load_state.compilation_target = compilation_target.clone();
+            loader.payload.compilation_target = compilation_target;
 
             let head = loader.read_term_from_heap(temp_v!(1))?;
             let body = loader.read_term_from_heap(temp_v!(2))?;
 
             let asserted_clause = Term::Clause(
                 Cell::default(),
-                clause_name!(":-"),
-                vec![Box::new(head.clone()), Box::new(body.clone())],
-                fetch_op_spec(clause_name!(":-"), 2, &loader.load_state.wam.indices.op_dir),
+                atom!(":-"),
+                vec![head.clone(), body.clone()],
             );
 
             // if a new predicate was just created, make it dynamic.
-            loader.add_dynamic_predicate(compilation_target.clone(), key.0.clone(), key.1)?;
+            loader.add_dynamic_predicate(compilation_target, key.0, key.1)?;
 
-            loader.load_state.incremental_compile_clause(
-                key.clone(),
+            loader.incremental_compile_clause(
+                key,
                 asserted_clause,
-                compilation_target.clone(),
+                compilation_target,
                 false,
                 append_or_prepend,
             )?;
 
             // the global clock is incremented after each assertion.
-            loader.load_state.wam.machine_st.global_clock += 1;
+            LiveLoadAndMachineState::machine_st(&mut loader.payload).global_clock += 1;
 
             loader.compile_clause_clauses(
                 key,
@@ -1810,15 +1917,15 @@ impl Machine {
                 append_or_prepend,
             )?;
 
-            LiveTermStream::evacuate(loader)
+            LiveLoadAndMachineState::evacuate(loader)
         };
 
         match compile_assert() {
             Ok(_) => {}
             Err(e) => {
                 let error_pi = match append_or_prepend {
-                    AppendOrPrepend::Append => (clause_name!("assertz"), 1),
-                    AppendOrPrepend::Prepend => (clause_name!("asserta"), 1),
+                    AppendOrPrepend::Append  => (atom!("assertz"), 1),
+                    AppendOrPrepend::Prepend => (atom!("asserta"), 1),
                 };
 
                 self.throw_session_error(e, error_pi);
@@ -1827,43 +1934,41 @@ impl Machine {
     }
 
     pub(crate) fn abolish_clause(&mut self) {
-        let module_name = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(1)]))
+        let module_name = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
         );
 
         let key = self
             .machine_st
             .read_predicate_key(self.machine_st[temp_v!(2)], self.machine_st[temp_v!(3)]);
 
-        let compilation_target = match module_name.as_str() {
-            "user" => CompilationTarget::User,
+        let compilation_target = match module_name {
+            atom!("user") => CompilationTarget::User,
             _ => CompilationTarget::Module(module_name),
         };
 
-        let abolish_clause = || {
-            let mut loader = Loader::new(LiveTermStream::new(ListingSource::User), self);
-            loader.load_state.compilation_target = compilation_target;
+        let mut abolish_clause = || {
+            let mut loader: Loader<'_, LiveLoadAndMachineState<'_>>
+                = Loader::new(self, LiveTermStream::new(ListingSource::User));
+
+            loader.payload.compilation_target = compilation_target;
 
-            let clause_clause_compilation_target = match &loader.load_state.compilation_target {
-                CompilationTarget::User => CompilationTarget::Module(clause_name!("builtins")),
-                module => module.clone(),
+            let clause_clause_compilation_target = match compilation_target {
+                CompilationTarget::User => CompilationTarget::Module(atom!("builtins")),
+                module => module,
             };
 
             let mut clause_clause_target_poses: Vec<_> = loader
-                .load_state
-                .wam
+                .wam_prelude
                 .indices
-                .get_predicate_skeleton(&loader.load_state.compilation_target, &key)
+                .get_predicate_skeleton(&compilation_target, &key)
                 .map(|skeleton| {
                     loader
-                        .load_state
-                        .wam
+                        .wam_prelude
                         .indices
                         .get_predicate_skeleton(
                             &clause_clause_compilation_target,
-                            &(clause_name!("$clause"), 2),
+                            &(atom!("$clause"), 2),
                         )
                         .map(|clause_clause_skeleton| {
                             skeleton
@@ -1882,33 +1987,29 @@ impl Machine {
                 .unwrap();
 
             loader
-                .load_state
-                .wam
+                .wam_prelude
                 .indices
-                .get_predicate_skeleton_mut(&loader.load_state.compilation_target, &key)
+                .get_predicate_skeleton_mut(&compilation_target, &key)
                 .map(|skeleton| skeleton.reset());
 
             let code_index = loader
-                .load_state
-                .get_or_insert_code_index(key, loader.load_state.compilation_target.clone());
+                .get_or_insert_code_index(key, compilation_target);
 
             code_index.set(IndexPtr::DynamicUndefined);
 
-            loader.load_state.compilation_target = clause_clause_compilation_target;
+            loader.payload.compilation_target = clause_clause_compilation_target;
 
             while let Some(target_pos) = clause_clause_target_poses.pop() {
-                loader
-                    .load_state
-                    .retract_clause((clause_name!("$clause"), 2), target_pos);
+                loader.retract_clause((atom!("$clause"), 2), target_pos);
             }
 
-            LiveTermStream::evacuate(loader)
+            LiveLoadAndMachineState::evacuate(loader)
         };
 
         match abolish_clause() {
             Ok(_) => {}
             Err(e) => {
-                self.throw_session_error(e, (clause_name!("abolish"), 1));
+                self.throw_session_error(e, (atom!("abolish"), 1));
             }
         }
     }
@@ -1922,40 +2023,40 @@ impl Machine {
             .machine_st
             .store(self.machine_st.deref(self.machine_st[temp_v!(3)]));
 
-        let target_pos = match Number::try_from((target_pos, &self.machine_st.heap)) {
+        let target_pos = match Number::try_from(target_pos) {
             Ok(Number::Integer(n)) => n.to_usize().unwrap(),
-            Ok(Number::Fixnum(n)) => usize::try_from(n).unwrap(),
+            Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).unwrap(),
             _ => unreachable!(),
         };
 
-        let module_name = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(4)]))
+        let module_name = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[4]))
         );
 
-        let compilation_target = match module_name.as_str() {
-            "user" => CompilationTarget::User,
+        let compilation_target = match module_name {
+            atom!("user") => CompilationTarget::User,
             _ => CompilationTarget::Module(module_name),
         };
 
-        let clause_clause_compilation_target = match &compilation_target {
-            CompilationTarget::User => CompilationTarget::Module(clause_name!("builtins")),
-            _ => compilation_target.clone(),
+        let clause_clause_compilation_target = match compilation_target {
+            CompilationTarget::User => CompilationTarget::Module(atom!("builtins")),
+            _ => compilation_target,
         };
 
-        let retract_clause = || {
-            let mut loader = Loader::new(LiveTermStream::new(ListingSource::User), self);
-            loader.load_state.compilation_target = compilation_target;
+        let mut retract_clause = || {
+            let mut loader: Loader<'_, LiveLoadAndMachineState<'_>> =
+                Loader::new(self, LiveTermStream::new(ListingSource::User));
+
+            loader.payload.compilation_target = compilation_target;
 
-            let clause_clause_loc = loader.load_state.retract_dynamic_clause(key, target_pos);
+            let clause_clause_loc = loader.retract_dynamic_clause(key, target_pos);
 
             // the global clock is incremented after each retraction.
-            loader.load_state.wam.machine_st.global_clock += 1;
+            LiveLoadAndMachineState::machine_st(&mut loader.payload).global_clock += 1;
 
-            let target_pos = match loader.load_state.wam.indices.get_predicate_skeleton(
+            let target_pos = match loader.wam_prelude.indices.get_predicate_skeleton(
                 &clause_clause_compilation_target,
-                &(clause_name!("$clause"), 2),
+                &(atom!("$clause"), 2),
             ) {
                 Some(skeleton) => skeleton
                     .target_pos_of_clause_clause_loc(clause_clause_loc)
@@ -1965,79 +2066,74 @@ impl Machine {
                 }
             };
 
-            loader.load_state.compilation_target = clause_clause_compilation_target;
-            loader
-                .load_state
-                .retract_clause((clause_name!("$clause"), 2), target_pos);
+            loader.payload.compilation_target = clause_clause_compilation_target;
+            loader.retract_clause((atom!("$clause"), 2), target_pos);
 
-            LiveTermStream::evacuate(loader)
+            LiveLoadAndMachineState::evacuate(loader)
         };
 
         match retract_clause() {
             Ok(_) => {}
             Err(e) => {
-                self.throw_session_error(e, (clause_name!("retract"), 1));
+                self.throw_session_error(e, (atom!("retract"), 1));
             }
         }
     }
 
     pub(crate) fn is_consistent_with_term_queue(&mut self) {
-        let module_name = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(1)]))
+        let module_name = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
         );
 
         let key = self
             .machine_st
             .read_predicate_key(self.machine_st[temp_v!(2)], self.machine_st[temp_v!(3)]);
 
-        let compilation_target = match module_name.as_str() {
-            "user" => CompilationTarget::User,
+        let compilation_target = match module_name {
+            atom!("user") => CompilationTarget::User,
             _ => CompilationTarget::Module(module_name),
         };
 
-        let (loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(4));
+        let mut loader = self.loader_from_heap_evacuable(temp_v!(4));
 
-        loader.load_state.wam.machine_st.fail = (!loader.predicates.is_empty()
-            && loader.predicates.compilation_target != compilation_target)
-            || !key.is_consistent(&loader.predicates);
+        LiveLoadAndMachineState::machine_st(&mut loader.payload).fail =
+            (!loader.payload.predicates.is_empty()
+             && loader.payload.predicates.compilation_target != compilation_target)
+             || !key.is_consistent(&loader.payload.predicates);
 
-        let result = LiveTermStream::evacuate(loader);
-        self.restore_load_state_payload(result, evacuable_h);
+        let result = LiveLoadAndMachineState::evacuate(loader);
+        self.restore_load_state_payload(result);
     }
 
     pub(crate) fn flush_term_queue(&mut self) {
-        let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(1));
+        let mut loader = self.loader_from_heap_evacuable(temp_v!(1));
 
         let flush_term_queue = || {
-            if !loader.predicates.is_empty() {
+            if !loader.payload.predicates.is_empty() {
                 loader.compile_and_submit()?;
             }
 
-            LiveTermStream::evacuate(loader)
+            LiveLoadAndMachineState::evacuate(loader)
         };
 
         let result = flush_term_queue();
-        self.restore_load_state_payload(result, evacuable_h);
+        self.restore_load_state_payload(result);
     }
 
     pub(crate) fn remove_module_exports(&mut self) {
-        let module_name = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(1)]))
+        let module_name = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
         );
 
-        let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(2));
+        let mut loader = self.loader_from_heap_evacuable(temp_v!(2));
 
         let remove_module_exports = || {
-            loader.load_state.remove_module_exports(module_name);
-            LiveTermStream::evacuate(loader)
+            loader.remove_module_exports(module_name);
+            LiveLoadAndMachineState::evacuate(loader)
         };
 
         let result = remove_module_exports();
-        self.restore_load_state_payload(result, evacuable_h);
+        self.restore_load_state_payload(result);
     }
 
     pub(crate) fn add_non_counted_backtracking(&mut self) {
@@ -2045,26 +2141,24 @@ impl Machine {
             .machine_st
             .read_predicate_key(self.machine_st[temp_v!(1)], self.machine_st[temp_v!(2)]);
 
-        let (mut loader, evacuable_h) = self.loader_from_heap_evacuable(temp_v!(3));
-        loader.non_counted_bt_preds.insert(key);
+        let mut loader = self.loader_from_heap_evacuable(temp_v!(3));
+        loader.payload.non_counted_bt_preds.insert(key);
 
-        let result = LiveTermStream::evacuate(loader);
-        self.restore_load_state_payload(result, evacuable_h);
+        let result = LiveLoadAndMachineState::evacuate(loader);
+        self.restore_load_state_payload(result);
     }
 
     pub(crate) fn meta_predicate_property(&mut self) {
-        let module_name = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(1)]))
+        let module_name = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
         );
 
         let (predicate_name, arity) = self
             .machine_st
             .read_predicate_key(self.machine_st[temp_v!(2)], self.machine_st[temp_v!(3)]);
 
-        let compilation_target = match module_name.as_str() {
-            "user" => CompilationTarget::User,
+        let compilation_target = match module_name {
+            atom!("user") => CompilationTarget::User,
             _ => CompilationTarget::Module(module_name),
         };
 
@@ -2073,30 +2167,23 @@ impl Machine {
             .get_meta_predicate_spec(predicate_name, arity, &compilation_target)
         {
             Some(meta_specs) => {
-                let list_loc = self
-                    .machine_st
-                    .heap
-                    .to_list(meta_specs.iter().map(|meta_spec| match meta_spec {
-                        MetaSpec::Minus => HeapCellValue::Atom(clause_name!("+"), None),
-                        MetaSpec::Plus => HeapCellValue::Atom(clause_name!("-"), None),
-                        MetaSpec::Either => HeapCellValue::Atom(clause_name!("?"), None),
+                let list_loc = iter_to_heap_list(
+                    &mut self.machine_st.heap,
+                    meta_specs.iter().map(|meta_spec| match meta_spec {
+                        MetaSpec::Minus => atom_as_cell!(atom!("+")),
+                        MetaSpec::Plus => atom_as_cell!(atom!("-")),
+                        MetaSpec::Either => atom_as_cell!(atom!("?")),
                         MetaSpec::RequiresExpansionWithArgument(ref arg_num) => {
-                            HeapCellValue::Addr(Addr::Usize(*arg_num))
+                            fixnum_as_cell!(Fixnum::build_with(*arg_num as i64))
                         }
                     }));
 
-                let heap_loc = self.machine_st.heap.push(HeapCellValue::NamedStr(
-                    1,
-                    clause_name!("meta_predicate"),
-                    None,
-                ));
+                let heap_loc = self.machine_st.heap.len();
 
-                self.machine_st
-                    .heap
-                    .push(HeapCellValue::Addr(Addr::HeapCell(list_loc)));
+                self.machine_st.heap.push(atom_as_cell!(atom!("meta_predicate"), 1));
+                self.machine_st.heap.push(heap_loc_as_cell!(list_loc));
 
-                self.machine_st
-                    .unify(Addr::HeapCell(heap_loc), self.machine_st[temp_v!(4)]);
+                unify!(self.machine_st, str_loc_as_cell!(heap_loc), self.machine_st.registers[4]);
             }
             None => {
                 self.machine_st.fail = true;
@@ -2105,18 +2192,16 @@ impl Machine {
     }
 
     pub(crate) fn dynamic_property(&mut self) {
-        let module_name = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(1)]))
+        let module_name = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
         );
 
         let key = self
             .machine_st
             .read_predicate_key(self.machine_st[temp_v!(2)], self.machine_st[temp_v!(3)]);
 
-        let compilation_target = match module_name.as_str() {
-            "user" => CompilationTarget::User,
+        let compilation_target = match module_name {
+            atom!("user") => CompilationTarget::User,
             _ => CompilationTarget::Module(module_name),
         };
 
@@ -2134,18 +2219,16 @@ impl Machine {
     }
 
     pub(crate) fn multifile_property(&mut self) {
-        let module_name = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(1)]))
+        let module_name = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
         );
 
         let key = self
             .machine_st
             .read_predicate_key(self.machine_st[temp_v!(2)], self.machine_st[temp_v!(3)]);
 
-        let compilation_target = match module_name.as_str() {
-            "user" => CompilationTarget::User,
+        let compilation_target = match module_name {
+            atom!("user") => CompilationTarget::User,
             _ => CompilationTarget::Module(module_name),
         };
 
@@ -2163,18 +2246,16 @@ impl Machine {
     }
 
     pub(crate) fn discontiguous_property(&mut self) {
-        let module_name = atom_from!(
-            self.machine_st,
-            self.machine_st
-                .store(self.machine_st.deref(self.machine_st[temp_v!(1)]))
+        let module_name = cell_as_atom!(
+            self.machine_st.store(self.machine_st.deref(self.machine_st.registers[1]))
         );
 
         let key = self
             .machine_st
             .read_predicate_key(self.machine_st[temp_v!(2)], self.machine_st[temp_v!(3)]);
 
-        let compilation_target = match module_name.as_str() {
-            "user" => CompilationTarget::User,
+        let compilation_target = match module_name {
+            atom!("user") => CompilationTarget::User,
             _ => CompilationTarget::Module(module_name),
         };
 
@@ -2194,24 +2275,15 @@ impl Machine {
     pub(crate) fn builtin_property(&mut self) {
         let key = self
             .machine_st
-            .read_predicate_key(self.machine_st[temp_v!(1)], self.machine_st[temp_v!(2)]);
+            .read_predicate_key(self.machine_st.registers[1], self.machine_st.registers[2]);
 
-        match ClauseType::from(key.0, key.1, None) {
+        match ClauseType::from(key.0, key.1) {
             ClauseType::BuiltIn(_) | ClauseType::Inlined(..) | ClauseType::CallN => {
                 return;
             }
-            ClauseType::Named(ref name, arity, _) => {
-                if let Some(module) = self.indices.modules.get(&(clause_name!("builtins"))) {
-                    self.machine_st.fail = !module.code_dir.contains_key(&(name.clone(), arity));
-
-                    return;
-                }
-            }
-            ClauseType::Op(ref name, ref op_desc, _) => {
-                if let Some(module) = self.indices.modules.get(&(clause_name!("builtins"))) {
-                    self.machine_st.fail = !module
-                        .code_dir
-                        .contains_key(&(name.clone(), op_desc.arity()));
+            ClauseType::Named(name, arity, _) => {
+                if let Some(module) = self.indices.modules.get(&(atom!("builtins"))) {
+                    self.machine_st.fail = !module.code_dir.contains_key(&(name, arity));
 
                     return;
                 }
@@ -2223,63 +2295,24 @@ impl Machine {
     }
 }
 
-impl<'a> Loader<'a, LiveTermStream> {
-    pub(super) fn to_load_state_payload(mut self) -> LoadStatePayload {
-        LoadStatePayload {
-            term_stream: mem::replace(
-                &mut self.term_stream,
-                LiveTermStream::new(ListingSource::User),
-            ),
-            non_counted_bt_preds: mem::replace(&mut self.non_counted_bt_preds, IndexSet::new()),
-            compilation_target: self.load_state.compilation_target.take(),
-            retraction_info: mem::replace(
-                &mut self.load_state.retraction_info,
-                RetractionInfo::new(self.load_state.wam.code_repo.code.len()),
-            ),
-            predicates: self.predicates.take(),
-            clause_clauses: mem::replace(&mut self.clause_clauses, vec![]),
-            module_op_exports: mem::replace(&mut self.load_state.module_op_exports, vec![]),
-        }
-    }
-
-    pub(super) fn from_load_state_payload(
-        wam: &'a mut Machine,
-        mut payload: Box<LoadStatePayload>,
-    ) -> Self {
-        Loader {
-            term_stream: mem::replace(
-                &mut payload.term_stream,
-                LiveTermStream::new(ListingSource::User),
-            ),
-            non_counted_bt_preds: mem::replace(&mut payload.non_counted_bt_preds, IndexSet::new()),
-            clause_clauses: mem::replace(&mut payload.clause_clauses, vec![]),
-            predicates: payload.predicates.take(),
-            load_state: LoadState {
-                compilation_target: payload.compilation_target.take(),
-                module_op_exports: mem::replace(&mut payload.module_op_exports, vec![]),
-                retraction_info: mem::replace(&mut payload.retraction_info, RetractionInfo::new(0)),
-                wam,
-            },
-        }
-    }
-
+impl<'a> Loader<'a, LiveLoadAndMachineState<'a>> {
     fn read_and_enqueue_term(
         mut self,
         term_reg: RegType,
         compilation_target: CompilationTarget,
-    ) -> Result<LoadStatePayload, SessionError> {
-        if self.predicates.compilation_target != compilation_target {
-            if !self.predicates.is_empty() {
+    ) -> Result<TypedArenaPtr<LoadStatePayload<LiveTermStream>>, SessionError> {
+        if self.payload.predicates.compilation_target != compilation_target {
+            if !self.payload.predicates.is_empty() {
                 self.compile_and_submit()?;
             }
 
-            self.predicates.compilation_target = compilation_target;
+            self.payload.predicates.compilation_target = compilation_target;
         }
 
         let term = self.read_term_from_heap(term_reg)?;
 
         self.add_clause_clause_if_dynamic(&term)?;
-        self.term_stream.term_queue.push_back(term);
+        self.payload.term_stream.term_queue.push_back(term);
 
         self.load()
     }
index fbfb1bcf57eaa19406c93af748b402156cafe759..d62e5b5b14ccb7725dfdfd69f0b0684677ee0c36 100644 (file)
@@ -1,17 +1,14 @@
-use prolog_parser::ast::*;
-use prolog_parser::{clause_name, temp_v};
+use crate::atom_table::*;
+use crate::parser::ast::*;
 
-use crate::forms::{ModuleSource, Number}; //, PredicateKey};
+use crate::forms::*;
 use crate::machine::heap::*;
 use crate::machine::loader::CompilationTarget;
-use crate::machine::machine_indices::*;
 use crate::machine::machine_state::*;
-use crate::machine::PredicateKey;
-use crate::rug::Integer;
+use crate::types::*;
 
-use std::rc::Rc;
-
-pub(crate) type MachineStub = Vec<HeapCellValue>;
+pub type MachineStub = Vec<HeapCellValue>;
+pub type MachineStubGen = Box<dyn Fn(&mut MachineState) -> MachineStub>;
 
 #[derive(Debug, Clone, Copy)]
 enum ErrorProvenance {
@@ -26,13 +23,64 @@ pub(crate) struct MachineError {
     from: ErrorProvenance,
 }
 
+// from 7.12.2 b) of 13211-1:1995
+#[derive(Debug, Clone, Copy)]
+pub(crate) enum ValidType {
+    Atom,
+    Atomic,
+    //    Boolean,
+    Byte,
+    Callable,
+    Character,
+    Compound,
+    Evaluable,
+    Float,
+    InByte,
+    InCharacter,
+    Integer,
+    List,
+    #[allow(unused)] Number,
+    Pair,
+    //    PredicateIndicator,
+    //    Variable
+    TcpListener,
+}
+
+impl ValidType {
+    pub(crate) fn as_atom(self) -> Atom {
+        match self {
+            ValidType::Atom => atom!("atom"),
+            ValidType::Atomic => atom!("atomic"),
+            //            ValidType::Boolean => atom!("boolean"),
+            ValidType::Byte => atom!("byte"),
+            ValidType::Callable => atom!("callable"),
+            ValidType::Character => atom!("character"),
+            ValidType::Compound => atom!("compound"),
+            ValidType::Evaluable => atom!("evaluable"),
+            ValidType::Float => atom!("float"),
+            ValidType::InByte => atom!("in_byte"),
+            ValidType::InCharacter => atom!("in_character"),
+            ValidType::Integer => atom!("integer"),
+            ValidType::List => atom!("list"),
+            ValidType::Number => atom!("number"),
+            ValidType::Pair => atom!("pair"),
+            //            ValidType::PredicateIndicator => atom!("predicate_indicator"),
+            //            ValidType::Variable => atom!("variable")
+            ValidType::TcpListener => atom!("tcp_listener"),
+        }
+    }
+}
+
 pub(crate) trait TypeError {
-    fn type_error(self, h: usize, valid_type: ValidType) -> MachineError;
+    fn type_error(self, machine_st: &mut MachineState, valid_type: ValidType) -> MachineError;
 }
 
-impl TypeError for Addr {
-    fn type_error(self, _: usize, valid_type: ValidType) -> MachineError {
-        let stub = functor!("type_error", [atom(valid_type.as_str()), addr(self)]);
+impl TypeError for HeapCellValue {
+    fn type_error(self, _machine_st: &mut MachineState, valid_type: ValidType) -> MachineError {
+        let stub = functor!(
+            atom!("type_error"),
+            [atom(valid_type.as_atom()), cell(self)]
+        );
 
         MachineError {
             stub,
@@ -42,21 +90,29 @@ impl TypeError for Addr {
     }
 }
 
-impl TypeError for HeapCellValue {
-    fn type_error(self, _: usize, valid_type: ValidType) -> MachineError {
-        let stub = functor!("type_error", [atom(valid_type.as_str()), value(self)]);
+impl TypeError for MachineStub {
+    fn type_error(self, machine_st: &mut MachineState, valid_type: ValidType) -> MachineError {
+        let stub = functor!(
+            atom!("type_error"),
+            [atom(valid_type.as_atom()), str(machine_st.heap.len(), 0)],
+            [self]
+        );
 
         MachineError {
             stub,
             location: None,
-            from: ErrorProvenance::Received,
+            from: ErrorProvenance::Constructed,
         }
     }
 }
 
-impl TypeError for MachineStub {
-    fn type_error(self, h: usize, valid_type: ValidType) -> MachineError {
-        let stub = functor!("type_error", [atom(valid_type.as_str()), aux(h, 0)], [self]);
+impl TypeError for FunctorStub {
+    fn type_error(self, machine_st: &mut MachineState, valid_type: ValidType) -> MachineError {
+        let stub = functor!(
+            atom!("type_error"),
+            [atom(valid_type.as_atom()), str(machine_st.heap.len(), 0)],
+            [self]
+        );
 
         MachineError {
             stub,
@@ -67,8 +123,14 @@ impl TypeError for MachineStub {
 }
 
 impl TypeError for Number {
-    fn type_error(self, _h: usize, valid_type: ValidType) -> MachineError {
-        let stub = functor!("type_error", [atom(valid_type.as_str()), number(self)]);
+    fn type_error(self, machine_st: &mut MachineState, valid_type: ValidType) -> MachineError {
+        let stub = functor!(
+            atom!("type_error"),
+            [
+                atom(valid_type.as_atom()),
+                number(&mut machine_st.arena, self)
+            ]
+        );
 
         MachineError {
             stub,
@@ -79,14 +141,24 @@ impl TypeError for Number {
 }
 
 pub(crate) trait PermissionError {
-    fn permission_error(self, h: usize, index_str: &'static str, perm: Permission) -> MachineError;
-}
-
-impl PermissionError for Addr {
-    fn permission_error(self, _: usize, index_str: &'static str, perm: Permission) -> MachineError {
+    fn permission_error(
+        self,
+        machine_st: &mut MachineState,
+        index_atom: Atom,
+        perm: Permission,
+    ) -> MachineError;
+}
+
+impl PermissionError for HeapCellValue {
+    fn permission_error(
+        self,
+        _machine_st: &mut MachineState,
+        index_atom: Atom,
+        perm: Permission,
+    ) -> MachineError {
         let stub = functor!(
-            "permission_error",
-            [atom(perm.as_str()), atom(index_str), addr(self)]
+            atom!("permission_error"),
+            [atom(perm.as_atom()), atom(index_atom), cell(self)]
         );
 
         MachineError {
@@ -98,10 +170,19 @@ impl PermissionError for Addr {
 }
 
 impl PermissionError for MachineStub {
-    fn permission_error(self, h: usize, index_str: &'static str, perm: Permission) -> MachineError {
+    fn permission_error(
+        self,
+        machine_st: &mut MachineState,
+        index_atom: Atom,
+        perm: Permission,
+    ) -> MachineError {
         let stub = functor!(
-            "permission_error",
-            [atom(perm.as_str()), atom(index_str), aux(h, 0)],
+            atom!("permission_error"),
+            [
+                atom(perm.as_atom()),
+                atom(index_atom),
+                str(machine_st.heap.len(), 0)
+            ],
             [self]
         );
 
@@ -114,12 +195,12 @@ impl PermissionError for MachineStub {
 }
 
 pub(super) trait DomainError {
-    fn domain_error(self, error: DomainErrorType) -> MachineError;
+    fn domain_error(self, machine_st: &mut MachineState, error: DomainErrorType) -> MachineError;
 }
 
-impl DomainError for Addr {
-    fn domain_error(self, error: DomainErrorType) -> MachineError {
-        let stub = functor!("domain_error", [atom(error.as_str()), addr(self)]);
+impl DomainError for HeapCellValue {
+    fn domain_error(self, _machine_st: &mut MachineState, error: DomainErrorType) -> MachineError {
+        let stub = functor!(atom!("domain_error"), [atom(error.as_atom()), cell(self)]);
 
         MachineError {
             stub,
@@ -130,8 +211,11 @@ impl DomainError for Addr {
 }
 
 impl DomainError for Number {
-    fn domain_error(self, error: DomainErrorType) -> MachineError {
-        let stub = functor!("domain_error", [atom(error.as_str()), number(self)]);
+    fn domain_error(self, machine_st: &mut MachineState, error: DomainErrorType) -> MachineError {
+        let stub = functor!(
+            atom!("domain_error"),
+            [atom(error.as_atom()), number(&mut machine_st.arena, self)]
+        );
 
         MachineError {
             stub,
@@ -141,18 +225,19 @@ impl DomainError for Number {
     }
 }
 
-impl MachineError {
-    pub(super) fn functor_stub(name: ClauseName, arity: usize) -> MachineStub {
-        functor!(
-            "/",
-            SharedOpDesc::new(400, YFX),
-            [clause_name(name), integer(arity)]
-        )
-    }
+pub(super) type FunctorStub = [HeapCellValue; 3];
 
+#[inline(always)]
+pub(super) fn functor_stub(name: Atom, arity: usize) -> FunctorStub {
+    [atom_as_cell!(atom!("/"), 2),
+     atom_as_cell!(name),
+     fixnum_as_cell!(Fixnum::build_with(arity as i64))]
+}
+
+impl MachineState {
     #[inline]
-    pub(super) fn interrupt_error() -> Self {
-        let stub = functor!("$interrupt_thrown");
+    pub(super) fn interrupt_error(&mut self) -> MachineError {
+        let stub = functor!(atom!("$interrupt_thrown"));
 
         MachineError {
             stub,
@@ -161,8 +246,8 @@ impl MachineError {
         }
     }
 
-    pub(super) fn evaluation_error(eval_error: EvalError) -> Self {
-        let stub = functor!("evaluation_error", [atom(eval_error.as_str())]);
+    pub(super) fn evaluation_error(&mut self, eval_error: EvalError) -> MachineError {
+        let stub = functor!(atom!("evaluation_error"), [atom(eval_error.as_atom())]);
 
         MachineError {
             stub,
@@ -171,30 +256,26 @@ impl MachineError {
         }
     }
 
-    pub(super) fn type_error<T: TypeError>(h: usize, valid_type: ValidType, culprit: T) -> Self {
-        culprit.type_error(h, valid_type)
+    pub(super) fn type_error<T: TypeError>(
+        &mut self,
+        valid_type: ValidType,
+        culprit: T,
+    ) -> MachineError {
+        culprit.type_error(self, valid_type)
     }
 
     pub(super) fn module_resolution_error(
-        h: usize,
-        mod_name: ClauseName,
-        name: ClauseName,
+        &mut self,
+        mod_name: Atom,
+        name: Atom,
         arity: usize,
-    ) -> Self {
-        let res_stub = functor!(
-            ":",
-            SharedOpDesc::new(600, XFY),
-            [clause_name(mod_name), clause_name(name)]
-        );
+    ) -> MachineError {
+        let h = self.heap.len();
 
-        let ind_stub = functor!(
-            "/",
-            SharedOpDesc::new(400, YFX),
-            [aux(h + 2, 0), integer(arity)],
-            [res_stub]
-        );
+        let res_stub = functor!(atom!(":"), [atom(mod_name), atom(name)]);
+        let ind_stub = functor!(atom!("/"), [str(h + 2, 0), fixnum(arity)], [res_stub]);
 
-        let stub = functor!("evaluation_error", [aux(h, 0)], [ind_stub]);
+        let stub = functor!(atom!("evaluation_error"), [str(h, 0)], [ind_stub]);
 
         MachineError {
             stub,
@@ -203,10 +284,13 @@ impl MachineError {
         }
     }
 
-    pub(super) fn existence_error(h: usize, err: ExistenceError) -> Self {
+    pub(super) fn existence_error(&mut self, err: ExistenceError) -> MachineError {
         match err {
             ExistenceError::Module(name) => {
-                let stub = functor!("existence_error", [atom("source_sink"), clause_name(name)]);
+                let stub = functor!(
+                    atom!("existence_error"),
+                    [atom(atom!("source_sink")), atom(name)]
+                );
 
                 MachineError {
                     stub,
@@ -215,13 +299,13 @@ impl MachineError {
                 }
             }
             ExistenceError::Procedure(name, arity) => {
-                let culprit = functor!(
-                    "/",
-                    SharedOpDesc::new(400, YFX),
-                    [clause_name(name), integer(arity)]
-                );
+                let culprit = functor!(atom!("/"), [atom(name), fixnum(arity)]);
 
-                let stub = functor!("existence_error", [atom("procedure"), aux(h, 0)], [culprit]);
+                let stub = functor!(
+                    atom!("existence_error"),
+                    [atom(atom!("procedure")), str(self.heap.len(), 0)],
+                    [culprit]
+                );
 
                 MachineError {
                     stub,
@@ -233,8 +317,8 @@ impl MachineError {
                 let source_stub = source.as_functor_stub();
 
                 let stub = functor!(
-                    "existence_error",
-                    [atom("source_sink"), aux(h, 0)],
+                    atom!("existence_error"),
+                    [atom(atom!("source_sink")), str(self.heap.len(), 0)],
                     [source_stub]
                 );
 
@@ -245,7 +329,10 @@ impl MachineError {
                 }
             }
             ExistenceError::SourceSink(culprit) => {
-                let stub = functor!("existence_error", [atom("source_sink"), addr(culprit)]);
+                let stub = functor!(
+                    atom!("existence_error"),
+                    [atom(atom!("source_sink")), cell(culprit)]
+                );
 
                 MachineError {
                     stub,
@@ -254,7 +341,10 @@ impl MachineError {
                 }
             }
             ExistenceError::Stream(culprit) => {
-                let stub = functor!("existence_error", [atom("stream"), addr(culprit)]);
+                let stub = functor!(
+                    atom!("existence_error"),
+                    [atom(atom!("stream")), cell(culprit)]
+                );
 
                 MachineError {
                     stub,
@@ -266,36 +356,41 @@ impl MachineError {
     }
 
     pub(super) fn permission_error<T: PermissionError>(
-        h: usize,
+        &mut self,
         err: Permission,
-        index_str: &'static str,
+        index_atom: Atom,
         culprit: T,
-    ) -> Self {
-        culprit.permission_error(h, index_str, err)
+    ) -> MachineError {
+        culprit.permission_error(self, index_atom, err)
     }
 
-    fn arithmetic_error(h: usize, err: ArithmeticError) -> Self {
+    pub(super) fn evaluable_error(&mut self, name: Atom, arity: usize) -> MachineError {
+        let evaluable_stub = functor_stub(name, arity);
+        evaluable_stub.type_error(self, ValidType::Evaluable)
+    }
+
+    fn arithmetic_error(&mut self, err: ArithmeticError) -> MachineError {
         match err {
-            ArithmeticError::UninstantiatedVar => Self::instantiation_error(),
-            ArithmeticError::NonEvaluableFunctor(name, arity) => {
-                let culprit = functor!(
-                    "/",
-                    SharedOpDesc::new(400, YFX),
-                    [constant(h, &name), integer(arity)]
-                );
+            ArithmeticError::UninstantiatedVar => self.instantiation_error(),
+            ArithmeticError::NonEvaluableFunctor(literal, arity) => {
+                let culprit = functor!(atom!("/"), [literal(literal), fixnum(arity)]);
 
-                Self::type_error(h, ValidType::Evaluable, culprit)
+                self.type_error(ValidType::Evaluable, culprit)
             }
         }
     }
 
     #[inline]
-    pub(super) fn domain_error<T: DomainError>(error: DomainErrorType, culprit: T) -> Self {
-        culprit.domain_error(error)
+    pub(super) fn domain_error<T: DomainError>(
+        &mut self,
+        error: DomainErrorType,
+        culprit: T,
+    ) -> MachineError {
+        culprit.domain_error(self, error)
     }
 
-    pub(super) fn instantiation_error() -> Self {
-        let stub = functor!("instantiation_error");
+    pub(super) fn instantiation_error(&mut self) -> MachineError {
+        let stub = functor!(atom!("instantiation_error"));
 
         MachineError {
             stub,
@@ -304,81 +399,91 @@ impl MachineError {
         }
     }
 
-    pub(super) fn session_error(h: usize, err: SessionError) -> Self {
+    pub(super) fn session_error(&mut self, err: SessionError) -> MachineError {
         match err {
             // SessionError::CannotOverwriteBuiltIn(pred_str) |
             /*
             SessionError::CannotOverwriteImport(pred_str) => {
                 Self::permission_error(
+                    atom_tbl,
                     h,
                     Permission::Modify,
-                    "private_procedure",
-                    functor!(clause_name(pred_str)),
+                    atom!("private_procedure"),
+                    functor!(atom(pred_str)),
                 )
             }
             */
-            SessionError::ExistenceError(err) => Self::existence_error(h, err),
+            SessionError::ExistenceError(err) => self.existence_error(err),
             // SessionError::InvalidFileName(filename) => {
             //     Self::existence_error(h, ExistenceError::Module(filename))
             // }
-            SessionError::ModuleDoesNotContainExport(..) => Self::permission_error(
-                h,
-                Permission::Access,
-                "private_procedure",
-                functor!("module_does_not_contain_claimed_export"),
-            ),
-            SessionError::ModuleCannotImportSelf(module_name) => Self::permission_error(
-                h,
-                Permission::Modify,
-                "module",
-                functor!("module_cannot_import_self", [clause_name(module_name)]),
-            ),
-            SessionError::NamelessEntry => Self::permission_error(
-                h,
-                Permission::Create,
-                "static_procedure",
-                functor!("nameless_procedure"),
-            ),
+            SessionError::ModuleDoesNotContainExport(..) => {
+                let error_atom = atom!("module_does_not_contain_claimed_export");
+
+                self.permission_error(
+                    Permission::Access,
+                    atom!("private_procedure"),
+                    functor!(error_atom),
+                )
+            }
+            SessionError::ModuleCannotImportSelf(module_name) => {
+                let error_atom = atom!("module_cannot_import_self");
+
+                self.permission_error(
+                    Permission::Modify,
+                    atom!("module"),
+                    functor!(error_atom, [atom(module_name)]),
+                )
+            }
+            SessionError::NamelessEntry => {
+                let error_atom = atom!("nameless_procedure");
+                self.permission_error(Permission::Create, atom!("static_procedure"), functor!(error_atom))
+            }
             SessionError::OpIsInfixAndPostFix(op) => {
-                Self::permission_error(h, Permission::Create, "operator", functor!(clause_name(op)))
+                self.permission_error(Permission::Create, atom!("operator"), functor!(op))
             }
-            SessionError::CompilationError(err) => Self::syntax_error(h, err),
+            SessionError::CompilationError(err) => self.syntax_error(err),
             SessionError::PredicateNotMultifileOrDiscontiguous(compilation_target, key) => {
-                let functor_stub = Self::functor_stub(key.0, key.1);
+                let functor_stub = functor_stub(key.0, key.1);
+
                 let stub = functor!(
-                    ":",
-                    SharedOpDesc::new(600, XFY),
-                    [clause_name(compilation_target.module_name()), aux(h + 4, 0)],
+                    atom!(":"),
+                    [
+                        atom(compilation_target.module_name()),
+                        str(self.heap.len() + 4, 0)
+                    ],
                     [functor_stub]
                 );
 
-                Self::permission_error(
-                    h,
+                self.permission_error(
                     Permission::Modify,
-                    "not_declared_multifile_or_discontiguous",
+                    atom!("not_declared_multifile_or_discontiguous"),
                     stub,
                 )
             }
-            SessionError::QueryCannotBeDefinedAsFact => Self::permission_error(
-                h,
-                Permission::Create,
-                "static_procedure",
-                functor!("query_cannot_be_defined_as_fact"),
-            ),
+            SessionError::QueryCannotBeDefinedAsFact => {
+                let error_atom = atom!("query_cannot_be_defined_as_fact");
+
+                self.permission_error(
+                    Permission::Create,
+                    atom!("static_procedure"),
+                    functor!(error_atom),
+                )
+            }
         }
     }
 
-    pub(super) fn syntax_error<E: Into<CompilationError>>(h: usize, err: E) -> Self {
+    pub(super) fn syntax_error<E: Into<CompilationError>>(&mut self, err: E) -> MachineError {
         let err = err.into();
 
         if let CompilationError::Arithmetic(err) = err {
-            return Self::arithmetic_error(h, err);
+            return self.arithmetic_error(err);
         }
 
         let location = err.line_and_col_num();
-        let stub = err.as_functor(h);
+        let stub = err.as_functor();
 
-        let stub = functor!("syntax_error", [aux(h, 0)], [stub]);
+        let stub = functor!(atom!("syntax_error"), [str(self.heap.len(), 0)], [stub]);
 
         MachineError {
             stub,
@@ -387,8 +492,8 @@ impl MachineError {
         }
     }
 
-    pub(super) fn representation_error(flag: RepFlag) -> Self {
-        let stub = functor!("representation_error", [atom(flag.as_str())]);
+    pub(super) fn representation_error(&mut self, flag: RepFlag) -> MachineError {
+        let stub = functor!(atom!("representation_error"), [atom(flag.as_atom())]);
 
         MachineError {
             stub,
@@ -397,13 +502,53 @@ impl MachineError {
         }
     }
 
+    pub(super) fn error_form(&mut self, err: MachineError, src: FunctorStub) -> MachineStub {
+        let location = err.location;
+        let err_len = err.len();
+
+        let h = self.heap.len();
+
+        let mut stub = vec![
+            atom_as_cell!(atom!("error"), 2),
+            str_loc_as_cell!(h + 3),
+            str_loc_as_cell!(h + 3 + err_len),
+        ];
+
+        stub.extend(err.into_iter(3));
+
+        if let Some((line_num, _)) = location {
+            stub.push(atom_as_cell!(atom!(":"), 2));
+            stub.push(str_loc_as_cell!(h + 6 + err_len));
+            stub.push(integer_as_cell!(Number::arena_from(
+                line_num,
+                &mut self.arena
+            )));
+        }
+
+        stub.extend(src.iter());
+        stub
+    }
+
+    pub(super) fn throw_exception(&mut self, err: MachineStub) {
+        let h = self.heap.len();
+
+        self.ball.boundary = 0;
+        self.ball.stub.truncate(0);
+
+        self.heap.extend(err.into_iter());
+
+        self.registers[1] = str_loc_as_cell!(h);
+
+        self.set_ball();
+        self.unwind_stack();
+    }
+}
+
+impl MachineError {
     fn into_iter(self, offset: usize) -> Box<dyn Iterator<Item = HeapCellValue>> {
         match self.from {
             ErrorProvenance::Constructed => {
-                Box::new(self.stub.into_iter().map(move |hcv| match hcv {
-                    HeapCellValue::Addr(addr) => HeapCellValue::Addr(addr + offset),
-                    hcv => hcv,
-                }))
+                Box::new(self.stub.into_iter().map(move |hcv| hcv + offset))
             }
             ErrorProvenance::Received => Box::new(self.stub.into_iter()),
         }
@@ -415,7 +560,7 @@ impl MachineError {
 }
 
 #[derive(Debug)]
-pub(crate) enum CompilationError {
+pub enum CompilationError {
     Arithmetic(ArithmeticError),
     ParserError(ParserError),
     // BadPendingByte,
@@ -433,7 +578,7 @@ pub(crate) enum CompilationError {
     InvalidModuleExport,
     InvalidRuleHead,
     InvalidUseModuleDecl,
-    InvalidModuleResolution(ClauseName),
+    InvalidModuleResolution(Atom),
     UnreadableTerm,
 }
 
@@ -459,34 +604,60 @@ impl CompilationError {
         }
     }
 
-    pub(crate) fn as_functor(&self, _h: usize) -> MachineStub {
+    pub(crate) fn as_functor(&self) -> MachineStub {
         match self {
-            &CompilationError::Arithmetic(..) => functor!("arithmetic_error"),
+            &CompilationError::Arithmetic(..) => {
+                functor!(atom!("arithmetic_error"))
+            }
             // &CompilationError::BadPendingByte =>
-            //     functor!("bad_pending_byte"),
-            &CompilationError::CannotParseCyclicTerm => functor!("cannot_parse_cyclic_term"),
+            //     functor!(atom_from_ss!("bad_pending_byte"), atom_tbl),
+            &CompilationError::CannotParseCyclicTerm => {
+                functor!(atom!("cannot_parse_cyclic_term"))
+            }
             // &CompilationError::ExpandedTermsListNotAList =>
-            //     functor!("expanded_terms_list_is_not_a_list"),
-            &CompilationError::ExpectedRel => functor!("expected_relation"),
+            //     functor!(atom_tbl.build_with_static_str("expanded_terms_list_is_not_a_list")),
+            &CompilationError::ExpectedRel => {
+                functor!(atom!("expected_relation"))
+            }
             // &CompilationError::ExpectedTopLevelTerm =>
-            //     functor!("expected_atom_or_cons_or_clause"),
-            &CompilationError::InadmissibleFact => functor!("inadmissible_fact"),
-            &CompilationError::InadmissibleQueryTerm => functor!("inadmissible_query_term"),
-            &CompilationError::InconsistentEntry => functor!("inconsistent_entry"),
+            //     functor!(atom_from_ss!("expected_atom_or_cons_or_clause"), atom_tbl),
+            &CompilationError::InadmissibleFact => {
+                functor!(atom!("inadmissible_fact"))
+            }
+            &CompilationError::InadmissibleQueryTerm => {
+                functor!(atom!("inadmissible_query_term"))
+            }
+            &CompilationError::InconsistentEntry => {
+                functor!(atom!("inconsistent_entry"))
+            }
             // &CompilationError::InvalidDoubleQuotesDecl =>
-            //     functor!("invalid_double_quotes_declaration"),
+            //     functor!(atom_from_ss!("invalid_double_quotes_declaration"), atom_tbl),
             // &CompilationError::InvalidHook =>
-            //     functor!("invalid_hook"),
-            &CompilationError::InvalidMetaPredicateDecl => functor!("invalid_meta_predicate_decl"),
-            &CompilationError::InvalidModuleDecl => functor!("invalid_module_declaration"),
-            &CompilationError::InvalidModuleExport => functor!("invalid_module_export"),
+            //     functor!(atom_from_ss!("invalid_hook"), atom_tbl),
+            &CompilationError::InvalidMetaPredicateDecl => {
+                functor!(atom!("invalid_meta_predicate_decl"))
+            }
+            &CompilationError::InvalidModuleDecl => {
+                functor!(atom!("invalid_module_declaration"))
+            }
+            &CompilationError::InvalidModuleExport => {
+                functor!(atom!("invalid_module_export"))
+            }
             &CompilationError::InvalidModuleResolution(ref module_name) => {
-                functor!("no_such_module", [clause_name(module_name.clone())])
+                functor!(atom!("no_such_module"), [atom(module_name)])
+            }
+            &CompilationError::InvalidRuleHead => {
+                functor!(atom!("invalid_head_of_rule"))
+            }
+            &CompilationError::InvalidUseModuleDecl => {
+                functor!(atom!("invalid_use_module_declaration"))
+            }
+            &CompilationError::ParserError(ref err) => {
+                functor!(err.as_atom())
+            }
+            &CompilationError::UnreadableTerm => {
+                functor!(atom!("unreadable_term"))
             }
-            &CompilationError::InvalidRuleHead => functor!("invalid_head_of_rule"),
-            &CompilationError::InvalidUseModuleDecl => functor!("invalid_use_module_declaration"),
-            &CompilationError::ParserError(ref err) => functor!(err.as_str()),
-            &CompilationError::UnreadableTerm => functor!("unreadable_term"),
         }
     }
 }
@@ -504,63 +675,15 @@ pub(crate) enum Permission {
 
 impl Permission {
     #[inline]
-    pub(crate) fn as_str(self) -> &'static str {
-        match self {
-            Permission::Access => "access",
-            Permission::Create => "create",
-            Permission::InputStream => "input",
-            Permission::Modify => "modify",
-            Permission::Open => "open",
-            Permission::OutputStream => "output",
-            Permission::Reposition => "reposition",
-        }
-    }
-}
-
-// from 7.12.2 b) of 13211-1:1995
-#[derive(Debug, Clone, Copy)]
-pub(crate) enum ValidType {
-    Atom,
-    Atomic,
-    //    Boolean,
-    Byte,
-    Callable,
-    Character,
-    Compound,
-    Evaluable,
-    Float,
-    InByte,
-    InCharacter,
-    Integer,
-    List,
-    Number,
-    Pair,
-    //    PredicateIndicator,
-    //    Variable
-    TcpListener,
-}
-
-impl ValidType {
-    pub(crate) fn as_str(self) -> &'static str {
+    pub(crate) fn as_atom(self) -> Atom {
         match self {
-            ValidType::Atom => "atom",
-            ValidType::Atomic => "atomic",
-            //            ValidType::Boolean => "boolean",
-            ValidType::Byte => "byte",
-            ValidType::Callable => "callable",
-            ValidType::Character => "character",
-            ValidType::Compound => "compound",
-            ValidType::Evaluable => "evaluable",
-            ValidType::Float => "float",
-            ValidType::InByte => "in_byte",
-            ValidType::InCharacter => "in_character",
-            ValidType::Integer => "integer",
-            ValidType::List => "list",
-            ValidType::Number => "number",
-            ValidType::Pair => "pair",
-            //            ValidType::PredicateIndicator => "predicate_indicator",
-            //            ValidType::Variable => "variable"
-            ValidType::TcpListener => "tcp_listener",
+            Permission::Access => atom!("access"),
+            Permission::Create => atom!("create"),
+            Permission::InputStream => atom!("input"),
+            Permission::Modify => atom!("modify"),
+            Permission::Open => atom!("open"),
+            Permission::OutputStream => atom!("output"),
+            Permission::Reposition => atom!("reposition"),
         }
     }
 }
@@ -576,14 +699,14 @@ pub(crate) enum DomainErrorType {
 }
 
 impl DomainErrorType {
-    pub(crate) fn as_str(self) -> &'static str {
+    pub(crate) fn as_atom(self) -> Atom {
         match self {
-            DomainErrorType::IOMode => "io_mode",
-            DomainErrorType::NotLessThanZero => "not_less_than_zero",
-            DomainErrorType::Order => "order",
-            DomainErrorType::SourceSink => "source_sink",
-            DomainErrorType::Stream => "stream",
-            DomainErrorType::StreamOrAlias => "stream_or_alias",
+            DomainErrorType::IOMode => atom!("io_mode"),
+            DomainErrorType::NotLessThanZero => atom!("not_less_than_zero"),
+            DomainErrorType::Order => atom!("order"),
+            DomainErrorType::SourceSink => atom!("source_sink"),
+            DomainErrorType::Stream => atom!("stream"),
+            DomainErrorType::StreamOrAlias => atom!("stream_or_alias"),
         }
     }
 }
@@ -601,22 +724,22 @@ pub(crate) enum RepFlag {
 }
 
 impl RepFlag {
-    pub(crate) fn as_str(self) -> &'static str {
+    pub(crate) fn as_atom(self) -> Atom {
         match self {
-            RepFlag::Character => "character",
-            RepFlag::CharacterCode => "character_code",
-            RepFlag::InCharacterCode => "in_character_code",
-            RepFlag::MaxArity => "max_arity",
-            RepFlag::Term => "term",
-            //            RepFlag::MaxInteger => "max_integer",
-            //            RepFlag::MinInteger => "min_integer"
+            RepFlag::Character => atom!("character"),
+            RepFlag::CharacterCode => atom!("character_code"),
+            RepFlag::InCharacterCode => atom!("in_character_code"),
+            RepFlag::MaxArity => atom!("max_arity"),
+            RepFlag::Term => atom!("term"),
+            //            RepFlag::MaxInteger => atom!("max_integer"),
+            //            RepFlag::MinInteger => atom!("min_integer")
         }
     }
 }
 
 // from 7.12.2 g) of 13211-1:1995
 #[derive(Debug, Clone, Copy)]
-pub(crate) enum EvalError {
+pub enum EvalError {
     FloatOverflow,
     Undefined,
     //    Underflow,
@@ -624,93 +747,108 @@ pub(crate) enum EvalError {
 }
 
 impl EvalError {
-    pub(crate) fn as_str(self) -> &'static str {
+    pub(crate) fn as_atom(self) -> Atom {
         match self {
-            EvalError::FloatOverflow => "float_overflow",
-            EvalError::Undefined => "undefined",
-            //            EvalError::FloatUnderflow => "underflow",
-            EvalError::ZeroDivisor => "zero_divisor",
+            EvalError::FloatOverflow => atom!("float_overflow"),
+            EvalError::Undefined => atom!("undefined"),
+            //            EvalError::FloatUnderflow => atom!("underflow"),
+            EvalError::ZeroDivisor => atom!("zero_divisor"),
         }
     }
 }
 
 // used by '$skip_max_list'.
-#[derive(Debug, Clone, Copy)]
-pub(super) enum CycleSearchResult {
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum CycleSearchResult {
     EmptyList,
     NotList,
     PartialList(usize, Ref), // the list length (up to max), and an offset into the heap.
     ProperList(usize),       // the list length.
-    PStrLocation(usize, usize, usize), // the list length (up to max), the heap offset, byte offset into the string.
-    UntouchedList(usize),              // the address of an uniterated Addr::Lis(address).
+    PStrLocation(usize, usize), // list length (up to max), the heap address of the PStrOffset
+    UntouchedList(usize),       // the address of an uniterated Addr::Lis(address).
+    UntouchedCStr(Atom, usize),
 }
 
 impl MachineState {
     // see 8.4.3 of Draft Technical Corrigendum 2.
-    pub(super) fn check_sort_errors(&self) -> CallResult {
-        let stub = MachineError::functor_stub(clause_name!("sort"), 2);
-        let list = self.store(self.deref(self[temp_v!(1)].clone()));
-        let sorted = self.store(self.deref(self[temp_v!(2)].clone()));
+    pub(super) fn check_sort_errors(&mut self) -> CallResult {
+        let stub_gen = || functor_stub(atom!("sort"), 2);
 
-        match self.detect_cycles(list.clone()) {
+        let list = self.registers[1];
+        let sorted = self.registers[2];
+
+        match self.detect_cycles(list) {
             CycleSearchResult::PartialList(..) => {
-                return Err(self.error_form(MachineError::instantiation_error(), stub))
+                let err = self.instantiation_error();
+                return Err(self.error_form(err, stub_gen()))
             }
             CycleSearchResult::NotList => {
-                return Err(
-                    self.error_form(MachineError::type_error(0, ValidType::List, list), stub)
-                )
+                let err = self.type_error(ValidType::List, list);
+                return Err(self.error_form(err, stub_gen()));
             }
             _ => {}
         };
 
-        match self.detect_cycles(sorted.clone()) {
-            CycleSearchResult::NotList if !sorted.is_ref() => {
-                Err(self.error_form(MachineError::type_error(0, ValidType::List, sorted), stub))
+        match self.detect_cycles(sorted) {
+            CycleSearchResult::NotList if !sorted.is_var() => {
+                let err = self.type_error(ValidType::List, sorted);
+                Err(self.error_form(err, stub_gen()))
             }
             _ => Ok(()),
         }
     }
 
-    fn check_for_list_pairs(&self, list: Addr) -> CallResult {
-        let stub = MachineError::functor_stub(clause_name!("keysort"), 2);
+    fn check_for_list_pairs(&mut self, mut list: HeapCellValue) -> CallResult {
+        let stub_gen = || functor_stub(atom!("keysort"), 2);
 
-        match self.detect_cycles(list.clone()) {
-            CycleSearchResult::NotList if !list.is_ref() => {
-                Err(self.error_form(MachineError::type_error(0, ValidType::List, list), stub))
+        match self.detect_cycles(list) {
+            CycleSearchResult::NotList if !list.is_var() => {
+                let err = self.type_error(ValidType::List, list);
+                Err(self.error_form(err, stub_gen()))
             }
             _ => {
-                let mut addr = list;
-
-                while let Addr::Lis(l) = self.store(self.deref(addr)) {
-                    let mut new_l = l;
-
-                    loop {
-                        match self.heap.clone(new_l) {
-                            HeapCellValue::Addr(Addr::Str(l)) => {
-                                new_l = l;
-                            }
-                            HeapCellValue::NamedStr(2, ref name, Some(_))
-                                if name.as_str() == "-" =>
-                            {
-                                break;
-                            }
-                            HeapCellValue::Addr(Addr::HeapCell(_)) => {
-                                break;
+                loop {
+                    read_heap_cell!(self.store(self.deref(list)),
+                        (HeapCellValueTag::Lis, l) => {
+                            let mut new_l = l;
+
+                            loop {
+                                read_heap_cell!(self.heap[new_l],
+                                    (HeapCellValueTag::Str, s) => {
+                                        new_l = s;
+                                    }
+                                    (HeapCellValueTag::Atom, (name, arity)) => {
+                                        if name == atom!("-") && arity == 2 {
+                                            break;
+                                        } else {
+                                            let err = self.type_error(
+                                                ValidType::Pair,
+                                                list_loc_as_cell!(l),
+                                            );
+
+                                            return Err(self.error_form(err, stub_gen()));
+                                        }
+                                    }
+                                    (HeapCellValueTag::Var | HeapCellValueTag::AttrVar) => {
+                                        break;
+                                    }
+                                    _ => {
+                                        let err = self.type_error(
+                                            ValidType::Pair,
+                                            list_loc_as_cell!(l),
+                                        );
+
+                                        return Err(self.error_form(err, stub_gen()));
+                                    }
+                                );
                             }
-                            HeapCellValue::Addr(Addr::StackCell(..)) => {
-                                break;
-                            }
-                            _ => {
-                                return Err(self.error_form(
-                                    MachineError::type_error(0, ValidType::Pair, Addr::HeapCell(l)),
-                                    stub,
-                                ))
-                            }
-                        };
-                    }
 
-                    addr = Addr::HeapCell(l + 1);
+                            list = heap_loc_as_cell!(l+1);
+                        }
+                        _ => {
+                            break;
+                        }
+                    );
                 }
 
                 Ok(())
@@ -719,112 +857,48 @@ impl MachineState {
     }
 
     // see 8.4.4 of Draft Technical Corrigendum 2.
-    pub(super) fn check_keysort_errors(&self) -> CallResult {
-        let stub = MachineError::functor_stub(clause_name!("keysort"), 2);
+    pub(super) fn check_keysort_errors(&mut self) -> CallResult {
+        let stub_gen = || functor_stub(atom!("keysort"), 2);
 
-        let pairs = self.store(self.deref(self[temp_v!(1)].clone()));
-        let sorted = self.store(self.deref(self[temp_v!(2)].clone()));
+        let pairs = self.store(self.deref(self[temp_v!(1)]));
+        let sorted = self.store(self.deref(self[temp_v!(2)]));
 
-        match self.detect_cycles(pairs.clone()) {
+        match self.detect_cycles(pairs) {
             CycleSearchResult::PartialList(..) => {
-                Err(self.error_form(MachineError::instantiation_error(), stub))
+                let err = self.instantiation_error();
+                Err(self.error_form(err, stub_gen()))
             }
             CycleSearchResult::NotList => {
-                Err(self.error_form(MachineError::type_error(0, ValidType::List, pairs), stub))
+                let err = self.type_error(ValidType::List, pairs);
+                Err(self.error_form(err, stub_gen()))
             }
             _ => Ok(()),
         }?;
 
         self.check_for_list_pairs(sorted)
     }
-
-    #[inline]
-    pub(crate) fn type_error<T: TypeError>(
-        &self,
-        valid_type: ValidType,
-        culprit: T,
-        caller: ClauseName,
-        arity: usize,
-    ) -> MachineStub {
-        let stub = MachineError::functor_stub(caller, arity);
-        let err = MachineError::type_error(self.heap.h(), valid_type, culprit);
-
-        return self.error_form(err, stub);
-    }
-
-    #[inline]
-    pub(crate) fn representation_error(
-        &self,
-        rep_flag: RepFlag,
-        caller: ClauseName,
-        arity: usize,
-    ) -> MachineStub {
-        let stub = MachineError::functor_stub(caller, arity);
-        let err = MachineError::representation_error(rep_flag);
-
-        return self.error_form(err, stub);
-    }
-
-    pub(super) fn error_form(&self, err: MachineError, src: MachineStub) -> MachineStub {
-        let location = err.location;
-        let err_len = err.len();
-
-        let h = self.heap.h();
-        let mut stub = vec![
-            HeapCellValue::NamedStr(2, clause_name!("error"), None),
-            HeapCellValue::Addr(Addr::HeapCell(h + 3)),
-            HeapCellValue::Addr(Addr::HeapCell(h + 3 + err_len)),
-        ];
-
-        stub.extend(err.into_iter(3));
-
-        if let Some((line_num, _)) = location {
-            let colon_op_desc = Some(SharedOpDesc::new(600, XFY));
-
-            stub.push(HeapCellValue::NamedStr(2, clause_name!(":"), colon_op_desc));
-            stub.push(HeapCellValue::Addr(Addr::HeapCell(h + 6 + err_len)));
-            stub.push(HeapCellValue::Integer(Rc::new(Integer::from(line_num))));
-        }
-
-        stub.extend(src.into_iter());
-        stub
-    }
-
-    pub(super) fn throw_exception(&mut self, err: MachineStub) {
-        let h = self.heap.h();
-
-        self.ball.boundary = 0;
-        self.ball.stub.truncate(0);
-
-        self.heap.append(err);
-
-        self.registers[1] = Addr::HeapCell(h);
-
-        self.set_ball();
-        self.unwind_stack();
-    }
 }
 
 #[derive(Debug)]
-pub(crate) enum ExistenceError {
-    Module(ClauseName),
+pub enum ExistenceError {
+    Module(Atom),
     ModuleSource(ModuleSource),
-    Procedure(ClauseName, usize),
-    SourceSink(Addr),
-    Stream(Addr),
+    Procedure(Atom, usize),
+    SourceSink(HeapCellValue),
+    Stream(HeapCellValue),
 }
 
 #[derive(Debug)]
-pub(crate) enum SessionError {
+pub enum SessionError {
     CompilationError(CompilationError),
-    // CannotOverwriteBuiltIn(ClauseName),
-    // CannotOverwriteImport(ClauseName),
+    // CannotOverwriteBuiltIn(Atom),
+    // CannotOverwriteImport(Atom),
     ExistenceError(ExistenceError),
-    // InvalidFileName(ClauseName),
-    ModuleDoesNotContainExport(ClauseName, PredicateKey),
-    ModuleCannotImportSelf(ClauseName),
+    // InvalidFileName(Atom),
+    ModuleDoesNotContainExport(Atom, PredicateKey),
+    ModuleCannotImportSelf(Atom),
     NamelessEntry,
-    OpIsInfixAndPostFix(ClauseName),
+    OpIsInfixAndPostFix(Atom),
     PredicateNotMultifileOrDiscontiguous(CompilationTarget, PredicateKey),
     QueryCannotBeDefinedAsFact,
 }
index e66a871381397d424fe9798a79c06bebbc00a10f..6d5be8c140f5b1732edd7e3e333c13d50927db1b 100644 (file)
@@ -1,53 +1,43 @@
-use prolog_parser::ast::*;
-use prolog_parser::clause_name;
+use crate::parser::ast::*;
 
+use crate::arena::*;
+use crate::atom_table::*;
 use crate::clause_types::*;
 use crate::fixtures::*;
 use crate::forms::*;
 use crate::instructions::*;
+
 use crate::machine::code_repo::CodeRepo;
 use crate::machine::heap::*;
+use crate::machine::loader::*;
+use crate::machine::machine_errors::MachineStub;
 use crate::machine::machine_state::*;
-use crate::machine::partial_string::*;
-use crate::machine::raw_block::RawBlockTraits;
 use crate::machine::streams::Stream;
-use crate::machine::term_stream::LoadStatePayload;
-use crate::machine::CompilationTarget;
-use crate::rug::{Integer, Rational};
-use ordered_float::OrderedFloat;
 
 use indexmap::IndexMap;
 
 use std::cell::Cell;
 use std::cmp::Ordering;
-use std::collections::{BTreeMap, BTreeSet};
-use std::convert::TryFrom;
+use std::collections::BTreeSet;
 use std::fmt;
-// use std::mem;
-use std::net::TcpListener;
 use std::ops::{Add, AddAssign, Deref, Sub, SubAssign};
 use std::rc::Rc;
 
+use crate::types::*;
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub(crate) struct OrderedOpDirKey(pub(crate) ClauseName, pub(crate) Fixity);
+pub(crate) struct OrderedOpDirKey(pub(crate) Atom, pub(crate) Fixity);
 
-pub(crate) type OssifiedOpDir = BTreeMap<OrderedOpDirKey, (usize, Specifier)>;
+pub(crate) type OssifiedOpDir = IndexMap<(Atom, Fixity), (usize, Specifier)>;
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub(crate) enum DBRef {
-    NamedPred(ClauseName, usize, Option<SharedOpDesc>),
-    Op(
-        usize,
-        Specifier,
-        ClauseName,
-        Rc<OssifiedOpDir>,
-        SharedOpDesc,
-    ),
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum DBRef {
+    NamedPred(Atom, usize),
+    Op(Atom, Fixity, TypedArenaPtr<OssifiedOpDir>),
 }
 
 // 7.2
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub(crate) enum TermOrderCategory {
+pub enum TermOrderCategory {
     Variable,
     FloatingPoint,
     Integer,
@@ -55,43 +45,95 @@ pub(crate) enum TermOrderCategory {
     Compound,
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub(crate) enum Addr {
-    AttrVar(usize),
-    Char(char),
-    Con(usize),
-    CutPoint(usize),
-    EmptyList,
-    Fixnum(isize),
-    Float(OrderedFloat<f64>),
-    Lis(usize),
-    LoadStatePayload(usize),
-    HeapCell(usize),
-    PStrLocation(usize, usize), // location of pstr in heap, offset into string in bytes.
-    StackCell(usize, usize),
-    Str(usize),
-    Stream(usize),
-    TcpListener(usize),
-    Usize(usize),
-}
-
-#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, PartialOrd)]
-pub(crate) enum Ref {
-    AttrVar(usize),
-    HeapCell(usize),
-    StackCell(usize, usize),
-}
-
-impl Ref {
-    pub(crate) fn as_addr(self) -> Addr {
-        match self {
-            Ref::AttrVar(h) => Addr::AttrVar(h),
-            Ref::HeapCell(h) => Addr::HeapCell(h),
-            Ref::StackCell(fr, sc) => Addr::StackCell(fr, sc),
-        }
+// the position-dependent heap template:
+
+/*
+        read_heap_cell!(
+            (HeapCellValueTag::AttrVar, n) => {
+            }
+            (HeapCellValueTag::Lis, n) => {
+            }
+            (HeapCellValueTag::Var, n) => {
+            }
+            (HeapCellValueTag::Str, n) => {
+            }
+            (HeapCellValueTag::PStrOffset, n) => {
+            }
+            _ => {
+            }
+        )
+*/
+
+impl PartialEq<Ref> for HeapCellValue {
+    fn eq(&self, r: &Ref) -> bool {
+        self.as_var() == Some(*r)
     }
 }
 
+impl PartialOrd<Ref> for HeapCellValue {
+    fn partial_cmp(&self, r: &Ref) -> Option<Ordering> {
+        read_heap_cell!(*self,
+            (HeapCellValueTag::StackVar, s1) => {
+                match r.get_tag() {
+                    RefTag::StackCell => {
+                        let s2 = r.get_value() as usize;
+                        s1.partial_cmp(&s2)
+                    }
+                    _ => Some(Ordering::Greater),
+                }
+            }
+            (HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h1) => {
+                match r.get_tag() {
+                    RefTag::StackCell => Some(Ordering::Less),
+                    _ => {
+                        let h2 = r.get_value() as usize;
+                        h1.partial_cmp(&h2)
+                    }
+                }
+            }
+            _ => {
+                None
+            }
+        )
+    }
+}
+/*
+impl HeapCellValue {
+    #[inline]
+    pub fn as_constant_index(self, machine_st: &MachineState) -> Option<Literal> {
+        read_heap_cell!(self,
+            (HeapCellValueTag::Char, c) => Some(Literal::Char(c)),
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                if arity == 0 {
+                    Some(Literal::Atom(name))
+                } else {
+                    None
+                }
+            }
+            (HeapCellValueTag::Fixnum, n) => {
+                Some(Literal::Fixnum(n))
+            }
+            (HeapCellValueTag::F64, f) => {
+                Some(Literal::Float(f))
+            }
+            (HeapCellValueTag::Cons, ptr) => {
+                match_untyped_arena_ptr!(ptr,
+                     (ArenaHeaderTag::Integer, n) => {
+                         Some(Literal::Integer(n))
+                     }
+                     (ArenaHeaderTag::Rational, r) => {
+                         Some(Literal::Rational(r))
+                     }
+                     _ => {
+                         None
+                     }
+                )
+            }
+        )
+    }
+}
+*/
+/*
 impl Ord for Ref {
     fn cmp(&self, other: &Ref) -> Ordering {
         match (self, other) {
@@ -114,31 +156,6 @@ impl PartialEq<Ref> for Addr {
     }
 }
 
-// for use crate::in MachineState::bind.
-impl PartialOrd<Ref> for Addr {
-    fn partial_cmp(&self, r: &Ref) -> Option<Ordering> {
-        match self {
-            &Addr::StackCell(fr, sc) => match *r {
-                Ref::AttrVar(_) | Ref::HeapCell(_) => Some(Ordering::Greater),
-                Ref::StackCell(fr1, sc1) => {
-                    if fr1 < fr || (fr1 == fr && sc1 < sc) {
-                        Some(Ordering::Greater)
-                    } else if fr1 == fr && sc1 == sc {
-                        Some(Ordering::Equal)
-                    } else {
-                        Some(Ordering::Less)
-                    }
-                }
-            },
-            &Addr::HeapCell(h) | &Addr::AttrVar(h) => match r {
-                Ref::StackCell(..) => Some(Ordering::Less),
-                Ref::AttrVar(h1) | Ref::HeapCell(h1) => h.partial_cmp(h1),
-            },
-            _ => None,
-        }
-    }
-}
-
 impl Addr {
     #[inline]
     pub(crate) fn is_heap_bound(&self) -> bool {
@@ -171,57 +188,6 @@ impl Addr {
         }
     }
 
-    pub(super) fn order_category(&self, heap: &Heap) -> Option<TermOrderCategory> {
-        match Number::try_from((*self, heap)) {
-            Ok(Number::Integer(_)) | Ok(Number::Fixnum(_)) | Ok(Number::Rational(_)) => {
-                Some(TermOrderCategory::Integer)
-            }
-            Ok(Number::Float(_)) => Some(TermOrderCategory::FloatingPoint),
-            _ => match self {
-                Addr::HeapCell(_) | Addr::AttrVar(_) | Addr::StackCell(..) => {
-                    Some(TermOrderCategory::Variable)
-                }
-                Addr::Float(_) => Some(TermOrderCategory::FloatingPoint),
-                &Addr::Con(h) => match &heap[h] {
-                    HeapCellValue::Atom(..) => Some(TermOrderCategory::Atom),
-                    HeapCellValue::DBRef(_) => None,
-                    _ => {
-                        unreachable!()
-                    }
-                },
-                Addr::Char(_) | Addr::EmptyList => Some(TermOrderCategory::Atom),
-                Addr::Fixnum(_) | Addr::Usize(_) => Some(TermOrderCategory::Integer),
-                Addr::Lis(_) | Addr::PStrLocation(..) | Addr::Str(_) => {
-                    Some(TermOrderCategory::Compound)
-                }
-                Addr::CutPoint(_)
-                | Addr::LoadStatePayload(_)
-                | Addr::Stream(_)
-                | Addr::TcpListener(_) => None,
-            },
-        }
-    }
-
-    pub(crate) fn as_constant_index(&self, machine_st: &MachineState) -> Option<Constant> {
-        match self {
-            &Addr::Char(c) => Some(Constant::Char(c)),
-            &Addr::Con(h) => match &machine_st.heap[h] {
-                &HeapCellValue::Atom(ref name, _) if name.is_char() => {
-                    Some(Constant::Char(name.as_str().chars().next().unwrap()))
-                }
-                &HeapCellValue::Atom(ref name, _) => Some(Constant::Atom(name.clone(), None)),
-                &HeapCellValue::Integer(ref n) => Some(Constant::Integer(n.clone())),
-                &HeapCellValue::Rational(ref n) => Some(Constant::Rational(n.clone())),
-                _ => None,
-            },
-            &Addr::EmptyList => Some(Constant::EmptyList),
-            &Addr::Fixnum(n) => Some(Constant::Fixnum(n)),
-            &Addr::Float(f) => Some(Constant::Float(f)),
-            &Addr::Usize(n) => Some(Constant::Usize(n)),
-            _ => None,
-        }
-    }
-
     pub(crate) fn is_protected(&self, e: usize) -> bool {
         match self {
             &Addr::StackCell(addr, _) if addr >= e => false,
@@ -230,61 +196,6 @@ impl Addr {
     }
 }
 
-impl Add<usize> for Addr {
-    type Output = Addr;
-
-    fn add(self, rhs: usize) -> Self::Output {
-        match self {
-            Addr::Stream(h) => Addr::Stream(h + rhs),
-            Addr::Con(h) => Addr::Con(h + rhs),
-            Addr::Lis(a) => Addr::Lis(a + rhs),
-            Addr::AttrVar(h) => Addr::AttrVar(h + rhs),
-            Addr::HeapCell(h) => Addr::HeapCell(h + rhs),
-            Addr::Str(s) => Addr::Str(s + rhs),
-            Addr::PStrLocation(h, n) => Addr::PStrLocation(h + rhs, n),
-            _ => self,
-        }
-    }
-}
-
-impl Sub<i64> for Addr {
-    type Output = Addr;
-
-    fn sub(self, rhs: i64) -> Self::Output {
-        if rhs < 0 {
-            match self {
-                Addr::Stream(h) => Addr::Stream(h + rhs.abs() as usize),
-                Addr::Con(h) => Addr::Con(h + rhs.abs() as usize),
-                Addr::Lis(a) => Addr::Lis(a + rhs.abs() as usize),
-                Addr::AttrVar(h) => Addr::AttrVar(h + rhs.abs() as usize),
-                Addr::HeapCell(h) => Addr::HeapCell(h + rhs.abs() as usize),
-                Addr::Str(s) => Addr::Str(s + rhs.abs() as usize),
-                Addr::PStrLocation(h, n) => Addr::PStrLocation(h + rhs.abs() as usize, n),
-                _ => self,
-            }
-        } else {
-            self.sub(rhs as usize)
-        }
-    }
-}
-
-impl Sub<usize> for Addr {
-    type Output = Addr;
-
-    fn sub(self, rhs: usize) -> Self::Output {
-        match self {
-            Addr::Stream(h) => Addr::Stream(h - rhs),
-            Addr::Con(h) => Addr::Con(h - rhs),
-            Addr::Lis(a) => Addr::Lis(a - rhs),
-            Addr::AttrVar(h) => Addr::AttrVar(h - rhs),
-            Addr::HeapCell(h) => Addr::HeapCell(h - rhs),
-            Addr::Str(s) => Addr::Str(s - rhs),
-            Addr::PStrLocation(h, n) => Addr::PStrLocation(h - rhs, n),
-            _ => self,
-        }
-    }
-}
-
 impl SubAssign<usize> for Addr {
     fn sub_assign(&mut self, rhs: usize) {
         *self = self.clone() - rhs;
@@ -305,72 +216,9 @@ impl From<Ref> for TrailRef {
         TrailRef::Ref(r)
     }
 }
-
-#[derive(Debug)]
-pub(crate) enum HeapCellValue {
-    Addr(Addr),
-    Atom(ClauseName, Option<SharedOpDesc>),
-    DBRef(DBRef),
-    Integer(Rc<Integer>),
-    LoadStatePayload(Box<LoadStatePayload>),
-    NamedStr(usize, ClauseName, Option<SharedOpDesc>), // arity, name, precedence/Specifier if it has one.
-    Rational(Rc<Rational>),
-    PartialString(PartialString, bool), // the partial string, a bool indicating whether it came from a Constant.
-    Stream(Stream),
-    TcpListener(TcpListener),
-}
-
-impl HeapCellValue {
-    #[inline]
-    pub(crate) fn as_addr(&self, focus: usize) -> Addr {
-        match self {
-            HeapCellValue::Addr(ref a) => *a,
-            HeapCellValue::Atom(..)
-            | HeapCellValue::DBRef(..)
-            | HeapCellValue::Integer(..)
-            | HeapCellValue::Rational(..) => Addr::Con(focus),
-            HeapCellValue::LoadStatePayload(_) => Addr::LoadStatePayload(focus),
-            HeapCellValue::NamedStr(_, _, _) => Addr::Str(focus),
-            HeapCellValue::PartialString(..) => Addr::PStrLocation(focus, 0),
-            HeapCellValue::Stream(_) => Addr::Stream(focus),
-            HeapCellValue::TcpListener(_) => Addr::TcpListener(focus),
-        }
-    }
-
-    #[inline]
-    pub(crate) fn context_free_clone(&self) -> HeapCellValue {
-        match self {
-            &HeapCellValue::Addr(addr) => HeapCellValue::Addr(addr),
-            &HeapCellValue::Atom(ref name, ref op) => HeapCellValue::Atom(name.clone(), op.clone()),
-            &HeapCellValue::DBRef(ref db_ref) => HeapCellValue::DBRef(db_ref.clone()),
-            &HeapCellValue::Integer(ref n) => HeapCellValue::Integer(n.clone()),
-            &HeapCellValue::LoadStatePayload(_) => {
-                HeapCellValue::Atom(clause_name!("$live_term_stream"), None)
-            }
-            &HeapCellValue::NamedStr(arity, ref name, ref op) => {
-                HeapCellValue::NamedStr(arity, name.clone(), op.clone())
-            }
-            &HeapCellValue::Rational(ref r) => HeapCellValue::Rational(r.clone()),
-            &HeapCellValue::PartialString(ref pstr, has_tail) => {
-                HeapCellValue::PartialString(pstr.clone(), has_tail)
-            }
-            &HeapCellValue::Stream(ref stream) => HeapCellValue::Stream(stream.clone()),
-            &HeapCellValue::TcpListener(_) => {
-                HeapCellValue::Atom(clause_name!("$tcp_listener"), None)
-            }
-        }
-    }
-}
-
-impl From<Addr> for HeapCellValue {
-    #[inline]
-    fn from(value: Addr) -> HeapCellValue {
-        HeapCellValue::Addr(value)
-    }
-}
-
+*/
 #[derive(Debug, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
-pub(crate) enum IndexPtr {
+pub enum IndexPtr {
     DynamicUndefined, // a predicate, declared as dynamic, whose location in code is as yet undefined.
     DynamicIndex(usize),
     Index(usize),
@@ -378,7 +226,7 @@ pub(crate) enum IndexPtr {
 }
 
 #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
-pub(crate) struct CodeIndex(pub(crate) Rc<Cell<IndexPtr>>);
+pub struct CodeIndex(pub(crate) Rc<Cell<IndexPtr>>);
 
 impl Deref for CodeIndex {
     type Target = Cell<IndexPtr>;
@@ -419,7 +267,7 @@ impl Default for CodeIndex {
 }
 
 #[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
-pub(crate) enum REPLCodePtr {
+pub enum REPLCodePtr {
     AddDiscontiguousPredicate,
     AddDynamicPredicate,
     AddMultifilePredicate,
@@ -456,12 +304,11 @@ pub(crate) enum REPLCodePtr {
     AddNonCountedBacktracking,
 }
 
-#[derive(Debug, Clone, PartialEq)]
-pub(crate) enum CodePtr {
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum CodePtr {
     BuiltInClause(BuiltInClauseType, LocalCodePtr), // local is the successor call.
     CallN(usize, LocalCodePtr, bool),               // arity, local, last call.
     Local(LocalCodePtr),
-    // DynamicTransaction(DynamicTransactionType, LocalCodePtr), // the type of transaction, the return pointer.
     REPL(REPLCodePtr, LocalCodePtr), // the REPL code, the return pointer.
     VerifyAttrInterrupt(usize), // location of the verify attribute interrupt code in the CodeDir.
 }
@@ -488,7 +335,7 @@ impl CodePtr {
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
-pub(crate) enum LocalCodePtr {
+pub enum LocalCodePtr {
     DirEntry(usize), // offset
     Halt,
     IndexingBuf(usize, usize, usize), // DirEntry offset, first internal offset, second internal offset
@@ -496,7 +343,7 @@ pub(crate) enum LocalCodePtr {
 }
 
 impl LocalCodePtr {
-    pub(crate) fn assign_if_local(&mut self, cp: CodePtr) {
+    pub fn assign_if_local(&mut self, cp: CodePtr) {
         match cp {
             CodePtr::Local(local) => *self = local,
             _ => {}
@@ -504,7 +351,7 @@ impl LocalCodePtr {
     }
 
     #[inline]
-    pub(crate) fn abs_loc(&self) -> usize {
+    pub fn abs_loc(&self) -> usize {
         match self {
             LocalCodePtr::DirEntry(ref p) => *p,
             LocalCodePtr::IndexingBuf(ref p, ..) => *p,
@@ -528,33 +375,21 @@ impl LocalCodePtr {
         false
     }
 
-    pub(crate) fn as_functor<T: RawBlockTraits>(&self, heap: &mut HeapTemplate<T>) -> Addr {
-        let addr = Addr::HeapCell(heap.h());
-
+    pub(crate) fn as_functor(&self) -> MachineStub {
         match self {
             LocalCodePtr::DirEntry(p) => {
-                heap.append(functor!("dir_entry", [integer(*p)]));
+                functor!(atom!("dir_entry"), [fixnum(*p)])
             }
             LocalCodePtr::Halt => {
-                heap.append(functor!("halt"));
+                functor!(atom!("halt"))
             }
-            /*
-            LocalCodePtr::TopLevel(chunk_num, offset) => {
-                heap.append(functor!(
-                    "top_level",
-                    [integer(*chunk_num), integer(*offset)]
-                ));
-            }
-            */
             LocalCodePtr::IndexingBuf(p, o, i) => {
-                heap.append(functor!(
-                    "indexed_buf",
-                    [integer(*p), integer(*o), integer(*i)]
-                ));
+                functor!(
+                    atom!("indexed_buf"),
+                    [fixnum(*p), fixnum(*o), fixnum(*i)]
+                )
             }
         }
-
-        addr
     }
 }
 
@@ -614,9 +449,8 @@ impl AddAssign<usize> for LocalCodePtr {
     #[inline]
     fn add_assign(&mut self, rhs: usize) {
         match self {
-            &mut LocalCodePtr::DirEntry(ref mut p) /* |
-            &mut LocalCodePtr::TopLevel(_, ref mut p) */    => *p += rhs,
-            &mut LocalCodePtr::IndexingBuf(_, _, ref mut i) => *i += rhs,
+            &mut LocalCodePtr::DirEntry(ref mut i)
+            | &mut LocalCodePtr::IndexingBuf(_, _, ref mut i) => *i += rhs,
             &mut LocalCodePtr::Halt => unreachable!(),
         }
     }
@@ -627,11 +461,7 @@ impl Add<usize> for CodePtr {
 
     fn add(self, rhs: usize) -> Self::Output {
         match self {
-            p @ CodePtr::REPL(..) | p @ CodePtr::VerifyAttrInterrupt(_) => {
-                // |
-                // p @ CodePtr::DynamicTransaction(..) => {
-                p
-            }
+            p @ CodePtr::REPL(..) | p @ CodePtr::VerifyAttrInterrupt(_) => p,
             CodePtr::Local(local) => CodePtr::Local(local + rhs),
             CodePtr::BuiltInClause(_, local) | CodePtr::CallN(_, local, _) => {
                 CodePtr::Local(local + rhs)
@@ -660,12 +490,12 @@ impl SubAssign<usize> for CodePtr {
     }
 }
 
-pub(crate) type HeapVarDict = IndexMap<Rc<Var>, Addr>;
-pub(crate) type AllocVarDict = IndexMap<Rc<Var>, VarData>;
+pub(crate) type HeapVarDict = IndexMap<Rc<String>, HeapCellValue>;
+pub(crate) type AllocVarDict = IndexMap<Rc<String>, VarData>;
 
-pub(crate) type GlobalVarDir = IndexMap<ClauseName, (Ball, Option<Addr>)>;
+pub(crate) type GlobalVarDir = IndexMap<Atom, (Ball, Option<HeapCellValue>)>;
 
-pub(crate) type StreamAliasDir = IndexMap<ClauseName, Stream>;
+pub(crate) type StreamAliasDir = IndexMap<Atom, Stream>;
 pub(crate) type StreamDir = BTreeSet<Stream>;
 
 pub(crate) type MetaPredicateDir = IndexMap<PredicateKey, Vec<MetaSpec>>;
@@ -676,7 +506,7 @@ pub(crate) type LocalExtensiblePredicates =
     IndexMap<(CompilationTarget, PredicateKey), LocalPredicateSkeleton>;
 
 #[derive(Debug)]
-pub(crate) struct IndexStore {
+pub struct IndexStore {
     pub(super) code_dir: CodeDir,
     pub(super) extensible_predicates: ExtensiblePredicates,
     pub(super) local_extensible_predicates: LocalExtensiblePredicates,
@@ -688,31 +518,21 @@ pub(crate) struct IndexStore {
     pub(super) stream_aliases: StreamAliasDir,
 }
 
-impl Default for IndexStore {
-    #[inline]
-    fn default() -> Self {
-        index_store!(CodeDir::new(), default_op_dir(), ModuleDir::new())
-    }
-}
-
 impl IndexStore {
     pub(crate) fn get_predicate_skeleton_mut(
         &mut self,
         compilation_target: &CompilationTarget,
         key: &PredicateKey,
     ) -> Option<&mut PredicateSkeleton> {
-        match (key.0.as_str(), key.1) {
-            //            ("term_expansion", 2) => self.extensible_predicates.get_mut(key),
-            _ => match compilation_target {
-                CompilationTarget::User => self.extensible_predicates.get_mut(key),
-                CompilationTarget::Module(ref module_name) => {
-                    if let Some(module) = self.modules.get_mut(module_name) {
-                        module.extensible_predicates.get_mut(key)
-                    } else {
-                        None
-                    }
+        match compilation_target {
+            CompilationTarget::User => self.extensible_predicates.get_mut(key),
+            CompilationTarget::Module(ref module_name) => {
+                if let Some(module) = self.modules.get_mut(module_name) {
+                    module.extensible_predicates.get_mut(key)
+                } else {
+                    None
                 }
-            },
+            }
         }
     }
 
@@ -737,7 +557,7 @@ impl IndexStore {
         &mut self,
         mut src_compilation_target: CompilationTarget,
         local_compilation_target: CompilationTarget,
-        listing_src_file_name: Option<ClauseName>,
+        listing_src_file_name: Option<Atom>,
         key: PredicateKey,
     ) -> Option<&mut LocalPredicateSkeleton> {
         if let Some(filename) = listing_src_file_name {
@@ -748,8 +568,8 @@ impl IndexStore {
             CompilationTarget::User => self
                 .local_extensible_predicates
                 .get_mut(&(local_compilation_target, key)),
-            CompilationTarget::Module(ref module_name) => {
-                if let Some(module) = self.modules.get_mut(module_name) {
+            CompilationTarget::Module(module_name) => {
+                if let Some(module) = self.modules.get_mut(&module_name) {
                     module
                         .local_extensible_predicates
                         .get_mut(&(local_compilation_target, key))
@@ -764,7 +584,7 @@ impl IndexStore {
         &self,
         mut src_compilation_target: CompilationTarget,
         local_compilation_target: CompilationTarget,
-        listing_src_file_name: Option<ClauseName>,
+        listing_src_file_name: Option<Atom>,
         key: PredicateKey,
     ) -> Option<&LocalPredicateSkeleton> {
         if let Some(filename) = listing_src_file_name {
@@ -775,8 +595,8 @@ impl IndexStore {
             CompilationTarget::User => self
                 .local_extensible_predicates
                 .get(&(local_compilation_target, key)),
-            CompilationTarget::Module(ref module_name) => {
-                if let Some(module) = self.modules.get(module_name) {
+            CompilationTarget::Module(module_name) => {
+                if let Some(module) = self.modules.get(&module_name) {
                     module
                         .local_extensible_predicates
                         .get(&(local_compilation_target, key))
@@ -806,35 +626,30 @@ impl IndexStore {
 
     pub(crate) fn get_predicate_code_index(
         &self,
-        name: ClauseName,
+        name: Atom,
         arity: usize,
-        module: ClauseName,
-        op_spec: Option<SharedOpDesc>,
+        module: Atom,
     ) -> Option<CodeIndex> {
-        if module.as_str() == "user" {
-            match ClauseType::from(name, arity, op_spec) {
+        if module == atom!("user") {
+            match ClauseType::from(name, arity) {
                 ClauseType::Named(name, arity, _) => self.code_dir.get(&(name, arity)).cloned(),
-                ClauseType::Op(name, spec, ..) => self.code_dir.get(&(name, spec.arity())).cloned(),
                 _ => None,
             }
         } else {
-            self.modules.get(&module).and_then(|module| {
-                match ClauseType::from(name, arity, op_spec) {
+            self.modules
+                .get(&module)
+                .and_then(|module| match ClauseType::from(name, arity) {
                     ClauseType::Named(name, arity, _) => {
                         module.code_dir.get(&(name, arity)).cloned()
                     }
-                    ClauseType::Op(name, spec, ..) => {
-                        module.code_dir.get(&(name, spec.arity())).cloned()
-                    }
                     _ => None,
-                }
-            })
+                })
         }
     }
 
     pub(crate) fn get_meta_predicate_spec(
         &self,
-        name: ClauseName,
+        name: Atom,
         arity: usize,
         compilation_target: &CompilationTarget,
     ) -> Option<&Vec<MetaSpec>> {
@@ -850,9 +665,13 @@ impl IndexStore {
         }
     }
 
-    pub(crate) fn is_dynamic_predicate(&self, module_name: ClauseName, key: PredicateKey) -> bool {
-        match module_name.as_str() {
-            "user" => self
+    pub(crate) fn is_dynamic_predicate(
+        &self,
+        module_name: Atom,
+        key: PredicateKey,
+    ) -> bool {
+        match module_name {
+            atom!("user") => self
                 .extensible_predicates
                 .get(&key)
                 .map(|skeleton| skeleton.core.is_dynamic)
@@ -870,19 +689,19 @@ impl IndexStore {
 
     #[inline]
     pub(super) fn new() -> Self {
-        IndexStore::default()
+        index_store!(CodeDir::new(), default_op_dir(), ModuleDir::new())
     }
 
     pub(super) fn get_cleaner_sites(&self) -> (usize, usize) {
-        let r_w_h = clause_name!("run_cleaners_with_handling");
-        let r_wo_h = clause_name!("run_cleaners_without_handling");
-        let iso_ext = clause_name!("iso_ext");
+        let r_w_h = atom!("run_cleaners_with_handling");
+        let r_wo_h = atom!("run_cleaners_without_handling");
+        let iso_ext = atom!("iso_ext");
 
         let r_w_h = self
-            .get_predicate_code_index(r_w_h, 0, iso_ext.clone(), None)
+            .get_predicate_code_index(r_w_h, 0, iso_ext)
             .and_then(|item| item.local());
         let r_wo_h = self
-            .get_predicate_code_index(r_wo_h, 1, iso_ext, None)
+            .get_predicate_code_index(r_wo_h, 1, iso_ext)
             .and_then(|item| item.local());
 
         if let Some(r_w_h) = r_w_h {
@@ -895,7 +714,7 @@ impl IndexStore {
     }
 }
 
-pub(crate) type CodeDir = BTreeMap<PredicateKey, CodeIndex>;
+pub(crate) type CodeDir = IndexMap<PredicateKey, CodeIndex>;
 
 pub(crate) enum RefOrOwned<'a, T: 'a> {
     Borrowed(&'a T),
@@ -918,14 +737,4 @@ impl<'a, T> RefOrOwned<'a, T> {
             &RefOrOwned::Owned(ref r) => r,
         }
     }
-
-    pub(crate) fn to_owned(self) -> T
-    where
-        T: Clone,
-    {
-        match self {
-            RefOrOwned::Borrowed(item) => item.clone(),
-            RefOrOwned::Owned(item) => item,
-        }
-    }
 }
index 9e845f04ab14465a4dc8df591d0fa03c69bf34a9..8d39edcdde01ace4eee6608121d22822fb7bb4db 100644 (file)
@@ -1,19 +1,22 @@
-use prolog_parser::ast::*;
-use prolog_parser::tabled_rc::*;
-use prolog_parser::{clause_name, temp_v};
+use crate::arena::*;
+use crate::atom_table::*;
+use crate::parser::ast::*;
+use crate::temp_v;
 
 use crate::clause_types::*;
 use crate::forms::*;
+use crate::heap_iter::*;
 use crate::heap_print::*;
 use crate::machine::attributed_variables::*;
 use crate::machine::copier::*;
 use crate::machine::heap::*;
 use crate::machine::machine_errors::*;
 use crate::machine::machine_indices::*;
-use crate::machine::partial_string::HeapPStrIter;
 use crate::machine::stack::*;
 use crate::machine::streams::*;
-use crate::rug::Integer;
+use crate::types::*;
+
+use crate::parser::rug::Integer;
 
 use downcast::{
     downcast, downcast_methods, downcast_methods_core, downcast_methods_std, impl_downcast, Any,
@@ -28,203 +31,7 @@ use std::mem;
 use std::ops::{Index, IndexMut};
 use std::rc::Rc;
 
-#[derive(Debug)]
-pub(crate) struct Ball {
-    pub(super) boundary: usize,
-    pub(super) stub: Heap,
-}
-
-impl Ball {
-    pub(super) fn new() -> Self {
-        Ball {
-            boundary: 0,
-            stub: Heap::new(),
-        }
-    }
-
-    pub(super) fn reset(&mut self) {
-        self.boundary = 0;
-        self.stub.clear();
-    }
-
-    pub(super) fn copy_and_align(&self, h: usize) -> Heap {
-        let diff = self.boundary as i64 - h as i64;
-        let mut stub = Heap::new();
-
-        for heap_value in self.stub.iter_from(0) {
-            stub.push(match heap_value {
-                &HeapCellValue::Addr(addr) => HeapCellValue::Addr(addr - diff),
-                heap_value => heap_value.context_free_clone(),
-            });
-        }
-
-        stub
-    }
-}
-
-#[derive(Debug)]
-pub(super) struct CopyTerm<'a> {
-    state: &'a mut MachineState,
-}
-
-impl<'a> CopyTerm<'a> {
-    pub(super) fn new(state: &'a mut MachineState) -> Self {
-        CopyTerm { state: state }
-    }
-}
-
-impl<'a> Index<usize> for CopyTerm<'a> {
-    type Output = HeapCellValue;
-
-    fn index(&self, index: usize) -> &Self::Output {
-        &self.state.heap[index]
-    }
-}
-
-impl<'a> IndexMut<usize> for CopyTerm<'a> {
-    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
-        &mut self.state.heap[index]
-    }
-}
-
-// the ordinary, heap term copier, used by duplicate_term.
-impl<'a> CopierTarget for CopyTerm<'a> {
-    fn threshold(&self) -> usize {
-        self.state.heap.h()
-    }
-
-    fn push(&mut self, hcv: HeapCellValue) {
-        self.state.heap.push(hcv);
-    }
-
-    fn store(&self, a: Addr) -> Addr {
-        self.state.store(a)
-    }
-
-    fn deref(&self, a: Addr) -> Addr {
-        self.state.deref(a)
-    }
-
-    fn stack(&mut self) -> &mut Stack {
-        &mut self.state.stack
-    }
-}
-
-#[derive(Debug)]
-pub(super) struct CopyBallTerm<'a> {
-    stack: &'a mut Stack,
-    heap: &'a mut Heap,
-    heap_boundary: usize,
-    stub: &'a mut Heap,
-}
-
-impl<'a> CopyBallTerm<'a> {
-    pub(super) fn new(stack: &'a mut Stack, heap: &'a mut Heap, stub: &'a mut Heap) -> Self {
-        let hb = heap.h();
-
-        CopyBallTerm {
-            stack,
-            heap,
-            heap_boundary: hb,
-            stub,
-        }
-    }
-}
-
-impl<'a> Index<usize> for CopyBallTerm<'a> {
-    type Output = HeapCellValue;
-
-    fn index(&self, index: usize) -> &Self::Output {
-        if index < self.heap_boundary {
-            &self.heap[index]
-        } else {
-            let index = index - self.heap_boundary;
-            &self.stub[index]
-        }
-    }
-}
-
-impl<'a> IndexMut<usize> for CopyBallTerm<'a> {
-    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
-        if index < self.heap_boundary {
-            &mut self.heap[index]
-        } else {
-            let index = index - self.heap_boundary;
-            &mut self.stub[index]
-        }
-    }
-}
-
-// the ordinary, heap term copier, used by duplicate_term.
-impl<'a> CopierTarget for CopyBallTerm<'a> {
-    fn threshold(&self) -> usize {
-        self.heap_boundary + self.stub.h()
-    }
-
-    fn push(&mut self, value: HeapCellValue) {
-        self.stub.push(value);
-    }
-
-    fn store(&self, addr: Addr) -> Addr {
-        match addr {
-            Addr::HeapCell(h) | Addr::AttrVar(h) if h < self.heap_boundary => {
-                self.heap[h].as_addr(h)
-            }
-            Addr::HeapCell(h) | Addr::AttrVar(h) => {
-                let index = h - self.heap_boundary;
-                self.stub[index].as_addr(h)
-            }
-            Addr::StackCell(fr, sc) => self.stack.index_and_frame(fr)[sc],
-            addr => addr,
-        }
-    }
-
-    fn deref(&self, mut addr: Addr) -> Addr {
-        loop {
-            let value = self.store(addr);
-
-            if value.is_ref() && value != addr {
-                addr = value;
-                continue;
-            }
-
-            return addr;
-        }
-    }
-
-    fn stack(&mut self) -> &mut Stack {
-        self.stack
-    }
-}
-
-impl Index<RegType> for MachineState {
-    type Output = Addr;
-
-    fn index(&self, reg: RegType) -> &Self::Output {
-        match reg {
-            RegType::Temp(temp) => &self.registers[temp],
-            RegType::Perm(perm) => {
-                let e = self.e;
-                &self.stack.index_and_frame(e)[perm]
-            }
-        }
-    }
-}
-
-impl IndexMut<RegType> for MachineState {
-    fn index_mut(&mut self, reg: RegType) -> &mut Self::Output {
-        match reg {
-            RegType::Temp(temp) => &mut self.registers[temp],
-            RegType::Perm(perm) => {
-                let e = self.e;
-
-                &mut self.stack.index_and_frame_mut(e)[perm]
-            }
-        }
-    }
-}
-
-pub(crate) type Registers = Vec<Addr>;
+pub(crate) type Registers = [HeapCellValue; MAX_ARITY + 1];
 
 #[derive(Debug, Clone, Copy)]
 pub(super) enum MachineMode {
@@ -239,29 +46,6 @@ pub(super) enum HeapPtr {
     PStrLocation(usize, usize),
 }
 
-impl HeapPtr {
-    #[inline]
-    pub(super) fn read(&self, heap: &Heap) -> Addr {
-        match self {
-            &HeapPtr::HeapCell(h) => Addr::HeapCell(h),
-            &HeapPtr::PStrChar(h, n) => {
-                if let &HeapCellValue::PartialString(ref pstr, has_tail) = &heap[h] {
-                    if let Some(c) = pstr.range_from(n..).next() {
-                        Addr::Char(c)
-                    } else if has_tail {
-                        Addr::HeapCell(h + 1)
-                    } else {
-                        Addr::EmptyList
-                    }
-                } else {
-                    unreachable!()
-                }
-            }
-            &HeapPtr::PStrLocation(h, n) => Addr::PStrLocation(h, n),
-        }
-    }
-}
-
 impl Default for HeapPtr {
     fn default() -> Self {
         HeapPtr::HeapCell(0)
@@ -274,9 +58,10 @@ pub enum FirstOrNext {
     Next,
 }
 
-// #[derive(Debug)]
-pub(crate) struct MachineState {
-    pub(crate) atom_tbl: TabledData<Atom>,
+pub struct MachineState {
+    pub atom_tbl: AtomTable,
+    pub arena: Arena,
+    pub(super) pdl: Vec<HeapCellValue>,
     pub(super) s: HeapPtr,
     pub(super) p: CodePtr,
     pub(super) b: usize,
@@ -286,11 +71,11 @@ pub(crate) struct MachineState {
     pub(super) cp: LocalCodePtr,
     pub(super) attr_var_init: AttrVarInitializer,
     pub(super) fail: bool,
-    pub(crate) heap: Heap,
+    pub heap: Heap,
     pub(super) mode: MachineMode,
     pub(crate) stack: Stack,
     pub(super) registers: Registers,
-    pub(super) trail: Vec<TrailRef>,
+    pub(super) trail: Vec<TrailEntry>,
     pub(super) tr: usize,
     pub(super) hb: usize,
     pub(super) block: usize, // an offset into the OR stack.
@@ -302,14 +87,15 @@ pub(crate) struct MachineState {
     pub(crate) cc: usize,
     pub(crate) global_clock: usize,
     pub(crate) dynamic_mode: FirstOrNext,
-    pub(crate) unify_fn: fn(&mut MachineState, Addr, Addr),
-    pub(crate) bind_fn: fn(&mut MachineState, Ref, Addr),
+    pub(crate) unify_fn: fn(&mut MachineState),
+    pub(crate) bind_fn: fn(&mut MachineState, Ref, HeapCellValue),
 }
 
 impl fmt::Debug for MachineState {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.debug_struct("MachineState")
             .field("atom_tbl", &self.atom_tbl)
+            .field("arena", &self.arena)
             .field("s", &self.s)
             .field("p", &self.p)
             .field("b", &self.b)
@@ -361,403 +147,73 @@ impl fmt::Debug for MachineState {
     }
 }
 
-impl MachineState {
-    pub(crate) fn read_term(&mut self, mut stream: Stream, indices: &mut IndexStore) -> CallResult {
-        fn push_var_eq_functors<'a>(
-            heap: &mut Heap,
-            iter: impl Iterator<Item = (&'a Rc<Var>, &'a Addr)>,
-            op_dir: &OpDir,
-            atom_tbl: TabledData<Atom>,
-        ) -> Vec<Addr> {
-            let mut list_of_var_eqs = vec![];
+impl Index<RegType> for MachineState {
+    type Output = HeapCellValue;
 
-            for (var, binding) in iter {
-                let var_atom = clause_name!(var.to_string(), atom_tbl);
+    #[inline(always)]
+    fn index(&self, reg: RegType) -> &Self::Output {
+        match reg {
+            RegType::Temp(temp) => &self.registers[temp],
+            RegType::Perm(perm) => {
+                let e = self.e;
+                &self.stack[stack_loc!(AndFrame, e, perm)]
+            }
+        }
+    }
+}
 
-                let h = heap.h();
-                let spec = fetch_atom_op_spec(clause_name!("="), None, op_dir);
+impl IndexMut<RegType> for MachineState {
+    #[inline(always)]
+    fn index_mut(&mut self, reg: RegType) -> &mut Self::Output {
+        match reg {
+            RegType::Temp(temp) => &mut self.registers[temp],
+            RegType::Perm(perm) => {
+                let e = self.e;
+                &mut self.stack[stack_loc!(AndFrame, e, perm)]
+            }
+        }
+    }
+}
 
-                heap.push(HeapCellValue::NamedStr(2, clause_name!("="), spec));
-                heap.push(HeapCellValue::Atom(var_atom, None));
-                heap.push(HeapCellValue::Addr(*binding));
+pub type CallResult = Result<(), Vec<HeapCellValue>>;
 
-                list_of_var_eqs.push(Addr::Str(h));
-            }
+pub trait CutPolicy: Any + fmt::Debug {
+    // returns true iff we fail or cut redirected the MachineState's p itself
+    fn cut(&mut self, machine_st: &mut MachineState, r: RegType) -> bool;
+}
 
-            list_of_var_eqs
-        }
+downcast!(dyn CutPolicy);
 
-        self.check_stream_properties(
-            &mut stream,
-            StreamType::Text,
-            Some(self[temp_v!(2)]),
-            clause_name!("read_term"),
-            3,
-        )?;
+pub trait CallPolicy: Any + fmt::Debug {
+    fn retry_me_else(
+        &mut self,
+        machine_st: &mut MachineState,
+        offset: usize,
+        global_variables: &mut GlobalVarDir,
+    ) -> CallResult {
+        let b = machine_st.b;
+        let n = machine_st
+            .stack
+            .index_or_frame(b)
+            .prelude
+            .univ_prelude
+            .num_cells;
 
-        if stream.past_end_of_stream() {
-            if EOFAction::Reset != stream.options().eof_action {
-                return return_from_clause!(self.last_call, self);
-            } else if self.fail {
-                return Ok(());
-            }
+        for i in 1..n + 1 {
+            machine_st.registers[i] = machine_st.stack[stack_loc!(OrFrame, b, i - 1)];
         }
 
-        let mut orig_stream = stream.clone();
-
-        loop {
-            match self.read(stream.clone(), self.atom_tbl.clone(), &indices.op_dir) {
-                Ok(term_write_result) => {
-                    let term = self[temp_v!(2)];
-                    (self.unify_fn)(self, Addr::HeapCell(term_write_result.heap_loc), term);
+        machine_st.num_of_args = n;
+        machine_st.e = machine_st.stack.index_or_frame(b).prelude.e;
+        machine_st.cp = machine_st.stack.index_or_frame(b).prelude.cp;
 
-                    if self.fail {
-                        return Ok(());
-                    }
+        machine_st.stack.index_or_frame_mut(b).prelude.bp = machine_st.p.local() + offset;
 
-                    let mut singleton_var_set: IndexMap<Ref, bool> = IndexMap::new();
+        let old_tr = machine_st.stack.index_or_frame(b).prelude.tr;
+        let curr_tr = machine_st.tr;
 
-                    for addr in self.acyclic_pre_order_iter(term) {
-                        if let Some(var) = addr.as_var() {
-                            if !singleton_var_set.contains_key(&var) {
-                                singleton_var_set.insert(var, true);
-                            } else {
-                                singleton_var_set.insert(var, false);
-                            }
-                        }
-                    }
-
-                    let singleton_var_list = push_var_eq_functors(
-                        &mut self.heap,
-                        term_write_result.var_dict.iter().filter(|(_, binding)| {
-                            if let Some(r) = binding.as_var() {
-                                *singleton_var_set.get(&r).unwrap_or(&false)
-                            } else {
-                                false
-                            }
-                        }),
-                        &indices.op_dir,
-                        self.atom_tbl.clone(),
-                    );
-
-                    let mut var_list = Vec::with_capacity(singleton_var_set.len());
-
-                    for (var_name, addr) in term_write_result.var_dict {
-                        if let Some(var) = addr.as_var() {
-                            let idx = singleton_var_set.get_index_of(&var).unwrap();
-                            var_list.push((var_name, addr, idx));
-                        }
-                    }
-
-                    var_list.sort_by(|(_,_,idx_1),(_,_,idx_2)| idx_1.cmp(idx_2));
-
-                    let list_of_var_eqs = push_var_eq_functors(
-                        &mut self.heap,
-                        var_list.iter().map(|(var_name, var,_)| (var_name,var)),
-                        &indices.op_dir,
-                        self.atom_tbl.clone(),
-                    );
-
-                    let singleton_addr = self[temp_v!(3)];
-                    let singletons_offset = Addr::HeapCell(self.heap.to_list(
-                        singleton_var_list.into_iter()
-                    ));
-
-                    (self.unify_fn)(self, singletons_offset, singleton_addr);
-
-                    if self.fail {
-                        return Ok(());
-                    }
-
-                    let vars_addr = self[temp_v!(4)];
-                    let vars_offset = Addr::HeapCell(self.heap.to_list(
-                        var_list.into_iter().map(|(_,var,_)| var)
-                    ));
-
-                    (self.unify_fn)(self, vars_offset, vars_addr);
-
-                    if self.fail {
-                        return Ok(());
-                    }
-
-                    let var_names_addr = self[temp_v!(5)];
-                    let var_names_offset =
-                        Addr::HeapCell(self.heap.to_list(list_of_var_eqs.into_iter()));
-
-                    return Ok((self.unify_fn)(self, var_names_offset, var_names_addr));
-                }
-                Err(err) => {
-                    if let ParserError::UnexpectedEOF = err {
-                        self.eof_action(
-                            self[temp_v!(2)],
-                            &mut orig_stream,
-                            clause_name!("read_term"),
-                            3,
-                        )?;
-
-                        if orig_stream.options().eof_action == EOFAction::Reset {
-                            if self.fail == false {
-                                continue;
-                            }
-                        }
-
-                        return Ok(());
-                    }
-
-                    let stub = MachineError::functor_stub(clause_name!("read_term"), 3);
-                    let err = MachineError::syntax_error(self.heap.h(), err);
-
-                    return Err(self.error_form(err, stub));
-                }
-            }
-        }
-    }
-
-    pub(crate) fn write_term<'a>(
-        &'a self,
-        op_dir: &'a OpDir,
-    ) -> Result<Option<HCPrinter<'a, PrinterOutputter>>, MachineStub> {
-        let ignore_ops = self.store(self.deref(self[temp_v!(3)]));
-        let numbervars = self.store(self.deref(self[temp_v!(4)]));
-        let quoted = self.store(self.deref(self[temp_v!(5)]));
-        let max_depth = self.store(self.deref(self[temp_v!(7)]));
-
-        let mut printer = HCPrinter::new(&self, op_dir, PrinterOutputter::new());
-
-        if let &Addr::Con(h) = &ignore_ops {
-            if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
-                printer.ignore_ops = name.as_str() == "true";
-            } else {
-                unreachable!()
-            }
-        }
-
-        if let &Addr::Con(h) = &numbervars {
-            if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
-                printer.numbervars = name.as_str() == "true";
-            } else {
-                unreachable!()
-            }
-        }
-
-        if let &Addr::Con(h) = &quoted {
-            if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
-                printer.quoted = name.as_str() == "true";
-            } else {
-                unreachable!()
-            }
-        }
-
-        match Number::try_from((max_depth, &self.heap)) {
-            Ok(Number::Fixnum(n)) => {
-                if let Ok(n) = usize::try_from(n) {
-                    printer.max_depth = n;
-                } else {
-                    return Ok(None);
-                }
-            }
-            Ok(Number::Integer(n)) => {
-                if let Some(n) = n.to_usize() {
-                    printer.max_depth = n;
-                } else {
-                    return Ok(None);
-                }
-            }
-            _ => {
-                unreachable!();
-            }
-        }
-
-        let stub = MachineError::functor_stub(clause_name!("write_term"), 2);
-
-        match self.try_from_list(temp_v!(6), stub) {
-            Ok(addrs) => {
-                let mut var_names: IndexMap<Addr, String> = IndexMap::new();
-
-                for addr in addrs {
-                    match addr {
-                        Addr::Str(s) => match &self.heap[s] {
-                            &HeapCellValue::NamedStr(2, ref name, _) if name.as_str() == "=" => {
-                                let atom = self.heap[s + 1].as_addr(s + 1);
-                                let var = self.heap[s + 2].as_addr(s + 2);
-
-                                let atom = match self.store(self.deref(atom)) {
-                                    Addr::Con(h) => {
-                                        if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
-                                            atom.to_string()
-                                        } else {
-                                            unreachable!()
-                                        }
-                                    }
-                                    Addr::Char(c) => c.to_string(),
-                                    _ => unreachable!(),
-                                };
-
-                                let var = self.store(self.deref(var));
-
-                                if var_names.contains_key(&var) {
-                                    continue;
-                                }
-
-                                var_names.insert(var, atom);
-                            }
-                            _ => {}
-                        },
-                        _ => {}
-                    }
-                }
-
-                printer.var_names = var_names;
-            }
-            Err(err) => {
-                return Err(err);
-            }
-        }
-
-        Ok(Some(printer))
-    }
-
-    pub(super) fn throw_undefined_error(&mut self, name: ClauseName, arity: usize) -> MachineStub {
-        let stub = MachineError::functor_stub(name.clone(), arity);
-        let h = self.heap.h();
-        let key = ExistenceError::Procedure(name, arity);
-
-        self.error_form(MachineError::existence_error(h, key), stub)
-    }
-
-    #[inline]
-    pub(crate) fn heap_pstr_iter<'a>(&'a self, focus: Addr) -> HeapPStrIter<'a> {
-        HeapPStrIter::new(self, focus)
-    }
-
-    pub(super) fn try_char_list(&self, addrs: Vec<Addr>) -> Result<String, MachineError> {
-        let mut chars = String::new();
-        let mut iter = addrs.iter();
-
-        while let Some(addr) = iter.next() {
-            let addr = self.store(self.deref(*addr));
-
-            match addr {
-                Addr::Char(c) => {
-                    chars.push(c);
-                    continue;
-                }
-                Addr::Con(h) => {
-                    if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
-                        if name.is_char() {
-                            chars += name.as_str();
-                            continue;
-                        }
-                    }
-                }
-                _ => {}
-            };
-
-            let h = self.heap.h();
-
-            return Err(MachineError::type_error(h, ValidType::Character, addr));
-        }
-
-        Ok(chars)
-    }
-
-    pub(super) fn read_predicate_key(&self, name: Addr, arity: Addr) -> (ClauseName, usize) {
-        let predicate_name = atom_from!(self, self.store(self.deref(name)));
-        let arity = self.store(self.deref(arity));
-
-        let arity = match Number::try_from((arity, &self.heap)) {
-            Ok(Number::Integer(n)) if &*n >= &0 && &*n <= &MAX_ARITY => n.to_usize().unwrap(),
-            Ok(Number::Fixnum(n)) if n >= 0 && n <= MAX_ARITY as isize => {
-                usize::try_from(n).unwrap()
-            }
-            _ => unreachable!(),
-        };
-
-        (predicate_name, arity)
-    }
-
-    pub(super) fn call_at_index(&mut self, arity: usize, p: LocalCodePtr) {
-        self.cp.assign_if_local(self.p.clone() + 1);
-        self.num_of_args = arity;
-        self.b0 = self.b;
-        self.p = CodePtr::Local(p);
-    }
-
-    pub(super) fn execute_at_index(&mut self, arity: usize, p: LocalCodePtr) {
-        self.num_of_args = arity;
-        self.b0 = self.b;
-        self.p = CodePtr::Local(p);
-    }
-
-    pub(super) fn module_lookup(
-        &mut self,
-        indices: &IndexStore,
-        call_policy: &mut Box<dyn CallPolicy>,
-        key: PredicateKey,
-        module_name: ClauseName,
-        _last_call: bool,
-        stream_aliases: &StreamAliasDir,
-    ) -> CallResult {
-        if module_name.as_str() == "user" {
-            return call_policy.call_clause_type(
-                self,
-                key,
-                &indices.code_dir,
-                &indices.op_dir,
-                stream_aliases,
-            );
-        } else if let Some(module) = indices.modules.get(&module_name) {
-            return call_policy.call_clause_type(
-                self,
-                key,
-                &module.code_dir,
-                &module.op_dir,
-                stream_aliases,
-            );
-        }
-
-        let (name, arity) = key;
-
-        let h = self.heap.h();
-        let stub = MachineError::functor_stub(name.clone(), arity);
-        let err = MachineError::module_resolution_error(h, module_name, name, arity);
-
-        return Err(self.error_form(err, stub));
-    }
-}
-
-pub(crate) type CallResult = Result<(), Vec<HeapCellValue>>;
-
-pub(crate) trait CallPolicy: Any + fmt::Debug {
-    fn retry_me_else(
-        &mut self,
-        machine_st: &mut MachineState,
-        offset: usize,
-        global_variables: &mut GlobalVarDir,
-    ) -> CallResult {
-        let b = machine_st.b;
-        let n = machine_st
-            .stack
-            .index_or_frame(b)
-            .prelude
-            .univ_prelude
-            .num_cells;
-
-        for i in 1..n + 1 {
-            machine_st.registers[i] = machine_st.stack.index_or_frame(b)[i - 1];
-        }
-
-        machine_st.num_of_args = n;
-        machine_st.e = machine_st.stack.index_or_frame(b).prelude.e;
-        machine_st.cp = machine_st.stack.index_or_frame(b).prelude.cp;
-
-        machine_st.stack.index_or_frame_mut(b).prelude.bp = machine_st.p.local() + offset;
-
-        let old_tr = machine_st.stack.index_or_frame(b).prelude.tr;
-        let curr_tr = machine_st.tr;
-
-        machine_st.unwind_trail(old_tr, curr_tr, global_variables);
-        machine_st.tr = machine_st.stack.index_or_frame(b).prelude.tr;
+        machine_st.unwind_trail(old_tr, curr_tr, global_variables);
+        machine_st.tr = machine_st.stack.index_or_frame(b).prelude.tr;
 
         machine_st.trail.truncate(machine_st.tr);
         machine_st
@@ -765,8 +221,7 @@ pub(crate) trait CallPolicy: Any + fmt::Debug {
             .truncate(machine_st.stack.index_or_frame(b).prelude.h);
 
         machine_st.attr_var_init.reset();
-
-        machine_st.hb = machine_st.heap.h();
+        machine_st.hb = machine_st.heap.len();
         machine_st.p += 1;
 
         Ok(())
@@ -808,8 +263,7 @@ pub(crate) trait CallPolicy: Any + fmt::Debug {
             .truncate(machine_st.stack.index_or_frame(b).prelude.h);
 
         machine_st.attr_var_init.reset();
-
-        machine_st.hb = machine_st.heap.h();
+        machine_st.hb = machine_st.heap.len();
         machine_st.p = CodePtr::Local(dir_entry!(machine_st.p.local().abs_loc() + offset));
 
         Ok(())
@@ -830,7 +284,7 @@ pub(crate) trait CallPolicy: Any + fmt::Debug {
             .num_cells;
 
         for i in 1..n + 1 {
-            machine_st.registers[i] = machine_st.stack.index_or_frame(b)[i - 1];
+            machine_st.registers[i] = machine_st.stack[stack_loc!(OrFrame, b, i - 1)];
         }
 
         machine_st.num_of_args = n;
@@ -849,11 +303,10 @@ pub(crate) trait CallPolicy: Any + fmt::Debug {
             .truncate(machine_st.stack.index_or_frame(b).prelude.h);
 
         machine_st.attr_var_init.reset();
-
         machine_st.b = machine_st.stack.index_or_frame(b).prelude.b;
         machine_st.stack.truncate(b);
 
-        machine_st.hb = machine_st.heap.h();
+        machine_st.hb = machine_st.heap.len();
         machine_st.p = CodePtr::Local(dir_entry!(machine_st.p.local().abs_loc() + offset));
 
         Ok(())
@@ -873,7 +326,7 @@ pub(crate) trait CallPolicy: Any + fmt::Debug {
             .num_cells;
 
         for i in 1..n + 1 {
-            machine_st.registers[i] = machine_st.stack.index_or_frame(b)[i - 1];
+            machine_st.registers[i] = machine_st.stack[stack_loc!(OrFrame, b, i - 1)];
         }
 
         machine_st.num_of_args = n;
@@ -892,11 +345,10 @@ pub(crate) trait CallPolicy: Any + fmt::Debug {
             .truncate(machine_st.stack.index_or_frame(b).prelude.h);
 
         machine_st.attr_var_init.reset();
-
         machine_st.b = machine_st.stack.index_or_frame(b).prelude.b;
         machine_st.stack.truncate(b);
 
-        machine_st.hb = machine_st.heap.h();
+        machine_st.hb = machine_st.heap.len();
         machine_st.p += 1;
 
         Ok(())
@@ -905,7 +357,7 @@ pub(crate) trait CallPolicy: Any + fmt::Debug {
     fn context_call(
         &mut self,
         machine_st: &mut MachineState,
-        name: ClauseName,
+        name: Atom,
         arity: usize,
         idx: &CodeIndex,
     ) -> CallResult {
@@ -919,7 +371,7 @@ pub(crate) trait CallPolicy: Any + fmt::Debug {
     fn try_call(
         &mut self,
         machine_st: &mut MachineState,
-        name: ClauseName,
+        name: Atom,
         arity: usize,
         idx: &CodeIndex,
     ) -> CallResult {
@@ -946,7 +398,7 @@ pub(crate) trait CallPolicy: Any + fmt::Debug {
     fn try_execute(
         &mut self,
         machine_st: &mut MachineState,
-        name: ClauseName,
+        name: Atom,
         arity: usize,
         idx: &CodeIndex,
     ) -> CallResult {
@@ -980,7 +432,7 @@ pub(crate) trait CallPolicy: Any + fmt::Debug {
     ) -> CallResult {
         match ct {
             &BuiltInClauseType::AcyclicTerm => {
-                let addr = machine_st[temp_v!(1)];
+                let addr = machine_st.registers[1];
                 machine_st.fail = machine_st.is_cyclic_term(addr);
                 return_from_clause!(machine_st.last_call, machine_st)
             }
@@ -989,57 +441,47 @@ pub(crate) trait CallPolicy: Any + fmt::Debug {
                 return_from_clause!(machine_st.last_call, machine_st)
             }
             &BuiltInClauseType::Compare => {
-                let a1 = machine_st.store(machine_st.deref(machine_st[temp_v!(1)]));
-                let a2 = machine_st[temp_v!(2)];
-                let a3 = machine_st[temp_v!(3)];
-
-                match a1 {
-                    Addr::Con(h) if machine_st.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(ref atom, _) = &machine_st.heap[h] {
-                            match atom.as_str() {
-                                ">" | "<" | "=" => {}
-                                _ => {
-                                    let stub =
-                                        MachineError::functor_stub(clause_name!("compare"), 3);
-
-                                    let err =
-                                        MachineError::domain_error(DomainErrorType::Order, a1);
-                                    return Err(machine_st.error_form(err, stub));
-                                }
+                let stub_gen = || functor_stub(atom!("compare"), 3);
+
+                let a1 = machine_st.store(machine_st.deref(machine_st.registers[1]));
+                let a2 = machine_st.registers[2];
+                let a3 = machine_st.registers[3];
+
+                read_heap_cell!(a1,
+                    (HeapCellValueTag::Str, s) => {
+                        let (name, arity) = cell_as_atom_cell!(machine_st.heap[s])
+                            .get_name_and_arity();
+
+                        match name {
+                            atom!(">") | atom!("<") | atom!("=") if arity == 2 => {
+                            }
+                            _ => {
+                                let err = machine_st.domain_error(DomainErrorType::Order, a1);
+                                return Err(machine_st.error_form(err, stub_gen()));
                             }
-                        } else {
-                            unreachable!()
                         }
                     }
-                    addr if !addr.is_ref() => {
-                        let h = machine_st.heap.h();
-                        let stub = MachineError::functor_stub(clause_name!("compare"), 3);
-                        let err = MachineError::type_error(h, ValidType::Atom, a1);
-                        return Err(machine_st.error_form(err, stub));
+                    (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
                     }
-                    _ => {}
-                }
+                    _ => {
+                        let err = machine_st.type_error(ValidType::Atom, a1);
+                        return Err(machine_st.error_form(err, stub_gen()));
+                    }
+                );
 
-                let atom = match machine_st.compare_term_test(&a2, &a3) {
+                let atom = match compare_term_test!(machine_st, a2, a3) {
                     Some(Ordering::Greater) => {
-                        let spec = fetch_atom_op_spec(clause_name!(">"), None, op_dir);
-                        HeapCellValue::Atom(clause_name!(">"), spec)
+                        atom!(">")
                     }
                     Some(Ordering::Equal) => {
-                        let spec = fetch_atom_op_spec(clause_name!("="), None, op_dir);
-                        HeapCellValue::Atom(clause_name!("="), spec)
+                        atom!("=")
                     }
                     None | Some(Ordering::Less) => {
-                        let spec = fetch_atom_op_spec(clause_name!("<"), None, op_dir);
-                        HeapCellValue::Atom(clause_name!("<"), spec)
+                        atom!("<")
                     }
                 };
 
-                let h = machine_st.heap.h();
-
-                machine_st.heap.push(atom);
-                (machine_st.unify_fn)(machine_st, a1, Addr::Con(h));
-
+                machine_st.unify_atom(atom, a1);
                 return_from_clause!(machine_st.last_call, machine_st)
             }
             &BuiltInClauseType::CompareTerm(qt) => {
@@ -1050,31 +492,24 @@ pub(crate) trait CallPolicy: Any + fmt::Debug {
                 let stream = machine_st.get_stream_or_alias(
                     machine_st[temp_v!(1)],
                     stream_aliases,
-                    "read",
+                    atom!("read"),
                     2,
                 )?;
 
-                match machine_st.read(stream, machine_st.atom_tbl.clone(), op_dir) {
+                match machine_st.read(stream, op_dir) {
                     Ok(offset) => {
-                        let addr = machine_st[temp_v!(2)];
-                        (machine_st.unify_fn)(machine_st, addr, Addr::HeapCell(offset.heap_loc));
+                        let value = machine_st.registers[2];
+                        unify_fn!(machine_st, value, heap_loc_as_cell!(offset.heap_loc));
                     }
                     Err(ParserError::UnexpectedEOF) => {
-                        let addr = machine_st[temp_v!(2)];
-                        let eof = clause_name!("end_of_file".to_string(), machine_st.atom_tbl);
-
-                        let atom = machine_st.heap.to_unifiable(HeapCellValue::Atom(eof, None));
-
-                        (machine_st.unify_fn)(machine_st, addr, atom);
+                        let value = machine_st.registers[2];
+                        machine_st.unify_atom(atom!("end_of_file"), value);
                     }
                     Err(e) => {
-                        let h = machine_st.heap.h();
-                        let stub = MachineError::functor_stub(clause_name!("read"), 2);
-
-                        let err = MachineError::syntax_error(h, e);
-                        let err = machine_st.error_form(err, stub);
+                        let stub = functor_stub(atom!("read"), 2);
+                        let err = machine_st.syntax_error(e);
 
-                        return Err(err);
+                        return Err(machine_st.error_form(err, stub));
                     }
                 };
 
@@ -1085,8 +520,8 @@ pub(crate) trait CallPolicy: Any + fmt::Debug {
                 return_from_clause!(machine_st.last_call, machine_st)
             }
             &BuiltInClauseType::Eq => {
-                let a1 = machine_st[temp_v!(1)];
-                let a2 = machine_st[temp_v!(2)];
+                let a1 = machine_st.registers[1];
+                let a2 = machine_st.registers[2];
 
                 machine_st.fail = machine_st.eq_test(a1, a2);
                 return_from_clause!(machine_st.last_call, machine_st)
@@ -1096,146 +531,757 @@ pub(crate) trait CallPolicy: Any + fmt::Debug {
                 return_from_clause!(machine_st.last_call, machine_st)
             }
             &BuiltInClauseType::Functor => {
-                machine_st.try_functor(op_dir)?;
+                machine_st.try_functor()?;
                 return_from_clause!(machine_st.last_call, machine_st)
             }
             &BuiltInClauseType::NotEq => {
-                let a1 = machine_st[temp_v!(1)];
-                let a2 = machine_st[temp_v!(2)];
+                let a1 = machine_st.registers[1];
+                let a2 = machine_st.registers[2];
+
+                machine_st.fail =
+                    if let Some(Ordering::Equal) = compare_term_test!(machine_st, a1, a2) {
+                        true
+                    } else {
+                        false
+                    };
+
+                return_from_clause!(machine_st.last_call, machine_st)
+            }
+            &BuiltInClauseType::Sort => {
+                machine_st.check_sort_errors()?;
+
+                let stub_gen = || functor_stub(atom!("sort"), 2);
+                let mut list = machine_st.try_from_list(machine_st.registers[1], stub_gen)?;
+
+                list.sort_unstable_by(|v1, v2| {
+                    compare_term_test!(machine_st, *v1, *v2).unwrap_or(Ordering::Less)
+                });
+
+                list.dedup_by(|v1, v2| {
+                    compare_term_test!(machine_st, *v1, *v2) == Some(Ordering::Equal)
+                });
+
+                let heap_addr = heap_loc_as_cell!(
+                    iter_to_heap_list(&mut machine_st.heap, list.into_iter())
+                );
+
+                let r2 = machine_st.registers[2];
+                unify_fn!(machine_st, r2, heap_addr);
+
+                return_from_clause!(machine_st.last_call, machine_st)
+            }
+            &BuiltInClauseType::KeySort => {
+                machine_st.check_keysort_errors()?;
+
+                let stub_gen = || functor_stub(atom!("keysort"), 2);
+                let list = machine_st.try_from_list(machine_st.registers[1], stub_gen)?;
+
+                let mut key_pairs = Vec::with_capacity(list.len());
+
+                for val in list {
+                    let key = machine_st.project_onto_key(val)?;
+                    key_pairs.push((key, val));
+                }
+
+                key_pairs.sort_by(|a1, a2| {
+                    compare_term_test!(machine_st, a1.0, a2.0).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 machine_st.heap, key_pairs)
+                );
+
+                let r2 = machine_st.registers[2];
+                unify_fn!(machine_st, r2, heap_addr);
+
+                return_from_clause!(machine_st.last_call, machine_st)
+            }
+            &BuiltInClauseType::Is(r, ref at) => {
+                let n1 = machine_st[r];
+                let n2 = machine_st.get_number(at)?;
+
+                match n2 {
+                    Number::Fixnum(n) => machine_st.unify_fixnum(n, n1),
+                    Number::Float(n) => {
+                        // TODO: argghh.. deal with it.
+                        let n = arena_alloc!(n, &mut machine_st.arena);
+                        machine_st.unify_f64(n, n1)
+                    }
+                    Number::Integer(n) => machine_st.unify_big_int(n, n1),
+                    Number::Rational(n) => machine_st.unify_rational(n, n1),
+                }
+
+                return_from_clause!(machine_st.last_call, machine_st)
+            }
+        }
+    }
+
+    fn call_clause_type(
+        &mut self,
+        machine_st: &mut MachineState,
+        key: PredicateKey,
+        code_dir: &CodeDir,
+        op_dir: &OpDir,
+        stream_aliases: &StreamAliasDir,
+    ) -> CallResult {
+        let (name, arity) = key;
+
+        match ClauseType::from(name, arity) {
+            ClauseType::BuiltIn(built_in) => {
+                machine_st.setup_built_in_call(built_in);
+                self.call_builtin(machine_st, &built_in, code_dir, op_dir, stream_aliases)?;
+            }
+            ClauseType::CallN => {
+                machine_st.handle_internal_call_n(arity);
+
+                if machine_st.fail {
+                    return Ok(());
+                }
+
+                machine_st.p = CodePtr::CallN(arity, machine_st.p.local(), machine_st.last_call);
+            }
+            ClauseType::Inlined(inlined) => {
+                machine_st.execute_inlined(&inlined);
+
+                if machine_st.last_call {
+                    machine_st.p = CodePtr::Local(machine_st.cp);
+                }
+            }
+            ClauseType::Named(..) => {
+                if let Some(idx) = code_dir.get(&(name, arity)) {
+                    self.context_call(machine_st, name, arity, idx)?;
+                } else {
+                    return Err(machine_st.throw_undefined_error(name, arity));
+                }
+            }
+            ClauseType::System(_) => {
+                let (name, arity) = key;
+                let name = functor!(name);
+
+                let stub = functor_stub(atom!("call"), arity + 1);
+                let err = machine_st.type_error(ValidType::Callable, name);
+
+                return Err(machine_st.error_form(err, stub));
+            }
+        }
+
+        Ok(())
+    }
+
+    fn call_n(
+        &mut self,
+        machine_st: &mut MachineState,
+        arity: usize,
+        code_dir: &CodeDir,
+        op_dir: &OpDir,
+        stream_aliases: &StreamAliasDir,
+    ) -> CallResult {
+        if let Some(key) = machine_st.setup_call_n(arity) {
+            self.call_clause_type(machine_st, key, code_dir, op_dir, stream_aliases)?;
+        }
+
+        Ok(())
+    }
+}
+
+#[inline(always)]
+pub fn pstr_loc_and_offset(heap: &[HeapCellValue], index: usize) -> (usize, Fixnum) {
+    read_heap_cell!(heap[index],
+        (HeapCellValueTag::PStr | HeapCellValueTag::CStr) => {
+            (index, Fixnum::build_with(0))
+        }
+        (HeapCellValueTag::PStrOffset, h) => {
+            (h, cell_as_fixnum!(heap[index+1]))
+        }
+        _ => {
+            unreachable!()
+        }
+    )
+}
+
+#[derive(Debug)]
+pub struct Ball {
+    pub(super) boundary: usize,
+    pub(super) stub: Heap,
+}
+
+impl Ball {
+    pub(super) fn new() -> Self {
+        Ball {
+            boundary: 0,
+            stub: Heap::new(),
+        }
+    }
+
+    pub(super) fn reset(&mut self) {
+        self.boundary = 0;
+        self.stub.clear();
+    }
+
+    pub(super) fn copy_and_align(&self, h: usize) -> Heap {
+        let diff = self.boundary as i64 - h as i64;
+
+        self.stub.iter().cloned().map(|heap_value| {
+            heap_value - diff
+        }).collect()
+    }
+}
+
+#[derive(Debug)]
+pub(super) struct CopyTerm<'a> {
+    state: &'a mut MachineState,
+}
+
+impl<'a> CopyTerm<'a> {
+    pub(super) fn new(state: &'a mut MachineState) -> Self {
+        CopyTerm { state }
+    }
+}
+
+impl<'a> Index<usize> for CopyTerm<'a> {
+    type Output = HeapCellValue;
+
+    #[inline(always)]
+    fn index(&self, index: usize) -> &Self::Output {
+        &self.state.heap[index]
+    }
+}
+
+impl<'a> IndexMut<usize> for CopyTerm<'a> {
+    #[inline(always)]
+    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+        &mut self.state.heap[index]
+    }
+}
+
+// the ordinary, heap term copier, used by duplicate_term.
+impl<'a> CopierTarget for CopyTerm<'a> {
+    #[inline(always)]
+    fn threshold(&self) -> usize {
+        self.state.heap.len()
+    }
+
+    #[inline(always)]
+    fn push(&mut self, hcv: HeapCellValue) {
+        self.state.heap.push(hcv);
+    }
+
+    #[inline(always)]
+    fn store(&self, value: HeapCellValue) -> HeapCellValue {
+        self.state.store(value)
+    }
+
+    #[inline(always)]
+    fn deref(&self, value: HeapCellValue) -> HeapCellValue {
+        self.state.deref(value)
+    }
+
+    #[inline(always)]
+    fn stack(&mut self) -> &mut Stack {
+        &mut self.state.stack
+    }
+}
+
+#[derive(Debug)]
+pub(super) struct CopyBallTerm<'a> {
+    stack: &'a mut Stack,
+    heap: &'a mut Heap,
+    heap_boundary: usize,
+    stub: &'a mut Heap,
+}
+
+impl<'a> CopyBallTerm<'a> {
+    pub(super) fn new(stack: &'a mut Stack, heap: &'a mut Heap, stub: &'a mut Heap) -> Self {
+        let hb = heap.len();
+
+        CopyBallTerm {
+            stack,
+            heap,
+            heap_boundary: hb,
+            stub,
+        }
+    }
+}
+
+impl<'a> Index<usize> for CopyBallTerm<'a> {
+    type Output = HeapCellValue;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        if index < self.heap_boundary {
+            &self.heap[index]
+        } else {
+            let index = index - self.heap_boundary;
+            &self.stub[index]
+        }
+    }
+}
+
+impl<'a> IndexMut<usize> for CopyBallTerm<'a> {
+    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+        if index < self.heap_boundary {
+            &mut self.heap[index]
+        } else {
+            let index = index - self.heap_boundary;
+            &mut self.stub[index]
+        }
+    }
+}
+
+// the ordinary, heap term copier, used by duplicate_term.
+impl<'a> CopierTarget for CopyBallTerm<'a> {
+    fn threshold(&self) -> usize {
+        self.heap_boundary + self.stub.len()
+    }
+
+    fn push(&mut self, value: HeapCellValue) {
+        self.stub.push(value);
+    }
+
+    fn store(&self, value: HeapCellValue) -> HeapCellValue {
+        read_heap_cell!(value,
+            (HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => {
+                if h < self.heap_boundary {
+                    self.heap[h]
+                } else {
+                    let index = h - self.heap_boundary;
+                    self.stub[index]
+                }
+            }
+            (HeapCellValueTag::StackVar, s) => {
+                self.stack[s]
+            }
+            _ => {
+                value
+            }
+        )
+    }
+
+    fn deref(&self, mut addr: HeapCellValue) -> HeapCellValue {
+        loop {
+            let value = self.store(addr);
+
+            if value.is_var() && value != addr {
+                addr = value;
+                continue;
+            }
+
+            return addr;
+        }
+    }
+
+    fn stack(&mut self) -> &mut Stack {
+        self.stack
+    }
+}
+
+impl MachineState {
+    #[allow(dead_code)]
+    pub(super) fn try_char_list(&mut self, addrs: Vec<HeapCellValue>) -> Result<String, MachineError> {
+        let mut chars = String::new();
+
+        for addr in addrs {
+            let addr = self.store(self.deref(addr));
+
+            read_heap_cell!(addr,
+                (HeapCellValueTag::Char, c) => {
+                    chars.push(c);
+                    continue;
+                }
+                (HeapCellValueTag::Atom, (name, arity)) => {
+                    if arity == 0 {
+                        if let Some(c) = name.as_char() {
+                            chars.push(c);
+                            continue;
+                        }
+                    }
+                }
+                _ => {
+                }
+            );
+
+            return Err(self.type_error(ValidType::Character, addr));
+        }
+
+        Ok(chars)
+    }
+
+    pub(super) fn throw_undefined_error(&mut self, name: Atom, arity: usize) -> MachineStub {
+        let stub = functor_stub(name, arity);
+        let err = self.existence_error(ExistenceError::Procedure(name, arity));
+
+        self.error_form(err, stub)
+    }
+
+    pub(super) fn call_at_index(&mut self, arity: usize, p: LocalCodePtr) {
+        self.cp.assign_if_local(self.p + 1);
+        self.num_of_args = arity;
+        self.b0 = self.b;
+        self.p = CodePtr::Local(p);
+    }
+
+    pub(super) fn execute_at_index(&mut self, arity: usize, p: LocalCodePtr) {
+        self.num_of_args = arity;
+        self.b0 = self.b;
+        self.p = CodePtr::Local(p);
+    }
+
+    pub fn read_term(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult {
+        fn push_var_eq_functors<'a>(
+            heap: &mut Heap,
+            iter: impl Iterator<Item = (&'a Rc<String>, &'a HeapCellValue)>,
+            atom_tbl: &mut AtomTable,
+        ) -> Vec<HeapCellValue> {
+            let mut list_of_var_eqs = vec![];
+
+            for (var, binding) in iter {
+                let var_atom = atom_tbl.build_with(&var);
+                let h = heap.len();
+
+                heap.push(atom_as_cell!(atom!("="), 2));
+                heap.push(atom_as_cell!(var_atom));
+                heap.push(*binding);
+
+                list_of_var_eqs.push(str_loc_as_cell!(h));
+            }
+
+            list_of_var_eqs
+        }
+
+        self.check_stream_properties(
+            stream,
+            StreamType::Text,
+            Some(self.registers[2]),
+            atom!("read_term"),
+            3,
+        )?;
+
+        if stream.past_end_of_stream() {
+            if EOFAction::Reset != stream.options().eof_action() {
+                return return_from_clause!(self.last_call, self);
+            } else if self.fail {
+                return Ok(());
+            }
+        }
+
+        loop {
+            match self.read(stream, &indices.op_dir) {
+                Ok(term_write_result) => {
+                    let term = self.registers[2];
+                    unify_fn!(self, heap_loc_as_cell!(term_write_result.heap_loc), term);
+                    let term = self.store(self.deref(term));
+
+                    if self.fail {
+                        return Ok(());
+                    }
+
+                    let mut singleton_var_set: IndexMap<Ref, bool> = IndexMap::new();
+                    let mut var_list = vec![];
+
+                    let list_of_var_eqs = push_var_eq_functors(
+                        &mut self.heap,
+                        term_write_result.var_dict.iter(),
+                        &mut self.atom_tbl,
+                    );
+
+                    for addr in stackful_preorder_iter(&mut self.heap, term) {
+                        let addr = unmark_cell_bits!(addr);
+
+                        if let Some(var) = addr.as_var() {
+                            if !singleton_var_set.contains_key(&var) {
+                                singleton_var_set.insert(var, true);
+                                var_list.push(addr);
+                            } else {
+                                singleton_var_set.insert(var, false);
+                            }
+                        }
+                    }
+
+                    let singleton_var_list = push_var_eq_functors(
+                        &mut self.heap,
+                        term_write_result.var_dict.iter().filter(|(_, binding)| {
+                            if let Some(r) = binding.as_var() {
+                                *singleton_var_set.get(&r).unwrap_or(&false)
+                            } else {
+                                false
+                            }
+                        }),
+                        &mut self.atom_tbl,
+                    );
+
+                    let singleton_addr = self.registers[3];
+                    let singletons_offset = heap_loc_as_cell!(
+                        iter_to_heap_list(&mut self.heap, singleton_var_list.into_iter())
+                    );
+
+                    unify_fn!(self, singletons_offset, singleton_addr);
+
+                    if self.fail {
+                        return Ok(());
+                    }
+
+                    let vars_addr = self.registers[4];
+                    let vars_offset = heap_loc_as_cell!(
+                        iter_to_heap_list(&mut self.heap, var_list.into_iter())
+                    );
+
+                    unify_fn!(self, vars_offset, vars_addr);
+
+                    if self.fail {
+                        return Ok(());
+                    }
+
+                    let var_names_addr = self.registers[5];
+                    let var_names_offset = heap_loc_as_cell!(
+                        iter_to_heap_list(&mut self.heap, list_of_var_eqs.into_iter())
+                    );
+
+                    return Ok(unify_fn!(self, var_names_offset, var_names_addr));
+                }
+                Err(err) => {
+                    if let ParserError::UnexpectedEOF = err {
+                        self.eof_action(
+                            self.registers[2],
+                            stream,
+                            atom!("read_term"),
+                            3,
+                        )?;
+
+                        if stream.options().eof_action() == EOFAction::Reset {
+                            if self.fail == false {
+                                continue;
+                            }
+                        }
+
+                        return Ok(());
+                    }
 
-                machine_st.fail =
-                    if let Some(Ordering::Equal) = machine_st.compare_term_test(&a1, &a2) {
-                        true
-                    } else {
-                        false
-                    };
+                    let stub = functor_stub(atom!("read_term"), 3);
+                    let err = self.syntax_error(err);
 
-                return_from_clause!(machine_st.last_call, machine_st)
+                    return Err(self.error_form(err, stub));
+                }
             }
-            &BuiltInClauseType::Sort => {
-                machine_st.check_sort_errors()?;
+        }
+    }
 
-                let stub = MachineError::functor_stub(clause_name!("sort"), 2);
-                let mut list = machine_st.try_from_list(temp_v!(1), stub)?;
+    pub(crate) fn write_term<'a>(
+        &'a mut self,
+        op_dir: &'a OpDir,
+    ) -> Result<Option<HCPrinter<'a, PrinterOutputter>>, MachineStub> {
+        let ignore_ops = self.store(self.deref(self.registers[3]));
+        let numbervars = self.store(self.deref(self.registers[4]));
+        let quoted = self.store(self.deref(self.registers[5]));
+        let max_depth = self.store(self.deref(self.registers[7]));
 
-                list.sort_unstable_by(|a1, a2| {
-                    machine_st
-                        .compare_term_test(a1, a2)
-                        .unwrap_or(Ordering::Less)
-                });
+        let term_to_be_printed = self.store(self.deref(self.registers[2]));
+        let stub_gen = || functor_stub(atom!("write_term"), 2);
 
-                machine_st.term_dedup(&mut list);
+        let printer = match self.try_from_list(self.registers[6], stub_gen) {
+            Ok(addrs) => {
+                let mut var_names: IndexMap<HeapCellValue, Rc<String>> = IndexMap::new();
 
-                let heap_addr = Addr::HeapCell(machine_st.heap.to_list(list.into_iter()));
+                for addr in addrs {
+                    read_heap_cell!(addr,
+                        (HeapCellValueTag::Str, s) => {
+                            let (name, arity) = cell_as_atom_cell!(self.heap[s])
+                                .get_name_and_arity();
 
-                let r2 = machine_st[temp_v!(2)];
-                (machine_st.unify_fn)(machine_st, r2, heap_addr);
+                            if name == atom!("=") && arity == 2 {
+                                let atom = self.store(self.deref(self.heap[s+1]));
+                                let var = self.store(self.deref(self.heap[s+2]));
 
-                return_from_clause!(machine_st.last_call, machine_st)
-            }
-            &BuiltInClauseType::KeySort => {
-                machine_st.check_keysort_errors()?;
+                                if var_names.contains_key(&var) {
+                                    continue;
+                                }
 
-                let stub = MachineError::functor_stub(clause_name!("keysort"), 2);
-                let list = machine_st.try_from_list(temp_v!(1), stub)?;
-                let mut key_pairs = Vec::new();
+                                var_names.insert(var, Rc::new(cell_as_atom!(atom).as_str().to_owned()));
+                            }
+                        }
+                        _ => {
+                        }
+                    );
+                }
 
-                for val in list {
-                    let key = machine_st.project_onto_key(val.clone())?;
-                    key_pairs.push((key, val.clone()));
+                let mut printer = HCPrinter::new(
+                    &mut self.heap,
+                    &mut self.arena,
+                    op_dir,
+                    PrinterOutputter::new(),
+                    term_to_be_printed,
+                );
+
+                if let HeapCellValueTag::Atom = ignore_ops.get_tag() {
+                    let name = cell_as_atom!(ignore_ops);
+                    printer.ignore_ops = name == atom!("true");
+                } else {
+                    unreachable!();
                 }
 
-                key_pairs.sort_by(|a1, a2| {
-                    machine_st
-                        .compare_term_test(&a1.0, &a2.0)
-                        .unwrap_or(Ordering::Less)
-                });
+                if let HeapCellValueTag::Atom = numbervars.get_tag() {
+                    let name = cell_as_atom!(numbervars);
+                    printer.numbervars = name == atom!("true");
+                } else {
+                    unreachable!();
+                }
 
-                let key_pairs = key_pairs.into_iter().map(|kp| kp.1);
-                let heap_addr = Addr::HeapCell(machine_st.heap.to_list(key_pairs));
+                if let HeapCellValueTag::Atom = quoted.get_tag() {
+                    let name = cell_as_atom!(quoted);
+                    printer.quoted = name == atom!("true");
+                } else {
+                    unreachable!();
+                }
 
-                let r2 = machine_st[temp_v!(2)];
-                (machine_st.unify_fn)(machine_st, r2, heap_addr);
+                match Number::try_from(max_depth) {
+                    Ok(Number::Fixnum(n)) => {
+                        if let Ok(n) = usize::try_from(n.get_num()) {
+                            printer.max_depth = n;
+                        } else {
+                            self.fail = true;
+                            return Ok(None);
+                        }
+                    }
+                    Ok(Number::Integer(n)) => {
+                        if let Some(n) = n.to_usize() {
+                            printer.max_depth = n;
+                        } else {
+                            self.fail = true;
+                            return Ok(None);
+                        }
+                    }
+                    _ => {
+                        unreachable!();
+                    }
+                }
 
-                return_from_clause!(machine_st.last_call, machine_st)
+                printer.var_names = var_names;
+
+                printer
             }
-            &BuiltInClauseType::Is(r, ref at) => {
-                let a1 = machine_st[r];
-                let n2 = machine_st.get_number(at)?;
+            Err(err) => {
+                return Err(err);
+            }
+        };
 
-                let n2 = machine_st.heap.put_constant(n2.into());
-                (machine_st.unify_fn)(machine_st, a1, n2);
+        Ok(Some(printer))
+    }
 
-                return_from_clause!(machine_st.last_call, machine_st)
-            }
-        }
+    pub(super) fn read_predicate_key(&self, name: HeapCellValue, arity: HeapCellValue) -> (Atom, usize) {
+        let name = cell_as_atom!(self.store(self.deref(name)));
+        let arity = cell_as_fixnum!(self.store(self.deref(arity)));
+
+        (name, usize::try_from(arity.get_num()).unwrap())
     }
 
-    fn call_clause_type(
+    pub(super) fn module_lookup(
         &mut self,
-        machine_st: &mut MachineState,
+        indices: &IndexStore,
+        call_policy: &mut Box<dyn CallPolicy>,
         key: PredicateKey,
-        code_dir: &CodeDir,
-        op_dir: &OpDir,
+        module_name: Atom,
+        _last_call: bool,
         stream_aliases: &StreamAliasDir,
     ) -> CallResult {
+        if module_name == atom!("user") {
+            return call_policy.call_clause_type(
+                self,
+                key,
+                &indices.code_dir,
+                &indices.op_dir,
+                stream_aliases,
+            );
+        } else if let Some(module) = indices.modules.get(&module_name) {
+            return call_policy.call_clause_type(
+                self,
+                key,
+                &module.code_dir,
+                &module.op_dir,
+                stream_aliases,
+            );
+        }
+
         let (name, arity) = key;
 
-        match ClauseType::from(name.clone(), arity, None) {
-            ClauseType::BuiltIn(built_in) => {
-                machine_st.setup_built_in_call(built_in.clone());
-                self.call_builtin(machine_st, &built_in, code_dir, op_dir, stream_aliases)?;
-            }
-            ClauseType::CallN => {
-                machine_st.handle_internal_call_n(arity);
+        let stub = functor_stub(name, arity);
+        let err = self.module_resolution_error(module_name, name, arity);
 
-                if machine_st.fail {
-                    return Ok(());
-                }
+        return Err(self.error_form(err, stub));
+    }
+}
 
-                machine_st.p = CodePtr::CallN(arity, machine_st.p.local(), machine_st.last_call);
-            }
-            ClauseType::Inlined(inlined) => {
-                machine_st.execute_inlined(&inlined);
+#[derive(Debug)]
+pub(crate) struct CWILCallPolicy {
+    pub(crate) prev_policy: Box<dyn CallPolicy>,
+    count: Integer,
+    limits: Vec<(Integer, usize)>,
+    inference_limit_exceeded: bool,
+}
 
-                if machine_st.last_call {
-                    machine_st.p = CodePtr::Local(machine_st.cp);
-                }
-            }
-            ClauseType::Op(..) | ClauseType::Named(..) => {
-                if let Some(idx) = code_dir.get(&(name.clone(), arity)) {
-                    self.context_call(machine_st, name, arity, idx)?;
-                } else {
-                    return Err(machine_st.throw_undefined_error(name, arity));
-                }
-            }
-            ClauseType::System(_) => {
-                let name = functor!(clause_name(name));
-                let stub = MachineError::functor_stub(clause_name!("call"), arity + 1);
+impl CWILCallPolicy {
+    pub(crate) fn new_in_place(policy: &mut Box<dyn CallPolicy>) {
+        let mut prev_policy: Box<dyn CallPolicy> = Box::new(DefaultCallPolicy {});
+        mem::swap(&mut prev_policy, policy);
+
+        let new_policy = CWILCallPolicy {
+            prev_policy,
+            count: Integer::from(0),
+            limits: vec![],
+            inference_limit_exceeded: false,
+        };
+
+        *policy = Box::new(new_policy);
+    }
+
+    fn increment(&mut self, machine_st: &MachineState) -> CallResult {
+        if self.inference_limit_exceeded || machine_st.ball.stub.len() > 0 {
+            return Ok(());
+        }
+
+        if let Some(&(ref limit, bp)) = self.limits.last() {
+            if self.count == *limit {
+                self.inference_limit_exceeded = true;
 
-                return Err(machine_st.error_form(
-                    MachineError::type_error(machine_st.heap.h(), ValidType::Callable, name),
-                    stub,
-                ));
+                return Err(
+                    functor!(atom!("inference_limit_exceeded"), [fixnum(bp)])
+                );
+            } else {
+                self.count += 1;
             }
         }
 
         Ok(())
     }
 
-    fn call_n(
-        &mut self,
-        machine_st: &mut MachineState,
-        arity: usize,
-        code_dir: &CodeDir,
-        op_dir: &OpDir,
-        stream_aliases: &StreamAliasDir,
-    ) -> CallResult {
-        if let Some(key) = machine_st.setup_call_n(arity) {
-            self.call_clause_type(machine_st, key, code_dir, op_dir, stream_aliases)?;
+    pub(crate) fn add_limit(&mut self, limit: usize, b: usize) -> &Integer {
+        let mut limit = Integer::from(limit);
+        limit += &self.count;
+
+        match self.limits.last() {
+            Some((ref inner_limit, _)) if *inner_limit <= limit => {}
+            _ => self.limits.push((limit, b)),
+        };
+
+        &self.count
+    }
+
+    pub(crate) fn remove_limit(&mut self, b: usize) -> &Integer {
+        if let Some((_, bp)) = self.limits.last() {
+            if bp == &b {
+                self.limits.pop();
+            }
         }
 
-        Ok(())
+        &self.count
+    }
+
+    pub(crate) fn is_empty(&self) -> bool {
+        self.limits.is_empty()
+    }
+
+    pub(crate) fn into_inner(&mut self) -> Box<dyn CallPolicy> {
+        let mut new_inner: Box<dyn CallPolicy> = Box::new(DefaultCallPolicy {});
+        mem::swap(&mut self.prev_policy, &mut new_inner);
+        new_inner
     }
 }
 
@@ -1243,12 +1289,11 @@ impl CallPolicy for CWILCallPolicy {
     fn context_call(
         &mut self,
         machine_st: &mut MachineState,
-        name: ClauseName,
+        name: Atom,
         arity: usize,
         idx: &CodeIndex,
     ) -> CallResult {
-        self.prev_policy
-            .context_call(machine_st, name, arity, idx)?; //, indices)?;
+        self.prev_policy.context_call(machine_st, name, arity, idx)?;
         self.increment(machine_st)
     }
 
@@ -1258,8 +1303,7 @@ impl CallPolicy for CWILCallPolicy {
         offset: usize,
         global_variables: &mut GlobalVarDir,
     ) -> CallResult {
-        self.prev_policy
-            .retry_me_else(machine_st, offset, global_variables)?;
+        self.prev_policy.retry_me_else(machine_st, offset, global_variables)?;
         self.increment(machine_st)
     }
 
@@ -1269,8 +1313,7 @@ impl CallPolicy for CWILCallPolicy {
         offset: usize,
         global_variables: &mut GlobalVarDir,
     ) -> CallResult {
-        self.prev_policy
-            .retry(machine_st, offset, global_variables)?;
+        self.prev_policy.retry(machine_st, offset, global_variables)?;
         self.increment(machine_st)
     }
 
@@ -1289,8 +1332,7 @@ impl CallPolicy for CWILCallPolicy {
         offset: usize,
         global_variables: &mut GlobalVarDir,
     ) -> CallResult {
-        self.prev_policy
-            .trust(machine_st, offset, global_variables)?;
+        self.prev_policy.trust(machine_st, offset, global_variables)?;
         self.increment(machine_st)
     }
 
@@ -1302,9 +1344,7 @@ impl CallPolicy for CWILCallPolicy {
         op_dir: &OpDir,
         stream_aliases: &StreamAliasDir,
     ) -> CallResult {
-        self.prev_policy
-            .call_builtin(machine_st, ct, code_dir, op_dir, stream_aliases)?;
-
+        self.prev_policy.call_builtin(machine_st, ct, code_dir, op_dir, stream_aliases)?;
         self.increment(machine_st)
     }
 
@@ -1316,9 +1356,7 @@ impl CallPolicy for CWILCallPolicy {
         op_dir: &OpDir,
         stream_aliases: &StreamAliasDir,
     ) -> CallResult {
-        self.prev_policy
-            .call_n(machine_st, arity, code_dir, op_dir, stream_aliases)?;
-
+        self.prev_policy.call_n(machine_st, arity, code_dir, op_dir, stream_aliases)?;
         self.increment(machine_st)
     }
 }
@@ -1330,94 +1368,13 @@ pub(crate) struct DefaultCallPolicy {}
 
 impl CallPolicy for DefaultCallPolicy {}
 
-#[derive(Debug)]
-pub(crate) struct CWILCallPolicy {
-    pub(crate) prev_policy: Box<dyn CallPolicy>,
-    count: Integer,
-    limits: Vec<(Integer, usize)>,
-    inference_limit_exceeded: bool,
-}
-
-impl CWILCallPolicy {
-    pub(crate) fn new_in_place(policy: &mut Box<dyn CallPolicy>) {
-        let mut prev_policy: Box<dyn CallPolicy> = Box::new(DefaultCallPolicy {});
-        mem::swap(&mut prev_policy, policy);
-
-        let new_policy = CWILCallPolicy {
-            prev_policy,
-            count: Integer::from(0),
-            limits: vec![],
-            inference_limit_exceeded: false,
-        };
-
-        *policy = Box::new(new_policy);
-    }
-
-    fn increment(&mut self, machine_st: &MachineState) -> CallResult {
-        if self.inference_limit_exceeded || machine_st.ball.stub.h() > 0 {
-            return Ok(());
-        }
-
-        if let Some(&(ref limit, bp)) = self.limits.last() {
-            if self.count == *limit {
-                self.inference_limit_exceeded = true;
-
-                return Err(functor!(
-                    "inference_limit_exceeded",
-                    [addr(Addr::Usize(bp))]
-                ));
-            } else {
-                self.count += 1;
-            }
-        }
-
-        Ok(())
-    }
-
-    pub(crate) fn add_limit(&mut self, mut limit: Integer, b: usize) -> &Integer {
-        limit += &self.count;
-
-        match self.limits.last().cloned() {
-            Some((ref inner_limit, _)) if *inner_limit <= limit => {}
-            _ => self.limits.push((limit, b)),
-        };
-
-        &self.count
-    }
-
-    pub(crate) fn remove_limit(&mut self, b: usize) -> &Integer {
-        if let Some((_, bp)) = self.limits.last().cloned() {
-            if bp == b {
-                self.limits.pop();
-            }
-        }
-
-        &self.count
-    }
-
-    pub(crate) fn is_empty(&self) -> bool {
-        self.limits.is_empty()
-    }
-
-    pub(crate) fn into_inner(&mut self) -> Box<dyn CallPolicy> {
-        let mut new_inner: Box<dyn CallPolicy> = Box::new(DefaultCallPolicy {});
-        mem::swap(&mut self.prev_policy, &mut new_inner);
-        new_inner
-    }
-}
-
-pub(crate) trait CutPolicy: Any + fmt::Debug {
-    // returns true iff we fail or cut redirected the MachineState's p itself
-    fn cut(&mut self, machine_st: &mut MachineState, r: RegType) -> bool;
-}
-
-downcast!(dyn CutPolicy);
-
-fn cut_body(machine_st: &mut MachineState, addr: &Addr) -> bool {
+fn cut_body(machine_st: &mut MachineState, addr: HeapCellValue) -> bool {
     let b = machine_st.b;
 
-    match addr {
-        &Addr::CutPoint(b0) | &Addr::Usize(b0) => {
+    read_heap_cell!(addr,
+        (HeapCellValueTag::Fixnum, b0) => {
+            let b0 = b0.get_num() as usize;
+
             if b > b0 {
                 machine_st.b = b0;
             }
@@ -1426,7 +1383,7 @@ fn cut_body(machine_st: &mut MachineState, addr: &Addr) -> bool {
             machine_st.fail = true;
             return true;
         }
-    };
+    );
 
     false
 }
@@ -1436,20 +1393,20 @@ pub(crate) struct DefaultCutPolicy {}
 
 pub(super) fn deref_cut(machine_st: &mut MachineState, r: RegType) {
     let addr = machine_st.store(machine_st.deref(machine_st[r]));
-    cut_body(machine_st, &addr);
+    cut_body(machine_st, addr);
 }
 
 impl CutPolicy for DefaultCutPolicy {
     fn cut(&mut self, machine_st: &mut MachineState, r: RegType) -> bool {
         let addr = machine_st[r];
-        cut_body(machine_st, &addr)
+        cut_body(machine_st, addr)
     }
 }
 
 #[derive(Debug)]
 pub(crate) struct SCCCutPolicy {
     // locations of cleaners, cut points, the previous block
-    cont_pts: Vec<(Addr, usize, usize)>,
+    cont_pts: Vec<(HeapCellValue, usize, usize)>,
     r_c_w_h: usize,
     r_c_wo_h: usize,
 }
@@ -1467,21 +1424,21 @@ impl SCCCutPolicy {
         self.cont_pts.is_empty()
     }
 
-    pub(crate) fn push_cont_pt(&mut self, addr: Addr, b: usize, prev_b: usize) {
+    pub(crate) fn push_cont_pt(&mut self, addr: HeapCellValue, b: usize, prev_b: usize) {
         self.cont_pts.push((addr, b, prev_b));
     }
 
-    pub(crate) fn pop_cont_pt(&mut self) -> Option<(Addr, usize, usize)> {
+    pub(crate) fn pop_cont_pt(&mut self) -> Option<(HeapCellValue, usize, usize)> {
         self.cont_pts.pop()
     }
 
     fn run_cleaners(&self, machine_st: &mut MachineState) -> bool {
         if let Some(&(_, b_cutoff, prev_block)) = self.cont_pts.last() {
             if machine_st.b < b_cutoff {
-                let (idx, arity) = if machine_st.block < prev_block {
+                let (idx, arity) = if machine_st.block > prev_block {
                     (dir_entry!(self.r_c_w_h), 0)
                 } else {
-                    machine_st[temp_v!(1)] = Addr::Usize(b_cutoff);
+                    machine_st.registers[1] = fixnum_as_cell!(Fixnum::build_with(b_cutoff as i64));
                     (dir_entry!(self.r_c_wo_h), 1)
                 };
 
@@ -1503,8 +1460,10 @@ impl CutPolicy for SCCCutPolicy {
     fn cut(&mut self, machine_st: &mut MachineState, r: RegType) -> bool {
         let b = machine_st.b;
 
-        match machine_st[r] {
-            Addr::Usize(b0) | Addr::CutPoint(b0) => {
+        read_heap_cell!(machine_st[r],
+            (HeapCellValueTag::Fixnum, b0) => {
+                let b0 = b0.get_num() as usize;
+
                 if b > b0 {
                     machine_st.b = b0;
                 }
@@ -1513,8 +1472,9 @@ impl CutPolicy for SCCCutPolicy {
                 machine_st.fail = true;
                 return true;
             }
-        }
+        );
 
         self.run_cleaners(machine_st)
     }
 }
+
index c4929d2d5e1de17c2cb5585bc3be2b2ebe223658..8f4dc44dc35235f3879d612f1e049a4e74846a26 100644 (file)
@@ -1,12 +1,12 @@
-use prolog_parser::ast::*;
-use prolog_parser::tabled_rc::*;
-use prolog_parser::{clause_name, perm_v, temp_v};
+use crate::arena::*;
+use crate::atom_table::*;
+use crate::types::*;
 
 use crate::clause_types::*;
 use crate::forms::*;
 use crate::heap_iter::*;
-use crate::indexing::*;
 use crate::instructions::*;
+use crate::machine::arithmetic_ops::*;
 use crate::machine::attributed_variables::*;
 use crate::machine::code_repo::CodeRepo;
 use crate::machine::copier::*;
@@ -18,19 +18,24 @@ use crate::machine::partial_string::*;
 use crate::machine::stack::*;
 use crate::machine::streams::*;
 use crate::machine::INTERRUPT;
-use crate::rug::Integer;
+use crate::parser::ast::*;
+use crate::parser::rug::{Integer, Rational};
+
+use crate::try_numeric_result;
+
 use ordered_float::*;
 
-use indexmap::{IndexMap, IndexSet};
+use indexmap::IndexSet;
 
 use std::cmp::Ordering;
 use std::convert::TryFrom;
-use std::rc::Rc;
 
 impl MachineState {
     pub(crate) fn new() -> Self {
         MachineState {
-            atom_tbl: TabledData::new(Rc::new("".to_owned())),
+            arena: Arena::new(),
+            atom_tbl: AtomTable::new(),
+            pdl: Vec::with_capacity(1024),
             s: HeapPtr::default(),
             p: CodePtr::default(),
             b: 0,
@@ -40,10 +45,10 @@ impl MachineState {
             cp: LocalCodePtr::default(),
             attr_var_init: AttrVarInitializer::new(0),
             fail: false,
-            heap: Heap::new(),
+            heap: Heap::with_capacity(256 * 256),
             mode: MachineMode::Write,
             stack: Stack::new(),
-            registers: vec![Addr::HeapCell(0); MAX_ARITY + 1], // self.registers[0] is never used.
+            registers: [heap_loc_as_cell!(0); MAX_ARITY + 1], // self.registers[0] is never used.
             trail: vec![],
             tr: 0,
             hb: 0,
@@ -62,36 +67,25 @@ impl MachineState {
     }
 
     #[inline]
-    pub(crate) fn machine_flags(&self) -> MachineFlags {
-        self.flags
-    }
-
-    pub(crate) fn store(&self, addr: Addr) -> Addr {
-        match addr {
-            Addr::AttrVar(h) | Addr::HeapCell(h) => self.heap[h].as_addr(h),
-            Addr::StackCell(fr, sc) => self.stack.index_and_frame(fr)[sc],
-            Addr::PStrLocation(h, n) => {
-                if let &HeapCellValue::PartialString(ref pstr, has_tail) = &self.heap[h] {
-                    if !pstr.at_end(n) {
-                        Addr::PStrLocation(h, n)
-                    } else if has_tail {
-                        Addr::HeapCell(h + 1)
-                    } else {
-                        Addr::EmptyList
-                    }
-                } else {
-                    unreachable!()
-                }
+    pub(crate) fn store(&self, value: HeapCellValue) -> HeapCellValue {
+        read_heap_cell!(value,
+            (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
+                self.heap[h]
             }
-            addr => addr,
-        }
+            (HeapCellValueTag::StackVar, s) => {
+                self.stack[s]
+            }
+            _ => {
+                value
+            }
+        )
     }
 
-    pub(crate) fn deref(&self, mut addr: Addr) -> Addr {
+    pub fn deref(&self, mut addr: HeapCellValue) -> HeapCellValue {
         loop {
             let value = self.store(addr);
 
-            if value.is_ref() && value != addr {
+            if value.is_var() && value != addr {
                 addr = value;
                 continue;
             }
@@ -100,589 +94,1713 @@ impl MachineState {
         }
     }
 
-    fn bind_attr_var(&mut self, h: usize, addr: Addr) {
-        match addr.as_var() {
-            Some(Ref::HeapCell(hc)) => {
-                self.heap[hc] = HeapCellValue::Addr(Addr::AttrVar(h));
-                self.trail(TrailRef::Ref(Ref::HeapCell(hc)));
+    pub fn trail(&mut self, r: TrailRef) {
+        match r {
+            TrailRef::Ref(r) => {
+                let h = r.get_value() as usize;
+
+                match r.get_tag() {
+                    RefTag::HeapCell => {
+                        if h < self.hb {
+                            self.trail.push(TrailEntry::build_with(
+                                TrailEntryTag::TrailedHeapVar,
+                                h as u64,
+                            ));
+
+                            self.tr += 1;
+                        }
+                    }
+                    RefTag::StackCell => {
+                        if h < self.b {
+                            self.trail.push(TrailEntry::build_with(
+                                TrailEntryTag::TrailedStackVar,
+                                h as u64,
+                            ));
+
+                            self.tr += 1;
+                        }
+                    }
+                    RefTag::AttrVar => {
+                        if h < self.hb {
+                            self.trail.push(TrailEntry::build_with(
+                                TrailEntryTag::TrailedAttrVar,
+                                h as u64,
+                            ));
+
+                            self.tr += 1;
+                        }
+                    }
+                }
             }
-            Some(Ref::StackCell(fr, sc)) => {
-                self.stack.index_and_frame_mut(fr)[sc] = Addr::AttrVar(h);
-                self.trail(TrailRef::Ref(Ref::StackCell(fr, sc)));
+            TrailRef::AttrVarHeapLink(h) => {
+                if h < self.hb {
+                    self.trail.push(TrailEntry::build_with(
+                        TrailEntryTag::TrailedAttrVarHeapLink,
+                        h as u64,
+                    ));
+
+                    self.tr += 1;
+                }
             }
-            _ => {
-                self.push_attr_var_binding(h, addr);
-                self.heap[h] = HeapCellValue::Addr(addr);
-                self.trail(TrailRef::Ref(Ref::AttrVar(h)));
+            TrailRef::AttrVarListLink(h, l) => {
+                if h < self.hb {
+                    self.trail.push(TrailEntry::build_with(
+                        TrailEntryTag::TrailedAttrVarListLink,
+                        h as u64,
+                    ));
+
+                    self.trail.push(TrailEntry::from_bytes(
+                        list_loc_as_cell!(l).into_bytes()
+                    ));
+
+                    self.tr += 2;
+                }
+            }
+            TrailRef::BlackboardEntry(key_atom) => {
+                self.trail.push(TrailEntry::build_with(
+                    TrailEntryTag::TrailedBlackboardEntry,
+                    key_atom.index as u64,
+                ));
+
+                self.tr += 1;
+            }
+            TrailRef::BlackboardOffset(key_atom, value_cell) => {
+                self.trail.push(TrailEntry::build_with(
+                    TrailEntryTag::TrailedBlackboardOffset,
+                    key_atom.index as u64,
+                ));
+
+                self.trail.push(TrailEntry::from_bytes(
+                    value_cell.into_bytes(),
+                ));
+
+                self.tr += 2;
             }
         }
     }
 
-    pub(super) fn bind(&mut self, r1: Ref, a2: Addr) {
-        let t1 = self.store(r1.as_addr());
+    pub fn allocate(&mut self, num_cells: usize) {
+        let e = self.stack.allocate_and_frame(num_cells);
+        let and_frame = self.stack.index_and_frame_mut(e);
+
+        and_frame.prelude.e = self.e;
+        and_frame.prelude.cp = self.cp;
+
+        self.e = e;
+        self.p += 1;
+    }
+
+    pub fn bind(&mut self, r1: Ref, a2: HeapCellValue) {
+        let t1 = self.store(r1.as_heap_cell_value());
         let t2 = self.store(a2);
 
-        if t1.is_ref() && (!t2.is_ref() || a2 < r1) {
-            match r1 {
-                Ref::StackCell(fr, sc) => {
-                    self.stack.index_and_frame_mut(fr)[sc] = t2;
+        if t1.is_var() && (!t2.is_var() || a2 < r1) {
+            match r1.get_tag() {
+                RefTag::StackCell => {
+                    self.stack[r1.get_value() as usize] = t2;
                 }
-                Ref::HeapCell(h) => {
-                    self.heap[h] = HeapCellValue::Addr(t2);
-                }
-                Ref::AttrVar(h) => {
-                    return self.bind_attr_var(h, t2);
+                RefTag::HeapCell => {
+                    self.heap[r1.get_value() as usize] = t2;
                 }
+               RefTag::AttrVar => {
+                   self.bind_attr_var(r1.get_value() as usize, t2);
+               }
             };
 
-            self.trail(TrailRef::from(r1));
+            self.trail(TrailRef::Ref(r1));
         } else {
-            match a2.as_var() {
-                Some(Ref::StackCell(fr, sc)) => {
-                    self.stack.index_and_frame_mut(fr)[sc] = t1;
-                    self.trail(TrailRef::Ref(Ref::StackCell(fr, sc)));
+            read_heap_cell!(a2,
+                (HeapCellValueTag::StackVar, s) => {
+                    self.stack[s] = t1;
+                    self.trail(TrailRef::Ref(Ref::stack_cell(s)));
                 }
-                Some(Ref::HeapCell(h)) => {
-                    self.heap[h] = HeapCellValue::Addr(t1);
-                    self.trail(TrailRef::Ref(Ref::HeapCell(h)));
+                (HeapCellValueTag::Var, h) => {
+                    self.heap[h] = t1;
+                    self.trail(TrailRef::Ref(Ref::heap_cell(h)));
                 }
-                Some(Ref::AttrVar(h)) => {
+                (HeapCellValueTag::AttrVar, h) => {
                     self.bind_attr_var(h, t1);
                 }
-                None => {}
-            }
-        }
-    }
-
-    #[inline]
-    pub(super) fn bind_with_occurs_check_with_error_wrapper(&mut self, r: Ref, addr: Addr) {
-        if self.bind_with_occurs_check(r, addr) {
-            let err = self.representation_error(
-                RepFlag::Term,
-                clause_name!("unify_with_occurs_check"),
-                2,
+                _ => {
+                    unreachable!();
+                }
             );
-
-            self.throw_exception(err);
         }
     }
 
-    #[inline]
-    pub(super) fn bind_with_occurs_check_wrapper(&mut self, r: Ref, addr: Addr) {
-        self.bind_with_occurs_check(r, addr);
+    pub fn bind_attr_var(&mut self, h: usize, addr: HeapCellValue) {
+        read_heap_cell!(addr,
+            (HeapCellValueTag::Var, hc) => {
+                self.heap[hc] = attr_var_as_cell!(h);
+                self.trail(TrailRef::Ref(Ref::heap_cell(hc)));
+            }
+            (HeapCellValueTag::StackVar, hc) => {
+                self.stack[hc] = attr_var_as_cell!(h);
+                self.trail(TrailRef::Ref(Ref::stack_cell(hc)));
+            }
+            _ => {
+                self.push_attr_var_binding(h, addr);
+                self.heap[h] = addr;
+                self.trail(TrailRef::Ref(Ref::attr_var(h)));
+            }
+        )
     }
 
-    #[inline]
-    pub(super) fn bind_with_occurs_check(&mut self, r: Ref, addr: Addr) -> bool {
-        if let Ref::StackCell(..) = r {
-            // local variable optimization -- r cannot occur in the
-            // data structure bound to addr, so don't bother
-            // traversing it.
-            self.bind(r, addr);
-            return false;
-        }
+    fn unify_structure(&mut self, s1: usize, value: HeapCellValue) {
+        // s1 is the value of a STR cell.
+        let (n1, a1) = cell_as_atom_cell!(self.heap[s1]).get_name_and_arity();
 
-        let mut occurs_triggered = false;
+        read_heap_cell!(value,
+            (HeapCellValueTag::Str, s2) => {
+                let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
+                    .get_name_and_arity();
 
-        for addr in self.acyclic_pre_order_iter(addr) {
-            if let Some(inner_r) = addr.as_var() {
-                if r == inner_r {
-                    occurs_triggered = true;
-                    break;
+                if n1 == n2 && a1 == a2 {
+                    for idx in 0..a1 {
+                        self.pdl.push(heap_loc_as_cell!(s2+1+idx));
+                        self.pdl.push(heap_loc_as_cell!(s1+1+idx));
+                    }
+                } else {
+                    self.fail = true;
                 }
             }
-        }
-
-        self.fail = occurs_triggered;
-        self.bind(r, addr);
-
-        return occurs_triggered;
+            (HeapCellValueTag::Lis, l2) => {
+                if a1 == 2 && n1 == atom!(".") {
+                    for idx in 0..2 {
+                        self.pdl.push(heap_loc_as_cell!(l2+1+idx));
+                        self.pdl.push(heap_loc_as_cell!(s1+1+idx));
+                    }
+                } else {
+                    self.fail = true;
+                }
+            }
+            (HeapCellValueTag::Atom, (n2, a2)) => {
+                if !(a1 == 0 && a2 == 0 && n1 == n2) {
+                    self.fail = true;
+                }
+            }
+            (HeapCellValueTag::AttrVar, h) => {
+                self.bind(Ref::attr_var(h), str_loc_as_cell!(s1));
+            }
+            (HeapCellValueTag::Var, h) => {
+                self.bind(Ref::heap_cell(h), str_loc_as_cell!(s1));
+            }
+            (HeapCellValueTag::StackVar, s) => {
+                self.bind(Ref::stack_cell(s), str_loc_as_cell!(s1));
+            }
+            _ => {
+                self.fail = true;
+            }
+        )
     }
 
-    pub(super) fn unify_with_occurs_check_with_error(&mut self, a1: Addr, a2: Addr) {
-        let mut throw_error = false;
-        self.unify_with_occurs_check_loop(a1, a2, || throw_error = true);
+    fn unify_list(&mut self, l1: usize, d2: HeapCellValue) {
+        read_heap_cell!(d2,
+            (HeapCellValueTag::Lis, l2) => {
+                for idx in 0..2 {
+                    self.pdl.push(heap_loc_as_cell!(l2 + idx));
+                    self.pdl.push(heap_loc_as_cell!(l1 + idx));
+                }
+            }
+            (HeapCellValueTag::Str, s2) => {
+                let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
+                    .get_name_and_arity();
 
-        if throw_error {
-            let err = self.representation_error(
-                RepFlag::Term,
-                clause_name!("unify_with_occurs_check"),
-                2,
-            );
+                if a2 == 2 && n2 == atom!(".") {
+                    for idx in 0..2 {
+                        self.pdl.push(heap_loc_as_cell!(s2+1+idx));
+                        self.pdl.push(heap_loc_as_cell!(l1+idx));
+                    }
+                } else {
+                    self.fail = true;
+                }
+            }
+            (HeapCellValueTag::PStrLoc | HeapCellValueTag::CStr | HeapCellValueTag::PStr) => {
+                self.unify_partial_string(list_loc_as_cell!(l1), d2)
+            }
+            (HeapCellValueTag::AttrVar, h) => {
+                self.bind(Ref::attr_var(h), list_loc_as_cell!(l1));
+            }
+            (HeapCellValueTag::Var, h) => {
+                self.bind(Ref::heap_cell(h), list_loc_as_cell!(l1));
+            }
+            (HeapCellValueTag::StackVar, s) => {
+                self.bind(Ref::stack_cell(s), list_loc_as_cell!(l1));
+            }
+            _ => {
+                self.fail = true;
+            }
+        )
+    }
 
-            self.throw_exception(err);
+    pub fn unify_complete_string(&mut self, atom: Atom, value: HeapCellValue) {
+        if let Some(r) = value.as_var() {
+            self.bind(r, atom_as_cstr_cell!(atom));
+            return;
         }
-    }
 
-    pub(super) fn unify_with_occurs_check(&mut self, a1: Addr, a2: Addr) {
-        self.unify_with_occurs_check_loop(a1, a2, || {})
+        read_heap_cell!(value,
+            (HeapCellValueTag::CStr, cstr_atom) => {
+                self.fail = atom != cstr_atom;
+            }
+            _ => {
+                self.fail = true;
+            }
+        );
     }
 
-    pub(super) fn unify_with_occurs_check_loop(
-        &mut self,
-        a1: Addr,
-        a2: Addr,
-        mut occurs_trigger: impl FnMut(),
-    ) {
-        let mut pdl = vec![a1, a2];
-        let mut tabu_list: IndexSet<(Addr, Addr)> = IndexSet::new();
+    // d1's tag is LIS, STR or PSTRLOC.
+    pub fn unify_partial_string(&mut self, d1: HeapCellValue, d2: HeapCellValue) {
+        if let Some(r) = d2.as_var() {
+            self.bind(r, d1);
+            return;
+        }
 
-        self.fail = false;
+        let s1 = self.heap.len();
 
-        while !(pdl.is_empty() || self.fail) {
-            let d1 = self.deref(pdl.pop().unwrap());
-            let d2 = self.deref(pdl.pop().unwrap());
+        self.heap.push(d1);
+        self.heap.push(d2);
 
-            if d1 != d2 {
-                let d1 = self.store(d1);
-                let d2 = self.store(d2);
+        let mut pstr_iter1 = HeapPStrIter::new(&self.heap, s1);
+        let mut pstr_iter2 = HeapPStrIter::new(&self.heap, s1 + 1);
 
-                if tabu_list.contains(&(d1, d2)) {
-                    continue;
-                } else {
-                    tabu_list.insert((d1, d2));
+        match compare_pstr_prefixes(&mut pstr_iter1, &mut pstr_iter2) {
+            PStrCmpResult::Ordered(Ordering::Equal) => {}
+            PStrCmpResult::Ordered(Ordering::Less) => {
+                self.pdl.push(empty_list_as_cell!());
+                self.pdl.push(pstr_iter2.focus);
+            }
+            PStrCmpResult::Ordered(Ordering::Greater) => {
+                self.pdl.push(empty_list_as_cell!());
+                self.pdl.push(pstr_iter1.focus);
+            }
+            continuable @ PStrCmpResult::FirstIterContinuable(iteratee) |
+            continuable @ PStrCmpResult::SecondIterContinuable(iteratee) => {
+                if continuable.is_second_iter() {
+                    std::mem::swap(&mut pstr_iter1, &mut pstr_iter2);
                 }
 
-                match (d1, d2) {
-                    (Addr::AttrVar(h), addr) | (addr, Addr::AttrVar(h)) => {
-                        if self.bind_with_occurs_check(Ref::AttrVar(h), addr) {
-                            occurs_trigger();
-                        }
-                    }
-                    (Addr::HeapCell(h), addr) | (addr, Addr::HeapCell(h)) => {
-                        if self.bind_with_occurs_check(Ref::HeapCell(h), addr) {
-                            occurs_trigger();
-                        }
-                    }
-                    (Addr::StackCell(fr, sc), addr) | (addr, Addr::StackCell(fr, sc)) => {
-                        if self.bind_with_occurs_check(Ref::StackCell(fr, sc), addr) {
-                            occurs_trigger();
-                        }
-                    }
-                    (Addr::Lis(a1), Addr::Str(a2)) | (Addr::Str(a2), Addr::Lis(a1)) => {
-                        if let &HeapCellValue::NamedStr(n2, ref f2, _) = &self.heap[a2] {
-                            if f2.as_str() == "." && n2 == 2 {
-                                pdl.push(Addr::HeapCell(a1));
-                                pdl.push(Addr::HeapCell(a2 + 1));
+                let mut chars_iter = PStrCharsIter {
+                    iter: pstr_iter1,
+                    item: Some(iteratee),
+                };
 
-                                pdl.push(Addr::HeapCell(a1 + 1));
-                                pdl.push(Addr::HeapCell(a2 + 2));
+                let mut focus = pstr_iter2.focus;
 
-                                continue;
-                            }
-                        }
+                'outer: loop {
+                    while let Some(c) = chars_iter.peek() {
+                        read_heap_cell!(focus,
+                            (HeapCellValueTag::Lis, l) => {
+                                let val = pstr_iter2.heap[l];
 
-                        self.fail = true;
-                    }
-                    (Addr::PStrLocation(h, n), Addr::Lis(l))
-                    | (Addr::Lis(l), Addr::PStrLocation(h, n)) => {
-                        if let HeapCellValue::PartialString(ref pstr, _) = &self.heap[h] {
-                            if let Some(c) = pstr.range_from(n..).next() {
-                                pdl.push(Addr::PStrLocation(h, n + c.len_utf8()));
-                                pdl.push(Addr::HeapCell(l + 1));
+                                self.pdl.push(val);
+                                self.pdl.push(char_as_cell!(c));
 
-                                pdl.push(Addr::Char(c));
-                                pdl.push(Addr::HeapCell(l));
-                            } else {
-                                unreachable!()
+                                focus = pstr_iter2.heap[l+1];
                             }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    (Addr::PStrLocation(h1, n1), Addr::PStrLocation(h2, n2)) => {
-                        if let &HeapCellValue::PartialString(ref pstr1, has_tail_1) = &self.heap[h1]
-                        {
-                            if let &HeapCellValue::PartialString(ref pstr2, has_tail_2) =
-                                &self.heap[h2]
-                            {
-                                let pstr1_s = pstr1.as_str_from(n1);
-                                let pstr2_s = pstr2.as_str_from(n2);
-
-                                let m_len = if pstr1_s.starts_with(pstr2_s) {
-                                    pstr2_s.len()
-                                } else if pstr2_s.starts_with(pstr1_s) {
-                                    pstr1_s.len()
-                                } else {
-                                    self.fail = true;
-                                    return;
-                                };
+                            (HeapCellValueTag::Str, s) => {
+                                let (name, arity) = cell_as_atom_cell!(pstr_iter2.heap[s])
+                                    .get_name_and_arity();
 
-                                if pstr1.at_end(n1 + m_len) {
-                                    if has_tail_1 {
-                                        pdl.push(Addr::HeapCell(h1 + 1));
-                                    } else {
-                                        pdl.push(Addr::EmptyList);
-                                    }
+                                if name == atom!(".") && arity == 2 {
+                                    self.pdl.push(pstr_iter2.heap[s+1]);
+                                    self.pdl.push(char_as_cell!(c));
 
-                                    if pstr2.at_end(n2 + m_len) {
-                                        if has_tail_2 {
-                                            pdl.push(Addr::HeapCell(h2 + 1));
-                                        } else {
-                                            pdl.push(Addr::EmptyList);
-                                        }
-                                    } else {
-                                        pdl.push(Addr::PStrLocation(h2, n2 + m_len));
-                                    }
+                                    focus = pstr_iter2.heap[s+2];
                                 } else {
-                                    pdl.push(Addr::PStrLocation(h1, n1 + m_len));
-
-                                    if pstr2.at_end(n2 + m_len) {
-                                        if has_tail_2 {
-                                            pdl.push(Addr::HeapCell(h2 + 1));
-                                        } else {
-                                            pdl.push(Addr::EmptyList);
-                                        }
-                                    } else {
-                                        pdl.push(Addr::PStrLocation(h2, n2 + m_len));
-                                    }
+                                    self.fail = true;
+                                    break 'outer;
                                 }
-                            } else {
-                                unreachable!()
                             }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    (Addr::Lis(a1), Addr::Lis(a2)) => {
-                        pdl.push(Addr::HeapCell(a1));
-                        pdl.push(Addr::HeapCell(a2));
-
-                        pdl.push(Addr::HeapCell(a1 + 1));
-                        pdl.push(Addr::HeapCell(a2 + 1));
-                    }
-                    (Addr::Str(a1), Addr::Str(a2)) => {
-                        let r1 = &self.heap[a1];
-                        let r2 = &self.heap[a2];
-
-                        if let &HeapCellValue::NamedStr(n1, ref f1, _) = r1 {
-                            if let &HeapCellValue::NamedStr(n2, ref f2, _) = r2 {
-                                if n1 == n2 && *f1 == *f2 {
-                                    for i in 1..n1 + 1 {
-                                        pdl.push(Addr::HeapCell(a1 + i));
-                                        pdl.push(Addr::HeapCell(a2 + i));
+                            (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
+                                match chars_iter.item.unwrap() {
+                                    PStrIteratee::Char(focus, _) => {
+                                        self.pdl.push(self.heap[focus]);
+                                        self.pdl.push(heap_loc_as_cell!(h));
                                     }
+                                    PStrIteratee::PStrSegment(focus, _, n) => {
+                                        read_heap_cell!(self.heap[focus],
+                                            (HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => {
+                                                if focus < self.heap.len() - 2 {
+                                                    self.heap.pop();
+                                                    self.heap.pop();
+                                                }
+
+                                                if n == 0 {
+                                                    let target_cell = match self.heap[focus].get_tag() {
+                                                        HeapCellValueTag::CStr => {
+                                                            atom_as_cstr_cell!(pstr_atom)
+                                                        }
+                                                        HeapCellValueTag::PStr => {
+                                                            pstr_loc_as_cell!(focus)
+                                                        }
+                                                        _ => {
+                                                            unreachable!()
+                                                        }
+                                                    };
+
+                                                    self.pdl.push(target_cell);
+                                                    self.pdl.push(heap_loc_as_cell!(h));
+                                                } else {
+                                                    let h_len = self.heap.len();
+
+                                                    self.heap.push(pstr_offset_as_cell!(focus));
+                                                    self.heap.push(fixnum_as_cell!(
+                                                        Fixnum::build_with(n as i64)
+                                                    ));
+
+                                                    self.pdl.push(pstr_loc_as_cell!(h_len));
+                                                    self.pdl.push(heap_loc_as_cell!(h));
+                                                }
+
+                                                return;
+                                            }
+                                            (HeapCellValueTag::PStrOffset, pstr_loc) => {
+                                                let n0 = cell_as_fixnum!(self.heap[focus+1])
+                                                    .get_num() as usize;
+
+                                                if pstr_loc < self.heap.len() - 2 {
+                                                    self.heap.pop();
+                                                    self.heap.pop();
+                                                }
+
+                                                if n == n0 {
+                                                    self.pdl.push(pstr_loc_as_cell!(focus));
+                                                    self.pdl.push(heap_loc_as_cell!(h));
+                                                } else {
+                                                    let h_len = self.heap.len();
+
+                                                    self.heap.push(pstr_offset_as_cell!(pstr_loc));
+                                                    self.heap.push(fixnum_as_cell!(
+                                                        Fixnum::build_with(n as i64)
+                                                    ));
+
+                                                    self.pdl.push(pstr_loc_as_cell!(h_len));
+                                                    self.pdl.push(heap_loc_as_cell!(h));
+                                                }
+
+                                                return;
+                                            }
+                                            _ => {
+                                            }
+                                        );
 
-                                    continue;
-                                }
-                            }
-                        }
+                                        if focus < self.heap.len() - 2 {
+                                            self.heap.pop();
+                                            self.heap.pop();
+                                        }
 
-                        self.fail = true;
-                    }
-                    (Addr::Con(c1), Addr::Con(c2)) => match (&self.heap[c1], &self.heap[c2]) {
-                        (&HeapCellValue::Atom(ref n1, _), &HeapCellValue::Atom(ref n2, _))
-                            if n1.as_str() == n2.as_str() => {}
-                        (
-                            &HeapCellValue::DBRef(ref db_ref_1),
-                            &HeapCellValue::DBRef(ref db_ref_2),
-                        ) if db_ref_1 == db_ref_2 => {}
-                        (v1, v2) => {
-                            if let Ok(n1) = Number::try_from(v1) {
-                                if let Ok(n2) = Number::try_from(v2) {
-                                    if n1 == n2 {
-                                        continue;
+                                        self.pdl.push(self.heap[focus]);
+                                        self.pdl.push(heap_loc_as_cell!(h));
+
+                                        return;
                                     }
                                 }
-                            }
 
-                            self.fail = true;
-                        }
-                    },
-                    (Addr::Con(h), Addr::Char(c)) | (Addr::Char(c), Addr::Con(h)) => {
-                        match &self.heap[h] {
-                            &HeapCellValue::Atom(ref name, _) if name.is_char() => {
-                                if name.as_str().chars().next() != Some(c) {
-                                    self.fail = true;
-                                    return;
-                                }
+                                break 'outer;
                             }
                             _ => {
                                 self.fail = true;
-                                return;
-                            }
-                        }
-                    }
-                    (Addr::Stream(s1), Addr::Stream(s2)) => {
-                        if s1 != s2 {
-                            self.fail = true;
-                        }
-                    }
-                    (v, Addr::Con(h)) | (Addr::Con(h), v) => {
-                        if let Ok(n1) = Number::try_from(&self.heap[h]) {
-                            if let Ok(v) = Number::try_from(&HeapCellValue::Addr(v)) {
-                                if n1 == v {
-                                    continue;
-                                }
-                            }
-                        }
-
-                        self.fail = true;
-                    }
-                    (a1, a2) => {
-                        if let Ok(n1) = Number::try_from(&HeapCellValue::Addr(a1)) {
-                            if let Ok(n2) = Number::try_from(&HeapCellValue::Addr(a2)) {
-                                if n1 == n2 {
-                                    continue;
-                                }
+                                break 'outer;
                             }
-                        }
+                        );
 
-                        if a1 != a2 {
-                            self.fail = true;
-                        }
+                        chars_iter.next();
                     }
-                };
-            }
-        }
-    }
-
-    pub(super) fn unify(&mut self, a1: Addr, a2: Addr) {
-        let mut pdl = vec![a1, a2];
 
-        let mut tabu_list: IndexSet<(Addr, Addr)> = IndexSet::new();
+                    chars_iter.iter.next();
 
-        self.fail = false;
+                    self.pdl.push(chars_iter.iter.focus);
+                    self.pdl.push(focus);
 
-        while !(pdl.is_empty() || self.fail) {
-            let d1 = self.deref(pdl.pop().unwrap());
-            let d2 = self.deref(pdl.pop().unwrap());
+                    break;
+                }
+            }
+            PStrCmpResult::Unordered => {
+                self.pdl.push(pstr_iter1.focus);
+                self.pdl.push(pstr_iter2.focus);
+            }
+        }
 
-            if d1 != d2 {
-                let d1 = self.store(d1);
-                let d2 = self.store(d2);
+        self.heap.pop();
+        self.heap.pop();
+    }
 
-                if tabu_list.contains(&(d1, d2)) {
-                    continue;
+    pub fn unify_atom(&mut self, atom: Atom, value: HeapCellValue) {
+        read_heap_cell!(value,
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                self.fail = !(arity == 0 && name == atom);
+            }
+            (HeapCellValueTag::Char, c1) => {
+                if let Some(c2) = atom.as_char() {
+                    self.fail = c1 != c2;
                 } else {
-                    tabu_list.insert((d1, d2));
+                    self.fail = true;
                 }
+            }
+            (HeapCellValueTag::AttrVar, h) => {
+                self.bind(Ref::attr_var(h), atom_as_cell!(atom));
+            }
+            (HeapCellValueTag::Var, h) => {
+                self.bind(Ref::heap_cell(h), atom_as_cell!(atom));
+            }
+            (HeapCellValueTag::StackVar, s) => {
+                self.bind(Ref::stack_cell(s), atom_as_cell!(atom));
+            }
+            _ => {
+                self.fail = true;
+            }
+        );
+    }
 
-                match (d1, d2) {
-                    (Addr::AttrVar(h), addr) | (addr, Addr::AttrVar(h)) => {
-                        self.bind(Ref::AttrVar(h), addr);
-                    }
-                    (Addr::HeapCell(h), addr) | (addr, Addr::HeapCell(h)) => {
-                        self.bind(Ref::HeapCell(h), addr);
-                    }
-                    (Addr::StackCell(fr, sc), addr) | (addr, Addr::StackCell(fr, sc)) => {
-                        self.bind(Ref::StackCell(fr, sc), addr);
-                    }
-                    (Addr::Lis(a1), Addr::Str(a2)) | (Addr::Str(a2), Addr::Lis(a1)) => {
-                        if let &HeapCellValue::NamedStr(n2, ref f2, _) = &self.heap[a2] {
-                            if f2.as_str() == "." && n2 == 2 {
-                                pdl.push(Addr::HeapCell(a1));
-                                pdl.push(Addr::HeapCell(a2 + 1));
-
-                                pdl.push(Addr::HeapCell(a1 + 1));
-                                pdl.push(Addr::HeapCell(a2 + 2));
+    pub fn unify_char(&mut self, c: char, value: HeapCellValue) {
+        read_heap_cell!(value,
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                if let Some(c2) = name.as_char() {
+                    self.fail = !(c == c2 && arity == 0);
+                } else {
+                    self.fail = true;
+                }
+            }
+            (HeapCellValueTag::Char, c2) => {
+                if c != c2 {
+                    self.fail = true;
+                }
+            }
+            (HeapCellValueTag::AttrVar, h) => {
+                self.bind(Ref::attr_var(h), char_as_cell!(c));
+            }
+            (HeapCellValueTag::Var, h) => {
+                self.bind(Ref::heap_cell(h), char_as_cell!(c));
+            }
+            (HeapCellValueTag::StackVar, s) => {
+                self.bind(Ref::stack_cell(s), char_as_cell!(c));
+            }
+            _ => {
+                self.fail = true;
+            }
+        );
+    }
 
-                                continue;
-                            }
-                        }
+    pub fn unify_fixnum(&mut self, n1: Fixnum, value: HeapCellValue) {
+        if let Some(r) = value.as_var() {
+            self.bind(r, fixnum_as_cell!(n1));
+            return;
+        }
 
-                        self.fail = true;
+        match Number::try_from(value) {
+            Ok(n2) => match n2 {
+                Number::Fixnum(n2) if n1.get_num() == n2.get_num() => {}
+                Number::Integer(n2) if n1.get_num() == *n2 => {}
+                Number::Rational(n2) if n1.get_num() == *n2 => {}
+                _ => {
+                    self.fail = true;
+                }
+            },
+            Err(_) => {
+                self.fail = true;
+            }
+        }
+    }
+
+    pub fn unify_big_int(&mut self, n1: TypedArenaPtr<Integer>, value: HeapCellValue) {
+        if let Some(r) = value.as_var() {
+            self.bind(r, typed_arena_ptr_as_cell!(n1));
+            return;
+        }
+
+        match Number::try_from(value) {
+            Ok(n2) => match n2 {
+                Number::Fixnum(n2) if *n1 == n2.get_num() => {}
+                Number::Integer(n2) if *n1 == *n2 => {}
+                Number::Rational(n2) if *n1 == *n2 => {}
+                _ => {
+                    self.fail = true;
+                }
+            },
+            Err(_) => {
+                self.fail = true;
+            }
+        }
+    }
+
+    pub fn unify_rational(&mut self, n1: TypedArenaPtr<Rational>, value: HeapCellValue) {
+        if let Some(r) = value.as_var() {
+            self.bind(r, typed_arena_ptr_as_cell!(n1));
+            return;
+        }
+
+        match Number::try_from(value) {
+            Ok(n2) => match n2 {
+                Number::Fixnum(n2) if *n1 == n2.get_num() => {}
+                Number::Integer(n2) if *n1 == *n2 => {}
+                Number::Rational(n2) if *n1 == *n2 => {}
+                _ => {
+                    self.fail = true;
+                }
+            },
+            Err(_) => {
+                self.fail = true;
+            }
+        }
+    }
+
+    pub fn unify_f64(&mut self, f1: F64Ptr, value: HeapCellValue) {
+        if let Some(r) = value.as_var() {
+            self.bind(r, typed_arena_ptr_as_cell!(f1));
+            return;
+        }
+
+        read_heap_cell!(value,
+            (HeapCellValueTag::F64, f2) => {
+                if *f1 != *f2 {
+                    self.fail = true;
+                }
+            }
+            _ => {
+                self.fail = true;
+            }
+        );
+    }
+
+    pub fn unify_constant(&mut self, ptr: UntypedArenaPtr, value: HeapCellValue) {
+        if let Some(ptr2) = value.to_untyped_arena_ptr() {
+            if ptr.get_ptr() == ptr2.get_ptr() {
+                return;
+            }
+        }
+
+        match_untyped_arena_ptr!(ptr,
+             (ArenaHeaderTag::Integer, int_ptr) => {
+                 self.unify_big_int(int_ptr, value);
+             }
+             (ArenaHeaderTag::Rational, rat_ptr) => {
+                 self.unify_rational(rat_ptr, value);
+             }
+             _ => {
+                 if let Some(r) = value.as_var() {
+                     self.bind(r, untyped_arena_ptr_as_cell!(ptr));
+                 } else {
+                     self.fail = true;
+                 }
+             }
+        );
+    }
+
+    pub fn unify(&mut self) {
+        let mut tabu_list: IndexSet<(usize, usize)> = IndexSet::new();
+        // self.fail = false;
+
+        while !(self.pdl.is_empty() || self.fail) {
+            let s1 = self.pdl.pop().unwrap();
+            let s1 = self.deref(s1);
+
+            let s2 = self.pdl.pop().unwrap();
+            let s2 = self.deref(s2);
+
+            if s1 != s2 {
+                let d1 = self.store(s1);
+                let d2 = self.store(s2);
+
+                read_heap_cell!(d1,
+                    (HeapCellValueTag::AttrVar, h) => {
+                        self.bind(Ref::attr_var(h), d2);
+                    }
+                    (HeapCellValueTag::Var, h) => {
+                        self.bind(Ref::heap_cell(h), d2);
+                    }
+                    (HeapCellValueTag::StackVar, s) => {
+                        self.bind(Ref::stack_cell(s), d2);
+                    }
+                    (HeapCellValueTag::Atom, (name, arity)) => {
+                        debug_assert!(arity == 0);
+                        self.unify_atom(name, d2);
+                    }
+                    (HeapCellValueTag::Str, s1) => {
+                        if d2.is_constant() {
+                            self.fail = true;
+                            break;
+                        }
+
+                        let s2 = s2.get_value() as usize;
+
+                        if tabu_list.contains(&(s1, s2)) {
+                            continue;
+                        }
+
+                        self.unify_structure(s1, d2);
+
+                        if !self.fail {
+                            tabu_list.insert((s1, s2));
+                        }
+                    }
+                    (HeapCellValueTag::Lis, l1) => {
+                        if d2.is_ref() {
+                            let l2 = s2.get_value();
+
+                            if tabu_list.contains(&(l1, l2)) {
+                                continue;
+                            }
+
+                            tabu_list.insert((l1, l2));
+                        }
+
+                        self.unify_list(l1, d2);
+                    }
+                    (HeapCellValueTag::PStrLoc, pstr1_loc) => {
+                        read_heap_cell!(d2,
+                            (HeapCellValueTag::PStrLoc |
+                             HeapCellValueTag::Lis |
+                             HeapCellValueTag::Str,
+                             pstr2_loc) => {
+                                if tabu_list.contains(&(pstr1_loc, pstr2_loc)) {
+                                    continue;
+                                }
+                            }
+                            (HeapCellValueTag::CStr |
+                             HeapCellValueTag::AttrVar |
+                             HeapCellValueTag::Var |
+                             HeapCellValueTag::StackVar) => {
+                            }
+                            _ => {
+                                self.fail = true;
+                                break;
+                            }
+                        );
+
+                        self.unify_partial_string(d1, d2);
+
+                        if !self.fail && !d2.is_constant() {
+                            tabu_list.insert((pstr1_loc, d2.get_value()));
+                        }
+                    }
+                    (HeapCellValueTag::CStr) => {
+                        read_heap_cell!(d2,
+                            (HeapCellValueTag::AttrVar, h) => {
+                                self.bind(Ref::attr_var(h), d1);
+                                continue;
+                            }
+                            (HeapCellValueTag::Var, h) => {
+                                self.bind(Ref::heap_cell(h), d1);
+                                continue;
+                            }
+                            (HeapCellValueTag::StackVar, s) => {
+                                self.bind(Ref::stack_cell(s), d1);
+                                continue;
+                            }
+                            (HeapCellValueTag::Str |
+                             HeapCellValueTag::Lis |
+                             HeapCellValueTag::PStrLoc) => {
+                            }
+                            (HeapCellValueTag::CStr) => {
+                                self.fail = d1 != d2;
+                                continue;
+                            }
+                            _ => {
+                                self.fail = true;
+                                return;
+                            }
+                        );
+
+                        self.unify_partial_string(d2, d1);
+                    }
+                    (HeapCellValueTag::F64, f1) => {
+                        self.unify_f64(f1, d2);
+                    }
+                    (HeapCellValueTag::Fixnum, n1) => {
+                        self.unify_fixnum(n1, d2);
+                    }
+                    (HeapCellValueTag::Char, c1) => {
+                        self.unify_char(c1, d2);
+                    }
+                    (HeapCellValueTag::Cons, ptr_1) => {
+                        self.unify_constant(ptr_1, d2);
+                    }
+                    _ => {
+                        unreachable!();
+                    }
+                );
+            }
+        }
+    }
+
+    pub(super) fn set_ball(&mut self) {
+        self.ball.reset();
+
+        let addr = self.registers[1];
+        self.ball.boundary = self.heap.len();
+
+        copy_term(
+            CopyBallTerm::new(&mut self.stack, &mut self.heap, &mut self.ball.stub),
+            addr,
+            AttrVarPolicy::DeepCopy,
+        );
+    }
+
+    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);
+    }
+
+    pub(super) fn unwind_stack(&mut self) {
+        self.b = self.block;
+        self.fail = true;
+    }
+
+    #[inline]
+    pub fn bind_with_occurs_check(&mut self, r: Ref, value: HeapCellValue) -> bool {
+        if let RefTag::StackCell = r.get_tag() {
+            // local variable optimization -- r cannot occur in the
+            // heap structure bound to value, so don't bother
+            // traversing value.
+            self.bind(r, value);
+            return false;
+        }
+
+        let mut occurs_triggered = false;
+
+        if !value.is_constant() {
+            for addr in stackful_preorder_iter(&mut self.heap, value) {
+                if let Some(inner_r) = addr.as_var() {
+                    if r == inner_r {
+                        occurs_triggered = true;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if occurs_triggered {
+            self.fail = true;
+        } else {
+            self.bind(r, value);
+        }
+
+        return occurs_triggered;
+    }
+
+    #[inline]
+    pub(super) fn bind_with_occurs_check_wrapper(&mut self, r: Ref, value: HeapCellValue) {
+        self.bind_with_occurs_check(r, value);
+    }
+
+    #[inline]
+    pub(super) fn bind_with_occurs_check_with_error_wrapper(
+        &mut self,
+        r: Ref,
+        value: HeapCellValue,
+    ) {
+        if self.bind_with_occurs_check(r, value) {
+            let err = self.representation_error(RepFlag::Term);
+            let stub = functor_stub(atom!("unify_with_occurs_check"), 2);
+            let err = self.error_form(err, stub);
+
+            self.throw_exception(err);
+        }
+    }
+
+    pub(super) fn unify_with_occurs_check_with_error(&mut self) {
+        let mut throw_error = false;
+        self.unify_with_occurs_check_loop(|| throw_error = true);
+
+        if throw_error {
+            let err = self.representation_error(RepFlag::Term);
+            let stub = functor_stub(atom!("unify_with_occurs_check"), 2);
+            let err = self.error_form(err, stub);
+
+            self.throw_exception(err);
+        }
+    }
+
+    pub(super) fn unify_with_occurs_check(&mut self) {
+        self.unify_with_occurs_check_loop(|| {})
+    }
+
+    fn unify_structure_with_occurs_check(
+        &mut self,
+        s1: usize,
+        value: HeapCellValue,
+        mut occurs_trigger: impl FnMut(),
+    ) {
+        // s1 is the value of a STR cell.
+        let (n1, a1) = cell_as_atom_cell!(self.heap[s1]).get_name_and_arity();
+
+        read_heap_cell!(value,
+            (HeapCellValueTag::Str, s2) => {
+                let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
+                    .get_name_and_arity();
+
+                if n1 == n2 && a1 == a2 {
+                    for idx in 0..a1 {
+                        self.pdl.push(heap_loc_as_cell!(s2+1+idx));
+                        self.pdl.push(heap_loc_as_cell!(s1+1+idx));
+                    }
+                } else {
+                    self.fail = true;
+                }
+            }
+            (HeapCellValueTag::Lis, l2) => {
+                if a1 == 2 && n1 == atom!(".") {
+                    for idx in 0..2 {
+                        self.pdl.push(heap_loc_as_cell!(l2+idx));
+                        self.pdl.push(heap_loc_as_cell!(s1+1+idx));
+                    }
+                } else {
+                    self.fail = true;
+                }
+            }
+            (HeapCellValueTag::Atom, (n2, a2)) => {
+                if !(a1 == 0 && a2 == 0 && n1 == n2) {
+                    self.fail = true;
+                }
+            }
+            (HeapCellValueTag::AttrVar, h) => {
+                if self.bind_with_occurs_check(Ref::attr_var(h), str_loc_as_cell!(s1)) {
+                    occurs_trigger();
+                }
+            }
+            (HeapCellValueTag::Var, h) => {
+                if self.bind_with_occurs_check(Ref::heap_cell(h), str_loc_as_cell!(s1)) {
+                    occurs_trigger();
+                }
+            }
+            (HeapCellValueTag::StackVar, s) => {
+                if self.bind_with_occurs_check(Ref::stack_cell(s), str_loc_as_cell!(s1)) {
+                    occurs_trigger();
+                }
+            }
+            _ => {
+                self.fail = true;
+            }
+        )
+    }
+
+    // the return value of unify_partial_string_with_occurs_check is
+    // interpreted as follows:
+    //
+    // Some(None) -- the strings are equal, nothing to unify
+    // Some(Some(f2,f1)) -- prefixes equal, try to unify focus values f2, f1
+    // None -- prefixes not equal, unification fails
+    //
+    // d1's tag is assumed to be one of LIS, STR or PSTRLOC.
+    pub fn unify_partial_string_with_occurs_check(
+        &mut self,
+        d1: HeapCellValue,
+        d2: HeapCellValue,
+        mut occurs_trigger: impl FnMut(),
+    ) {
+        if let Some(r) = d2.as_var() {
+            if self.bind_with_occurs_check(r, d1) {
+                occurs_trigger();
+            }
+
+            return;
+        }
+
+        let s1 = self.heap.len();
+
+        self.heap.push(d1);
+        self.heap.push(d2);
+
+        let mut pstr_iter1 = HeapPStrIter::new(&self.heap, s1);
+        let mut pstr_iter2 = HeapPStrIter::new(&self.heap, s1 + 1);
+
+        match compare_pstr_prefixes(&mut pstr_iter1, &mut pstr_iter2) {
+            PStrCmpResult::Ordered(Ordering::Equal) => {}
+            PStrCmpResult::Ordered(Ordering::Less) => {
+                self.pdl.push(empty_list_as_cell!());
+                self.pdl.push(pstr_iter2.focus);
+            }
+            PStrCmpResult::Ordered(Ordering::Greater) => {
+                self.pdl.push(empty_list_as_cell!());
+                self.pdl.push(pstr_iter1.focus);
+            }
+            continuable @ PStrCmpResult::FirstIterContinuable(iteratee) |
+            continuable @ PStrCmpResult::SecondIterContinuable(iteratee) => {
+                if continuable.is_second_iter() {
+                    std::mem::swap(&mut pstr_iter1, &mut pstr_iter2);
+                }
+
+                let mut chars_iter = PStrCharsIter {
+                    iter: pstr_iter1,
+                    item: Some(iteratee),
+                };
+
+                let mut focus = pstr_iter2.focus;
+
+                'outer: loop {
+                    while let Some(c) = chars_iter.peek() {
+                        read_heap_cell!(focus,
+                            (HeapCellValueTag::Lis, l) => {
+                                let val = pstr_iter2.heap[l];
+
+                                self.pdl.push(val);
+                                self.pdl.push(char_as_cell!(c));
+
+                                focus = pstr_iter2.heap[l+1];
+                            }
+                            (HeapCellValueTag::Str, s) => {
+                                let (name, arity) = cell_as_atom_cell!(pstr_iter2.heap[s])
+                                    .get_name_and_arity();
+
+                                if name == atom!(".") && arity == 2 {
+                                    self.pdl.push(pstr_iter2.heap[s+1]);
+                                    self.pdl.push(char_as_cell!(c));
+
+                                    focus = pstr_iter2.heap[s+2];
+                                } else {
+                                    self.fail = true;
+                                    break 'outer;
+                                }
+                            }
+                            (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
+                                match chars_iter.item.unwrap() {
+                                    PStrIteratee::Char(focus, _) => {
+                                        self.pdl.push(self.heap[focus]);
+                                        self.pdl.push(heap_loc_as_cell!(h));
+                                    }
+                                    PStrIteratee::PStrSegment(focus, _, n) => {
+                                        read_heap_cell!(self.heap[focus],
+                                            (HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => {
+                                                if focus < self.heap.len() - 2 {
+                                                    self.heap.pop();
+                                                    self.heap.pop();
+                                                }
+
+                                                if n == 0 {
+                                                    let target_cell = match self.heap[focus].get_tag() {
+                                                        HeapCellValueTag::CStr => {
+                                                            atom_as_cstr_cell!(pstr_atom)
+                                                        }
+                                                        HeapCellValueTag::PStr => {
+                                                            pstr_loc_as_cell!(focus)
+                                                        }
+                                                        _ => {
+                                                            unreachable!()
+                                                        }
+                                                    };
+
+                                                    self.pdl.push(target_cell);
+                                                    self.pdl.push(heap_loc_as_cell!(h));
+                                                } else {
+                                                    let h_len = self.heap.len();
+
+                                                    self.heap.push(pstr_offset_as_cell!(focus));
+                                                    self.heap.push(fixnum_as_cell!(
+                                                        Fixnum::build_with(n as i64)
+                                                    ));
+
+                                                    self.pdl.push(pstr_loc_as_cell!(h_len));
+                                                    self.pdl.push(heap_loc_as_cell!(h));
+                                                }
+
+                                                return;
+                                            }
+                                            (HeapCellValueTag::PStrOffset, pstr_loc) => {
+                                                let n0 = cell_as_fixnum!(self.heap[focus+1])
+                                                    .get_num() as usize;
+
+                                                if pstr_loc < self.heap.len() - 2 {
+                                                    self.heap.pop();
+                                                    self.heap.pop();
+                                                }
+
+                                                if n == n0 {
+                                                    self.pdl.push(pstr_loc_as_cell!(focus));
+                                                    self.pdl.push(heap_loc_as_cell!(h));
+                                                } else {
+                                                    let h_len = self.heap.len();
+
+                                                    self.heap.push(pstr_offset_as_cell!(pstr_loc));
+                                                    self.heap.push(fixnum_as_cell!(
+                                                        Fixnum::build_with(n as i64)
+                                                    ));
+
+                                                    self.pdl.push(pstr_loc_as_cell!(h_len));
+                                                    self.pdl.push(heap_loc_as_cell!(h));
+                                                }
+
+                                                return;
+                                            }
+                                            _ => {
+                                            }
+                                        );
+
+                                        if focus < self.heap.len() - 2 {
+                                            self.heap.pop();
+                                            self.heap.pop();
+                                        }
+
+                                        self.pdl.push(self.heap[focus]);
+                                        self.pdl.push(heap_loc_as_cell!(h));
+
+                                        return;
+                                    }
+                                }
+
+                                break 'outer;
+                            }
+                            _ => {
+                                self.fail = true;
+                                break 'outer;
+                            }
+                        );
+
+                        chars_iter.next();
+                    }
+
+                    chars_iter.iter.next();
+
+                    self.pdl.push(chars_iter.iter.focus);
+                    self.pdl.push(focus);
+
+                    break;
+                }
+            }
+            PStrCmpResult::Unordered => {
+                self.pdl.push(pstr_iter1.focus);
+                self.pdl.push(pstr_iter2.focus);
+            }
+        }
+
+        self.heap.pop();
+        self.heap.pop();
+    }
+
+    fn unify_list_with_occurs_trigger(
+        &mut self,
+        l1: usize,
+        d2: HeapCellValue,
+        mut occurs_trigger: impl FnMut(),
+    ) {
+        read_heap_cell!(d2,
+            (HeapCellValueTag::Lis, l2) => {
+                for idx in 0..2 {
+                    self.pdl.push(heap_loc_as_cell!(l2+idx));
+                    self.pdl.push(heap_loc_as_cell!(l1+idx));
+                }
+            }
+            (HeapCellValueTag::Str, s2) => {
+                let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
+                    .get_name_and_arity();
+
+                if a2 == 2 && n2 == atom!(".") {
+                    for idx in 0..2 {
+                        self.pdl.push(heap_loc_as_cell!(s2+1+idx));
+                        self.pdl.push(heap_loc_as_cell!(l1+idx));
                     }
-                    (Addr::PStrLocation(h, n), Addr::Lis(l))
-                    | (Addr::Lis(l), Addr::PStrLocation(h, n)) => {
-                        if let HeapCellValue::PartialString(ref pstr, _) = &self.heap[h] {
-                            if let Some(c) = pstr.range_from(n..).next() {
-                                pdl.push(Addr::PStrLocation(h, n + c.len_utf8()));
-                                pdl.push(Addr::HeapCell(l + 1));
+                } else {
+                    self.fail = true;
+                }
+            }
+            (HeapCellValueTag::PStrLoc | HeapCellValueTag::CStr | HeapCellValueTag::PStr) => {
+                self.unify_partial_string_with_occurs_check(
+                    list_loc_as_cell!(l1),
+                    d2,
+                    &mut occurs_trigger,
+                )
+            }
+            (HeapCellValueTag::AttrVar, h) => {
+                if self.bind_with_occurs_check(Ref::attr_var(h), list_loc_as_cell!(l1)) {
+                    occurs_trigger();
+                }
+            }
+            (HeapCellValueTag::Var, h) => {
+                if self.bind_with_occurs_check(Ref::heap_cell(h), list_loc_as_cell!(l1)) {
+                    occurs_trigger();
+                }
+            }
+            (HeapCellValueTag::StackVar, s) => {
+                if self.bind_with_occurs_check(Ref::stack_cell(s), list_loc_as_cell!(l1)) {
+                    occurs_trigger();
+                }
+            }
+            _ => {
+                self.fail = true;
+            }
+        )
+    }
 
-                                pdl.push(Addr::Char(c));
-                                pdl.push(Addr::HeapCell(l));
-                            } else {
-                                unreachable!()
+    pub(super) fn unify_with_occurs_check_loop(&mut self, mut occurs_trigger: impl FnMut()) {
+        let mut tabu_list: IndexSet<(usize, usize)> = IndexSet::new();
+
+        // self.fail = false;
+
+        while !(self.pdl.is_empty() || self.fail) {
+            let s1 = self.pdl.pop().unwrap();
+            let s1 = self.deref(s1);
+
+            let s2 = self.pdl.pop().unwrap();
+            let s2 = self.deref(s2);
+
+            if s1 != s2 {
+                let d1 = self.store(s1);
+                let d2 = self.store(s2);
+
+                read_heap_cell!(d1,
+                    (HeapCellValueTag::AttrVar, h) => {
+                        if self.bind_with_occurs_check(Ref::attr_var(h), d2) {
+                            occurs_trigger();
+                        }
+                    }
+                    (HeapCellValueTag::Var, h) => {
+                        if self.bind_with_occurs_check(Ref::heap_cell(h), d2) {
+                            occurs_trigger();
+                        }
+                    }
+                    (HeapCellValueTag::StackVar, s) => {
+                        if self.bind_with_occurs_check(Ref::stack_cell(s), d2) {
+                            occurs_trigger();
+                        }
+                    }
+                    (HeapCellValueTag::Atom, (name, arity)) => {
+                        debug_assert!(arity == 0);
+                        self.unify_atom(name, d2);
+                    }
+                    (HeapCellValueTag::Str, s1) => {
+                        if d2.is_constant() {
+                            self.fail = true;
+                            break;
+                        }
+
+                        let s2 = s2.get_value() as usize;
+
+                        if tabu_list.contains(&(s1, s2)) {
+                            continue;
+                        }
+
+                        self.unify_structure_with_occurs_check(s1, d2, &mut occurs_trigger);
+
+                        if !self.fail {
+                            tabu_list.insert((s1, s2));
+                        }
+                    }
+                    (HeapCellValueTag::Lis, l1) => {
+                        if d2.is_ref() {
+                            let l2 = s2.get_value() as usize;
+
+                            if tabu_list.contains(&(l1, l2)) {
+                                continue;
+                            }
+
+                            tabu_list.insert((l1, l2));
+                        }
+
+                        self.unify_list_with_occurs_trigger(l1, d2, &mut occurs_trigger);
+                    }
+                    (HeapCellValueTag::PStrLoc, pstr1_loc) => {
+                        read_heap_cell!(d2,
+                            (HeapCellValueTag::PStrLoc |
+                             HeapCellValueTag::Lis |
+                             HeapCellValueTag::Str,
+                             pstr2_loc) => {
+                                if tabu_list.contains(&(pstr1_loc, pstr2_loc)) {
+                                    continue;
+                                }
+                            }
+                            (HeapCellValueTag::CStr |
+                             HeapCellValueTag::AttrVar |
+                             HeapCellValueTag::Var |
+                             HeapCellValueTag::StackVar) => {
+                            }
+                            _ => {
+                                self.fail = true;
+                                break;
+                            }
+                        );
+
+                        self.unify_partial_string_with_occurs_check(
+                            d1,
+                            d2,
+                            &mut occurs_trigger,
+                        );
+
+                        if !self.fail && !d2.is_constant() {
+                            tabu_list.insert((pstr1_loc, d2.get_value()));
+                        }
+                    }
+                    (HeapCellValueTag::CStr) => {
+                        read_heap_cell!(d2,
+                            (HeapCellValueTag::AttrVar, h) => {
+                                self.bind(Ref::attr_var(h), d1);
+                                continue;
+                            }
+                            (HeapCellValueTag::Var, h) => {
+                                self.bind(Ref::heap_cell(h), d1);
+                                continue;
+                            }
+                            (HeapCellValueTag::StackVar, s) => {
+                                self.bind(Ref::stack_cell(s), d1);
+                                continue;
+                            }
+                            (HeapCellValueTag::Str |
+                             HeapCellValueTag::Lis |
+                             HeapCellValueTag::PStrLoc) => {
+                            }
+                            _ => {
+                                self.fail = true;
+                                return;
                             }
+                        );
+
+                        self.unify_partial_string(d2, d1);
+                    }
+                    (HeapCellValueTag::F64, f1) => {
+                        self.unify_f64(f1, d2);
+                    }
+                    (HeapCellValueTag::Fixnum, n1) => {
+                        self.unify_fixnum(n1, d2);
+                    }
+                    (HeapCellValueTag::Char, c1) => {
+                        self.unify_char(c1, d2);
+                    }
+                    (HeapCellValueTag::Cons, ptr_1) => {
+                        self.unify_constant(ptr_1, d2);
+                    }
+                    _ => {
+                        unreachable!();
+                    }
+                );
+            }
+        }
+    }
+
+    fn read_s(&mut self) -> HeapCellValue {
+        match &self.s {
+            &HeapPtr::HeapCell(h) => self.deref(self.heap[h]),
+            &HeapPtr::PStrChar(h, n) => {
+                read_heap_cell!(self.heap[h],
+                    (HeapCellValueTag::PStr, pstr_atom) => {
+                        let pstr = PartialString::from(pstr_atom);
+
+                        if let Some(c) = pstr.as_str_from(n).chars().next() {
+                            char_as_cell!(c)
+                        } else { // if has_tail {
+                            self.deref(self.heap[h+1]) // heap_loc_as_cell!(h+1)
+                        }
+                        // } else {
+                        //     empty_list_as_cell!()
+                        // }
+                    }
+                    (HeapCellValueTag::CStr, cstr_atom) => {
+                        let pstr = PartialString::from(cstr_atom);
+
+                        if let Some(c) = pstr.as_str_from(n).chars().next() {
+                            char_as_cell!(c)
+                        } else { // if has_tail {
+                            empty_list_as_cell!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
+                    }
+                )
+            }
+            &HeapPtr::PStrLocation(h, n) => {
+                read_heap_cell!(self.heap[h],
+                    (HeapCellValueTag::PStr, pstr_atom) => {
+                        if n < pstr_atom.len() {
+                            let h_len = self.heap.len();
+
+                            self.heap.push(pstr_offset_as_cell!(h));
+                            self.heap.push(fixnum_as_cell!(Fixnum::build_with(n as i64)));
+
+                            pstr_loc_as_cell!(h_len)
                         } else {
-                            unreachable!()
+                            self.deref(self.heap[h+1])
                         }
                     }
-                    (Addr::PStrLocation(h1, n1), Addr::PStrLocation(h2, n2)) => {
-                        if let &HeapCellValue::PartialString(ref pstr1, has_tail_1) = &self.heap[h1]
-                        {
-                            if let &HeapCellValue::PartialString(ref pstr2, has_tail_2) =
-                                &self.heap[h2]
-                            {
-                                let pstr1_s = pstr1.as_str_from(n1);
-                                let pstr2_s = pstr2.as_str_from(n2);
+                    (HeapCellValueTag::CStr, cstr_atom) => {
+                        if n < cstr_atom.len() {
+                            let h_len = self.heap.len();
 
-                                let m_len = if pstr1_s.starts_with(pstr2_s) {
-                                    pstr2_s.len()
-                                } else if pstr2_s.starts_with(pstr1_s) {
-                                    pstr1_s.len()
-                                } else {
-                                    self.fail = true;
-                                    return;
-                                };
+                            self.heap.push(pstr_offset_as_cell!(h));
+                            self.heap.push(fixnum_as_cell!(Fixnum::build_with(n as i64)));
 
-                                if pstr1.at_end(n1 + m_len) {
-                                    if has_tail_1 {
-                                        pdl.push(Addr::HeapCell(h1 + 1));
-                                    } else {
-                                        pdl.push(Addr::EmptyList);
-                                    }
+                            pstr_loc_as_cell!(h_len)
+                        } else {
+                            empty_list_as_cell!()
+                        }
+                    }
+                    _ => {
+                        unreachable!()
+                    }
+                )
+            }
+        }
+    }
+
+    pub fn compare_term_test(&mut self) -> Option<Ordering> {
+        let mut tabu_list = IndexSet::new();
+
+        while !self.pdl.is_empty() {
+            let s1 = self.pdl.pop().unwrap();
+            let s1 = self.deref(s1);
+
+            let s2 = self.pdl.pop().unwrap();
+            let s2 = self.deref(s2);
+
+            if s1 == s2 {
+                continue;
+            }
+
+            let v1 = self.store(s1);
+            let v2 = self.store(s2);
+
+            let order_cat_v1 = v1.order_category();
+            let order_cat_v2 = v2.order_category();
+
+            if order_cat_v1 != order_cat_v2 {
+                       self.pdl.clear();
+                return Some(order_cat_v1.cmp(&order_cat_v2));
+            }
 
-                                    if pstr2.at_end(n2 + m_len) {
-                                        if has_tail_2 {
-                                            pdl.push(Addr::HeapCell(h2 + 1));
-                                        } else {
-                                            pdl.push(Addr::EmptyList);
+            match order_cat_v1 {
+                Some(TermOrderCategory::Variable) => {
+                    let v1 = v1.as_var().unwrap();
+                    let v2 = v2.as_var().unwrap();
+
+                    if v1 != v2 {
+                                   self.pdl.clear();
+                        return Some(v1.cmp(&v2));
+                    }
+                }
+                Some(TermOrderCategory::FloatingPoint) => {
+                    let v1 = cell_as_f64_ptr!(v1);
+                    let v2 = cell_as_f64_ptr!(v2);
+
+                    if v1 != v2 {
+                                   self.pdl.clear();
+                        return Some(v1.cmp(&v2));
+                    }
+                }
+                Some(TermOrderCategory::Integer) => {
+                    let v1 = Number::try_from(v1).unwrap();
+                    let v2 = Number::try_from(v2).unwrap();
+
+                    if v1 != v2 {
+                                   self.pdl.clear();
+                        return Some(v1.cmp(&v2));
+                    }
+                }
+                Some(TermOrderCategory::Atom) => {
+                    read_heap_cell!(v1,
+                        (HeapCellValueTag::Atom, (n1, _a1)) => {
+                            read_heap_cell!(v2,
+                                (HeapCellValueTag::Atom, (n2, _a2)) => {
+                                    if n1 != n2 {
+                                                           self.pdl.clear();
+                                        return Some(n1.cmp(&n2));
+                                    }
+                                }
+                                (HeapCellValueTag::Char, c2) => {
+                                    if let Some(c1) = n1.as_char() {
+                                        if c1 != c2 {
+                                                               self.pdl.clear();
+                                            return Some(c1.cmp(&c2));
                                         }
                                     } else {
-                                        pdl.push(Addr::PStrLocation(h2, n2 + m_len));
+                                                           self.pdl.clear();
+                                        return Some(Ordering::Greater);
                                     }
-                                } else {
-                                    pdl.push(Addr::PStrLocation(h1, n1 + m_len));
-
-                                    if pstr2.at_end(n2 + m_len) {
-                                        if has_tail_2 {
-                                            pdl.push(Addr::HeapCell(h2 + 1));
-                                        } else {
-                                            pdl.push(Addr::EmptyList);
+                                }
+                                _ => {
+                                    unreachable!();
+                                }
+                            )
+                        }
+                        (HeapCellValueTag::Char, c1) => {
+                            read_heap_cell!(v2,
+                                (HeapCellValueTag::Atom, (n2, _a2)) => {
+                                    if let Some(c2) = n2.as_char() {
+                                        if c1 != c2 {
+                                                               self.pdl.clear();
+                                            return Some(c1.cmp(&c2));
                                         }
                                     } else {
-                                        pdl.push(Addr::PStrLocation(h2, n2 + m_len));
+                                                           self.pdl.clear();
+                                        return Some(Ordering::Less);
                                     }
                                 }
-                            }
+                                (HeapCellValueTag::Char, c2) => {
+                                    if c1 != c2 {
+                                                           self.pdl.clear();
+                                        return Some(c1.cmp(&c2));
+                                    }
+                                }
+                                _ => {
+                                    unreachable!()
+                                }
+                            )
                         }
-                    }
-                    (Addr::Lis(a1), Addr::Lis(a2)) => {
-                        pdl.push(Addr::HeapCell(a1));
-                        pdl.push(Addr::HeapCell(a2));
+                        _ => {
+                            unreachable!()
+                        }
+                    )
+                }
+                Some(TermOrderCategory::Compound) => {
+                    fn stalled_pstr_iter_handler(
+                        string_iter: HeapPStrIter,
+                        stalled_iter: HeapPStrIter,
+                        pdl: &mut Vec<HeapCellValue>,
+                    ) -> Option<Ordering> {
+                        let l = read_heap_cell!(stalled_iter.focus,
+                            (HeapCellValueTag::Str, s) => {
+                                let (name, arity) = cell_as_atom_cell!(stalled_iter.heap[s])
+                                    .get_name_and_arity();
+
+                                if !(name == atom!(".") && arity == 2) {
+                                    pdl.clear();
+                                    return Some((atom!("."),2).cmp(&(name,arity)));
+                                }
+
+                                s+1
+                            }
+                            (HeapCellValueTag::Lis, l) => {
+                                l
+                            }
+                            _ => {
+                                unreachable!()
+                            }
+                        );
+
+                        let c2 = stalled_iter.heap[l];
+                        let c1 = string_iter.chars().next().unwrap();
 
-                        pdl.push(Addr::HeapCell(a1 + 1));
-                        pdl.push(Addr::HeapCell(a2 + 1));
+                        pdl.push(c2);
+                        pdl.push(char_as_cell!(c1));
+
+                        None
                     }
-                    (Addr::Str(a1), Addr::Str(a2)) => {
-                        let r1 = &self.heap[a1];
-                        let r2 = &self.heap[a2];
 
-                        if let &HeapCellValue::NamedStr(n1, ref f1, _) = r1 {
-                            if let &HeapCellValue::NamedStr(n2, ref f2, _) = r2 {
-                                if n1 == n2 && *f1 == *f2 {
-                                    for i in 1..n1 + 1 {
-                                        pdl.push(Addr::HeapCell(a1 + i));
-                                        pdl.push(Addr::HeapCell(a2 + i));
-                                    }
+                    fn pstr_comparator(
+                        heap: &[HeapCellValue],
+                        pdl: &mut Vec<HeapCellValue>,
+                        s1: usize,
+                        s2: usize,
+                    ) -> Option<Ordering> {
+                        let mut iter1 = HeapPStrIter::new(heap, s1);
+                        let mut iter2 = HeapPStrIter::new(heap, s2);
 
-                                    continue;
+                        match compare_pstr_prefixes(&mut iter1, &mut iter2) {
+                            PStrCmpResult::Ordered(ordering) => Some(ordering),
+                            _ => {
+                                if iter1.num_steps() == 0 && iter2.num_steps() == 0 {
+                                    return match iter2.focus.get_tag() {
+                                        HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc => {
+                                            let result = stalled_pstr_iter_handler(iter2, iter1, pdl);
+
+                                            if let Some(ordering) = result {
+                                                Some(ordering.reverse())
+                                            } else {
+                                                let pdl_len = pdl.len();
+                                                pdl.swap(pdl_len - 2, pdl_len - 1);
+                                                result
+                                            }
+                                        }
+                                        _ => {
+                                            stalled_pstr_iter_handler(iter1, iter2, pdl)
+                                        }
+                                    };
                                 }
+
+                                pdl.push(iter2.focus);
+                                pdl.push(iter1.focus);
+
+                                None
                             }
                         }
-
-                        self.fail = true;
                     }
-                    (Addr::Con(c1), Addr::Con(c2)) => match (&self.heap[c1], &self.heap[c2]) {
-                        (&HeapCellValue::Atom(ref n1, _), &HeapCellValue::Atom(ref n2, _))
-                            if n1.as_str() == n2.as_str() => {}
-                        (
-                            &HeapCellValue::DBRef(ref db_ref_1),
-                            &HeapCellValue::DBRef(ref db_ref_2),
-                        ) if db_ref_1 == db_ref_2 => {}
-                        (v1, v2) => {
-                            if let Ok(n1) = Number::try_from(v1) {
-                                if let Ok(n2) = Number::try_from(v2) {
-                                    if n1 == n2 {
+
+                    read_heap_cell!(v1,
+                        (HeapCellValueTag::Lis, l1) => {
+                            read_heap_cell!(v2,
+                                (HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc) => {
+                                    let h = self.heap.len();
+
+                                    self.heap.push(v1);
+                                    self.heap.push(v2);
+
+                                    if let Some(ordering) = pstr_comparator(
+                                        &self.heap, &mut self.pdl, h, h+1
+                                    ) {
+                                        if ordering != Ordering::Equal {
+                                            self.heap.pop();
+                                            self.heap.pop();
+
+                                                               self.pdl.clear();
+
+                                            return Some(ordering);
+                                        }
+                                    }
+
+                                    self.heap.pop();
+                                    self.heap.pop();
+                                }
+                                (HeapCellValueTag::Lis, l2) => {
+                                    if tabu_list.contains(&(l1, l2)) {
                                         continue;
                                     }
+
+                                    tabu_list.insert((l1, l2));
+
+                                    self.pdl.push(self.heap[l2 + 1]);
+                                    self.pdl.push(self.heap[l1 + 1]);
+
+                                    self.pdl.push(self.heap[l2]);
+                                    self.pdl.push(self.heap[l1]);
                                 }
-                            }
+                                (HeapCellValueTag::Str, s2) => {
+                                    if tabu_list.contains(&(l1, s2)) {
+                                        continue;
+                                    }
 
-                            self.fail = true;
-                        }
-                    },
-                    (Addr::Con(h), Addr::Char(c)) | (Addr::Char(c), Addr::Con(h)) => {
-                        match &self.heap[h] {
-                            &HeapCellValue::Atom(ref name, _) if name.is_char() => {
-                                if name.as_str().chars().next() != Some(c) {
-                                    self.fail = true;
-                                    return;
+                                    let (name, arity) = cell_as_atom_cell!(self.heap[s2])
+                                        .get_name_and_arity();
+
+                                    match (atom!("."), 2).cmp(&(name, arity)) {
+                                        Ordering::Equal => {
+                                            tabu_list.insert((l1, s2));
+
+                                            self.pdl.push(self.heap[s2 + 2]);
+                                            self.pdl.push(self.heap[l1 + 1]);
+
+                                            self.pdl.push(self.heap[s2 + 1]);
+                                            self.pdl.push(self.heap[l1]);
+                                        }
+                                        ordering => {
+                                                               self.pdl.clear();
+                                            return Some(ordering);
+                                        }
+                                    }
                                 }
-                            }
-                            _ => {
-                                self.fail = true;
-                                return;
-                            }
-                        }
-                    }
-                    (Addr::Stream(s1), Addr::Stream(s2)) => {
-                        if s1 != s2 {
-                            self.fail = true;
+                                _ => {
+                                    unreachable!();
+                                }
+                            )
                         }
-                    }
-                    (v, Addr::Con(h)) | (Addr::Con(h), v) => {
-                        if let Ok(n1) = Number::try_from(&self.heap[h]) {
-                            if let Ok(v) = Number::try_from(&HeapCellValue::Addr(v)) {
-                                if n1 == v {
-                                    continue;
+                        (HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc) => {
+                            let h = self.heap.len();
+
+                            self.heap.push(v1);
+                            self.heap.push(v2);
+
+                            if let Some(ordering) = pstr_comparator(
+                                &self.heap, &mut self.pdl, h, h+1,
+                            ) {
+                                if ordering != Ordering::Equal {
+                                    self.heap.pop();
+                                    self.heap.pop();
+
+                                                   self.pdl.clear();
+
+                                    return Some(ordering);
                                 }
                             }
+
+                            self.heap.pop();
+                            self.heap.pop();
                         }
+                        (HeapCellValueTag::Str, s1) => {
+                            read_heap_cell!(v2,
+                                (HeapCellValueTag::Str, s2) => {
+                                    if tabu_list.contains(&(s1, s2)) {
+                                        continue;
+                                    }
 
-                        self.fail = true;
-                    }
-                    (a1, a2) => {
-                        if let Ok(n1) = Number::try_from(&HeapCellValue::Addr(a1)) {
-                            if let Ok(n2) = Number::try_from(&HeapCellValue::Addr(a2)) {
-                                if n1 == n2 {
-                                    continue;
+                                    let (n1, a1) = cell_as_atom_cell!(self.heap[s1])
+                                        .get_name_and_arity();
+
+                                    let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
+                                        .get_name_and_arity();
+
+                                    match (n1,a1).cmp(&(n2,a2)) {
+                                        Ordering::Equal => {
+                                            tabu_list.insert((s1, s2));
+
+                                            for idx in (1 .. a1+1).rev() {
+                                                self.pdl.push(self.heap[s2+idx]);
+                                                self.pdl.push(self.heap[s1+idx]);
+                                            }
+                                        }
+                                        ordering => {
+                                                               self.pdl.clear();
+                                            return Some(ordering);
+                                        }
+                                    }
+                                }
+                                (HeapCellValueTag::Lis, l2) => {
+                                    if tabu_list.contains(&(s1, l2)) {
+                                        continue;
+                                    }
+
+                                    tabu_list.insert((s1, l2));
+
+                                    let (n1, a1) = cell_as_atom_cell!(self.heap[s1])
+                                        .get_name_and_arity();
+
+                                    match (n1,a1).cmp(&(atom!("."), 2)) {
+                                        Ordering::Equal => {
+                                            self.pdl.push(self.heap[l2]);
+                                            self.pdl.push(self.heap[s1+1]);
+
+                                            self.pdl.push(self.heap[l2+1]);
+                                            self.pdl.push(self.heap[s1+2]);
+                                        }
+                                        ordering => {
+                                                               self.pdl.clear();
+                                            return Some(ordering);
+                                        }
+                                    }
+                                }
+                                (HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc) => {
+                                    let h = self.heap.len();
+
+                                    self.heap.push(v1);
+                                    self.heap.push(v2);
+
+                                    if let Some(ordering) = pstr_comparator(
+                                        &self.heap, &mut self.pdl, h, h+1,
+                                    ) {
+                                        if ordering != Ordering::Equal {
+                                            self.heap.pop();
+                                            self.heap.pop();
+
+                                                               self.pdl.clear();
+
+                                            return Some(ordering);
+                                        }
+                                    }
+
+                                    self.heap.pop();
+                                    self.heap.pop();
+                                }
+                                _ => {
+                                    unreachable!()
                                 }
-                            }
+                            )
                         }
-
-                        if a1 != a2 {
-                            self.fail = true;
+                        _ => {
+                            unreachable!()
                         }
-                    }
-                };
-            }
-        }
-    }
-
-    pub(super) fn trail(&mut self, r: TrailRef) {
-        match r {
-            TrailRef::Ref(Ref::HeapCell(h)) => {
-                if h < self.hb {
-                    self.trail.push(TrailRef::Ref(Ref::HeapCell(h)));
-                    self.tr += 1;
-                }
-            }
-            TrailRef::Ref(Ref::AttrVar(h)) => {
-                if h < self.hb {
-                    self.trail.push(TrailRef::Ref(Ref::AttrVar(h)));
-                    self.tr += 1;
-                }
-            }
-            TrailRef::AttrVarHeapLink(h) => {
-                if h < self.hb {
-                    self.trail.push(TrailRef::AttrVarHeapLink(h));
-                    self.tr += 1;
-                }
-            }
-            TrailRef::AttrVarListLink(h, l) => {
-                if h < self.hb {
-                    self.trail.push(TrailRef::AttrVarListLink(h, l));
-                    self.tr += 1;
+                    );
                 }
-            }
-            TrailRef::Ref(Ref::StackCell(b, sc)) => {
-                if b < self.b {
-                    self.trail.push(TrailRef::Ref(Ref::StackCell(b, sc)));
-                    self.tr += 1;
+                None => {
+                    if v1 != v2 {
+                                   self.pdl.clear();
+                        return None;
+                    }
                 }
             }
-            TrailRef::BlackboardOffset(key_h, value_h) => {
-                self.trail.push(TrailRef::BlackboardOffset(key_h, value_h));
-                self.tr += 1;
-            }
-            TrailRef::BlackboardEntry(key_h) => {
-                self.trail.push(TrailRef::BlackboardEntry(key_h));
-                self.tr += 1;
-            }
         }
+
+        Some(Ordering::Equal)
     }
 
     fn increment_s_ptr(&mut self, rhs: usize) {
@@ -691,16 +1809,20 @@ impl MachineState {
                 *h += rhs;
             }
             &mut HeapPtr::PStrChar(h, ref mut n) | &mut HeapPtr::PStrLocation(h, ref mut n) => {
-                match &self.heap[h] {
-                    &HeapCellValue::PartialString(ref pstr, _) => {
-                        for c in pstr.range_from(*n..).take(rhs) {
+                read_heap_cell!(self.heap[h],
+                    (HeapCellValueTag::PStr | HeapCellValueTag::CStr, pstr_atom) => {
+                        let pstr = PartialString::from(pstr_atom);
+
+                        for c in pstr.as_str_from(*n).chars().take(rhs) {
                             *n += c.len_utf8();
                         }
 
                         self.s = HeapPtr::PStrLocation(h, *n);
                     }
-                    _ => {}
-                }
+                    _ => {
+                        unreachable!()
+                    }
+                )
             }
         }
     }
@@ -715,425 +1837,409 @@ impl MachineState {
         // additions, now that deleted attributes can be undeleted by
         // backtracking.
         for i in (a1..a2).rev() {
-            match self.trail[i] {
-                TrailRef::Ref(Ref::HeapCell(h)) => {
-                    self.heap[h] = HeapCellValue::Addr(Addr::HeapCell(h))
+            let h = self.trail[i].get_value() as usize;
+
+            match self.trail[i].get_tag() {
+                TrailEntryTag::TrailedHeapVar => {
+                    self.heap[h] = heap_loc_as_cell!(h);
                 }
-                TrailRef::Ref(Ref::AttrVar(h)) => {
-                    self.heap[h] = HeapCellValue::Addr(Addr::AttrVar(h))
+                TrailEntryTag::TrailedStackVar => {
+                    self.stack[h] = stack_loc_as_cell!(h);
                 }
-                TrailRef::Ref(Ref::StackCell(fr, sc)) => {
-                    self.stack.index_and_frame_mut(fr)[sc] = Addr::StackCell(fr, sc)
+                TrailEntryTag::TrailedAttrVar => {
+                    self.heap[h] = attr_var_as_cell!(h);
                 }
-                TrailRef::AttrVarHeapLink(h) => {
-                    self.heap[h] = HeapCellValue::Addr(Addr::HeapCell(h));
+                TrailEntryTag::TrailedAttrVarHeapLink => {
+                    self.heap[h] = heap_loc_as_cell!(h);
                 }
-                TrailRef::AttrVarListLink(h, l) => {
-                    self.heap[h] = HeapCellValue::Addr(Addr::Lis(l));
+                TrailEntryTag::TrailedAttrVarListLink => {
+                    let l = self.trail[i + 1].get_value();
+                    self.heap[h] = list_loc_as_cell!(l);
                 }
-                TrailRef::BlackboardOffset(key_h, value_h) => {
-                    let key = atom_from!(
-                        self,
-                        self.store(self.deref(self.heap[key_h].as_addr(key_h)))
-                    );
-
-                    let value_addr = self.heap[value_h].as_addr(value_h);
+                TrailEntryTag::TrailedBlackboardEntry => {
+                    let key = Atom::from(h);
 
                     match global_variables.get_mut(&key) {
-                        Some((_, ref mut loc)) => *loc = Some(value_addr),
+                        Some((_, ref mut loc)) => *loc = None,
                         None => unreachable!(),
                     }
                 }
-                TrailRef::BlackboardEntry(key_h) => {
-                    let key = atom_from!(
-                        self,
-                        self.store(self.deref(self.heap[key_h].as_addr(key_h)))
-                    );
+                TrailEntryTag::TrailedBlackboardOffset => {
+                    let key = Atom::from(h);
+                    let value_cell = HeapCellValue::from(u64::from(self.trail[i + 1]));
 
                     match global_variables.get_mut(&key) {
-                        Some((_, ref mut loc)) => *loc = None,
+                        Some((_, ref mut loc)) => *loc = Some(value_cell),
                         None => unreachable!(),
                     }
                 }
+               TrailEntryTag::TrailedAttachedValue => {
+               }
             }
         }
     }
 
-    pub(super) fn match_partial_string(&mut self, addr: Addr, string: &String, has_tail: bool) {
-        let mut heap_pstr_iter = self.heap_pstr_iter(addr);
+    pub fn match_partial_string(&mut self, value: HeapCellValue, string: Atom, has_tail: bool) {
+        let h = self.heap.len();
+        self.heap.push(value);
 
-        match compare_pstr_to_string(&mut heap_pstr_iter, string) {
-            Some(prefix_len) if prefix_len == string.len() => {
-                let focus = heap_pstr_iter.focus();
+        let mut heap_pstr_iter = HeapPStrIter::new(&self.heap, h);
+        let s = string.as_str();
+
+        match heap_pstr_iter.compare_pstr_to_string(s) {
+            Some(PStrPrefixCmpResult { focus, offset, prefix_len }) if prefix_len == s.len() => {
+                let focus_addr = self.heap[focus];
 
-                match focus {
-                    Addr::PStrLocation(h, n) => {
+                read_heap_cell!(focus_addr,
+                    (HeapCellValueTag::PStr | HeapCellValueTag::CStr, pstr_atom) => {
                         if has_tail {
-                            self.s = HeapPtr::PStrLocation(h, n);
+                            self.s = HeapPtr::PStrLocation(focus, offset);
                             self.mode = MachineMode::Read;
+                        } else if offset == pstr_atom.len() {
+                            let focus_addr = heap_pstr_iter.focus;
+                            unify!(self, focus_addr, empty_list_as_cell!());
                         } else {
                             self.fail = true;
                         }
                     }
-                    addr => {
+                    (HeapCellValueTag::PStrLoc | HeapCellValueTag::PStrOffset, h) => {
                         if has_tail {
-                            let h = self.heap.h();
+                            let (h, _) = pstr_loc_and_offset(&self.heap, h);
 
-                            self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
-                            self.bind(Ref::HeapCell(h), addr);
+                            self.s = HeapPtr::PStrLocation(h, offset);
+                            self.mode = MachineMode::Read;
+                        } else {
+                            let end_cell = heap_pstr_iter.focus;
+                            self.fail = end_cell != empty_list_as_cell!();
+                        }
+                    }
+                    _ => {
+                        let focus = heap_pstr_iter.focus();
 
-                            self.s = HeapPtr::HeapCell(h);
+                        if has_tail {
+                            self.s = HeapPtr::HeapCell(focus);
                             self.mode = MachineMode::Read;
                         } else {
-                            if let Some(var) = addr.as_var() {
-                                self.bind(var, Addr::EmptyList);
-                            } else {
-                                self.fail = addr != Addr::EmptyList;
-                            }
+                            let focus = heap_pstr_iter.focus;
+                            unify!(self, focus, empty_list_as_cell!());
                         }
                     }
-                }
+                );
             }
-            Some(prefix_len) => match heap_pstr_iter.focus() {
-                addr if addr.is_ref() => {
-                    let h = self.heap.h();
-
-                    let pstr_addr = if has_tail {
-                        self.s = HeapPtr::HeapCell(h + 1);
-                        self.mode = MachineMode::Read;
-
-                        self.heap.allocate_pstr(&string[prefix_len..])
-                    } else {
-                        self.heap.put_complete_string(&string[prefix_len..])
-                    };
+            Some(PStrPrefixCmpResult { prefix_len, .. }) => {
+                // TODO: this is woefully insufficient! you need to
+                // match the remaining portion of string if offset <
+                // pstr.len().
+                let focus = heap_pstr_iter.focus();
+                let tail_addr = self.heap[focus];
 
-                    self.bind(addr.as_var().unwrap(), pstr_addr);
-                }
-                Addr::Lis(l) => {
-                    let h = self.heap.h();
+                let h = self.heap.len();
 
-                    let pstr_addr = if has_tail {
-                        self.s = HeapPtr::HeapCell(h + 1);
-                        self.mode = MachineMode::Read;
+                let target_cell = if has_tail {
+                    self.s = HeapPtr::HeapCell(h + 1);
+                    self.mode = MachineMode::Read;
 
-                        self.heap.allocate_pstr(&string[prefix_len..])
-                    } else {
-                        self.heap.put_complete_string(&string[prefix_len..])
-                    };
+                    put_partial_string(
+                        &mut self.heap,
+                        &string.as_str()[prefix_len ..],
+                        &mut self.atom_tbl,
+                    )
+                } else {
+                    put_complete_string(
+                        &mut self.heap,
+                        &string.as_str()[prefix_len ..],
+                        &mut self.atom_tbl,
+                    )
+                };
 
-                    (self.unify_fn)(self, Addr::Lis(l), pstr_addr);
-                }
-                _ => {
-                    self.fail = true;
-                }
-            },
+                unify!(self, tail_addr, target_cell);
+            }
             None => {
                 self.fail = true;
             }
         }
     }
 
-    pub(super) fn write_constant_to_var(&mut self, addr: Addr, c: &Constant) {
-        match self.store(self.deref(addr)) {
-            Addr::Con(c1) => {
-                match &self.heap[c1] {
-                    HeapCellValue::Atom(ref n1, _) => {
-                        self.fail = match c {
-                            Constant::Atom(ref n2, _) => n1 != n2,
-                            Constant::Char(c) if n1.is_char() => {
-                                Some(*c) != n1.as_str().chars().next()
-                            }
-                            _ => true,
-                        };
-                    }
-                    HeapCellValue::Integer(ref n1) => {
-                        self.fail = match c {
-                            Constant::Fixnum(n2) => n1.to_isize() != Some(*n2),
-                            Constant::Integer(ref n2) => n1 != n2,
-                            Constant::Usize(n2) => n1.to_usize() != Some(*n2),
-                            _ => true,
-                        };
-                    }
-                    HeapCellValue::Rational(ref r1) => {
-                        self.fail = if let Constant::Rational(ref r2) = c {
-                            r1 != r2
-                        } else {
-                            true
-                        }
-                    }
-                    HeapCellValue::PartialString(..) => {
-                        if let Constant::String(ref s2) = c {
-                            self.match_partial_string(Addr::PStrLocation(c1, 0), &s2, false);
-                        } else {
-                            self.fail = true;
-                        }
-                    }
-                    _ => {
-                        unreachable!()
-                    }
-                };
-            }
-            Addr::Char(ch) => {
-                self.fail = match c {
-                    Constant::Atom(ref n2, _) if n2.is_char() => {
-                        Some(ch) != n2.as_str().chars().next()
-                    }
-                    Constant::Char(c) => *c != ch,
-                    _ => true,
-                };
-            }
-            Addr::EmptyList => {
-                if let Constant::EmptyList = c {
+    pub(super) fn write_literal_to_var(&mut self, deref_v: HeapCellValue, lit: HeapCellValue) {
+        let store_v = self.store(deref_v);
+
+        read_heap_cell!(lit,
+            (HeapCellValueTag::Atom, (atom, arity)) => {
+                if arity == 0 {
+                    self.unify_atom(atom, store_v);
                 } else {
                     self.fail = true;
                 }
             }
-            Addr::Lis(l) => {
-                let addr = self.heap.put_constant(c.clone());
-                self.unify(Addr::Lis(l), addr);
-            }
-            Addr::PStrLocation(h, n) => {
-                if let Constant::String(ref s2) = c {
-                    self.match_partial_string(Addr::PStrLocation(h, n), &s2, false)
-                } else {
-                    self.fail = true;
-                };
-            }
-            Addr::Stream(_) => {
-                self.fail = true;
+            (HeapCellValueTag::Char, c) => {
+                self.unify_char(c, store_v);
+            }
+            (HeapCellValueTag::Fixnum, n) => {
+                self.unify_fixnum(n, store_v);
+            }
+            (HeapCellValueTag::F64, f64_ptr) => {
+                self.unify_f64(f64_ptr, store_v);
+            }
+            (HeapCellValueTag::Cons, ptr) => {
+                match_untyped_arena_ptr!(ptr,
+                     (ArenaHeaderTag::Integer, n) => {
+                         self.unify_big_int(n, store_v);
+                     }
+                     (ArenaHeaderTag::Rational, r) => {
+                         self.unify_rational(r, store_v);
+                     }
+                     _ => {
+                         self.fail = true;
+                     }
+                )
             }
-            addr => {
-                let c = self.heap.put_constant(c.clone());
-
-                if let Some(r) = addr.as_var() {
-                    self.bind(r, c);
-                } else {
-                    self.unify(addr, c);
+            (HeapCellValueTag::CStr, cstr_atom) => {
+                match store_v.get_tag() {
+                    HeapCellValueTag::PStrLoc
+                    | HeapCellValueTag::Lis
+                    | HeapCellValueTag::Str => {
+                        self.match_partial_string(store_v, cstr_atom, false);
+                    }
+                    _ => {
+                        self.fail = true;
+                    }
                 }
             }
-        };
+            _ => {
+                unreachable!()
+            }
+        )
     }
 
-    pub(super) fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) {
-        let stub = MachineError::functor_stub(clause_name!("is"), 2);
+    pub fn execute_arith_instr(&mut self, instr: &ArithmeticInstruction) {
+        let stub_gen = || functor_stub(atom!("is"), 2);
 
         match instr {
             &ArithmeticInstruction::Add(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, try_numeric_result!(self, n1 + n2, stub));
+                self.interms[t - 1] = try_or_fail_gen!(
+                    self,
+                    try_numeric_result!(add(n1, n2, &mut self.arena), stub_gen)
+                );
                 self.p += 1;
             }
             &ArithmeticInstruction::Sub(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, try_numeric_result!(self, n1 - n2, stub));
+                self.interms[t - 1] = try_or_fail_gen!(
+                    self,
+                    try_numeric_result!(sub(n1, n2, &mut self.arena), stub_gen)
+                );
                 self.p += 1;
             }
             &ArithmeticInstruction::Mul(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, try_numeric_result!(self, n1 * n2, stub));
+                self.interms[t - 1] = try_or_fail_gen!(
+                    self,
+                    try_numeric_result!(mul(n1, n2, &mut self.arena), stub_gen)
+                );
                 self.p += 1;
             }
             &ArithmeticInstruction::Max(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.max(n1, n2));
+                self.interms[t - 1] = try_or_fail_gen!(self, max(n1, n2));
                 self.p += 1;
             }
             &ArithmeticInstruction::Min(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.min(n1, n2));
+                self.interms[t - 1] = try_or_fail_gen!(self, min(n1, n2));
                 self.p += 1;
             }
             &ArithmeticInstruction::IntPow(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.int_pow(n1, n2));
+                self.interms[t - 1] = try_or_fail_gen!(self, int_pow(n1, n2, &mut self.arena));
                 self.p += 1;
             }
             &ArithmeticInstruction::Gcd(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.gcd(n1, n2));
+                self.interms[t - 1] = try_or_fail_gen!(self, gcd(n1, n2, &mut self.arena));
                 self.p += 1;
             }
             &ArithmeticInstruction::Pow(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.pow(n1, n2, "(**)"));
+                self.interms[t - 1] = try_or_fail_gen!(self, pow(n1, n2, atom!("**")));
                 self.p += 1;
             }
             &ArithmeticInstruction::RDiv(ref a1, ref a2, t) => {
-                let stub = MachineError::functor_stub(clause_name!("(rdiv)"), 2);
+                let stub_gen = || functor_stub(atom!("(rdiv)"), 2);
 
-                let (r1, stub) = try_or_fail!(self, self.get_rational(a1, stub));
-                let (r2, _) = try_or_fail!(self, self.get_rational(a2, stub));
+                let r1 = try_or_fail!(self, self.get_rational(a1, stub_gen));
+                let r2 = try_or_fail!(self, self.get_rational(a2, stub_gen));
 
-                self.interms[t - 1] =
-                    Number::Rational(Rc::new(try_or_fail!(self, self.rdiv(r1, r2))));
+                self.interms[t - 1] = Number::Rational(arena_alloc!(
+                    try_or_fail_gen!(self, rdiv(r1, r2)),
+                    self.arena
+                ));
                 self.p += 1;
             }
             &ArithmeticInstruction::IntFloorDiv(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.int_floor_div(n1, n2));
+                self.interms[t - 1] =
+                    try_or_fail_gen!(self, int_floor_div(n1, n2, &mut self.arena));
                 self.p += 1;
             }
             &ArithmeticInstruction::IDiv(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.idiv(n1, n2));
+                self.interms[t - 1] = try_or_fail_gen!(self, idiv(n1, n2, &mut self.arena));
                 self.p += 1;
             }
             &ArithmeticInstruction::Abs(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = n1.abs();
+                self.interms[t - 1] = abs(n1, &mut self.arena);
                 self.p += 1;
             }
             &ArithmeticInstruction::Sign(ref a1, t) => {
                 let n = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = self.sign(n);
+                self.interms[t - 1] = sign(n);
                 self.p += 1;
             }
             &ArithmeticInstruction::Neg(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = -n1;
+                self.interms[t - 1] = neg(n1, &mut self.arena);
                 self.p += 1;
             }
             &ArithmeticInstruction::BitwiseComplement(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = try_or_fail!(self, self.bitwise_complement(n1));
+                self.interms[t - 1] =
+                    try_or_fail_gen!(self, bitwise_complement(n1, &mut self.arena));
                 self.p += 1;
             }
             &ArithmeticInstruction::Div(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.div(n1, n2));
+                self.interms[t - 1] = try_or_fail_gen!(self, div(n1, n2));
                 self.p += 1;
             }
             &ArithmeticInstruction::Shr(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.shr(n1, n2));
+                self.interms[t - 1] = try_or_fail_gen!(self, shr(n1, n2, &mut self.arena));
                 self.p += 1;
             }
             &ArithmeticInstruction::Shl(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.shl(n1, n2));
+                self.interms[t - 1] = try_or_fail_gen!(self, shl(n1, n2, &mut self.arena));
                 self.p += 1;
             }
             &ArithmeticInstruction::Xor(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.xor(n1, n2));
+                self.interms[t - 1] = try_or_fail_gen!(self, xor(n1, n2, &mut self.arena));
                 self.p += 1;
             }
             &ArithmeticInstruction::And(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.and(n1, n2));
+                self.interms[t - 1] = try_or_fail_gen!(self, and(n1, n2, &mut self.arena));
                 self.p += 1;
             }
             &ArithmeticInstruction::Or(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.or(n1, n2));
+                self.interms[t - 1] = try_or_fail_gen!(self, or(n1, n2, &mut self.arena));
                 self.p += 1;
             }
             &ArithmeticInstruction::Mod(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.modulus(n1, n2));
+                self.interms[t - 1] = try_or_fail_gen!(self, modulus(n1, n2, &mut self.arena));
                 self.p += 1;
             }
             &ArithmeticInstruction::Rem(ref a1, ref a2, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
-                self.interms[t - 1] = try_or_fail!(self, self.remainder(n1, n2));
+                self.interms[t - 1] = try_or_fail_gen!(self, remainder(n1, n2, &mut self.arena));
                 self.p += 1;
             }
             &ArithmeticInstruction::Cos(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.cos(n1))));
+                self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail_gen!(self, cos(n1))));
                 self.p += 1;
             }
             &ArithmeticInstruction::Sin(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.sin(n1))));
+                self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail_gen!(self, sin(n1))));
                 self.p += 1;
             }
             &ArithmeticInstruction::Tan(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.tan(n1))));
+                self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail_gen!(self, tan(n1))));
                 self.p += 1;
             }
             &ArithmeticInstruction::Sqrt(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] =
-                    Number::Float(OrderedFloat(try_or_fail!(self, self.sqrt(n1))));
+                self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail_gen!(self, sqrt(n1))));
                 self.p += 1;
             }
             &ArithmeticInstruction::Log(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.log(n1))));
+                self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail_gen!(self, log(n1))));
                 self.p += 1;
             }
             &ArithmeticInstruction::Exp(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail!(self, self.exp(n1))));
+                self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail_gen!(self, exp(n1))));
                 self.p += 1;
             }
             &ArithmeticInstruction::ACos(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] =
-                    Number::Float(OrderedFloat(try_or_fail!(self, self.acos(n1))));
+                self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail_gen!(self, acos(n1))));
                 self.p += 1;
             }
             &ArithmeticInstruction::ASin(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] =
-                    Number::Float(OrderedFloat(try_or_fail!(self, self.asin(n1))));
+                self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail_gen!(self, asin(n1))));
                 self.p += 1;
             }
             &ArithmeticInstruction::ATan(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] =
-                    Number::Float(OrderedFloat(try_or_fail!(self, self.atan(n1))));
+                self.interms[t - 1] = Number::Float(OrderedFloat(try_or_fail_gen!(self, atan(n1))));
                 self.p += 1;
             }
             &ArithmeticInstruction::ATan2(ref a1, ref a2, t) => {
@@ -1141,38 +2247,38 @@ impl MachineState {
                 let n2 = try_or_fail!(self, self.get_number(a2));
 
                 self.interms[t - 1] =
-                    Number::Float(OrderedFloat(try_or_fail!(self, self.atan2(n1, n2))));
+                    Number::Float(OrderedFloat(try_or_fail_gen!(self, atan2(n1, n2))));
                 self.p += 1;
             }
             &ArithmeticInstruction::Float(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
                 self.interms[t - 1] =
-                    Number::Float(OrderedFloat(try_or_fail!(self, self.float(n1))));
+                    Number::Float(OrderedFloat(try_or_fail_gen!(self, float(n1))));
                 self.p += 1;
             }
             &ArithmeticInstruction::Truncate(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = self.truncate(n1);
+                self.interms[t - 1] = truncate(n1, &mut self.arena);
                 self.p += 1;
             }
             &ArithmeticInstruction::Round(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = try_or_fail!(self, self.round(n1));
+                self.interms[t - 1] = try_or_fail_gen!(self, round(n1, &mut self.arena));
                 self.p += 1;
             }
             &ArithmeticInstruction::Ceiling(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = self.ceiling(n1);
+                self.interms[t - 1] = ceiling(n1, &mut self.arena);
                 self.p += 1;
             }
             &ArithmeticInstruction::Floor(ref a1, t) => {
                 let n1 = try_or_fail!(self, self.get_number(a1));
 
-                self.interms[t - 1] = self.floor(n1);
+                self.interms[t - 1] = floor(n1, &mut self.arena);
                 self.p += 1;
             }
             &ArithmeticInstruction::Plus(ref a1, t) => {
@@ -1184,74 +2290,99 @@ impl MachineState {
         };
     }
 
-    pub(super) fn execute_fact_instr(&mut self, instr: &FactInstruction) {
+    pub fn execute_fact_instr(&mut self, instr: &FactInstruction) {
         match instr {
-            &FactInstruction::GetConstant(_, ref c, reg) => {
-                let addr = self[reg];
-                self.write_constant_to_var(addr, c);
+            &FactInstruction::GetConstant(_, c, reg) => {
+                let value = self.deref(self[reg]);
+                self.write_literal_to_var(value, c);
             }
             &FactInstruction::GetList(_, reg) => {
-                let addr = self.store(self.deref(self[reg]));
+                let deref_v = self.deref(self[reg]);
+                let store_v = self.store(deref_v);
+
+                read_heap_cell!(store_v,
+                    (HeapCellValueTag::PStrLoc, h) => {
+                        let (h, n) = pstr_loc_and_offset(&self.heap, h);
 
-                match addr {
-                    Addr::PStrLocation(h, n) => {
-                        self.s = HeapPtr::PStrChar(h, n);
+                        self.s = HeapPtr::PStrChar(h, n.get_num() as usize);
                         self.mode = MachineMode::Read;
                     }
-                    addr @ Addr::AttrVar(_)
-                    | addr @ Addr::StackCell(..)
-                    | addr @ Addr::HeapCell(_) => {
-                        let h = self.heap.h();
+                    (HeapCellValueTag::CStr) => {
+                        let h = self.heap.len();
+                        self.heap.push(store_v);
 
-                        self.heap.push(HeapCellValue::Addr(Addr::Lis(h + 1)));
-                        self.bind(addr.as_var().unwrap(), Addr::HeapCell(h));
-
-                        self.mode = MachineMode::Write;
+                        self.s = HeapPtr::PStrChar(h, 0);
+                        self.mode = MachineMode::Read;
                     }
-                    Addr::Lis(a) => {
-                        self.s = HeapPtr::HeapCell(a);
+                    (HeapCellValueTag::Lis, l) => {
+                        self.s = HeapPtr::HeapCell(l);
                         self.mode = MachineMode::Read;
                     }
+                    (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
+                        let h = self.heap.len();
+
+                        self.heap.push(list_loc_as_cell!(h+1));
+                        self.bind(store_v.as_var().unwrap(), heap_loc_as_cell!(h));
+
+                        self.mode = MachineMode::Write;
+                    }
                     _ => {
                         self.fail = true;
                     }
-                };
+                );
             }
-            &FactInstruction::GetPartialString(_, ref string, reg, has_tail) => {
-                let addr = self.store(self.deref(self[reg]));
-                self.match_partial_string(addr, string, has_tail);
+            &FactInstruction::GetPartialString(_, string, reg, has_tail) => {
+                let deref_v = self.deref(self[reg]);
+                let store_v = self.store(deref_v);
+
+                read_heap_cell!(store_v,
+                    (HeapCellValueTag::Str | HeapCellValueTag::Lis |
+                     HeapCellValueTag::PStrLoc | HeapCellValueTag::AttrVar |
+                     HeapCellValueTag::StackVar | HeapCellValueTag::Var |
+                     HeapCellValueTag::CStr) => {
+                        self.match_partial_string(store_v, string, has_tail);
+                    }
+                    _ => {
+                        self.fail = true;
+                    }
+                );
             }
             &FactInstruction::GetStructure(ref ct, arity, reg) => {
-                let addr = self.deref(self[reg]);
-
-                match self.store(addr) {
-                    Addr::Str(a) => {
-                        let result = &self.heap[a];
-
-                        if let &HeapCellValue::NamedStr(narity, ref s, _) = result {
-                            if narity == arity && ct.name() == *s {
-                                self.s = HeapPtr::HeapCell(a + 1);
-                                self.mode = MachineMode::Read;
-                            } else {
-                                self.fail = true;
+                let deref_v = self.deref(self[reg]);
+                let store_v = self.store(deref_v);
+
+                read_heap_cell!(store_v,
+                    (HeapCellValueTag::Str, a) => {
+                        let result = self.heap[a];
+
+                        read_heap_cell!(result,
+                            (HeapCellValueTag::Atom, (name, narity)) => {
+                                if narity == arity && ct.name() == name {
+                                    self.s = HeapPtr::HeapCell(a + 1);
+                                    self.mode = MachineMode::Read;
+                                } else {
+                                    self.fail = true;
+                                }
                             }
-                        }
+                            _ => {
+                                unreachable!();
+                            }
+                        );
                     }
-                    Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(_, _) => {
-                        let h = self.heap.h();
+                    (HeapCellValueTag::AttrVar | HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
+                        let h = self.heap.len();
 
-                        self.heap.push(HeapCellValue::Addr(Addr::Str(h + 1)));
-                        self.heap
-                            .push(HeapCellValue::NamedStr(arity, ct.name(), ct.spec()));
+                        self.heap.push(str_loc_as_cell!(h+1));
+                        self.heap.push(atom_as_cell!(ct.name(), arity));
 
-                        self.bind(addr.as_var().unwrap(), Addr::HeapCell(h));
+                        self.bind(store_v.as_var().unwrap(), heap_loc_as_cell!(h));
 
                         self.mode = MachineMode::Write;
                     }
                     _ => {
                         self.fail = true;
                     }
-                };
+                );
             }
             &FactInstruction::GetVariable(norm, arg) => {
                 self[norm] = self.registers[arg];
@@ -1260,36 +2391,32 @@ impl MachineState {
                 let norm_addr = self[norm];
                 let reg_addr = self.registers[arg];
 
-                (self.unify_fn)(self, norm_addr, reg_addr);
+                unify_fn!(self, norm_addr, reg_addr);
             }
-            &FactInstruction::UnifyConstant(ref c) => {
+            &FactInstruction::UnifyConstant(v) => {
                 match self.mode {
                     MachineMode::Read => {
-                        let addr = self.s.read(&self.heap);
+                        let addr = self.read_s();
 
-                        self.write_constant_to_var(addr, c);
+                        self.write_literal_to_var(addr, v);
                         self.increment_s_ptr(1);
                     }
                     MachineMode::Write => {
-                        let addr = self.heap.put_constant(c.clone());
-
-                        if !addr.is_heap_bound() {
-                            self.heap.push(HeapCellValue::Addr(addr));
-                        }
+                        self.heap.push(v);
                     }
                 };
             }
             &FactInstruction::UnifyVariable(reg) => {
                 match self.mode {
                     MachineMode::Read => {
-                        self[reg] = self.s.read(&self.heap);
+                        self[reg] = self.read_s();
                         self.increment_s_ptr(1);
                     }
                     MachineMode::Write => {
-                        let h = self.heap.h();
+                        let h = self.heap.len();
 
-                        self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
-                        self[reg] = Addr::HeapCell(h);
+                        self.heap.push(heap_loc_as_cell!(h));
+                        self[reg] = heap_loc_as_cell!(h);
                     }
                 };
             }
@@ -1297,25 +2424,30 @@ impl MachineState {
                 match self.mode {
                     MachineMode::Read => {
                         let reg_addr = self[reg];
+                        let value = self.read_s();
 
-                        (self.unify_fn)(self, reg_addr, self.s.read(&self.heap));
+                        unify_fn!(self, reg_addr, value);
                         self.increment_s_ptr(1);
                     }
                     MachineMode::Write => {
-                        let addr = self.store(self.deref(self[reg]));
-                        let h = self.heap.h();
+                        let value = self.store(self.deref(self[reg]));
+                        let h = self.heap.len();
 
-                        if let Addr::HeapCell(hc) = addr {
-                            let val = self.heap.clone(hc);
+                        read_heap_cell!(value,
+                            (HeapCellValueTag::Var | HeapCellValueTag::AttrVar, hc) => {
+                                let value = self.heap[hc];
 
-                            self.heap.push(val);
-                            self.increment_s_ptr(1);
+                                self.heap.push(value);
+                                self.increment_s_ptr(1);
 
-                            return;
-                        }
+                                return;
+                            }
+                            _ => {
+                            }
+                        );
 
-                        self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
-                        (self.bind_fn)(self, Ref::HeapCell(h), addr);
+                        self.heap.push(heap_loc_as_cell!(h));
+                        (self.bind_fn)(self, Ref::heap_cell(h), value);
                     }
                 };
             }
@@ -1323,16 +2455,17 @@ impl MachineState {
                 match self.mode {
                     MachineMode::Read => {
                         let reg_addr = self[reg];
+                        let value = self.read_s();
 
-                        (self.unify_fn)(self, reg_addr, self.s.read(&self.heap));
+                        unify_fn!(self, reg_addr, value);
                         self.increment_s_ptr(1);
                     }
                     MachineMode::Write => {
-                        let h = self.heap.h();
-                        self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
+                        let h = self.heap.len();
+                        self.heap.push(heap_loc_as_cell!(h));
 
                         let addr = self.store(self[reg]);
-                        (self.bind_fn)(self, Ref::HeapCell(h), addr);
+                        (self.bind_fn)(self, Ref::heap_cell(h), addr);
 
                         // the former code of this match arm was:
 
@@ -1352,10 +2485,10 @@ impl MachineState {
                         self.increment_s_ptr(n);
                     }
                     MachineMode::Write => {
-                        let h = self.heap.h();
+                        let h = self.heap.len();
 
                         for i in h..h + n {
-                            self.heap.push(HeapCellValue::Addr(Addr::HeapCell(i)));
+                            self.heap.push(heap_loc_as_cell!(i));
                         }
                     }
                 };
@@ -1408,22 +2541,45 @@ impl MachineState {
         loop {
             match &indexing_lines[index] {
                 &IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, v, c, l, s)) => {
-                    let offset = match addr {
-                        Addr::LoadStatePayload(_) | Addr::Stream(_) | Addr::TcpListener(_) => {
-                            IndexingCodePtr::Fail
+                    let offset = read_heap_cell!(addr,
+                        (HeapCellValueTag::Var
+                         | HeapCellValueTag::StackVar
+                         | HeapCellValueTag::AttrVar) => {
+                            v
                         }
-                        Addr::HeapCell(_) | Addr::StackCell(..) | Addr::AttrVar(..) => v,
-                        Addr::PStrLocation(..) => l,
-                        Addr::Char(_)
-                        | Addr::Con(_)
-                        | Addr::CutPoint(_)
-                        | Addr::EmptyList
-                        | Addr::Fixnum(_)
-                        | Addr::Float(_)
-                        | Addr::Usize(_) => c,
-                        Addr::Lis(_) => l,
-                        Addr::Str(_) => s,
-                    };
+                        (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) => {
+                            s
+                        }
+                        (HeapCellValueTag::Cons, ptr) => {
+                            match ptr.get_tag() {
+                                ArenaHeaderTag::Rational | ArenaHeaderTag::Integer |
+                                ArenaHeaderTag::F64 => {
+                                    c
+                                }
+                                _ => {
+                                    IndexingCodePtr::Fail
+                                }
+                            }
+                        }
+                        _ => {
+                            unreachable!();
+                        }
+                    );
 
                     match offset {
                         IndexingCodePtr::Fail => {
@@ -1451,15 +2607,47 @@ impl MachineState {
                         IndexingCodePtr::Internal(o) => {
                             index += o;
                         }
-                    };
+                    }
                 }
                 &IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref hm)) => {
-                    let offset = match addr.as_constant_index(&self) {
-                        Some(c) => match hm.get(&c) {
-                            Some(offset) => *offset,
-                            _ => IndexingCodePtr::Fail,
-                        },
-                        None => IndexingCodePtr::Fail,
+                    let lit = read_heap_cell!(addr,
+                        (HeapCellValueTag::Char, c) => {
+                            Literal::Char(c)
+                        }
+                        (HeapCellValueTag::Fixnum, n) => {
+                            Literal::Fixnum(n)
+                        }
+                        (HeapCellValueTag::F64, f) => {
+                            Literal::Float(f)
+                        }
+                        (HeapCellValueTag::Atom, (atom, arity)) => {
+                            debug_assert_eq!(arity, 0);
+                            Literal::Atom(atom)
+                        }
+                        (HeapCellValueTag::Cons, cons_ptr) => {
+                            match_untyped_arena_ptr!(cons_ptr,
+                                 (ArenaHeaderTag::Rational, r) => {
+                                     Literal::Rational(r)
+                                 }
+                                 (ArenaHeaderTag::F64, f) => {
+                                     Literal::Float(F64Ptr(f))
+                                 }
+                                 (ArenaHeaderTag::Integer, n) => {
+                                     Literal::Integer(n)
+                                 }
+                                 _ => {
+                                     unreachable!()
+                                 }
+                            )
+                        }
+                        _ => {
+                            unreachable!()
+                        }
+                    );
+
+                    let offset = match hm.get(&lit) {
+                        Some(offset) => *offset,
+                        _ => IndexingCodePtr::Fail,
                     };
 
                     match offset {
@@ -1488,22 +2676,28 @@ impl MachineState {
                         IndexingCodePtr::Internal(o) => {
                             index += o;
                         }
-                    };
+                    }
                 }
                 &IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref hm)) => {
-                    let offset = match addr {
-                        Addr::Str(s) => {
-                            if let &HeapCellValue::NamedStr(arity, ref name, _) = &self.heap[s] {
-                                match hm.get(&(name.clone(), arity)) {
-                                    Some(offset) => *offset,
-                                    _ => IndexingCodePtr::Fail,
-                                }
-                            } else {
-                                IndexingCodePtr::Fail
+                    let offset = read_heap_cell!(addr,
+                        (HeapCellValueTag::Atom, (name, arity)) => {
+                            match hm.get(&(name, arity)) {
+                                Some(offset) => *offset,
+                                None => IndexingCodePtr::Fail,
                             }
                         }
-                        _ => 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
+                        }
+                    );
 
                     match offset {
                         IndexingCodePtr::Fail => {
@@ -1559,47 +2753,50 @@ impl MachineState {
             &QueryInstruction::GetVariable(norm, arg) => {
                 self[norm] = self.registers[arg];
             }
-            &QueryInstruction::PutConstant(_, ref c, reg) => {
-                self[reg] = self.heap.put_constant(c.clone());
+            &QueryInstruction::PutConstant(_, c, reg) => {
+                self[reg] = c;
             }
             &QueryInstruction::PutList(_, reg) => {
-                self[reg] = Addr::Lis(self.heap.h());
+                self[reg] = list_loc_as_cell!(self.heap.len());
             }
-            &QueryInstruction::PutPartialString(_, ref string, reg, has_tail) => {
+            &QueryInstruction::PutPartialString(_, string, reg, has_tail) => {
                 let pstr_addr = if has_tail {
-                    if !string.is_empty() {
-                        let pstr_addr = self.heap.allocate_pstr(&string);
-                        self.heap.pop(); // the tail will be added by the next instruction.
-                        pstr_addr
+                    if string != atom!("") {
+                        let h = self.heap.len();
+                        self.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 {
-                        Addr::EmptyList
+                        empty_list_as_cell!()
                     }
                 } else {
-                    self.heap.put_complete_string(&string)
+                    string_as_cstr_cell!(string)
                 };
 
                 self[reg] = pstr_addr;
             }
             &QueryInstruction::PutStructure(ref ct, arity, reg) => {
-                let h = self.heap.h();
+                let h = self.heap.len();
 
-                self.heap
-                    .push(HeapCellValue::NamedStr(arity, ct.name(), ct.spec()));
-                self[reg] = Addr::Str(h);
+                self.heap.push(atom_as_cell!(ct.name(), arity));
+                self[reg] = str_loc_as_cell!(h);
             }
             &QueryInstruction::PutUnsafeValue(n, arg) => {
-                let e = self.e;
-                let addr = self.store(self.deref(Addr::StackCell(e, n)));
+                let s = stack_loc!(AndFrame, self.e, n);
+                let addr = self.store(self.deref(stack_loc_as_cell!(s)));
 
-                if addr.is_protected(e) {
+                if addr.is_protected(self.e) {
                     self.registers[arg] = addr;
                 } else {
-                    let h = self.heap.h();
+                    let h = self.heap.len();
 
-                    self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
-                    (self.bind_fn)(self, Ref::HeapCell(h), addr);
+                    self.heap.push(heap_loc_as_cell!(h));
+                    (self.bind_fn)(self, Ref::heap_cell(h), addr);
 
-                    self.registers[arg] = self.heap[h].as_addr(h);
+                    self.registers[arg] = heap_loc_as_cell!(h);
                 }
             }
             &QueryInstruction::PutValue(norm, arg) => {
@@ -1608,71 +2805,52 @@ impl MachineState {
             &QueryInstruction::PutVariable(norm, arg) => {
                 match norm {
                     RegType::Perm(n) => {
-                        let e = self.e;
-
-                        self[norm] = Addr::StackCell(e, n);
+                        self[norm] = stack_loc_as_cell!(AndFrame, self.e, n);
                         self.registers[arg] = self[norm];
                     }
                     RegType::Temp(_) => {
-                        let h = self.heap.h();
-                        self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
+                        let h = self.heap.len();
+                        self.heap.push(heap_loc_as_cell!(h));
 
-                        self[norm] = Addr::HeapCell(h);
-                        self.registers[arg] = Addr::HeapCell(h);
+                        self[norm] = heap_loc_as_cell!(h);
+                        self.registers[arg] = heap_loc_as_cell!(h);
                     }
                 };
             }
-            &QueryInstruction::SetConstant(ref c) => {
-                let addr = self.heap.put_constant(c.clone());
-
-                if !addr.is_heap_bound() {
-                    self.heap.push(HeapCellValue::Addr(addr));
-                }
+            &QueryInstruction::SetConstant(c) => {
+                self.heap.push(c);
             }
             &QueryInstruction::SetLocalValue(reg) => {
                 let addr = self.deref(self[reg]);
-                let h = self.heap.h();
+                let h = self.heap.len();
 
-                if addr < Ref::HeapCell(h) {
-                    self.heap.push(HeapCellValue::Addr(addr));
+                if addr < Ref::heap_cell(h) {
+                    self.heap.push(addr);
                     return;
                 }
 
-                self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
-                (self.bind_fn)(self, Ref::HeapCell(h), addr);
+                self.heap.push(heap_loc_as_cell!(h));
+                (self.bind_fn)(self, Ref::heap_cell(h), addr);
             }
             &QueryInstruction::SetVariable(reg) => {
-                let h = self.heap.h();
-                self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
-                self[reg] = Addr::HeapCell(h);
+                let h = self.heap.len();
+                self.heap.push(heap_loc_as_cell!(h));
+                self[reg] = heap_loc_as_cell!(h);
             }
             &QueryInstruction::SetValue(reg) => {
                 let heap_val = self.store(self[reg]);
-                self.heap.push(HeapCellValue::Addr(heap_val));
+                self.heap.push(heap_val);
             }
             &QueryInstruction::SetVoid(n) => {
-                let h = self.heap.h();
+                let h = self.heap.len();
 
                 for i in h..h + n {
-                    self.heap.push(HeapCellValue::Addr(Addr::HeapCell(i)));
+                    self.heap.push(heap_loc_as_cell!(i));
                 }
             }
         }
     }
 
-    pub(super) fn set_ball(&mut self) {
-        self.ball.reset();
-
-        let addr = self[temp_v!(1)];
-        self.ball.boundary = self.heap.h();
-
-        copy_term(
-            CopyBallTerm::new(&mut self.stack, &mut self.heap, &mut self.ball.stub),
-            addr,
-            AttrVarPolicy::DeepCopy,
-        );
-    }
-
     pub(super) fn handle_internal_call_n(&mut self, arity: usize) {
         let arity = arity + 1;
         let pred = self.registers[1];
@@ -1692,238 +2870,183 @@ impl MachineState {
     pub(super) fn setup_call_n(&mut self, arity: usize) -> Option<PredicateKey> {
         let addr = self.store(self.deref(self.registers[arity]));
 
-        let (name, narity) = match addr {
-            Addr::Str(a) => {
-                let result = self.heap.clone(a);
-
-                if let HeapCellValue::NamedStr(narity, name, _) = result {
-                    let stub = MachineError::functor_stub(clause_name!("call"), arity + 1);
+        let (name, narity) = read_heap_cell!(addr,
+            (HeapCellValueTag::Str, s) => {
+                let (name, narity) = cell_as_atom_cell!(self.heap[s]).get_name_and_arity();
 
-                    if narity + arity > MAX_ARITY {
-                        let representation_error = self.error_form(
-                            MachineError::representation_error(RepFlag::MaxArity),
-                            stub,
-                        );
+                if narity + arity > MAX_ARITY {
+                    let stub = functor_stub(atom!("call"), arity + 1);
+                    let err = self.representation_error(RepFlag::MaxArity);
+                    let representation_error = self.error_form(err, stub);
 
-                        self.throw_exception(representation_error);
-                        return None;
-                    }
+                    self.throw_exception(representation_error);
+                    return None;
+                }
 
-                    for i in (1..arity).rev() {
-                        self.registers[i + narity] = self.registers[i];
-                    }
+                for i in (1..arity).rev() {
+                    self.registers[i + narity] = self.registers[i];
+                }
 
-                    for i in 1..narity + 1 {
-                        self.registers[i] = self.heap[a + i].as_addr(a + i);
-                    }
+                for i in 1..narity + 1 {
+                    self.registers[i] = self.heap[s + i];
+                }
 
-                    (name, narity)
+                (name, narity)
+            }
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                if arity == 0 {
+                    (name, 0)
                 } else {
                     self.fail = true;
                     return None;
                 }
             }
-            Addr::Char(c) => (clause_name!(c.to_string(), self.atom_tbl), 0),
-            Addr::Con(h) => match &self.heap[h] {
-                HeapCellValue::Atom(ref name, _) => (name.clone(), 0),
-                _ => {
-                    self.fail = true;
-                    return None;
-                }
-            },
-            Addr::HeapCell(_) | Addr::StackCell(_, _) => {
-                let stub = MachineError::functor_stub(clause_name!("call"), arity + 1);
-                let instantiation_error =
-                    self.error_form(MachineError::instantiation_error(), stub);
+            (HeapCellValueTag::Char, c) => {
+                (self.atom_tbl.build_with(&c.to_string()), 0)
+            }
+            (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar, _h) => {
+                let stub = functor_stub(atom!("call"), arity + 1);
+                let err = self.instantiation_error();
+                let instantiation_error = self.error_form(err, stub);
 
                 self.throw_exception(instantiation_error);
                 return None;
             }
-            addr => {
-                let stub = MachineError::functor_stub(clause_name!("call"), arity + 1);
-                let type_error = self.error_form(
-                    MachineError::type_error(self.heap.h(), ValidType::Callable, addr),
-                    stub,
-                );
+            _ => {
+                let stub = functor_stub(atom!("call"), arity + 1);
+                let err = self.type_error(ValidType::Callable, addr);
+                let type_error = self.error_form(err, stub);
 
                 self.throw_exception(type_error);
-                return None;
-            }
-        };
-
-        Some((name, arity + narity - 1))
-    }
-
-    pub(super) fn unwind_stack(&mut self) {
-        self.b = self.block;
-        self.fail = true;
-    }
-
-    pub(crate) fn is_cyclic_term(&self, addr: Addr) -> bool {
-        let mut seen = IndexSet::new();
-        let mut fail = false;
-        let mut iter = self.pre_order_iter(addr);
-        let mut parent_stack = vec![];
-
-        let is_composite = |addr: Addr| match addr {
-            Addr::Str(_) | Addr::Lis(_) | Addr::PStrLocation(..) => true,
-            _ => false,
-        };
+                return None;
+            }
+        );
 
-        'outer: loop {
-            if let Some(addr) = iter.stack().last().cloned() {
-                let addr = self.store(self.deref(addr));
+        Some((name, arity + narity - 1))
+    }
 
-                if is_composite(addr) {
-                    if !seen.contains(&addr) {
-                        seen.insert(addr);
-                    } else {
-                        // when we again encounter a seen composite
-                        // term, check that it precedes itself as a
-                        // parent in the post-order traversal. in the
-                        // future, when value cells have mark bits,
-                        // use them to designate parenthood instead of
-                        // this linear search.
-
-                        for (_, prec_addr) in parent_stack.iter().rev().cloned() {
-                            if prec_addr == addr {
-                                fail = true;
-                                break 'outer;
-                            }
-                        }
-                    }
+    #[inline]
+    pub fn is_cyclic_term(&mut self, addr: HeapCellValue) -> bool {
+        if addr.is_constant() {
+            return false;
+        }
 
-                    let arity = match addr {
-                        Addr::Str(h) => match &self.heap[h] {
-                            &HeapCellValue::NamedStr(arity, ..) => arity,
-                            _ => unreachable!(),
-                        },
-                        _ => 2,
-                    };
+        let mut iter = stackful_preorder_iter(&mut self.heap, addr);
 
-                    parent_stack.push((arity, addr));
-                }
-            }
+        while let Some(value) = iter.next() {
+            if value.is_forwarded() {
+                let value = heap_bound_store(iter.heap, heap_bound_deref(iter.heap, value));
 
-            if iter.next().is_none() {
-                break;
-            } else {
-                while let Some((rem_children, addr)) = parent_stack.pop() {
-                    if rem_children > 0 {
-                        parent_stack.push((rem_children - 1, addr));
-                        break;
-                    }
+                if value.is_compound() {
+                    return true;
                 }
             }
         }
 
-        fail
+        false
     }
 
     // arg(+N, +Term, ?Arg)
-    pub(super) fn try_arg(&mut self) -> CallResult {
-        let stub = MachineError::functor_stub(clause_name!("arg"), 3);
-        let n = self.store(self.deref(self[temp_v!(1)]));
+    pub fn try_arg(&mut self) -> CallResult {
+        let stub_gen = || functor_stub(atom!("arg"), 3);
+        let n = self.registers[1]; //self.store(self.deref(self.registers[1])); // TODO: necessary?
 
-        match n {
-            Addr::HeapCell(_) | Addr::StackCell(..) => {
+        read_heap_cell!(n,
+            (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
                 // 8.5.2.3 a)
-                return Err(self.error_form(MachineError::instantiation_error(), stub));
+                let err = self.instantiation_error();
+                return Err(self.error_form(err, stub_gen()));
             }
-            addr => {
-                let n = match Number::try_from((addr, &self.heap)) {
-                    Ok(Number::Fixnum(n)) => Integer::from(n),
-                    Ok(Number::Integer(n)) => Integer::from(n.as_ref()),
+            _ => {
+                let n = match Number::try_from(n) {
+                    Ok(Number::Fixnum(n)) => Number::Fixnum(n),
+                    Ok(Number::Integer(n)) => Number::Integer(n),
                     _ => {
-                        return Err(self.error_form(
-                            MachineError::type_error(self.heap.h(), ValidType::Integer, addr),
-                            stub,
-                        ));
+                        let err = self.type_error(ValidType::Integer, n);
+                        return Err(self.error_form(err, stub_gen()));
                     }
                 };
 
                 if n < 0 {
                     // 8.5.2.3 e)
-                    let n = Number::from(n);
-                    let dom_err = MachineError::domain_error(DomainErrorType::NotLessThanZero, n);
-
-                    return Err(self.error_form(dom_err, stub));
+                    let err = self.domain_error(DomainErrorType::NotLessThanZero, n);
+                    return Err(self.error_form(err, stub_gen()));
                 }
 
-                let n = match n.to_usize() {
-                    Some(n) => n,
-                    None => {
+                let n = match n {
+                    Number::Fixnum(n) => n.get_num() as usize,
+                    Number::Integer(n) => n.to_usize().unwrap(),
+                    _ => {
                         self.fail = true;
                         return Ok(());
                     }
                 };
 
-                let term = self.store(self.deref(self[temp_v!(2)]));
+                let term = self.deref(self.registers[2]);
 
-                match term {
-                    Addr::HeapCell(_) | Addr::StackCell(..) | Addr::AttrVar(_) => {
-                        // 8.5.2.3 b)
-                        return Err(self.error_form(MachineError::instantiation_error(), stub));
+                read_heap_cell!(self.store(term),
+                    (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
+                        let err = self.instantiation_error();
+                        return Err(self.error_form(err, stub_gen()));
                     }
-                    Addr::Str(o) => match self.heap.clone(o) {
-                        HeapCellValue::NamedStr(arity, _, _) if 1 <= n && n <= arity => {
-                            let a3 = self[temp_v!(3)];
-                            let h_a = Addr::HeapCell(o + n);
+                    (HeapCellValueTag::Str, o) => {
+                        let arity = cell_as_atom_cell!(self.heap[o]).get_arity();
 
-                            (self.unify_fn)(self, a3, h_a);
-                        }
-                        _ => {
+                        if 1 <= n && n <= arity {
+                            let a3 = self.registers[3];
+                            unify_fn!(self, a3, heap_loc_as_cell!(o + n));
+                        } else {
                             self.fail = true;
                         }
-                    },
-                    Addr::Lis(l) => {
+                    }
+                    (HeapCellValueTag::Lis, l) => {
                         if n == 1 || n == 2 {
-                            let a3 = self[temp_v!(3)];
-                            let h_a = Addr::HeapCell(l + n - 1);
-
-                            (self.unify_fn)(self, a3, h_a);
+                            let a3 = self.registers[3];
+                            unify_fn!(self, a3, heap_loc_as_cell!(l + n - 1));
                         } else {
                             self.fail = true;
                         }
                     }
-                    Addr::PStrLocation(h, offset) => {
+                    (HeapCellValueTag::PStrLoc, pstr_loc) => {
                         if n == 1 || n == 2 {
-                            let a3 = self[temp_v!(3)];
-                            let h_a =
-                                if let HeapCellValue::PartialString(ref pstr, _) = &self.heap[h] {
-                                    if let Some(c) = pstr.range_from(offset..).next() {
-                                        if n == 1 {
-                                            Addr::Char(c)
-                                        } else {
-                                            Addr::PStrLocation(h, offset + c.len_utf8())
-                                        }
-                                    } else {
-                                        unreachable!()
-                                    }
+                            let a3 = self.registers[3];
+                            let (h, offset) = pstr_loc_and_offset(&self.heap, pstr_loc);
+
+                            let pstr = cell_as_string!(self.heap[h]);
+                            let offset = offset.get_num() as usize;
+
+                            if let Some(c) = pstr.as_str_from(offset).chars().next() {
+                                if n == 1 {
+                                    self.unify_char(c, a3);
                                 } else {
-                                    unreachable!()
-                                };
+                                    let offset = (offset + c.len_utf8()) as i64;
+                                    let h_len = self.heap.len();
 
-                            (self.unify_fn)(self, a3, h_a);
+                                    self.heap.push(pstr_offset_as_cell!(h_len));
+                                    self.heap.push(fixnum_as_cell!(Fixnum::build_with(offset)));
+
+                                    unify_fn!(self, pstr_loc_as_cell!(h_len), a3);
+                                }
+                            } else {
+                                unreachable!()
+                            }
                         } else {
                             self.fail = true;
                         }
                     }
                     _ => {
                         // 8.5.2.3 d)
-                        return Err(self.error_form(
-                            MachineError::type_error(self.heap.h(), ValidType::Compound, term),
-                            stub,
-                        ));
+                        let err = self.type_error(ValidType::Compound, term);
+                        return Err(self.error_form(err, stub_gen()));
                     }
-                }
+                )
             }
-        }
+        );
 
         Ok(())
     }
 
-    fn compare_numbers(&mut self, cmp: CompareNumberQT, n1: Number, n2: Number) {
+    pub fn compare_numbers(&mut self, cmp: CompareNumberQT, n1: Number, n2: Number) {
         let ordering = n1.cmp(&n2);
 
         self.fail = match cmp {
@@ -1939,403 +3062,52 @@ impl MachineState {
         self.p += 1;
     }
 
-    pub(super) fn compare_term(&mut self, qt: CompareTermQT) {
-        let a1 = self[temp_v!(1)];
-        let a2 = self[temp_v!(2)];
+    pub fn compare_term(&mut self, qt: CompareTermQT) {
+        let a1 = self.registers[1];
+        let a2 = self.registers[2];
 
-        match self.compare_term_test(&a1, &a2) {
+        match compare_term_test!(self, a1, a2) {
             Some(Ordering::Greater) => match qt {
-                CompareTermQT::GreaterThan | CompareTermQT::GreaterThanOrEqual => return,
+                CompareTermQT::GreaterThan | CompareTermQT::GreaterThanOrEqual => {}
                 _ => self.fail = true,
             },
             Some(Ordering::Equal) => match qt {
-                CompareTermQT::GreaterThanOrEqual | CompareTermQT::LessThanOrEqual => return,
+                CompareTermQT::GreaterThanOrEqual | CompareTermQT::LessThanOrEqual => {}
                 _ => self.fail = true,
             },
             Some(Ordering::Less) => match qt {
-                CompareTermQT::LessThan | CompareTermQT::LessThanOrEqual => return,
+                CompareTermQT::LessThan | CompareTermQT::LessThanOrEqual => {}
                 _ => self.fail = true,
             },
             None => {
                 self.fail = true;
             }
-        };
+        }
     }
 
-    // returns true on failure.
-    pub(super) fn eq_test(&self, a1: Addr, a2: Addr) -> bool {
-        let mut iter = self.zipped_acyclic_pre_order_iter(a1, a2);
-
-        while let Some((v1, v2)) = iter.next() {
-            match (v1, v2) {
-                (Addr::Str(s1), Addr::Str(s2)) => {
-                    if let HeapCellValue::NamedStr(ar1, n1, _) = &self.heap[s1] {
-                        if let HeapCellValue::NamedStr(ar2, n2, _) = &self.heap[s2] {
-                            if ar1 != ar2 || n1 != n2 {
-                                return true;
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    } else {
-                        unreachable!()
-                    }
-                }
-                (Addr::PStrLocation(..), Addr::Lis(_)) | (Addr::Lis(_), Addr::PStrLocation(..)) => {
-                    continue;
-                }
-                (pstr1 @ Addr::PStrLocation(..), pstr2 @ Addr::PStrLocation(..)) => {
-                    let mut i1 = self.heap_pstr_iter(pstr1);
-                    let mut i2 = self.heap_pstr_iter(pstr2);
-
-                    let ordering = compare_pstr_prefixes(&mut i1, &mut i2);
-
-                    if let Some(ordering) = ordering {
-                        if ordering != Ordering::Equal {
-                            return true;
-                        }
-                    }
-
-                    let (lstack, rstack) = iter.stack();
-
-                    lstack.pop();
-                    lstack.pop();
-
-                    rstack.pop();
-                    rstack.pop();
-
-                    lstack.push(i1.focus());
-                    rstack.push(i2.focus());
-                }
-                (Addr::Lis(_), Addr::Lis(_)) => {
-                    continue;
-                }
-                (Addr::Con(h1), Addr::Con(h2)) => match (&self.heap[h1], &self.heap[h2]) {
-                    (
-                        &HeapCellValue::Atom(ref n1, ref spec_1),
-                        &HeapCellValue::Atom(ref n2, ref spec_2),
-                    ) => {
-                        if n1 != n2 || spec_1 != spec_2 {
-                            return true;
-                        }
-                    }
-                    (&HeapCellValue::DBRef(ref db_ref_1), &HeapCellValue::DBRef(ref db_ref_2)) => {
-                        if db_ref_1 != db_ref_2 {
-                            return true;
-                        }
-                    }
-                    (v1, v2) => {
-                        if let Ok(n1) = Number::try_from(v1) {
-                            if let Ok(n2) = Number::try_from(v2) {
-                                if n1 == n2 {
-                                    continue;
-                                }
-                            }
-                        }
-
-                        return true;
-                    }
-                },
-                (Addr::Con(h), Addr::Char(c)) | (Addr::Char(c), Addr::Con(h)) => {
-                    match &self.heap[h] {
-                        &HeapCellValue::Atom(ref name, _) if name.is_char() => {
-                            if name.as_str().chars().next() != Some(c) {
-                                return true;
-                            }
-                        }
-                        _ => {
-                            return true;
-                        }
-                    }
-                }
-                (a1, a2) => {
-                    if let Ok(n1) = Number::try_from((a1, &self.heap)) {
-                        if let Ok(n2) = Number::try_from((a2, &self.heap)) {
-                            if n1 != n2 {
-                                return true;
-                            } else {
-                                continue;
-                            }
-                        }
-                    }
-
-                    if a1 != a2 {
-                        return true;
-                    }
-                }
-            }
+    // returns true on failure, false on success.
+    pub fn eq_test(&mut self, h1: HeapCellValue, h2: HeapCellValue) -> bool {
+        if h1 == h2 {
+            return false;
         }
 
-        // did the two iterators expire at the same step?
-        iter.first_to_expire != Ordering::Equal
+        compare_term_test!(self, h1, h2)
+            .map(|o| o != Ordering::Equal)
+            .unwrap_or(true)
     }
 
-    pub(super) fn compare_term_test(&self, a1: &Addr, a2: &Addr) -> Option<Ordering> {
-        let mut iter = self.zipped_acyclic_pre_order_iter(*a1, *a2);
-
-        while let Some((v1, v2)) = iter.next() {
-            let order_cat_v1 = v1.order_category(&self.heap);
-            let order_cat_v2 = v2.order_category(&self.heap);
-
-            if order_cat_v1 != order_cat_v2 {
-                return Some(order_cat_v1.cmp(&order_cat_v2));
+    pub fn reset_block(&mut self, addr: HeapCellValue) {
+        read_heap_cell!(self.store(addr),
+            (HeapCellValueTag::Fixnum, n) => {
+                self.block = n.get_num() as usize;
             }
-
-            match order_cat_v1 {
-                Some(TermOrderCategory::Variable) => {
-                    let v1 = v1.as_var().unwrap();
-                    let v2 = v2.as_var().unwrap();
-
-                    if v1 != v2 {
-                        return Some(v1.cmp(&v2));
-                    }
-                }
-                Some(TermOrderCategory::FloatingPoint) => {
-                    if let Addr::Float(f1) = v1 {
-                        if let Addr::Float(f2) = v2 {
-                            return Some(f1.cmp(&f2));
-                        } else {
-                            unreachable!()
-                        }
-                    } else {
-                        unreachable!()
-                    }
-                }
-                Some(TermOrderCategory::Integer) => match (v1, v2) {
-                    (Addr::Con(h1), Addr::Con(h2)) => {
-                        if let Ok(n1) = Number::try_from(&self.heap[h1]) {
-                            if let Ok(n2) = Number::try_from(&self.heap[h2]) {
-                                if n1 != n2 {
-                                    return Some(n1.cmp(&n2));
-                                }
-                            } else {
-                                unreachable!()
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    (Addr::Con(h1), v2) => {
-                        if let Ok(n1) = Number::try_from(&self.heap[h1]) {
-                            if let Ok(n2) = Number::try_from(&HeapCellValue::Addr(v2)) {
-                                if n1 != n2 {
-                                    return Some(n1.cmp(&n2));
-                                }
-                            } else {
-                                unreachable!()
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    (v1, Addr::Con(h2)) => {
-                        if let Ok(n1) = Number::try_from(&HeapCellValue::Addr(v1)) {
-                            if let Ok(n2) = Number::try_from(&self.heap[h2]) {
-                                if n1 != n2 {
-                                    return Some(n1.cmp(&n2));
-                                }
-                            } else {
-                                unreachable!()
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    (v1, v2) => {
-                        if let Ok(n1) = Number::try_from(&HeapCellValue::Addr(v1)) {
-                            if let Ok(n2) = Number::try_from(&HeapCellValue::Addr(v2)) {
-                                if n1 != n2 {
-                                    return Some(n1.cmp(&n2));
-                                }
-                            } else {
-                                unreachable!()
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                },
-                Some(TermOrderCategory::Atom) => match (v1, v2) {
-                    (Addr::Con(h1), Addr::Con(h2)) => {
-                        if let HeapCellValue::Atom(ref n1, _) = &self.heap[h1] {
-                            if let HeapCellValue::Atom(ref n2, _) = &self.heap[h2] {
-                                if n1 != n2 {
-                                    return Some(n1.cmp(&n2));
-                                }
-                            } else {
-                                unreachable!()
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    (Addr::Con(h1), Addr::Char(c)) => {
-                        if let HeapCellValue::Atom(ref n1, _) = &self.heap[h1] {
-                            if n1.is_char() {
-                                if n1.as_str().chars().next() != Some(c) {
-                                    return Some(n1.as_str().chars().next().cmp(&Some(c)));
-                                }
-                            } else {
-                                return Some(Ordering::Greater);
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    (Addr::Char(c), Addr::Con(h1)) => {
-                        if let HeapCellValue::Atom(ref n1, _) = &self.heap[h1] {
-                            if n1.is_char() {
-                                if n1.as_str().chars().next() != Some(c) {
-                                    return Some(Some(c).cmp(&n1.as_str().chars().next()));
-                                }
-                            } else {
-                                return Some(Ordering::Less);
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    (Addr::EmptyList, Addr::Con(h)) => {
-                        if let HeapCellValue::Atom(ref n1, _) = &self.heap[h] {
-                            if "[]" != n1.as_str() {
-                                return Some("[]".cmp(n1.as_str()));
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    (Addr::Con(h), Addr::EmptyList) => {
-                        if let HeapCellValue::Atom(ref n1, _) = &self.heap[h] {
-                            if "[]" != n1.as_str() {
-                                return Some(n1.as_str().cmp("[]"));
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    (Addr::Char(c1), Addr::Char(c2)) => {
-                        if c1 != c2 {
-                            return Some(c1.cmp(&c2));
-                        }
-                    }
-                    (Addr::Char(c), Addr::EmptyList) => {
-                        return if c == '[' {
-                            Some(Ordering::Less)
-                        } else {
-                            Some(c.cmp(&'['))
-                        };
-                    }
-                    (Addr::EmptyList, Addr::Char(c)) => {
-                        return if c == '[' {
-                            Some(Ordering::Greater)
-                        } else {
-                            Some('['.cmp(&c))
-                        };
-                    }
-                    (Addr::EmptyList, Addr::EmptyList) => {}
-                    _ => {
-                        return None;
-                    }
-                },
-                Some(TermOrderCategory::Compound) => match (v1, v2) {
-                    (Addr::Lis(_), Addr::Lis(_)) => {}
-                    (pstr1 @ Addr::PStrLocation(..), pstr2 @ Addr::PStrLocation(..)) => {
-                        let mut i1 = self.heap_pstr_iter(pstr1);
-                        let mut i2 = self.heap_pstr_iter(pstr2);
-
-                        let ordering = compare_pstr_prefixes(&mut i1, &mut i2);
-
-                        if let Some(ordering) = ordering {
-                            if ordering != Ordering::Equal {
-                                return Some(ordering);
-                            }
-                        } else {
-                            let (lstack, rstack) = iter.stack();
-
-                            lstack.pop();
-                            lstack.pop();
-
-                            rstack.pop();
-                            rstack.pop();
-
-                            lstack.push(i1.focus());
-                            rstack.push(i2.focus());
-                        }
-                    }
-                    (Addr::Str(h1), Addr::Str(h2)) => {
-                        if let HeapCellValue::NamedStr(a1, ref n1, _) = &self.heap[h1] {
-                            if let HeapCellValue::NamedStr(a2, ref n2, _) = &self.heap[h2] {
-                                if a1 != a2 || n1.as_str() != n2.as_str() {
-                                    return Some(
-                                        a1.cmp(&a2).then_with(|| n1.as_str().cmp(n2.as_str())),
-                                    );
-                                }
-                            } else {
-                                unreachable!()
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    (Addr::Lis(_), Addr::PStrLocation(..))
-                    | (Addr::PStrLocation(..), Addr::Lis(_)) => {}
-                    (Addr::Lis(_), Addr::Str(s)) => {
-                        if let &HeapCellValue::NamedStr(a1, ref n1, _) = &self.heap[s] {
-                            if a1 != 2 || n1.as_str() != "." {
-                                return Some(a1.cmp(&2).then_with(|| n1.as_str().cmp(".")));
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    (Addr::Str(s), Addr::Lis(_)) => {
-                        if let &HeapCellValue::NamedStr(a1, ref n1, _) = &self.heap[s] {
-                            if a1 != 2 || n1.as_str() != "." {
-                                return Some(2.cmp(&a1).then_with(|| ".".cmp(n1.as_str())));
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    (Addr::PStrLocation(..), Addr::Str(s)) => {
-                        if let &HeapCellValue::NamedStr(a1, ref n1, _) = &self.heap[s] {
-                            if a1 != 2 || n1.as_str() != "." {
-                                return Some(a1.cmp(&2).then_with(|| n1.as_str().cmp(".")));
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    (Addr::Str(s), Addr::PStrLocation(..)) => {
-                        if let &HeapCellValue::NamedStr(a1, ref n1, _) = &self.heap[s] {
-                            if a1 != 2 || n1.as_str() != "." {
-                                return Some(2.cmp(&a1).then_with(|| ".".cmp(n1.as_str())));
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    _ => {
-                        return None;
-                    }
-                },
-                None => {
-                    return None;
-                }
+            _ => {
+                self.fail = true;
             }
-        }
-
-        Some(iter.first_to_expire)
-    }
-
-    pub(super) fn reset_block(&mut self, addr: Addr) {
-        match self.store(addr) {
-            Addr::Usize(b) => self.block = b,
-            _ => self.fail = true,
-        };
+        )
     }
 
-    pub(super) fn execute_inlined(&mut self, inlined: &InlinedClauseType) {
+    pub fn execute_inlined(&mut self, inlined: &InlinedClauseType) {
         match inlined {
             &InlinedClauseType::CompareNumber(cmp, ref at_1, ref at_2) => {
                 let n1 = try_or_fail!(self, self.get_number(at_1));
@@ -2346,36 +3118,46 @@ impl MachineState {
             &InlinedClauseType::IsAtom(r1) => {
                 let d = self.store(self.deref(self[r1]));
 
-                match d {
-                    Addr::Con(h) => {
-                        if let HeapCellValue::Atom(..) = &self.heap[h] {
+                read_heap_cell!(d,
+                    (HeapCellValueTag::Atom, (_name, arity)) => {
+                        if arity == 0 {
+                            self.p += 1;
+                        } else {
+                            self.fail = true;
+                        }
+                    }
+                    (HeapCellValueTag::Char) => {
+                        self.p += 1;
+                    }
+                    _ => {
+                        self.fail = true;
+                    }
+                );
+            }
+            &InlinedClauseType::IsAtomic(r1) => {
+                let d = self.store(self.deref(self[r1]));
+
+                read_heap_cell!(d,
+                    (HeapCellValueTag::Char | HeapCellValueTag::Fixnum | HeapCellValueTag::F64 |
+                     HeapCellValueTag::Cons) => {
+                        self.p += 1;
+                    }
+                    (HeapCellValueTag::Atom, (_name, arity)) => {
+                        if arity == 0 {
                             self.p += 1;
                         } else {
                             self.fail = true;
                         }
                     }
-                    Addr::Char(_) => self.p += 1,
-                    Addr::EmptyList => self.p += 1,
-                    _ => self.fail = true,
-                };
-            }
-            &InlinedClauseType::IsAtomic(r1) => {
-                let d = self.store(self.deref(self[r1]));
-
-                match d {
-                    Addr::Char(_)
-                    | Addr::Con(_)
-                    | Addr::EmptyList
-                    | Addr::Fixnum(_)
-                    | Addr::Float(_)
-                    | Addr::Usize(_) => self.p += 1,
-                    _ => self.fail = true,
-                };
+                    _ => {
+                        self.fail = true;
+                    }
+                );
             }
             &InlinedClauseType::IsInteger(r1) => {
                 let d = self.store(self.deref(self[r1]));
 
-                match Number::try_from((d, &self.heap)) {
+                match Number::try_from(d) {
                     Ok(Number::Fixnum(_)) => {
                         self.p += 1;
                     }
@@ -2397,22 +3179,39 @@ impl MachineState {
             &InlinedClauseType::IsCompound(r1) => {
                 let d = self.store(self.deref(self[r1]));
 
-                match d {
-                    Addr::Str(_) | Addr::Lis(_) | Addr::PStrLocation(..) => self.p += 1,
-                    _ => self.fail = true,
-                };
+                read_heap_cell!(d,
+                    (HeapCellValueTag::Str | HeapCellValueTag::Lis |
+                     HeapCellValueTag::PStrLoc | HeapCellValueTag::CStr) => {
+                        self.p += 1;
+                    }
+                    (HeapCellValueTag::Atom, (_name, arity)) => {
+                        if arity > 0 {
+                            self.p += 1;
+                        } else {
+                            self.fail = true;
+                        }
+                    }
+                    _ => {
+                        self.fail = true;
+                    }
+                );
             }
             &InlinedClauseType::IsFloat(r1) => {
                 let d = self.store(self.deref(self[r1]));
 
-                match d {
-                    Addr::Float(_) => self.p += 1,
-                    _ => self.fail = true,
-                };
+                match Number::try_from(d) {
+                    Ok(Number::Float(_)) => {
+                        self.p += 1;
+                    }
+                    _ => {
+                        self.fail = true;
+                    }
+                }
             }
-            &InlinedClauseType::IsNumber(r1) => match self.store(self.deref(self[r1])) {
-                Addr::Float(_) => self.p += 1,
-                d => match Number::try_from((d, &self.heap)) {
+            &InlinedClauseType::IsNumber(r1) => {
+                let d = self.store(self.deref(self[r1]));
+
+                match Number::try_from(d) {
                     Ok(Number::Fixnum(_)) => {
                         self.p += 1;
                     }
@@ -2429,156 +3228,134 @@ impl MachineState {
                     _ => {
                         self.fail = true;
                     }
-                },
-            },
+                }
+            }
             &InlinedClauseType::IsRational(r1) => {
                 let d = self.store(self.deref(self[r1]));
 
-                match d {
-                    Addr::Con(h) => {
-                        if let HeapCellValue::Rational(_) = &self.heap[h] {
-                            self.p += 1;
-                        } else {
-                            self.fail = true;
-                        }
+                read_heap_cell!(d,
+                    (HeapCellValueTag::Cons, ptr) => {
+                        match_untyped_arena_ptr!(ptr,
+                             (ArenaHeaderTag::Rational, _r) => {
+                                 self.p += 1;
+                             }
+                             _ => {
+                                 self.fail = true;
+                             }
+                        );
                     }
                     _ => {
                         self.fail = true;
                     }
-                };
+                );
             }
             &InlinedClauseType::IsNonVar(r1) => {
                 let d = self.store(self.deref(self[r1]));
 
-                match d {
-                    Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(..) => {
+                match d.get_tag() {
+                    HeapCellValueTag::AttrVar
+                    | HeapCellValueTag::Var
+                    | HeapCellValueTag::StackVar => {
                         self.fail = true;
                     }
                     _ => {
                         self.p += 1;
                     }
-                };
+                }
             }
             &InlinedClauseType::IsVar(r1) => {
                 let d = self.store(self.deref(self[r1]));
 
-                match d {
-                    Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(_, _) => {
+                match d.get_tag() {
+                    HeapCellValueTag::AttrVar
+                    | HeapCellValueTag::Var
+                    | HeapCellValueTag::StackVar => {
                         self.p += 1;
                     }
                     _ => {
                         self.fail = true;
                     }
-                };
+                }
             }
         }
     }
 
-    fn try_functor_compound_case(
-        &mut self,
-        name: ClauseName,
-        arity: usize,
-        spec: Option<SharedOpDesc>,
-    ) {
-        let name = self.heap.to_unifiable(HeapCellValue::Atom(name, spec));
-        self.try_functor_unify_components(name, arity);
+    #[inline(always)]
+    fn try_functor_compound_case(&mut self, name: Atom, arity: usize) {
+        self.try_functor_unify_components(atom_as_cell!(name), arity);
     }
 
-    fn try_functor_unify_components(&mut self, name: Addr, arity: usize) {
-        let a2 = self[temp_v!(2)];
-        let a3 = self[temp_v!(3)];
-
-        (self.unify_fn)(self, a2, name);
+    fn try_functor_unify_components(&mut self, name: HeapCellValue, arity: usize) {
+        let a2 = self.deref(self.registers[2]);
+        self.write_literal_to_var(a2, name);
 
         if !self.fail {
-            (self.unify_fn)(self, a3, Addr::Usize(arity));
+            let a3 = self.store(self.deref(self.registers[3]));
+            self.unify_fixnum(Fixnum::build_with(arity as i64), a3);
         }
     }
 
-    fn try_functor_fabricate_struct(
-        &mut self,
-        name: ClauseName,
-        arity: usize,
-        spec: Option<SharedOpDesc>,
-        op_dir: &OpDir,
-        r: Ref,
-    ) {
-        let spec = spec.and_then(|spec| {
-            if spec.arity() != arity {
-                fetch_op_spec(name.clone(), arity, op_dir)
-            } else {
-                Some(spec)
-            }
-        });
+    fn try_functor_fabricate_struct(&mut self, name: Atom, arity: usize, r: Ref) {
+        let h = self.heap.len();
+
+        let f_a = if name == atom!(".") && arity == 2 {
+            self.heap.push(heap_loc_as_cell!(h));
+            self.heap.push(heap_loc_as_cell!(h+1));
 
-        let f_a = if name.as_str() == "." && arity == 2 {
-            Addr::Lis(self.heap.h())
+            list_loc_as_cell!(h)
         } else {
-            self.heap
-                .to_unifiable(HeapCellValue::NamedStr(arity, name, spec))
-        };
+            self.heap.push(atom_as_cell!(name, arity));
 
-        let h = self.heap.h();
+            for i in 0..arity {
+               self.heap.push(heap_loc_as_cell!(h + i + 1));
+           }
 
-        for i in 0..arity {
-            self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h + i)));
-        }
+            str_loc_as_cell!(h)
+       };
 
         (self.bind_fn)(self, r, f_a);
     }
 
-    pub(super) fn try_functor(&mut self, op_dir: &OpDir) -> CallResult {
-        let stub = MachineError::functor_stub(clause_name!("functor"), 3);
-        let a1 = self.store(self.deref(self[temp_v!(1)]));
+    pub fn try_functor(&mut self) -> CallResult {
+        let stub_gen = || functor_stub(atom!("functor"), 3);
+        let a1 = self.store(self.deref(self.registers[1]));
 
-        match a1 {
-            Addr::Stream(_) => {
-                self.fail = true;
+        read_heap_cell!(a1,
+            (HeapCellValueTag::Cons | HeapCellValueTag::Char | HeapCellValueTag::Fixnum |
+             HeapCellValueTag::F64) => {
+                self.try_functor_unify_components(a1, 0);
             }
-            Addr::Char(_)
-            | Addr::Con(_)
-            | Addr::Fixnum(_)
-            | Addr::Float(_)
-            | Addr::EmptyList
-            | Addr::Usize(_) => {
+            (HeapCellValueTag::Atom, (_name, arity)) => {
+                debug_assert_eq!(arity, 0);
                 self.try_functor_unify_components(a1, 0);
             }
-            Addr::Str(o) => match self.heap.clone(o) {
-                HeapCellValue::NamedStr(arity, name, spec) => {
-                    let spec = fetch_op_spec_from_existing(name.clone(), arity, spec, &op_dir);
-
-                    self.try_functor_compound_case(name, arity, spec)
-                }
-                _ => {
-                    self.fail = true;
-                }
-            },
-            Addr::Lis(_) | Addr::PStrLocation(..) => {
-                let spec = fetch_op_spec_from_existing(clause_name!("."), 2, None, &op_dir);
-
-                self.try_functor_compound_case(clause_name!("."), 2, spec)
+            (HeapCellValueTag::Str, s) => {
+                let (name, arity) = cell_as_atom_cell!(self.heap[s]).get_name_and_arity();
+                self.try_functor_compound_case(name, arity);
+            }
+            (HeapCellValueTag::Lis | HeapCellValueTag::PStrOffset) => {
+                self.try_functor_compound_case(atom!("."), 2);
             }
-            Addr::AttrVar(..) | Addr::HeapCell(_) | Addr::StackCell(..) => {
-                let name = self.store(self.deref(self[temp_v!(2)]));
-                let arity = self.store(self.deref(self[temp_v!(3)]));
+            (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
+                let deref_name = self.deref(self.registers[2]);
+                let store_name = self.store(deref_name);
 
-                if name.is_ref() || arity.is_ref() {
+                let arity = self.store(self.deref(self.registers[3]));
+
+                if store_name.is_var() || arity.is_var() {
                     // 8.5.1.3 a) & 8.5.1.3 b)
-                    return Err(self.error_form(MachineError::instantiation_error(), stub));
-                }
-
-                let arity = match Number::try_from((arity, &self.heap)) {
-                    Ok(Number::Fixnum(n)) => Some(n),
-                    Ok(Number::Integer(n)) => n.to_isize(),
-                    Ok(Number::Rational(n)) if n.denom() == &1 => n.numer().to_isize(),
-                    _ => match arity {
-                        arity => {
-                            return Err(self.error_form(
-                                MachineError::type_error(self.heap.h(), ValidType::Integer, arity),
-                                stub,
-                            ));
-                        }
-                    },
+                    let err = self.instantiation_error();
+                    return Err(self.error_form(err, stub_gen()));
+                }
+
+                let arity = match Number::try_from(arity) {
+                    Ok(Number::Fixnum(n)) => Some(n.get_num()),
+                    Ok(Number::Integer(n)) => n.to_i64(),
+                    Ok(Number::Rational(n)) if n.denom() == &1 => n.numer().to_i64(),
+                    _ => {
+                        let err = self.type_error(ValidType::Integer, arity);
+                        return Err(self.error_form(err, stub_gen()));
+                    }
                 };
 
                 let arity = match arity {
@@ -2589,449 +3366,276 @@ impl MachineState {
                     }
                 };
 
-                if arity > MAX_ARITY as isize {
+                if arity > MAX_ARITY as i64 {
                     // 8.5.1.3 f)
-                    let rep_err = MachineError::representation_error(RepFlag::MaxArity);
-                    return Err(self.error_form(rep_err, stub));
+                    let err = self.representation_error(RepFlag::MaxArity);
+                    return Err(self.error_form(err, stub_gen()));
                 } else if arity < 0 {
                     // 8.5.1.3 g)
-                    let arity = Number::Integer(Rc::new(Integer::from(arity)));
-                    let dom_err =
-                        MachineError::domain_error(DomainErrorType::NotLessThanZero, arity);
-
-                    return Err(self.error_form(dom_err, stub));
-                }
-
-                match name {
-                    Addr::Char(_)
-                    | Addr::Con(_)
-                    | Addr::Fixnum(_)
-                    | Addr::Float(_)
-                    | Addr::EmptyList
-                    | Addr::PStrLocation(..)
-                    | Addr::Usize(_)
-                        if arity == 0 =>
-                    {
-                        (self.unify_fn)(self, a1, name);
-                    }
-                    Addr::Con(h) => {
-                        if let HeapCellValue::Atom(name, spec) = self.heap.clone(h) {
-                            self.try_functor_fabricate_struct(
-                                name,
-                                arity as usize,
-                                spec,
-                                &op_dir,
-                                a1.as_var().unwrap(),
-                            );
-                        } else {
-                            // 8.5.1.3 e)
-                            return Err(self.error_form(
-                                MachineError::type_error(self.heap.h(), ValidType::Atom, name),
-                                stub,
-                            ));
-                        }
+                    let arity = Number::Fixnum(Fixnum::build_with(arity));
+                    let err = self.domain_error(DomainErrorType::NotLessThanZero, arity);
+
+                    return Err(self.error_form(err, stub_gen()));
+                }
+
+                read_heap_cell!(store_name,
+                    (HeapCellValueTag::Cons | HeapCellValueTag::Char | HeapCellValueTag::Fixnum |
+                     HeapCellValueTag::F64) if arity == 0 => {
+                        self.bind(a1.as_var().unwrap(), deref_name);
+                    }
+                    (HeapCellValueTag::Atom, (name, atom_arity)) => {
+                        debug_assert_eq!(atom_arity, 0);
+                        self.try_functor_fabricate_struct(
+                            name,
+                            arity as usize,
+                            a1.as_var().unwrap(),
+                        );
                     }
-                    Addr::Char(c) => {
+                    (HeapCellValueTag::Char, c) => {
+                        let c = self.atom_tbl.build_with(&c.to_string());
+
                         self.try_functor_fabricate_struct(
-                            clause_name!(c.to_string(), self.atom_tbl),
+                            c,
                             arity as usize,
-                            None,
-                            &op_dir,
                             a1.as_var().unwrap(),
                         );
                     }
                     _ => {
-                        return Err(self.error_form(
-                            MachineError::type_error(self.heap.h(), ValidType::Atomic, name),
-                            stub,
-                        ));
+                        let err = self.type_error(ValidType::Atomic, store_name);
+                        return Err(self.error_form(err, stub_gen()));
                     } // 8.5.1.3 c)
-                }
+                );
             }
             _ => {
                 self.fail = true;
             }
-        }
+        );
 
         Ok(())
     }
 
-    pub(super) fn term_dedup(&self, list: &mut Vec<Addr>) {
-        let mut result = vec![];
-
-        for a2 in list.iter() {
-            if let Some(a1) = result.last() {
-                if self.compare_term_test(&a1, &a2) == Some(Ordering::Equal) {
-                    continue;
+    pub fn try_from_list(
+        &mut self,
+        value: HeapCellValue,
+        stub_gen: impl Fn() -> FunctorStub,
+    ) -> Result<Vec<HeapCellValue>, MachineStub> {
+        let deref_v = self.deref(value);
+        let store_v = self.store(deref_v);
+
+        read_heap_cell!(store_v,
+            (HeapCellValueTag::Lis, l) => {
+                self.try_from_inner_list(vec![], l, stub_gen, store_v)
+            }
+            (HeapCellValueTag::PStrLoc, h) => {
+                self.try_from_partial_string(vec![], h, stub_gen, store_v)
+            }
+            (HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar | HeapCellValueTag::Var) => {
+                let err = self.instantiation_error();
+                Err(self.error_form(err, stub_gen()))
+            }
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                if name == atom!("[]") && arity == 0 {
+                    Ok(vec![])
+                } else {
+                    let err = self.type_error(ValidType::List, store_v);
+                    Err(self.error_form(err, stub_gen()))
                 }
             }
-
-            result.push(*a2);
-        }
-
-        *list = result;
-    }
-
-    pub(super) fn integers_to_bytevec(&self, r: RegType, caller: MachineStub) -> Vec<u8> {
-        let mut bytes: Vec<u8> = Vec::new();
-
-        match self.try_from_list(r, caller) {
-            Err(_) => {
-                unreachable!()
-            }
-            Ok(addrs) => {
-                for addr in addrs {
-                    let addr = self.store(self.deref(addr));
-
-                    match Number::try_from((addr, &self.heap)) {
-                        Ok(Number::Fixnum(n)) => {
-                            match u8::try_from(n) {
-                                Ok(b) => {
-                                    bytes.push(b);
-                                }
-                                Err(_) => {}
-                            }
-
-                            continue;
-                        }
-                        Ok(Number::Integer(n)) => {
-                            if let Some(b) = n.to_u8() {
-                                bytes.push(b);
-                            }
-
-                            continue;
-                        }
-                        _ => {}
-                    }
-                }
+           (HeapCellValueTag::CStr, cstr_atom) => {
+               let cstr = cstr_atom.as_str();
+               Ok(cstr.chars().map(|c| char_as_cell!(c)).collect())            
+           }
+            _ => {
+                let err = self.type_error(ValidType::List, store_v);
+                Err(self.error_form(err, stub_gen()))
             }
-        }
-        bytes
-    }
-
-    pub(super) fn try_from_list(
-        &self,
-        r: RegType,
-        caller: MachineStub,
-    ) -> Result<Vec<Addr>, MachineStub> {
-        let a1 = self.store(self.deref(self[r]));
-
-        match a1 {
-            Addr::Lis(l) => self.try_from_inner_list(vec![], l, caller, a1),
-            Addr::PStrLocation(h, n) => self.try_from_partial_string(vec![], h, n, caller, a1),
-            Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(..) => {
-                Err(self.error_form(MachineError::instantiation_error(), caller))
-            }
-            Addr::EmptyList => Ok(vec![]),
-            _ => Err(self.error_form(
-                MachineError::type_error(self.heap.h(), ValidType::List, a1),
-                caller,
-            )),
-        }
+        )
     }
 
     fn try_from_inner_list(
-        &self,
-        mut result: Vec<Addr>,
+        &mut self,
+        mut result: Vec<HeapCellValue>,
         mut l: usize,
-        caller: MachineStub,
-        a1: Addr,
-    ) -> Result<Vec<Addr>, MachineStub> {
-        result.push(self.heap[l].as_addr(l));
+        stub_gen: impl Fn() -> FunctorStub,
+        a1: HeapCellValue,
+    ) -> Result<Vec<HeapCellValue>, MachineStub> {
+        result.push(self.heap[l]);
         l += 1;
 
         loop {
-            match &self.heap[l] {
-                HeapCellValue::Addr(ref addr) => match self.store(self.deref(*addr)) {
-                    Addr::Lis(hcp) => {
-                        result.push(self.heap[hcp].as_addr(hcp));
-                        l = hcp + 1;
-                    }
-                    Addr::PStrLocation(h, n) => {
-                        return self.try_from_partial_string(result, h, n, caller, a1);
-                    }
-                    Addr::EmptyList => {
+            let deref_v = self.deref(self.heap[l]);
+            let store_v = self.store(self.heap[l]);
+
+            read_heap_cell!(store_v,
+                (HeapCellValueTag::Lis, hcp) => {
+                    result.push(self.heap[hcp]);
+                    l = hcp + 1;
+                }
+                (HeapCellValueTag::PStrOffset) => {
+                    return self.try_from_partial_string(result, deref_v.get_value(), stub_gen, a1);
+                }
+                (HeapCellValueTag::Atom, (name, arity)) => {
+                    if name == atom!("[]") && arity == 0 {
                         break;
+                    } else {
+                        let err = self.type_error(ValidType::List, a1);
+                        return Err(self.error_form(err, stub_gen()));
                     }
-                    Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(..) => {
-                        return Err(self.error_form(MachineError::instantiation_error(), caller))
-                    }
-                    _ => {
-                        return Err(self.error_form(
-                            MachineError::type_error(self.heap.h(), ValidType::List, a1),
-                            caller,
-                        ))
-                    }
-                },
+                }
                 _ => {
-                    return Err(self.error_form(
-                        MachineError::type_error(self.heap.h(), ValidType::List, a1),
-                        caller,
-                    ))
+                    if store_v.is_var() {
+                        let err = self.instantiation_error();
+                        return Err(self.error_form(err, stub_gen()));
+                    } else {
+                        let err = self.type_error(ValidType::List, a1);
+                        return Err(self.error_form(err, stub_gen()));
+                    }
                 }
-            }
+            );
         }
 
         Ok(result)
     }
 
     fn try_from_partial_string(
-        &self,
-        mut chars: Vec<Addr>,
-        mut h: usize,
-        mut n: usize,
-        caller: MachineStub,
-        a1: Addr,
-    ) -> Result<Vec<Addr>, MachineStub> {
-        loop {
-            if let &HeapCellValue::PartialString(ref pstr, has_tail) = &self.heap[h] {
-                chars.extend(pstr.range_from(n..).map(Addr::Char));
-
-                if !has_tail {
-                    return Ok(chars);
+        &mut self,
+        mut chars: Vec<HeapCellValue>,
+        h: usize,
+        stub_gen: impl Fn() -> FunctorStub,
+        a1: HeapCellValue,
+    ) -> Result<Vec<HeapCellValue>, MachineStub> {
+        let mut heap_pstr_iter = HeapPStrIter::new(&self.heap, h);
+
+        while let Some(iteratee) = heap_pstr_iter.next() {
+            match iteratee {
+                PStrIteratee::Char(_, c) =>
+                    chars.push(char_as_cell!(c)),
+                PStrIteratee::PStrSegment(_, pstr_atom, n) => {
+                    let pstr = PartialString::from(pstr_atom);
+                    chars.extend(pstr.as_str_from(n).chars().map(|c| char_as_cell!(c)));
                 }
+            }
+        }
 
-                let tail = self.heap[h + 1].as_addr(h + 1);
-
-                match self.store(self.deref(tail)) {
-                    Addr::EmptyList => {
-                        return Ok(chars);
-                    }
-                    Addr::Lis(l) => {
-                        return self.try_from_inner_list(chars, l, caller, a1);
-                    }
-                    Addr::PStrLocation(h1, n1) => {
-                        chars.push(Addr::Char('\u{0}'));
-
-                        h = h1;
-                        n = n1;
-                    }
-                    _ => {
-                        return Err(self.error_form(
-                            MachineError::type_error(self.heap.h(), ValidType::List, a1),
-                            caller,
-                        ))
-                    }
+        match self.heap[h].get_tag() {
+            HeapCellValueTag::PStr => {
+                if heap_pstr_iter.at_string_terminator() {
+                    Ok(chars)
+                } else {
+                    read_heap_cell!(self.heap[heap_pstr_iter.focus()],
+                        (HeapCellValueTag::Lis, l) => {
+                            self.try_from_inner_list(chars, l, stub_gen, a1)
+                        }
+                        (HeapCellValueTag::Atom, (name, arity)) => {
+                            if name == atom!(".") && arity == 2 {
+                                let l = heap_pstr_iter.focus() + 1;
+                                self.try_from_inner_list(chars, l, stub_gen, a1)
+                            } else {
+                                let err = self.type_error(ValidType::List, a1);
+                                Err(self.error_form(err, stub_gen()))
+                            }
+                        }
+                        _ => {
+                            let err = self.type_error(ValidType::List, a1);
+                            Err(self.error_form(err, stub_gen()))
+                        }
+                    )
                 }
-            } else {
+            }
+            HeapCellValueTag::CStr => Ok(chars),
+            _ => {
                 unreachable!()
             }
         }
     }
 
-    // see 8.4.4.3 of Draft Technical Corrigendum 2 for an error guide.
-    pub(super) fn project_onto_key(&self, a: Addr) -> Result<Addr, MachineStub> {
-        let stub = MachineError::functor_stub(clause_name!("keysort"), 2);
-
-        match self.store(self.deref(a)) {
-            Addr::HeapCell(_) | Addr::StackCell(..) => {
-                Err(self.error_form(MachineError::instantiation_error(), stub))
-            }
-            Addr::Str(s) => match self.heap.clone(s) {
-                HeapCellValue::NamedStr(2, ref name, Some(_)) if *name == clause_name!("-") => {
-                    Ok(Addr::HeapCell(s + 1))
-                }
-                _ => Err(self.error_form(
-                    MachineError::type_error(
-                        self.heap.h(),
-                        ValidType::Pair,
-                        self.heap[s].as_addr(s),
-                    ),
-                    stub,
-                )),
-            },
-            a => Err(self.error_form(
-                MachineError::type_error(self.heap.h(), ValidType::Pair, a),
-                stub,
-            )),
+    // returns true on failure.
+    pub fn ground_test(&mut self) -> bool {
+        if self.registers[1].is_constant() {
+            return false;
         }
-    }
 
-    pub(super) fn copy_term(&mut self, attr_var_policy: AttrVarPolicy) {
-        let old_h = self.heap.h();
+        let value = self.registers[1];
 
-        let a1 = self[temp_v!(1)];
-        let a2 = self[temp_v!(2)];
-
-        copy_term(CopyTerm::new(self), a1, attr_var_policy);
+        for v in stackful_preorder_iter(&mut self.heap, value) {
+            if v.is_var() {
+                return true;
+            }
+        }
 
-        (self.unify_fn)(self, Addr::HeapCell(old_h), a2);
+        false
     }
 
-    // returns true on failure.
-    pub(super) fn structural_eq_test(&self) -> bool {
-        let a1 = self[temp_v!(1)];
-        let a2 = self[temp_v!(2)];
-
-        let mut var_pairs = IndexMap::new();
-
-        let iter = self.zipped_acyclic_pre_order_iter(a1, a2);
-
-        for (v1, v2) in iter {
-            match (
-                self.heap.index_addr(&v1).as_ref(),
-                self.heap.index_addr(&v2).as_ref(),
-            ) {
-                (
-                    HeapCellValue::Addr(Addr::Lis(_)),
-                    HeapCellValue::Addr(Addr::PStrLocation(..)),
-                )
-                | (
-                    HeapCellValue::Addr(Addr::PStrLocation(..)),
-                    HeapCellValue::Addr(Addr::Lis(_)),
-                ) => {}
-                (HeapCellValue::NamedStr(ar1, n1, _), HeapCellValue::NamedStr(ar2, n2, _)) => {
-                    if ar1 != ar2 || n1 != n2 {
-                        return true;
-                    }
-                }
-                (HeapCellValue::Addr(Addr::Lis(_)), HeapCellValue::Addr(Addr::Lis(_))) => {}
-                (
-                    &HeapCellValue::Addr(v1 @ Addr::HeapCell(_)),
-                    &HeapCellValue::Addr(v2 @ Addr::AttrVar(_)),
-                )
-                | (
-                    &HeapCellValue::Addr(v1 @ Addr::StackCell(..)),
-                    &HeapCellValue::Addr(v2 @ Addr::AttrVar(_)),
-                )
-                | (
-                    &HeapCellValue::Addr(v1 @ Addr::AttrVar(_)),
-                    &HeapCellValue::Addr(v2 @ Addr::AttrVar(_)),
-                )
-                | (
-                    &HeapCellValue::Addr(v1 @ Addr::AttrVar(_)),
-                    &HeapCellValue::Addr(v2 @ Addr::HeapCell(_)),
-                )
-                | (
-                    &HeapCellValue::Addr(v1 @ Addr::AttrVar(_)),
-                    &HeapCellValue::Addr(v2 @ Addr::StackCell(..)),
-                )
-                | (
-                    &HeapCellValue::Addr(v1 @ Addr::HeapCell(_)),
-                    &HeapCellValue::Addr(v2 @ Addr::HeapCell(_)),
-                )
-                | (
-                    &HeapCellValue::Addr(v1 @ Addr::HeapCell(_)),
-                    &HeapCellValue::Addr(v2 @ Addr::StackCell(..)),
-                )
-                | (
-                    &HeapCellValue::Addr(v1 @ Addr::StackCell(..)),
-                    &HeapCellValue::Addr(v2 @ Addr::StackCell(..)),
-                )
-                | (
-                    &HeapCellValue::Addr(v1 @ Addr::StackCell(..)),
-                    &HeapCellValue::Addr(v2 @ Addr::HeapCell(_)),
-                ) => match (var_pairs.get(&v1), var_pairs.get(&v2)) {
-                    (Some(ref v2_p), Some(ref v1_p)) if **v1_p == v1 && **v2_p == v2 => {
-                        continue;
-                    }
-                    (Some(_), _) | (_, Some(_)) => {
-                        return true;
-                    }
-                    (None, None) => {
-                        var_pairs.insert(v1, v2);
-                        var_pairs.insert(v2, v1);
-                    }
-                },
-                (
-                    HeapCellValue::PartialString(ref pstr1, has_tail_1),
-                    HeapCellValue::PartialString(ref pstr2, has_tail_2),
-                ) => {
-                    if has_tail_1 != has_tail_2 {
-                        return true;
-                    }
-
-                    let pstr1_iter = pstr1.range_from(0..);
-                    let pstr2_iter = pstr2.range_from(0..);
+    pub fn integers_to_bytevec(
+        &mut self,
+        value: HeapCellValue,
+        stub_gen: impl Fn() -> FunctorStub,
+    ) -> Vec<u8> {
+        let mut bytes: Vec<u8> = Vec::new();
 
-                    for (c1, c2) in pstr1_iter.zip(pstr2_iter) {
-                        if c1 != c2 {
-                            return true;
-                        }
-                    }
-                }
-                (
-                    HeapCellValue::Addr(Addr::PStrLocation(..)),
-                    HeapCellValue::Addr(Addr::PStrLocation(..)),
-                ) => {}
-                (
-                    HeapCellValue::Atom(ref n1, ref spec_1),
-                    HeapCellValue::Atom(ref n2, ref spec_2),
-                ) => {
-                    if n1 != n2 || spec_1 != spec_2 {
-                        return true;
-                    }
-                }
-                (HeapCellValue::DBRef(ref db_ref_1), HeapCellValue::DBRef(ref db_ref_2)) => {
-                    if db_ref_1 != db_ref_2 {
-                        return true;
-                    }
-                }
-                (v1, v2) => {
-                    if let Ok(n1) = Number::try_from(v1) {
-                        if let Ok(n2) = Number::try_from(v2) {
-                            if n1 != n2 {
-                                return true;
-                            } else {
-                                continue;
-                            }
-                        } else {
-                            return true;
-                        }
-                    }
+        match self.try_from_list(value, stub_gen) {
+            Err(_) => {
+                unreachable!()
+            }
+            Ok(addrs) => {
+                for addr in addrs {
+                    let addr = self.store(self.deref(addr));
 
-                    match (v1, v2) {
-                        (HeapCellValue::Addr(a1), HeapCellValue::Addr(a2)) => {
-                            if a1 != a2 {
-                                return true;
+                    match Number::try_from(addr) {
+                        Ok(Number::Fixnum(n)) => match u8::try_from(n.get_num()) {
+                            Ok(b) => bytes.push(b),
+                            Err(_) => {}
+                        },
+                        Ok(Number::Integer(n)) => {
+                            if let Some(b) = n.to_u8() {
+                                bytes.push(b);
                             }
                         }
-                        _ => {
-                            return true;
-                        }
+                        _ => {}
                     }
                 }
             }
         }
 
-        false
+        bytes
     }
 
-    // returns true on failure.
-    pub(super) fn ground_test(&self) -> bool {
-        let a = self.store(self.deref(self[temp_v!(1)]));
-
-        for v in self.acyclic_pre_order_iter(a) {
-            match v {
-                Addr::HeapCell(..) => return true,
-                Addr::StackCell(..) => return true,
-                Addr::AttrVar(..) => return true,
-                _ => {}
-            }
+    // see 8.4.4.3 of Draft Technical Corrigendum 2 for an error guide.
+    pub fn project_onto_key(&mut self, value: HeapCellValue) -> Result<HeapCellValue, MachineStub> {
+        let stub_gen = || functor_stub(atom!("keysort"), 2);
+        let store_v = self.store(self.deref(value));
+
+        if store_v.is_var() {
+            let err = self.instantiation_error();
+            return Err(self.error_form(err, stub_gen()));
         }
 
-        false
+        read_heap_cell!(store_v,
+            (HeapCellValueTag::Str, s) => {
+                let (name, arity) = cell_as_atom_cell!(self.heap[s]).get_name_and_arity();
+
+                if name == atom!("-") && arity == 2 {
+                    Ok(heap_loc_as_cell!(s + 1))
+                } else {
+                    let err = self.type_error(ValidType::Pair, self.heap[s]);
+                    Err(self.error_form(err, stub_gen()))
+                }
+            }
+            _ => {
+                let err = self.type_error(ValidType::Pair, store_v);
+                Err(self.error_form(err, stub_gen()))
+            }
+        )
     }
 
-    pub(super) fn setup_built_in_call(&mut self, ct: BuiltInClauseType) {
+    pub fn setup_built_in_call(&mut self, ct: BuiltInClauseType) {
         self.num_of_args = ct.arity();
         self.b0 = self.b;
 
         self.p = CodePtr::BuiltInClause(ct, self.p.local());
     }
 
-    pub(super) fn allocate(&mut self, num_cells: usize) {
-        let e = self.stack.allocate_and_frame(num_cells);
-        let and_frame = self.stack.index_and_frame_mut(e);
-
-        and_frame.prelude.e = self.e;
-        and_frame.prelude.cp = self.cp;
-
-        self.e = e;
-        self.p += 1;
-    }
-
-    pub(super) fn deallocate(&mut self) {
+    pub fn deallocate(&mut self) {
         let e = self.e;
         let frame = self.stack.index_and_frame(e);
 
@@ -3046,8 +3650,8 @@ impl MachineState {
     }
 
     fn throw_interrupt_exception(&mut self) {
-        let err = MachineError::interrupt_error();
-        let src = functor!("repl");
+        let err = self.interrupt_error();
+        let src = functor_stub(atom!("repl"), 0);
         let err = self.error_form(err, src);
 
         self.throw_exception(err);
@@ -3121,11 +3725,8 @@ impl MachineState {
                     self.p = CodePtr::Local(self.cp);
                 }
             }
-            &ClauseType::Named(ref name, _, ref idx) | &ClauseType::Op(ref name, _, ref idx) => {
-                try_or_fail!(
-                    self,
-                    call_policy.context_call(self, name.clone(), arity, idx)
-                )
+            &ClauseType::Named(ref name, _, ref idx) => {
+                try_or_fail!(self, call_policy.context_call(self, *name, arity, idx))
             }
             &ClauseType::System(ref ct) => try_or_fail!(
                 self,
@@ -3144,7 +3745,7 @@ impl MachineState {
         self.last_call = false;
     }
 
-    pub(super) fn execute_ctrl_instr(
+    pub fn execute_ctrl_instr(
         &mut self,
         indices: &mut IndexStore,
         code_repo: &CodeRepo,
@@ -3215,7 +3816,8 @@ impl MachineState {
                             self.cc,
                         ) {
                             Some(_) => {
-                                self.registers[self.num_of_args + 1] = Addr::Usize(self.cc);
+                                self.registers[self.num_of_args + 1] =
+                                    fixnum_as_cell!(Fixnum::build_with(self.cc as i64));
                                 self.num_of_args += 1;
 
                                 self.execute_indexed_choice_instr(
@@ -3240,10 +3842,7 @@ impl MachineState {
                             .univ_prelude
                             .num_cells;
 
-                        self.cc = match self.stack.index_or_frame(self.b)[n - 1] {
-                            Addr::Usize(cc) => cc,
-                            _ => unreachable!(),
-                        };
+                        self.cc = cell_as_fixnum!(self.stack[n - 1]).get_num() as usize;
 
                         if is_next_clause {
                             match code_repo.find_living_dynamic(
@@ -3264,7 +3863,7 @@ impl MachineState {
                                 }
                             }
                         } else {
-                            try_or_fail!(self, call_policy.trust(self, offset, global_variables,))
+                            try_or_fail!(self, call_policy.trust(self, offset, global_variables))
                         }
                     }
                 }
@@ -3295,7 +3894,7 @@ impl MachineState {
                 or_frame.prelude.b = self.b;
                 or_frame.prelude.bp = self.p.local() + 1;
                 or_frame.prelude.tr = self.tr;
-                or_frame.prelude.h = self.heap.h();
+                or_frame.prelude.h = self.heap.len();
                 or_frame.prelude.b0 = self.b0;
 
                 self.b = b;
@@ -3304,7 +3903,7 @@ impl MachineState {
                     self.stack.index_or_frame_mut(b)[i - 1] = self.registers[i];
                 }
 
-                self.hb = self.heap.h();
+                self.hb = self.heap.len();
                 self.p = CodePtr::Local(dir_entry!(self.p.local().abs_loc() + offset));
             }
             &IndexedChoiceInstruction::Retry(l) => {
@@ -3344,7 +3943,8 @@ impl MachineState {
 
                                 match code_repo.find_living_dynamic_else(p + next_i, self.cc) {
                                     Some(_) => {
-                                        self.registers[self.num_of_args + 1] = Addr::Usize(self.cc);
+                                        self.registers[self.num_of_args + 1] =
+                                            fixnum_as_cell!(Fixnum::build_with(self.cc as i64));
                                         self.num_of_args += 1;
 
                                         self.execute_choice_instr(
@@ -3369,10 +3969,8 @@ impl MachineState {
                                     .univ_prelude
                                     .num_cells;
 
-                                self.cc = match self.stack.index_or_frame(self.b)[n - 1] {
-                                    Addr::Usize(cc) => cc,
-                                    _ => unreachable!(),
-                                };
+                                self.cc = cell_as_fixnum!(self.stack.index_or_frame(self.b)[n - 1])
+                                    .get_num() as usize;
 
                                 if next_i > 0 {
                                     match code_repo.find_living_dynamic_else(p + next_i, self.cc) {
@@ -3389,15 +3987,12 @@ impl MachineState {
                                         None => {
                                             try_or_fail!(
                                                 self,
-                                                call_policy.trust_me(self, global_variables,)
+                                                call_policy.trust_me(self, global_variables)
                                             )
                                         }
                                     }
                                 } else {
-                                    try_or_fail!(
-                                        self,
-                                        call_policy.trust_me(self, global_variables,)
-                                    )
+                                    try_or_fail!(self, call_policy.trust_me(self, global_variables))
                                 }
                             }
                         }
@@ -3423,7 +4018,8 @@ impl MachineState {
                             FirstOrNext::First => {
                                 match code_repo.find_living_dynamic_else(p + next_i, self.cc) {
                                     Some(_) => {
-                                        self.registers[self.num_of_args + 1] = Addr::Usize(self.cc);
+                                        self.registers[self.num_of_args + 1] =
+                                            fixnum_as_cell!(Fixnum::build_with(self.cc as i64));
                                         self.num_of_args += 1;
 
                                         self.execute_choice_instr(
@@ -3448,10 +4044,8 @@ impl MachineState {
                                     .univ_prelude
                                     .num_cells;
 
-                                self.cc = match self.stack.index_or_frame(self.b)[n - 1] {
-                                    Addr::Usize(cc) => cc,
-                                    _ => unreachable!(),
-                                };
+                                self.cc = cell_as_fixnum!(self.stack.index_or_frame(self.b)[n - 1])
+                                    .get_num() as usize;
 
                                 if next_i > 0 {
                                     match code_repo.find_living_dynamic_else(p + next_i, self.cc) {
@@ -3499,7 +4093,7 @@ impl MachineState {
                 or_frame.prelude.b = self.b;
                 or_frame.prelude.bp = self.p.local() + offset;
                 or_frame.prelude.tr = self.tr;
-                or_frame.prelude.h = self.heap.h();
+                or_frame.prelude.h = self.heap.len();
                 or_frame.prelude.b0 = self.b0;
 
                 self.b = b;
@@ -3508,7 +4102,7 @@ impl MachineState {
                     self.stack.index_or_frame_mut(b)[i - 1] = self.registers[i];
                 }
 
-                self.hb = self.heap.h();
+                self.hb = self.heap.len();
                 self.p += 1;
             }
             &ChoiceInstruction::DefaultRetryMeElse(offset) => {
@@ -3557,14 +4151,14 @@ impl MachineState {
             &CutInstruction::GetLevel(r) => {
                 let b0 = self.b0;
 
-                self[r] = Addr::CutPoint(b0);
+                self[r] = fixnum_as_cell!(Fixnum::build_with(b0 as i64));
                 self.p += 1;
             }
             &CutInstruction::GetLevelAndUnify(r) => {
                 let b0 = self[perm_v!(1)];
                 let a = self[r];
 
-                (self.unify_fn)(self, a, b0);
+                unify_fn!(self, a, b0);
                 self.p += 1;
             }
             &CutInstruction::Cut(r) => {
diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs
new file mode 100644 (file)
index 0000000..540db78
--- /dev/null
@@ -0,0 +1,766 @@
+pub use crate::arena::*;
+pub use crate::atom_table::*;
+use crate::heap_print::*;
+pub use crate::machine::heap::*;
+pub use crate::machine::Machine;
+pub use crate::machine::machine_state::*;
+pub use crate::machine::stack::*;
+pub use crate::machine::streams::*;
+pub use crate::macros::*;
+pub use crate::parser::ast::*;
+use crate::read::*;
+pub use crate::types::*;
+
+#[cfg(test)]
+use crate::machine::copier::CopierTarget;
+
+#[cfg(test)]
+use std::ops::{Deref, DerefMut, Index, IndexMut};
+
+// a mini-WAM for test purposes.
+
+pub struct MockWAM {
+    pub machine_st: MachineState,
+    pub op_dir: OpDir,
+    pub flags: MachineFlags,
+}
+
+impl MockWAM {
+    pub fn new() -> Self {
+        let op_dir = default_op_dir();
+
+        Self {
+            machine_st: MachineState::new(),
+            op_dir,
+            flags: MachineFlags::default(),
+        }
+    }
+
+    pub fn write_parsed_term_to_heap(
+        &mut self,
+        input_stream: Stream,
+    ) -> Result<TermWriteResult, ParserError> {
+        self.machine_st.read(input_stream, &self.op_dir)
+    }
+
+    pub fn parse_and_write_parsed_term_to_heap(
+        &mut self,
+        term_string: &'static str,
+    ) -> Result<TermWriteResult, ParserError> {
+        let stream = Stream::from_static_string(term_string, &mut self.machine_st.arena);
+        self.write_parsed_term_to_heap(stream)
+    }
+
+    pub fn parse_and_print_term(
+        &mut self,
+        term_string: &'static str,
+    ) -> Result<String, ParserError> {
+        let term_write_result = self.parse_and_write_parsed_term_to_heap(term_string)?;
+
+        print_heap_terms(self.machine_st.heap.iter(), term_write_result.heap_loc);
+
+        let mut printer = HCPrinter::new(
+            &mut self.machine_st.heap,
+            &mut self.machine_st.arena,
+            &self.op_dir,
+            PrinterOutputter::new(),
+            heap_loc_as_cell!(term_write_result.heap_loc),
+        );
+
+        printer.var_names = term_write_result
+            .var_dict
+            .into_iter()
+            .map(|(var, cell)| (cell, var))
+            .collect();
+
+        Ok(printer.print().result())
+    }
+}
+
+#[cfg(test)]
+pub struct TermCopyingMockWAM<'a> {
+    pub wam: &'a mut MockWAM,
+}
+
+#[cfg(test)]
+impl<'a> Index<usize> for TermCopyingMockWAM<'a> {
+    type Output = HeapCellValue;
+
+    fn index(&self, index: usize) -> &HeapCellValue {
+        &self.wam.machine_st.heap[index]
+    }
+}
+
+#[cfg(test)]
+impl<'a> IndexMut<usize> for TermCopyingMockWAM<'a> {
+    #[inline]
+    fn index_mut(&mut self, index: usize) -> &mut HeapCellValue {
+        &mut self.wam.machine_st.heap[index]
+    }
+}
+
+#[cfg(test)]
+impl<'a> Deref for TermCopyingMockWAM<'a> {
+    type Target = MockWAM;
+
+    fn deref(&self) -> &Self::Target {
+        &self.wam
+    }
+}
+
+#[cfg(test)]
+impl<'a> DerefMut for TermCopyingMockWAM<'a> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.wam
+    }
+}
+
+#[cfg(test)]
+impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
+    fn store(&self, val: HeapCellValue) -> HeapCellValue {
+        read_heap_cell!(val,
+            (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
+                self.wam.machine_st.heap[h]
+            }
+            (HeapCellValueTag::StackVar, s) => {
+                self.wam.machine_st.stack[s]
+            }
+            _ => {
+                val
+            }
+        )
+    }
+
+    fn deref(&self, mut val: HeapCellValue) -> HeapCellValue {
+        loop {
+            let value = self.store(val);
+
+            if value.is_var() && value != val {
+                val = value;
+                continue;
+            }
+
+            return val;
+        }
+    }
+
+    fn push(&mut self, val: HeapCellValue) {
+        self.wam.machine_st.heap.push(val);
+    }
+
+    fn stack(&mut self) -> &mut Stack {
+        &mut self.wam.machine_st.stack
+    }
+
+    fn threshold(&self) -> usize {
+        self.wam.machine_st.heap.len()
+    }
+}
+
+#[cfg(test)]
+pub fn all_cells_marked_and_unforwarded(heap: &[HeapCellValue]) {
+    for (idx, cell) in heap.iter().enumerate() {
+        assert_eq!(
+            cell.get_mark_bit(),
+            true,
+            "cell {:?} at index {} is not marked",
+            cell,
+            idx
+        );
+        assert!(
+            cell.get_forwarding_bit() != Some(true),
+            "cell {:?} at index {} is forwarded",
+            cell,
+            idx
+        );
+    }
+}
+
+#[cfg(test)]
+pub fn all_cells_unmarked(heap: &Heap) {
+    for (idx, cell) in heap.iter().enumerate() {
+        assert!(
+            !cell.get_mark_bit(),
+            "cell {:?} at index {} is still marked",
+            cell,
+            idx
+        );
+
+        assert!(
+            cell.get_forwarding_bit() != Some(true),
+            "cell {:?} at index {} is still forwarded",
+            cell,
+            idx
+        );
+    }
+}
+
+#[cfg(test)]
+pub(crate) fn write_parsed_term_to_heap(
+    machine_st: &mut MachineState,
+    input_stream: Stream,
+    op_dir: &OpDir,
+) -> Result<TermWriteResult, ParserError> {
+    machine_st.read(input_stream, op_dir)
+}
+
+#[cfg(test)]
+pub(crate) fn parse_and_write_parsed_term_to_heap(
+    machine_st: &mut MachineState,
+    term_string: &'static str,
+    op_dir: &OpDir,
+) -> Result<TermWriteResult, ParserError> {
+    let stream = Stream::from_static_string(term_string, &mut machine_st.arena);
+    write_parsed_term_to_heap(machine_st, stream, op_dir)
+}
+
+impl Machine {
+    pub fn test_load_file(&mut self, file: &str) -> Vec<u8> {
+        use std::io::Read;
+
+        let old_output = std::mem::replace(
+            &mut self.user_output,
+            Stream::from_owned_string("".to_owned(), &mut self.machine_st.arena),
+        );
+
+        let stream = Stream::from_owned_string(
+            std::fs::read_to_string(AsRef::<std::path::Path>::as_ref(file)).unwrap(),
+            &mut self.machine_st.arena,
+        );
+
+        self.load_file(file.into(), stream);
+
+        let output = self.user_output.bytes().map(|b| b.unwrap()).collect();
+        self.user_output = old_output;
+        output
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn unify_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::In),
+            OpDesc::build_with(500, YFX as u8),
+        );
+        op_dir.insert(
+            (atom!("/"), Fixity::In),
+            OpDesc::build_with(400, YFX as u8),
+        );
+        op_dir.insert(
+            (atom!("="), Fixity::In),
+            OpDesc::build_with(700, XFX as u8),
+        );
+
+        {
+            parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap();
+
+            let term_write_result_2 =
+                parse_and_write_parsed_term_to_heap(&mut wam, "f(b,a).", &op_dir).unwrap();
+
+            unify!(
+                wam,
+                str_loc_as_cell!(0),
+                str_loc_as_cell!(term_write_result_2.heap_loc)
+            );
+
+            assert!(wam.fail);
+        }
+
+        all_cells_unmarked(&wam.heap);
+
+        wam.fail = false;
+        wam.heap.clear();
+
+        {
+            parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap();
+
+            let term_write_result_2 =
+                parse_and_write_parsed_term_to_heap(&mut wam, "f(b,b).", &op_dir).unwrap();
+
+            unify!(
+                wam,
+                str_loc_as_cell!(1),
+                heap_loc_as_cell!(term_write_result_2.heap_loc)
+            );
+
+            assert!(!wam.fail);
+        }
+
+        all_cells_unmarked(&wam.heap);
+
+        wam.fail = false;
+        wam.heap.clear();
+
+        {
+            parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap();
+
+            let term_write_result_2 =
+                parse_and_write_parsed_term_to_heap(&mut wam, "f(f(A),Y).", &op_dir).unwrap();
+
+            unify!(
+                wam,
+                heap_loc_as_cell!(0),
+                heap_loc_as_cell!(term_write_result_2.heap_loc)
+            );
+
+            assert!(!wam.fail);
+        }
+
+        all_cells_unmarked(&wam.heap);
+
+        wam.fail = false;
+        wam.heap.clear();
+
+        {
+            parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap();
+
+            let term_write_result_2 =
+                parse_and_write_parsed_term_to_heap(&mut wam, "f(f(A),Y).", &op_dir).unwrap();
+
+            unify!(
+                wam,
+                heap_loc_as_cell!(0),
+                heap_loc_as_cell!(term_write_result_2.heap_loc)
+            );
+
+            assert!(!wam.fail);
+        }
+
+        all_cells_unmarked(&wam.heap);
+
+        wam.fail = false;
+        wam.heap.clear();
+
+        {
+            parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap();
+
+            let term_write_result_2 =
+                parse_and_write_parsed_term_to_heap(&mut wam, "f(f(A),A).", &op_dir).unwrap();
+
+            unify!(
+                wam,
+                heap_loc_as_cell!(0),
+                heap_loc_as_cell!(term_write_result_2.heap_loc)
+            );
+
+            assert!(!wam.fail);
+        }
+
+        all_cells_unmarked(&wam.heap);
+
+        wam.fail = false;
+        wam.heap.clear();
+
+        {
+            parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap();
+
+            let term_write_result_2 =
+                parse_and_write_parsed_term_to_heap(&mut wam, "f(A,f(A)).", &op_dir).unwrap();
+
+            all_cells_unmarked(&wam.heap);
+
+            unify!(
+                wam,
+                heap_loc_as_cell!(0),
+                heap_loc_as_cell!(term_write_result_2.heap_loc)
+            );
+
+            assert!(!wam.fail);
+        }
+
+        all_cells_unmarked(&wam.heap);
+
+        wam.heap.clear();
+
+        wam.heap.push(pstr_as_cell!(atom!("this is a string")));
+        wam.heap.push(heap_loc_as_cell!(1));
+
+        wam.heap.push(pstr_as_cell!(atom!("this is a string")));
+        wam.heap.push(pstr_loc_as_cell!(4));
+
+        wam.heap.push(pstr_offset_as_cell!(0));
+        wam.heap.push(fixnum_as_cell!(Fixnum::build_with(6)));
+
+        unify!(wam, pstr_loc_as_cell!(0), pstr_loc_as_cell!(2));
+
+        assert!(!wam.fail);
+
+        assert_eq!(wam.heap[1], pstr_loc_as_cell!(4));
+
+        all_cells_unmarked(&wam.heap);
+
+        wam.heap.clear();
+
+        wam.heap.push(list_loc_as_cell!(1));
+        wam.heap.push(atom_as_cell!(atom!("a")));
+        wam.heap.push(list_loc_as_cell!(3));
+        wam.heap.push(atom_as_cell!(atom!("b")));
+        wam.heap.push(heap_loc_as_cell!(0));
+
+        wam.heap.push(list_loc_as_cell!(6));
+        wam.heap.push(atom_as_cell!(atom!("a")));
+        wam.heap.push(list_loc_as_cell!(8));
+        wam.heap.push(atom_as_cell!(atom!("b")));
+        wam.heap.push(heap_loc_as_cell!(5));
+
+        unify!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(5));
+
+        assert!(!wam.fail);
+
+        all_cells_unmarked(&wam.heap);
+
+        wam.heap.clear();
+
+        wam.heap.push(list_loc_as_cell!(1));
+        wam.heap.push(atom_as_cell!(atom!("a")));
+        wam.heap.push(list_loc_as_cell!(3));
+        wam.heap.push(atom_as_cell!(atom!("b")));
+        wam.heap.push(heap_loc_as_cell!(0));
+
+        wam.heap.push(list_loc_as_cell!(6));
+        wam.heap.push(atom_as_cell!(atom!("a")));
+        wam.heap.push(list_loc_as_cell!(8));
+        wam.heap.push(atom_as_cell!(atom!("c")));
+        wam.heap.push(heap_loc_as_cell!(5));
+
+        unify!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(5));
+
+        assert!(wam.fail);
+
+        wam.fail = false;
+        all_cells_unmarked(&wam.heap);
+        wam.heap.clear();
+
+        wam.heap.push(list_loc_as_cell!(1));
+        wam.heap.push(atom_as_cell!(atom!("a")));
+        wam.heap.push(list_loc_as_cell!(3));
+        wam.heap.push(atom_as_cell!(atom!("b")));
+        wam.heap.push(heap_loc_as_cell!(5));
+
+        wam.heap.push(list_loc_as_cell!(6));
+        wam.heap.push(atom_as_cell!(atom!("a")));
+        wam.heap.push(list_loc_as_cell!(8));
+        wam.heap.push(atom_as_cell!(atom!("b")));
+        wam.heap.push(heap_loc_as_cell!(0));
+
+        unify!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(5));
+
+        assert!(!wam.fail);
+        all_cells_unmarked(&wam.heap);
+        wam.heap.clear();
+
+        {
+            let term_write_result_1 =
+                parse_and_write_parsed_term_to_heap(&mut wam, "X = g(X,y).", &op_dir).unwrap();
+
+            print_heap_terms(wam.heap.iter(), term_write_result_1.heap_loc);
+
+            unify!(wam, heap_loc_as_cell!(2), str_loc_as_cell!(4));
+
+            assert_eq!(wam.heap[2], str_loc_as_cell!(4));
+        }
+    }
+
+    #[test]
+    fn test_unify_with_occurs_check() {
+        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::In),
+            OpDesc::build_with(400, YFX as u8),
+        );
+        op_dir.insert(
+            (atom!("/"), Fixity::In),
+            OpDesc::build_with(400, YFX as u8),
+        );
+
+        {
+            parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap();
+
+            let term_write_result_2 =
+                parse_and_write_parsed_term_to_heap(&mut wam, "f(A,f(A)).", &op_dir).unwrap();
+
+            all_cells_unmarked(&wam.heap);
+
+            unify_with_occurs_check!(
+                wam,
+                str_loc_as_cell!(0),
+                str_loc_as_cell!(term_write_result_2.heap_loc)
+            );
+
+            assert!(wam.fail);
+        }
+    }
+
+    #[test]
+    fn test_term_compare() {
+        use ordered_float::OrderedFloat;
+        use std::cmp::Ordering;
+
+        let mut wam = MachineState::new();
+
+        wam.heap.push(heap_loc_as_cell!(0));
+        wam.heap.push(heap_loc_as_cell!(1));
+
+        assert_eq!(
+            compare_term_test!(wam, wam.heap[0], wam.heap[1]),
+            Some(Ordering::Less)
+        );
+
+        assert_eq!(
+            compare_term_test!(wam, wam.heap[1], wam.heap[0]),
+            Some(Ordering::Greater)
+        );
+
+        assert_eq!(
+            compare_term_test!(wam, wam.heap[0], wam.heap[0]),
+            Some(Ordering::Equal)
+        );
+
+        assert_eq!(
+            compare_term_test!(wam, wam.heap[1], wam.heap[1]),
+            Some(Ordering::Equal)
+        );
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                atom_as_cell!(atom!("atom")),
+                atom_as_cstr_cell!(atom!("string"))
+            ),
+            Some(Ordering::Less)
+        );
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                atom_as_cell!(atom!("atom")),
+                atom_as_cell!(atom!("atom"))
+            ),
+            Some(Ordering::Equal)
+        );
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                atom_as_cell!(atom!("atom")),
+                atom_as_cell!(atom!("aaa"))
+            ),
+            Some(Ordering::Greater)
+        );
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                fixnum_as_cell!(Fixnum::build_with(6)),
+                heap_loc_as_cell!(1)
+            ),
+            Some(Ordering::Greater)
+        );
+
+        wam.heap.clear();
+
+        wam.heap.push(atom_as_cell!(atom!("f"), 1));
+        wam.heap.push(heap_loc_as_cell!(1));
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                heap_loc_as_cell!(0),
+                heap_loc_as_cell!(0)
+            ),
+            Some(Ordering::Equal)
+        );
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                heap_loc_as_cell!(0),
+                atom_as_cell!(atom!("a"))
+            ),
+            Some(Ordering::Greater)
+        );
+
+        wam.heap.clear();
+
+        // [1,2,3]
+        wam.heap.push(list_loc_as_cell!(1));
+        wam.heap.push(fixnum_as_cell!(Fixnum::build_with(1)));
+        wam.heap.push(list_loc_as_cell!(3));
+        wam.heap.push(fixnum_as_cell!(Fixnum::build_with(2)));
+        wam.heap.push(list_loc_as_cell!(5));
+        wam.heap.push(fixnum_as_cell!(Fixnum::build_with(3)));
+        wam.heap.push(empty_list_as_cell!());
+
+        // [1,2]
+        wam.heap.push(list_loc_as_cell!(8));
+        wam.heap.push(fixnum_as_cell!(Fixnum::build_with(1)));
+        wam.heap.push(list_loc_as_cell!(10));
+        wam.heap.push(fixnum_as_cell!(Fixnum::build_with(2)));
+        wam.heap.push(empty_list_as_cell!());
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                heap_loc_as_cell!(7),
+                heap_loc_as_cell!(7)
+            ),
+            Some(Ordering::Equal)
+        );
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                heap_loc_as_cell!(0),
+                heap_loc_as_cell!(7)
+            ),
+            Some(Ordering::Greater)
+        );
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                empty_list_as_cell!(),
+                heap_loc_as_cell!(7)
+            ),
+            Some(Ordering::Less)
+        );
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                empty_list_as_cell!(),
+                fixnum_as_cell!(Fixnum::build_with(1))
+            ),
+            Some(Ordering::Greater)
+        );
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                empty_list_as_cell!(),
+                atom_as_cstr_cell!(atom!("string"))
+            ),
+            Some(Ordering::Less)
+        );
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                empty_list_as_cell!(),
+                atom_as_cell!(atom!("atom"))
+            ),
+            Some(Ordering::Less)
+        );
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                atom_as_cell!(atom!("atom")),
+                empty_list_as_cell!()
+            ),
+            Some(Ordering::Greater)
+        );
+
+        let one_p_one = typed_arena_ptr_as_cell!(
+            arena_alloc!(OrderedFloat(1.1), &mut wam.arena)
+        );
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                one_p_one,
+                fixnum_as_cell!(Fixnum::build_with(1))
+            ),
+            Some(Ordering::Less)
+        );
+
+        assert_eq!(
+            compare_term_test!(
+                wam,
+                fixnum_as_cell!(Fixnum::build_with(1)),
+                one_p_one
+            ),
+            Some(Ordering::Greater)
+        );
+    }
+
+    #[test]
+    fn is_cyclic_term_tests() {
+        let mut wam = MachineState::new();
+
+        assert!(!wam.is_cyclic_term(atom_as_cell!(atom!("f"))));
+        assert!(!wam.is_cyclic_term(fixnum_as_cell!(Fixnum::build_with(555))));
+
+        wam.heap.push(heap_loc_as_cell!(0));
+
+        assert!(!wam.is_cyclic_term(heap_loc_as_cell!(0)));
+
+        all_cells_unmarked(&wam.heap);
+        wam.heap.clear();
+
+        wam.heap.extend(functor!(atom!("f"), [atom(atom!("a")), atom(atom!("b"))]));
+
+        assert!(!wam.is_cyclic_term(str_loc_as_cell!(0)));
+
+        all_cells_unmarked(&wam.heap);
+
+        assert!(!wam.is_cyclic_term(heap_loc_as_cell!(1)));
+
+        all_cells_unmarked(&wam.heap);
+
+        assert!(!wam.is_cyclic_term(heap_loc_as_cell!(2)));
+
+        all_cells_unmarked(&wam.heap);
+
+        wam.heap[2] = str_loc_as_cell!(0);
+
+        print_heap_terms(wam.heap.iter(), 0);
+
+        assert!(wam.is_cyclic_term(str_loc_as_cell!(0)));
+
+        all_cells_unmarked(&wam.heap);
+
+        wam.heap[2] = atom_as_cell!(atom!("b"));
+        wam.heap[1] = str_loc_as_cell!(0);
+
+        assert!(wam.is_cyclic_term(str_loc_as_cell!(0)));
+
+        all_cells_unmarked(&wam.heap);
+
+        assert!(wam.is_cyclic_term(heap_loc_as_cell!(1)));
+
+        all_cells_unmarked(&wam.heap);
+
+        wam.heap.clear();
+
+        wam.heap.push(pstr_as_cell!(atom!("a string")));
+        wam.heap.push(empty_list_as_cell!());
+
+        assert!(!wam.is_cyclic_term(heap_loc_as_cell!(0)));
+    }
+}
index 1f7ef41a90563dc7815d1042e9384d4e2bcc8ae2..ccb952cd7738df807e0b6971b690d403b9e5c9c5 100644 (file)
@@ -1,67 +1,70 @@
-use prolog_parser::ast::*;
-use prolog_parser::tabled_rc::*;
-use prolog_parser::{clause_name, temp_v};
-
-use lazy_static::lazy_static;
-
-use crate::clause_types::*;
+pub mod arithmetic_ops;
+pub mod attributed_variables;
+pub mod code_repo;
+pub mod code_walker;
+#[macro_use]
+pub mod loader;
+pub mod compile;
+pub mod copier;
+pub mod gc;
+pub mod heap;
+pub mod load_state;
+pub mod machine_errors;
+pub mod machine_indices;
+pub mod machine_state;
+pub mod machine_state_impl;
+pub mod mock_wam;
+pub mod partial_string;
+pub mod preprocessor;
+pub mod stack;
+pub mod streams;
+pub mod system_calls;
+pub mod term_stream;
+
+use crate::atom_table::*;
 use crate::forms::*;
 use crate::instructions::*;
-use crate::machine::loader::*;
-use crate::machine::term_stream::{LiveTermStream, LoadStatePayload, TermStream};
-use crate::read::*;
-
-mod attributed_variables;
-pub(super) mod code_repo;
-pub(crate) mod code_walker;
-#[macro_use]
-pub(crate) mod loader;
-mod compile;
-mod copier;
-pub(crate) mod heap;
-mod load_state;
-pub(crate) mod machine_errors;
-pub(crate) mod machine_indices;
-pub(super) mod machine_state;
-pub(crate) mod partial_string;
-mod preprocessor;
-mod raw_block;
-mod stack;
-pub(crate) mod streams;
-mod term_stream;
-
-#[macro_use]
-mod arithmetic_ops;
-#[macro_use]
-mod machine_state_impl;
-mod system_calls;
-
 use crate::machine::code_repo::*;
 use crate::machine::compile::*;
+use crate::machine::heap::*;
+use crate::machine::loader::*;
 use crate::machine::machine_errors::*;
 use crate::machine::machine_indices::*;
 use crate::machine::machine_state::*;
-pub use crate::machine::streams::Stream;
+use crate::machine::streams::*;
+use crate::types::*;
 
 use indexmap::IndexMap;
 
-//use std::convert::TryFrom;
-use prolog_parser::ast::ClauseName;
-use std::fs::File;
-use std::mem;
+use lazy_static::lazy_static;
+
+use std::env;
 use std::path::PathBuf;
 use std::sync::atomic::AtomicBool;
 
+lazy_static! {
+    pub static ref INTERRUPT: AtomicBool = AtomicBool::new(false);
+}
+
+#[derive(Debug)]
+pub struct Machine {
+    pub(super) machine_st: MachineState,
+    pub(super) inner_heap: Heap,
+    pub(super) policies: MachinePolicies,
+    pub(super) indices: IndexStore,
+    pub(super) code_repo: CodeRepo,
+    pub(super) user_input: Stream,
+    pub(super) user_output: Stream,
+    pub(super) user_error: Stream,
+    pub(super) load_contexts: Vec<LoadContext>,
+}
+
 #[derive(Debug)]
 pub(crate) struct MachinePolicies {
     call_policy: Box<dyn CallPolicy>,
     cut_policy: Box<dyn CutPolicy>,
 }
 
-lazy_static! {
-    pub static ref INTERRUPT: AtomicBool = AtomicBool::new(false);
-}
-
 impl MachinePolicies {
     #[inline]
     fn new() -> Self {
@@ -80,10 +83,10 @@ impl Default for MachinePolicies {
 }
 
 #[derive(Debug)]
-pub(super) struct LoadContext {
+pub struct LoadContext {
     pub(super) path: PathBuf,
     pub(super) stream: Stream,
-    pub(super) module: ClauseName,
+    pub(super) module: Atom,
 }
 
 impl LoadContext {
@@ -100,32 +103,49 @@ impl LoadContext {
         LoadContext {
             path: path_buf,
             stream,
-            module: clause_name!("user"),
+            module: atom!("user"),
         }
     }
 }
 
-#[derive(Debug)]
-pub struct Machine {
-    pub(super) machine_st: MachineState,
-    pub(super) policies: MachinePolicies,
-    pub(super) indices: IndexStore,
-    pub(super) code_repo: CodeRepo,
-    pub(super) user_input: Stream,
-    pub(super) user_output: Stream,
-    pub(super) user_error: Stream,
-    pub(super) load_contexts: Vec<LoadContext>,
-}
-
 #[inline]
 fn current_dir() -> PathBuf {
-    std::env::current_dir().unwrap_or(PathBuf::from("./"))
+    env::current_dir().unwrap_or(PathBuf::from("./"))
 }
 
 include!(concat!(env!("OUT_DIR"), "/libraries.rs"));
 
+pub struct MachinePreludeView<'a> {
+    pub indices: &'a mut IndexStore,
+    pub code_repo: &'a mut CodeRepo,
+    pub load_contexts: &'a mut Vec<LoadContext>,
+}
+
+impl Machine {
+    #[inline]
+    pub fn prelude_view_and_machine_st(&mut self) -> (MachinePreludeView, &mut MachineState) {
+        (
+            MachinePreludeView {
+                indices: &mut self.indices,
+                code_repo: &mut self.code_repo,
+                load_contexts: &mut self.load_contexts,
+            },
+            &mut self.machine_st
+        )
+    }
+
+    pub fn throw_session_error(&mut self, err: SessionError, key: PredicateKey) {
+        let err = self.machine_st.session_error(err);
+        let stub = functor_stub(key.0, key.1);
+        let err = self.machine_st.error_form(err, stub);
+
+        self.machine_st.throw_exception(err);
+        return;
+    }
+}
+
 impl Machine {
-    fn run_module_predicate(&mut self, module_name: ClauseName, key: PredicateKey) {
+    fn run_module_predicate(&mut self, module_name: Atom, key: PredicateKey) {
         if let Some(module) = self.indices.modules.get(&module_name) {
             if let Some(ref code_index) = module.code_dir.get(&key) {
                 let p = code_index.local().unwrap();
@@ -140,27 +160,29 @@ impl Machine {
         unreachable!();
     }
 
-    pub fn load_file(&mut self, path: String, stream: Stream) {
-        self.machine_st[temp_v!(1)] =
-            Addr::Stream(self.machine_st.heap.push(HeapCellValue::Stream(stream)));
-
-        self.machine_st[temp_v!(2)] = Addr::Con(self.machine_st.heap.push(HeapCellValue::Atom(
-            clause_name!(path, self.machine_st.atom_tbl),
-            None,
-        )));
+    pub fn load_file(&mut self, path: &str, stream: Stream) {
+        self.machine_st.registers[1] = stream_as_cell!(stream);
+        self.machine_st.registers[2] = atom_as_cell!(
+            self.machine_st.atom_tbl.build_with(path)
+        );
 
-        self.run_module_predicate(clause_name!("loader"), (clause_name!("file_load"), 2));
+        self.run_module_predicate(atom!("loader"), (atom!("file_load"), 2));
     }
 
     fn load_top_level(&mut self) {
         let mut path_buf = current_dir();
-        path_buf.push("toplevel.pl");
 
-        let path = path_buf.to_str().unwrap().to_string();
+        path_buf.push("src/toplevel.pl");
 
-        self.load_file(path, Stream::from(include_str!("../toplevel.pl")));
+        let path = path_buf.to_str().unwrap();
+        let toplevel_stream = Stream::from_static_string(
+            include_str!("../toplevel.pl"),
+            &mut self.machine_st.arena,
+        );
 
-        if let Some(toplevel) = self.indices.modules.get(&clause_name!("$toplevel")) {
+        self.load_file(path, toplevel_stream);
+
+        if let Some(toplevel) = self.indices.modules.get(&atom!("$toplevel")) {
             load_module(
                 &mut self.indices.code_dir,
                 &mut self.indices.op_dir,
@@ -178,9 +200,15 @@ impl Machine {
         path_buf.push("machine/attributed_variables.pl");
 
         bootstrapping_compile(
-            Stream::from(include_str!("attributed_variables.pl")),
+            Stream::from_static_string(
+                include_str!("attributed_variables.pl"),
+                &mut self.machine_st.arena,
+            ),
             self,
-            ListingSource::from_file_and_path(clause_name!("attributed_variables"), path_buf),
+            ListingSource::from_file_and_path(
+                atom!("attributed_variables"),
+                path_buf,
+            ),
         )
         .unwrap();
 
@@ -188,44 +216,49 @@ impl Machine {
         path_buf.push("machine/project_attributes.pl");
 
         bootstrapping_compile(
-            Stream::from(include_str!("project_attributes.pl")),
+            Stream::from_static_string(
+                include_str!("project_attributes.pl"),
+                &mut self.machine_st.arena,
+            ),
             self,
-            ListingSource::from_file_and_path(clause_name!("project_attributes"), path_buf),
+            ListingSource::from_file_and_path(atom!("project_attributes"), path_buf),
         )
         .unwrap();
 
-        if let Some(module) = self.indices.modules.get(&clause_name!("$atts")) {
-            if let Some(code_index) = module.code_dir.get(&(clause_name!("driver"), 2)) {
+        if let Some(module) = self.indices.modules.get(&atom!("$atts")) {
+            if let Some(code_index) = module.code_dir.get(&(atom!("driver"), 2)) {
                 self.machine_st.attr_var_init.verify_attrs_loc = code_index.local().unwrap();
             }
         }
     }
 
     pub fn run_top_level(&mut self) {
-        use std::env;
-
         let mut arg_pstrs = vec![];
 
         for arg in env::args() {
-            arg_pstrs.push(self.machine_st.heap.put_complete_string(&arg));
+            arg_pstrs.push(put_complete_string(
+                &mut self.machine_st.heap,
+                &arg,
+                &mut self.machine_st.atom_tbl,
+            ));
         }
 
-        let list_addr = Addr::HeapCell(self.machine_st.heap.to_list(arg_pstrs.into_iter()));
-
-        self.machine_st[temp_v!(1)] = list_addr;
+        self.machine_st.registers[1] = heap_loc_as_cell!(
+            iter_to_heap_list(&mut self.machine_st.heap, arg_pstrs.into_iter())
+        );
 
-        self.run_module_predicate(clause_name!("$toplevel"), (clause_name!("$repl"), 1));
+        self.run_module_predicate(atom!("$toplevel"), (atom!("$repl"), 1));
     }
 
     pub(crate) fn configure_modules(&mut self) {
         fn update_call_n_indices(loader: &Module, target_code_dir: &mut CodeDir) {
             for arity in 1..66 {
-                let key = (clause_name!("call"), arity);
+                let key = (atom!("call"), arity);
 
                 match loader.code_dir.get(&key) {
                     Some(src_code_index) => {
                         let target_code_index = target_code_dir
-                            .entry(key.clone())
+                            .entry(key)
                             .or_insert_with(|| CodeIndex::new(IndexPtr::Undefined));
 
                         target_code_index.set(src_code_index.get());
@@ -237,15 +270,15 @@ impl Machine {
             }
         }
 
-        if let Some(loader) = self.indices.modules.swap_remove(&clause_name!("loader")) {
-            if let Some(builtins) = self.indices.modules.get_mut(&clause_name!("builtins")) {
+        if let Some(loader) = self.indices.modules.swap_remove(&atom!("loader")) {
+            if let Some(builtins) = self.indices.modules.get_mut(&atom!("builtins")) {
                 // Import loader's exports into the builtins module so they will be
                 // implicitly included in every further module.
                 load_module(
                     &mut builtins.code_dir,
                     &mut builtins.op_dir,
                     &mut builtins.meta_predicates,
-                    &CompilationTarget::Module(clause_name!("builtins")),
+                    &CompilationTarget::Module(atom!("builtins")),
                     &loader,
                 );
 
@@ -257,7 +290,7 @@ impl Machine {
                     builtins
                         .module_decl
                         .exports
-                        .push(ModuleExport::PredicateKey((clause_name!("call"), arity)));
+                        .push(ModuleExport::PredicateKey((atom!("call"), arity)));
                 }
             }
 
@@ -267,17 +300,24 @@ impl Machine {
 
             update_call_n_indices(&loader, &mut self.indices.code_dir);
 
-            self.indices.modules.insert(clause_name!("loader"), loader);
+            self.indices.modules.insert(atom!("loader"), loader);
         } else {
             unreachable!()
         }
     }
 
-    pub fn new(user_input: Stream, user_output: Stream, user_error: Stream) -> Self {
+    pub fn new() -> Self {
         use ref_thread_local::RefThreadLocal;
 
+        let mut machine_st = MachineState::new();
+
+        let user_input = Stream::stdin(&mut machine_st.arena);
+        let user_output = Stream::stdout(&mut machine_st.arena);
+        let user_error = Stream::stderr(&mut machine_st.arena);
+
         let mut wam = Machine {
-            machine_st: MachineState::new(),
+            machine_st,
+            inner_heap: Heap::new(),
             policies: MachinePolicies::new(),
             indices: IndexStore::new(),
             code_repo: CodeRepo::new(),
@@ -293,23 +333,29 @@ impl Machine {
         lib_path.push("lib");
 
         bootstrapping_compile(
-            Stream::from(LIBRARIES.borrow()["ops_and_meta_predicates"]),
+            Stream::from_static_string(
+                LIBRARIES.borrow()["ops_and_meta_predicates"],
+                &mut wam.machine_st.arena,
+            ),
             &mut wam,
             ListingSource::from_file_and_path(
-                clause_name!("ops_and_meta_predicates.pl"),
+                atom!("ops_and_meta_predicates.pl"),
                 lib_path.clone(),
             ),
         )
         .unwrap();
 
         bootstrapping_compile(
-            Stream::from(LIBRARIES.borrow()["builtins"]),
+            Stream::from_static_string(
+                LIBRARIES.borrow()["builtins"],
+                &mut wam.machine_st.arena,
+            ),
             &mut wam,
-            ListingSource::from_file_and_path(clause_name!("builtins.pl"), lib_path.clone()),
+            ListingSource::from_file_and_path(atom!("builtins.pl"), lib_path.clone()),
         )
         .unwrap();
 
-        if let Some(builtins) = wam.indices.modules.get(&clause_name!("builtins")) {
+        if let Some(builtins) = wam.indices.modules.get(&atom!("builtins")) {
             load_module(
                 &mut wam.indices.code_dir,
                 &mut wam.indices.op_dir,
@@ -324,15 +370,15 @@ impl Machine {
         lib_path.pop(); // remove the "lib" at the end
 
         bootstrapping_compile(
-            Stream::from(include_str!("../loader.pl")),
+            Stream::from_static_string(include_str!("../loader.pl"), &mut wam.machine_st.arena),
             &mut wam,
-            ListingSource::from_file_and_path(clause_name!("loader.pl"), lib_path.clone()),
+            ListingSource::from_file_and_path(atom!("loader.pl"), lib_path.clone()),
         )
         .unwrap();
 
         wam.configure_modules();
 
-        if let Some(loader) = wam.indices.modules.get(&clause_name!("loader")) {
+        if let Some(loader) = wam.indices.modules.get(&atom!("loader")) {
             load_module(
                 &mut wam.indices.code_dir,
                 &mut wam.indices.op_dir,
@@ -352,38 +398,21 @@ impl Machine {
     }
 
     pub(crate) fn configure_streams(&mut self) {
-        self.user_input.options_mut().alias = Some(clause_name!("user_input"));
+        self.user_input.options_mut().set_alias_to_atom_opt(Some(atom!("user_input")));
 
         self.indices
             .stream_aliases
-            .insert(clause_name!("user_input"), self.user_input.clone());
+            .insert(atom!("user_input"), self.user_input);
 
-        self.indices.streams.insert(self.user_input.clone());
+        self.indices.streams.insert(self.user_input);
 
-        self.user_output.options_mut().alias = Some(clause_name!("user_output"));
+        self.user_output.options_mut().set_alias_to_atom_opt(Some(atom!("user_output")));
 
         self.indices
             .stream_aliases
-            .insert(clause_name!("user_output"), self.user_output.clone());
-
-        self.user_error.options_mut().alias = Some(clause_name!("user_error"));
+            .insert(atom!("user_output"), self.user_output);
 
-        self.indices
-            .stream_aliases
-            .insert(clause_name!("user_error"), self.user_error.clone());
-
-        self.indices.streams.insert(self.user_output.clone());
-    }
-
-    fn throw_session_error(&mut self, err: SessionError, key: PredicateKey) {
-        let h = self.machine_st.heap.h();
-
-        let err = MachineError::session_error(h, err);
-        let stub = MachineError::functor_stub(key.0, key.1);
-        let err = self.machine_st.error_form(err, stub);
-
-        self.machine_st.throw_exception(err);
-        return;
+        self.indices.streams.insert(self.user_output);
     }
 
     fn handle_toplevel_command(&mut self, code_ptr: REPLCodePtr, p: LocalCodePtr) {
@@ -604,13 +633,14 @@ impl MachineState {
         self.b0 = self.stack.index_or_frame(b).prelude.b0;
         self.p = CodePtr::Local(self.stack.index_or_frame(b).prelude.bp);
 
+        self.pdl.clear();
         self.fail = false;
     }
 
     fn check_machine_index(&mut self, code_repo: &CodeRepo) -> bool {
         match self.p {
-            CodePtr::Local(LocalCodePtr::DirEntry(p))
-            CodePtr::Local(LocalCodePtr::IndexingBuf(p, ..))
+            CodePtr::Local(LocalCodePtr::DirEntry(p)) |
+            CodePtr::Local(LocalCodePtr::IndexingBuf(p, ..))
                 if p < code_repo.code.len() => {}
             CodePtr::Local(LocalCodePtr::Halt) | CodePtr::REPL(..) => {
                 return false;
@@ -690,7 +720,9 @@ impl MachineState {
                     self.p = CodePtr::Local(self.attr_var_init.cp);
 
                     let instigating_p = CodePtr::Local(self.attr_var_init.instigating_p);
-                    let instigating_instr = code_repo.lookup_instr(false, &instigating_p).unwrap();
+                    let instigating_instr = code_repo
+                        .lookup_instr(false, &instigating_p)
+                        .unwrap();
 
                     if !instigating_instr.as_ref().is_head_instr() {
                         let cp = self.p.local();
index 79354874785ba44c0516a3de007b123821eb1f99..da32db4f910b94642909a3b149e2d19a8fe81b02 100644 (file)
@@ -1,43 +1,16 @@
-use crate::machine::machine_indices::*;
-use crate::machine::*;
+use crate::atom_table::*;
+use crate::parser::ast::*;
 
-use core::marker::PhantomData;
+use crate::machine::machine_errors::CycleSearchResult;
+use crate::machine::system_calls::BrentAlgState;
+use crate::types::*;
 
-use std::alloc;
 use std::cmp::Ordering;
-use std::mem;
-use std::ops::RangeFrom;
-use std::ptr;
-use std::slice;
+use std::ops::Deref;
 use std::str;
 
-use indexmap::IndexSet;
-
-#[derive(Debug)]
-pub(crate) struct PartialString {
-    buf: *const u8,
-    len: usize,
-    _marker: PhantomData<[u8]>,
-}
-
-impl Drop for PartialString {
-    fn drop(&mut self) {
-        unsafe {
-            let layout = alloc::Layout::from_size_align_unchecked(self.len, mem::align_of::<u8>());
-            alloc::dealloc(self.buf as *mut u8, layout);
-
-            self.buf = ptr::null();
-            self.len = 0;
-        }
-    }
-}
-
-impl Clone for PartialString {
-    #[inline]
-    fn clone(&self) -> Self {
-        self.clone_from_offset(0)
-    }
-}
+#[derive(Copy, Clone, Debug)]
+pub struct PartialString(Atom);
 
 fn scan_for_terminator<Iter: Iterator<Item = char>>(iter: Iter) -> usize {
     let mut terminator_idx = 0;
@@ -53,432 +26,1114 @@ fn scan_for_terminator<Iter: Iterator<Item = char>>(iter: Iter) -> usize {
     terminator_idx
 }
 
-#[derive(Debug)]
-pub(crate) struct PStrIter {
-    buf: *const u8,
-    len: usize,
-}
-
-impl PStrIter {
+impl From<Atom> for PartialString {
     #[inline]
-    fn from(buf: *const u8, len: usize, idx: usize) -> Self {
-        PStrIter {
-            buf: (buf as usize + idx) as *const _,
-            len: len - idx,
-        }
+    fn from(buf: Atom) -> PartialString {
+        PartialString(buf)
     }
 }
 
-impl Iterator for PStrIter {
-    type Item = char;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        unsafe {
-            let slice = slice::from_raw_parts(self.buf, self.len);
-            let s = str::from_utf8(slice).unwrap();
-
-            if let Some(c) = s.chars().next() {
-                self.buf = self.buf.offset(c.len_utf8() as isize);
-                self.len -= c.len_utf8();
-
-                Some(c)
-            } else {
-                None
-            }
-        }
+impl Into<Atom> for PartialString {
+    #[inline]
+    fn into(self: Self) -> Atom {
+        self.0
     }
 }
 
 impl PartialString {
     #[inline]
-    pub(super) fn new(src: &str) -> Option<(Self, &str)> {
-        let pstr = PartialString {
-            buf: ptr::null_mut(),
-            len: 0,
-            _marker: PhantomData,
-        };
+    pub(super) fn new<'a>(src: &'a str, atom_tbl: &mut AtomTable) -> Option<(Self, &'a str)> {
+        let terminator_idx = scan_for_terminator(src.chars());
+        let pstr = PartialString(atom_tbl.build_with(src));
+
+        Some(if terminator_idx != src.as_bytes().len() {
+            (pstr, &src[terminator_idx..])
+        } else {
+            (pstr, "")
+        })
+    }
 
-        unsafe { pstr.append_chars(src) }
+    #[inline(always)]
+    pub(crate) fn as_str_from(&self, n: usize) -> &str {
+        &self.0.as_str()[n..]
     }
+}
 
-    unsafe fn append_chars(mut self, src: &str) -> Option<(Self, &str)> {
-        let terminator_idx = scan_for_terminator(src.chars());
+#[derive(Clone, Copy)]
+pub struct HeapPStrIter<'a> {
+    pub heap: &'a [HeapCellValue],
+    pub focus: HeapCellValue,
+    orig_focus: usize,
+    brent_st: BrentAlgState,
+    stepper: fn(&mut HeapPStrIter<'a>) -> Option<PStrIteratee>,
+}
 
-        let layout = alloc::Layout::from_size_align_unchecked(
-            terminator_idx + '\u{0}'.len_utf8(),
-            mem::align_of::<u8>(),
-        );
+#[derive(Debug)]
+pub struct PStrPrefixCmpResult {
+    pub focus: usize,
+    pub offset: usize,
+    pub prefix_len: usize,
+}
 
-        self.buf = alloc::alloc(layout) as *const _;
-        self.len = terminator_idx + '\u{0}'.len_utf8();
+struct PStrIterStep {
+    iteratee: PStrIteratee,
+    next_hare: usize,
+}
 
-        ptr::copy(src.as_ptr(), self.buf as *mut _, terminator_idx);
+impl<'a> HeapPStrIter<'a> {
+    pub fn new(heap: &'a [HeapCellValue], h: usize) -> Self {
+        let value = heap[h];
+
+        Self {
+            heap,
+            focus: value,
+            orig_focus: h,
+            brent_st: BrentAlgState::new(h),
+            stepper: HeapPStrIter::pre_cycle_discovery_stepper,
+        }
+    }
 
-        self.write_terminator_at(terminator_idx);
+    #[inline(always)]
+    pub fn focus(&self) -> usize {
+        self.brent_st.hare
+    }
 
-        Some(if terminator_idx != src.as_bytes().len() {
-            (self, &src[terminator_idx..])
-        } else {
-            (self, "")
-        })
+    #[inline(always)]
+    pub fn at_string_terminator(&self) -> bool {
+        self.focus.is_string_terminator(self.heap)
     }
 
-    pub(super) fn clone_from_offset(&self, n: usize) -> Self {
-        let len = if self.len - '\u{0}'.len_utf8() > n {
-            self.len - n - '\u{0}'.len_utf8()
-        } else {
-            0
-        };
+    #[inline(always)]
+    pub fn num_steps(&self) -> usize {
+        self.brent_st.num_steps()
+    }
 
-        let mut pstr = PartialString {
-            buf: ptr::null_mut(),
-            len: len + '\u{0}'.len_utf8(),
-            _marker: PhantomData,
+    pub fn compare_pstr_to_string(&mut self, s: &str) -> Option<PStrPrefixCmpResult> {
+        let mut result = PStrPrefixCmpResult {
+            focus: self.brent_st.hare,
+            offset: 0,
+            prefix_len: 0,
         };
 
-        unsafe {
-            let layout = alloc::Layout::from_size_align_unchecked(
-                len + '\u{0}'.len_utf8(),
-                mem::align_of::<u8>(),
-            );
+        while let Some(iteratee) = self.next() {
+            result.focus  = iteratee.focus();
+            result.offset = iteratee.offset();
 
-            pstr.buf = alloc::alloc(layout);
+            match iteratee {
+                PStrIteratee::Char(_, c1) => {
+                    if let Some(c2) = s[result.prefix_len..].chars().next() {
+                        if c1 != c2 {
+                            return None;
+                        } else {
+                            result.prefix_len += c1.len_utf8();
+                            result.offset += c1.len_utf8();
+                        }
+                    } else {
+                        return Some(result);
+                    }
+                }
+                PStrIteratee::PStrSegment(_, pstr_atom, n) => {
+                    let pstr = PartialString::from(pstr_atom);
+                    let t = pstr.as_str_from(n);
+                    let s = &s[result.prefix_len..];
+
+                    if s.len() >= t.len() {
+                        if s.starts_with(t) {
+                            result.prefix_len += t.len();
+                            result.offset += t.len();
+                        } else {
+                            return None;
+                        }
+                    } else if t.starts_with(&s) {
+                        result.prefix_len += s.len();
+                        result.offset += s.len();
 
-            if len > 0 {
-                ptr::copy(
-                    (self.buf as usize + n) as *const u8,
-                    pstr.buf as *mut _,
-                    len,
-                );
+                        return Some(result);
+                    } else {
+                        return None;
+                    }
+                }
             }
 
-            pstr.write_terminator_at(len);
+            if s.len() == result.prefix_len {
+                return Some(result);
+            }
         }
 
-        pstr
+        Some(result)
     }
 
     #[inline]
-    pub(super) fn write_terminator_at(&mut self, index: usize) {
-        unsafe {
-            ptr::write((self.buf as usize + index) as *mut u8, 0u8);
+    pub fn chars(mut self) -> PStrCharsIter<'a> {
+        let item = self.next();
+        PStrCharsIter { iter: self, item }
+    }
+
+    fn walk_hare_to_cycle_end(&mut self) {
+        // walk_hare_to_cycle_end assumes a cycle has been found,
+        // so it is always safe to unwrap self.step()
+
+        let orig_hare = self.brent_st.hare;
+
+        self.brent_st.hare = self.orig_focus;
+        self.brent_st.tortoise = self.orig_focus;
+
+        for _ in 0 .. self.brent_st.lam {
+            self.brent_st.hare = self.step(self.brent_st.hare).unwrap().next_hare;
         }
+
+        while self.brent_st.hare != self.brent_st.tortoise {
+            self.brent_st.tortoise = self.step(self.brent_st.tortoise).unwrap().next_hare;
+            self.brent_st.hare = self.step(self.brent_st.hare).unwrap().next_hare;
+        }
+
+        self.focus = self.heap[orig_hare];
+        self.brent_st.hare = orig_hare;
     }
 
-    #[inline]
-    pub(crate) fn range_from(&self, index: RangeFrom<usize>) -> PStrIter {
-        if self.len >= '\u{0}'.len_utf8() {
-            PStrIter::from(self.buf, self.len - '\u{0}'.len_utf8(), index.start)
-        } else {
-            PStrIter::from(self.buf, 0, 0)
+    pub fn to_string(&mut self) -> String {
+        let mut buf = String::with_capacity(32);
+
+        while let Some(iteratee) = self.next() {
+            match iteratee {
+                PStrIteratee::Char(_, c) => {
+                    buf.push(c);
+                }
+                PStrIteratee::PStrSegment(_, pstr_atom, n) => {
+                    let pstr = PartialString::from(pstr_atom);
+                    buf += pstr.as_str_from(n);
+                }
+            }
         }
+
+        buf
     }
 
     #[inline]
-    pub(crate) fn at_end(&self, end_n: usize) -> bool {
-        end_n + 1 == self.len
+    pub fn is_continuable(&self) -> bool {
+        let mut focus = self.focus;
+
+        loop {
+           read_heap_cell!(focus,
+               (HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc) => {
+                   return true;
+               }
+               (HeapCellValueTag::Atom, (name, arity)) => { // TODO: use Str here?
+                   return name == atom!(".") && arity == 2;
+               }
+               (HeapCellValueTag::Lis, h) => {
+                   return read_heap_cell!(self.heap[h],
+                       (HeapCellValueTag::Atom, (name, arity)) => {
+                           arity == 0 && name.as_char().is_some()
+                       }
+                       (HeapCellValueTag::Char) => {
+                           true
+                       }
+                       _ => {
+                           false
+                       }
+                   );
+               }
+               (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
+                   if focus == self.heap[h] {
+                       return false;
+                   }
+
+                   focus = self.heap[h];
+               }
+               _ => {
+                   return false;
+               }
+           );
+        }
     }
 
-    #[inline]
-    pub(crate) fn as_str_from(&self, n: usize) -> &str {
-        unsafe {
-            let slice = slice::from_raw_parts(self.buf, self.len - '\u{0}'.len_utf8());
+    #[inline(always)]
+    pub fn cycle_detected(&self) -> bool {
+        self.stepper as usize == HeapPStrIter::post_cycle_discovery_stepper as usize
+    }
+
+    fn step(&self, mut curr_hare: usize) -> Option<PStrIterStep> {
+        loop {
+            read_heap_cell!(self.heap[curr_hare],
+                (HeapCellValueTag::CStr, cstr_atom) => {
+                    return if self.focus == empty_list_as_cell!() {
+                        None
+                    } else {
+                        Some(PStrIterStep {
+                            iteratee: PStrIteratee::PStrSegment(curr_hare, cstr_atom, 0),
+                            next_hare: curr_hare,
+                        })
+                    }
+                }
+                (HeapCellValueTag::PStrLoc, h) => {
+                    curr_hare = h;
+                }
+                (HeapCellValueTag::PStr, pstr_atom) => {
+                    return Some(PStrIterStep {
+                        iteratee: PStrIteratee::PStrSegment(curr_hare, pstr_atom, 0),
+                        next_hare: curr_hare+1,
+                    });
+                }
+                (HeapCellValueTag::PStrOffset, pstr_offset) => {
+                    if self.focus == empty_list_as_cell!() {
+                        return None;
+                    }
+
+                    let pstr_atom = cell_as_atom!(self.heap[pstr_offset]);
+                    let n = cell_as_fixnum!(self.heap[curr_hare+1]).get_num() as usize;
+
+                    return if self.heap[pstr_offset].get_tag() == HeapCellValueTag::CStr {
+                        Some(PStrIterStep {
+                            iteratee: PStrIteratee::PStrSegment(curr_hare, pstr_atom, n),
+                            next_hare: pstr_offset,
+                        })
+                    } else {
+                        Some(PStrIterStep {
+                            iteratee: PStrIteratee::PStrSegment(curr_hare, pstr_atom, n),
+                            next_hare: pstr_offset+1,
+                        })
+                    };
+                }
+                (HeapCellValueTag::Lis, h) => {
+                    return if let Some(c) = self.heap[h].as_char() {
+                        Some(PStrIterStep {
+                            iteratee: PStrIteratee::Char(curr_hare, c),
+                            next_hare: h+1,
+                        })
+                    } else {
+                        None
+                    }
+                }
+                (HeapCellValueTag::Str, s) => {
+                    let (name, arity) = cell_as_atom_cell!(self.heap[s])
+                        .get_name_and_arity();
+
+                    return if name == atom!(".") && arity == 2 {
+                        if let Some(c) = self.heap[s+1].as_char() {
+                            Some(PStrIterStep {
+                                iteratee: PStrIteratee::Char(curr_hare, c),
+                                next_hare: s+2,
+                            })
+                        } else {
+                            None
+                        }
+                    } else {
+                        None
+                    };
+                }
+                (HeapCellValueTag::Atom, (_name, arity)) => {
+                    debug_assert!(arity == 0);
+                    return None;
+                }
+                (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
+                    if h == curr_hare {
+                        return None;
+                    }
+
+                    curr_hare = h;
+                }
+                _ => {
+                    return None;
+                }
+            );
+        }
+    }
+
+    fn pre_cycle_discovery_stepper(&mut self) -> Option<PStrIteratee> {
+        let PStrIterStep { iteratee, next_hare } =
+            match self.step(self.brent_st.hare) {
+                Some(results) => results,
+                None => {
+                    return None;
+                }
+            };
+
+        self.focus = self.heap[iteratee.focus()];
+
+        if self.focus.is_string_terminator(self.heap) {
+            self.focus = empty_list_as_cell!();
+            self.brent_st.hare = iteratee.focus();
+
+            return Some(iteratee);
+        }
+
+        match self.brent_st.step(next_hare) {
+            Some(cycle_result) => {
+                debug_assert!(cycle_result == CycleSearchResult::NotList);
+
+                self.walk_hare_to_cycle_end();
+                self.stepper = HeapPStrIter::post_cycle_discovery_stepper;
+            }
+            None => {
+                self.focus = self.heap[next_hare];
+            }
+        }
 
-            let s = str::from_utf8(slice).unwrap();
+        Some(iteratee)
+    }
 
-            &s[n..]
+    fn post_cycle_discovery_stepper(&mut self) -> Option<PStrIteratee> {
+        if self.brent_st.hare == self.brent_st.tortoise {
+            return None;
         }
+
+        let PStrIterStep { iteratee, next_hare } =
+            match self.step(self.brent_st.hare) {
+                Some(results) => results,
+                None => {
+                    return None;
+                }
+            };
+
+        self.focus = self.heap[next_hare];
+        self.brent_st.hare = next_hare;
+
+        Some(iteratee)
     }
 }
 
-#[derive(Debug)]
-pub(crate) struct HeapPStrIter<'a> {
-    focus: Addr,
-    machine_st: &'a MachineState,
-    seen: IndexSet<Addr>,
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum PStrIteratee {
+    Char(usize, char),
+    PStrSegment(usize, Atom, usize),
 }
 
-impl<'a> HeapPStrIter<'a> {
+impl PStrIteratee {
     #[inline]
-    pub(super) fn new(machine_st: &'a MachineState, focus: Addr) -> Self {
-        HeapPStrIter {
-            focus,
-            machine_st,
-            seen: IndexSet::new(),
+    fn offset(&self) -> usize {
+        match self {
+            PStrIteratee::Char(_, _) => 0,
+            PStrIteratee::PStrSegment(_, _, n) => *n,
         }
     }
 
     #[inline]
-    pub(crate) fn focus(&self) -> Addr {
-        self.machine_st.store(self.machine_st.deref(self.focus))
+    fn focus(&self) -> usize {
+        match self {
+            PStrIteratee::Char(focus, _) => *focus,
+            PStrIteratee::PStrSegment(focus, _, _) => *focus,
+        }
     }
+}
 
-    #[inline]
-    pub(crate) fn to_string(&mut self) -> String {
-        let mut buf = String::new();
+impl<'a> Iterator for HeapPStrIter<'a> {
+    type Item = PStrIteratee;
 
-        while let Some(iteratee) = self.next() {
+    #[inline(always)]
+    fn next(&mut self) -> Option<Self::Item> {
+        (self.stepper)(self)
+    }
+}
+
+pub struct PStrCharsIter<'a> {
+    pub iter: HeapPStrIter<'a>,
+    pub item: Option<PStrIteratee>,
+}
+
+impl<'a> PStrCharsIter<'a> {
+    pub fn peek(&self) -> Option<char> {
+        if let Some(iteratee) = self.item {
             match iteratee {
-                PStrIteratee::Char(c) => {
-                    buf.push(c);
+                PStrIteratee::Char(_, c) => {
+                    return Some(c);
+                }
+                PStrIteratee::PStrSegment(_, pstr_atom, n) => {
+                    let pstr = PartialString::from(pstr_atom);
+                    return pstr.as_str_from(n).chars().next();
                 }
-                PStrIteratee::PStrSegment(h, n) => match &self.machine_st.heap[h] {
-                    HeapCellValue::PartialString(ref pstr, _) => {
-                        buf += pstr.as_str_from(n);
-                    }
-                    _ => {
-                        unreachable!()
-                    }
-                },
             }
         }
 
-        buf
+        None
     }
 }
 
-#[derive(Debug, Clone, Copy)]
-pub(crate) enum PStrIteratee {
-    Char(char),
-    PStrSegment(usize, usize),
+impl<'a> Deref for PStrCharsIter<'a> {
+    type Target = HeapPStrIter<'a>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.iter
+    }
 }
 
-impl<'a> Iterator for HeapPStrIter<'a> {
-    type Item = PStrIteratee;
+impl<'a> Iterator for PStrCharsIter<'a> {
+    type Item = char;
 
     fn next(&mut self) -> Option<Self::Item> {
-        let addr = self.machine_st.store(self.machine_st.deref(self.focus));
-
-        if !self.seen.contains(&addr) {
-            self.seen.insert(addr);
-        } else {
-            return None;
+        while let Some(item) = self.item {
+            match item {
+                PStrIteratee::Char(_, c) => {
+                    self.item = self.iter.next();
+                    return Some(c);
+                }
+                PStrIteratee::PStrSegment(f1, pstr_atom, n) => {
+                    let pstr = PartialString::from(pstr_atom);
+
+                    match pstr.as_str_from(n).chars().next() {
+                        Some(c) => {
+                            self.item = Some(PStrIteratee::PStrSegment(
+                                f1,
+                                pstr_atom,
+                                n + c.len_utf8(),
+                            ));
+
+                            return Some(c);
+                        }
+                        None => {
+                            self.item = self.iter.next();
+                        }
+                    }
+                }
+            }
         }
 
-        match addr {
-            Addr::PStrLocation(h, n) => {
-                if let &HeapCellValue::PartialString(_, has_tail) = &self.machine_st.heap[h] {
-                    self.focus = if has_tail {
-                        Addr::HeapCell(h + 1)
-                    } else {
-                        Addr::EmptyList
-                    };
+        /*
+        if !self.iter.at_string_terminator() {
+            // at a cycle. emit the final character.
+            match self.iter.step(self.iter.brent_st.hare) {
+                Some(PStrIterStep { iteratee: PStrIteratee::Char(_, c), .. }) => {
+                    self.iter.focus = empty_list_as_cell!();
+                    return Some(c);
+                }
+                Some(PStrIterStep { iteratee: PStrIteratee::PStrSegment(_, pstr_atom, _), .. }) => {
+                    self.iter.focus = empty_list_as_cell!();
 
-                    return Some(PStrIteratee::PStrSegment(h, n));
-                } else {
-                    unreachable!()
+                    let c = PartialString::from(pstr_atom).as_str_from(0).chars().next().unwrap();
+                    return Some(c);
                 }
-            }
-            Addr::Lis(l) => {
-                let addr = self
-                    .machine_st
-                    .store(self.machine_st.deref(Addr::HeapCell(l)));
-
-                let opt_c = match addr {
-                    Addr::Con(h) if self.machine_st.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(ref atom, _) = &self.machine_st.heap[h] {
-                            if atom.is_char() {
-                                atom.as_str().chars().next()
-                            } else {
-                                None
-                            }
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    Addr::Char(c) => Some(c),
-                    _ => None,
-                };
-
-                if let Some(c) = opt_c {
-                    self.focus = Addr::HeapCell(l + 1);
-                    return Some(PStrIteratee::Char(c));
-                } else {
+                _ => {
+                    self.iter.focus = empty_list_as_cell!();
                     return None;
                 }
             }
-            Addr::EmptyList => {
-                self.focus = Addr::EmptyList;
-                return None;
-            }
-            _ => {
-                return None;
-            }
+        }
+        */
+
+        None
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum PStrCmpResult {
+    Ordered(Ordering),
+    FirstIterContinuable(PStrIteratee),
+    SecondIterContinuable(PStrIteratee),
+    Unordered,
+}
+
+impl PStrCmpResult {
+    #[inline]
+    pub fn is_second_iter(&self) -> bool {
+        if let PStrCmpResult::SecondIterContinuable(_) = self {
+            true
+        } else {
+            false
         }
     }
 }
 
 #[inline]
-pub(super) fn compare_pstr_prefixes<'a>(
+pub fn compare_pstr_prefixes<'a>(
     i1: &mut HeapPStrIter<'a>,
     i2: &mut HeapPStrIter<'a>,
-) -> Option<Ordering> {
-    let mut r1 = i1.next();
-    let mut r2 = i2.next();
+) -> PStrCmpResult {
+    #[inline(always)]
+    fn consolidate_step(iter: &mut HeapPStrIter, step: &PStrIterStep) -> bool {
+        iter.focus = iter.heap[step.next_hare];
+
+        if iter.focus.is_string_terminator(iter.heap) {
+            iter.focus = empty_list_as_cell!();
+        }
+
+        !iter.brent_st.step(step.next_hare).is_some()
+    }
+
+    let mut r1 = i1.step(i1.brent_st.hare);
+    let mut r2 = i2.step(i2.brent_st.hare);
 
     loop {
-        if let Some(r1i) = r1 {
-            if let Some(r2i) = r2 {
-                match (r1i, r2i) {
-                    (PStrIteratee::Char(c1), PStrIteratee::Char(c2)) => {
+        if let Some(step_1) = r1.as_mut() {
+            if let Some(step_2) = r2.as_mut() {
+                match (step_1.iteratee, step_2.iteratee) {
+                    (PStrIteratee::Char(_, c1), PStrIteratee::Char(_, c2)) => {
                         if c1 != c2 {
-                            return c1.partial_cmp(&c2);
+                            return PStrCmpResult::Ordered(c1.cmp(&c2));
+                        }
+
+                        let c1_result = consolidate_step(i1, &step_1);
+                        let c2_result = consolidate_step(i2, &step_2);
+
+                        if c1_result {
+                            r1 = i1.step(i1.brent_st.hare);
+                        }
+
+                        if c2_result {
+                            r2 = i2.step(i2.brent_st.hare);
+                        }
+
+                        if c1_result && c2_result {
+                            continue;
+                        } else {
+                            break;
                         }
                     }
-                    (PStrIteratee::Char(c1), PStrIteratee::PStrSegment(h, n)) => {
-                        if let &HeapCellValue::PartialString(ref pstr, _) = &i2.machine_st.heap[h] {
-                            if let Some(c2) = pstr.as_str_from(n).chars().next() {
-                                if c1 != c2 {
-                                    return c1.partial_cmp(&c2);
-                                } else {
-                                    r1 = i1.next();
-                                    r2 = Some(PStrIteratee::PStrSegment(h, n + c2.len_utf8()));
+                    (PStrIteratee::Char(_, c1), PStrIteratee::PStrSegment(f2, pstr_atom, n)) => {
+                        let pstr = PartialString::from(pstr_atom);
+
+                        if let Some(c2) = pstr.as_str_from(n).chars().next() {
+                            if c1 != c2 {
+                                return PStrCmpResult::Ordered(c1.cmp(&c2));
+                            }
+
+                            let n1 = n + c2.len_utf8();
+
+                            if n1 < pstr_atom.len() {
+                                step_2.iteratee = PStrIteratee::PStrSegment(f2, pstr_atom, n1);
 
+                                if consolidate_step(i1, &step_1) {
+                                    r1 = i1.step(step_1.next_hare);
                                     continue;
+                                } else {
+                                    break;
                                 }
                             } else {
-                                r2 = i2.next();
-                                continue;
+                                let c1_result = consolidate_step(i1, &step_1);
+                                let c2_result = consolidate_step(i2, &step_2);
+
+                                if c1_result {
+                                    r1 = i1.step(i1.brent_st.hare);
+                                }
+
+                                if c2_result {
+                                    r2 = i2.step(i2.brent_st.hare);
+                                }
+
+                                if c1_result && c2_result {
+                                    continue;
+                                } else {
+                                    break;
+                                }
                             }
                         } else {
-                            unreachable!()
+                            if consolidate_step(i2, &step_2) {
+                                r2 = i2.step(step_2.next_hare);
+                                continue;
+                            } else {
+                                break;
+                            }
                         }
                     }
-                    (PStrIteratee::PStrSegment(h, n), PStrIteratee::Char(c2)) => {
-                        if let &HeapCellValue::PartialString(ref pstr, _) = &i1.machine_st.heap[h] {
-                            if let Some(c1) = pstr.as_str_from(n).chars().next() {
-                                if c1 != c2 {
-                                    return c2.partial_cmp(&c1);
-                                } else {
-                                    r1 = i1.next();
-                                    r2 = Some(PStrIteratee::PStrSegment(h, n + c1.len_utf8()));
+                    (PStrIteratee::PStrSegment(f1, pstr_atom, n), PStrIteratee::Char(_, c2)) => {
+                        let pstr = PartialString::from(pstr_atom);
 
+                        if let Some(c1) = pstr.as_str_from(n).chars().next() {
+                            if c1 != c2 {
+                                return PStrCmpResult::Ordered(c2.cmp(&c1));
+                            }
+
+                            let n1 = n + c1.len_utf8();
+
+                            if n1 < pstr_atom.len() {
+                                step_1.iteratee = PStrIteratee::PStrSegment(f1, pstr_atom, n1);
+
+                                if consolidate_step(i2, &step_2) {
+                                    r2 = i2.step(step_2.next_hare);
                                     continue;
+                                } else {
+                                    break;
                                 }
                             } else {
-                                r1 = i1.next();
-                                continue;
+                                let c1_result = consolidate_step(i1, &step_1);
+                                let c2_result = consolidate_step(i2, &step_2);
+
+                                if c1_result {
+                                    r1 = i1.step(i1.brent_st.hare);
+                                }
+
+                                if c2_result {
+                                    r2 = i2.step(i2.brent_st.hare);
+                                }
+
+                                if c1_result && c2_result {
+                                    continue;
+                                } else {
+                                    break;
+                                }
                             }
                         } else {
-                            unreachable!()
+                            if consolidate_step(i1, &step_1) {
+                                r1 = i1.step(step_1.next_hare);
+                                continue;
+                            } else {
+                                break;
+                            }
                         }
                     }
-                    (PStrIteratee::PStrSegment(h1, n1), PStrIteratee::PStrSegment(h2, n2)) => {
-                        match (&i1.machine_st.heap[h1], &i2.machine_st.heap[h2]) {
-                            (
-                                &HeapCellValue::PartialString(ref pstr1, _),
-                                &HeapCellValue::PartialString(ref pstr2, _),
-                            ) => {
-                                let str1 = pstr1.as_str_from(n1);
-                                let str2 = pstr2.as_str_from(n2);
-
-                                if str1.starts_with(str2) {
-                                    r1 = Some(PStrIteratee::PStrSegment(h1, n1 + str2.len()));
-                                    r2 = i2.next();
+                    (PStrIteratee::PStrSegment(f1, pstr1_atom, n1),
+                     PStrIteratee::PStrSegment(f2, pstr2_atom, n2)) => {
+                        if pstr1_atom == pstr2_atom && n1 == n2 {
+                            let c_result1 = consolidate_step(i1, &step_1);
+                            let c_result2 = consolidate_step(i2, &step_2);
+
+                            if c_result1 {
+                                r1 = i1.step(step_1.next_hare);
+                            }
+
+                            if c_result2 {
+                                r2 = i2.step(step_2.next_hare);
+                            }
+
+                            if c_result1 && c_result2 {
+                                continue;
+                            }
+
+                            break;
+                        }
+
+                        let pstr1 = PartialString::from(pstr1_atom);
+                        let pstr2 = PartialString::from(pstr2_atom);
+
+                        let str1 = pstr1.as_str_from(n1);
+                        let str2 = pstr2.as_str_from(n2);
 
+                        match str1.len().cmp(&str2.len()) {
+                            Ordering::Equal if str1 == str2 => {
+                                let c_result1 = consolidate_step(i1, &step_1);
+                                let c_result2 = consolidate_step(i2, &step_2);
+
+                                if c_result1 {
+                                    r1 = i1.step(step_1.next_hare);
+                                }
+
+                                if c_result2 {
+                                    r2 = i2.step(step_2.next_hare);
+                                }
+
+                                if c_result1 && c_result2 {
                                     continue;
-                                } else if str2.starts_with(str1) {
-                                    r1 = i1.next();
-                                    r2 = Some(PStrIteratee::PStrSegment(h2, n2 + str1.len()));
+                                }
+
+                                break;
+                            }
+                            Ordering::Less if str2.starts_with(str1) => {
+                                step_2.iteratee = PStrIteratee::PStrSegment(f2, pstr2_atom, n2 + str1.len());
+
+                                if consolidate_step(i1, &step_1) {
+                                    r1 = i1.step(step_1.next_hare);
+                                    continue;
+                                } else {
+                                    break;
+                                }
+                            }
+                            Ordering::Greater if str1.starts_with(str2) => {
+                                step_1.iteratee = PStrIteratee::PStrSegment(f1, pstr1_atom, n1 + str2.len());
 
+                                if consolidate_step(i2, &step_2) {
+                                    r2 = i2.step(step_2.next_hare);
                                     continue;
                                 } else {
-                                    return str1.partial_cmp(str2);
+                                    break;
                                 }
                             }
                             _ => {
-                                unreachable!()
+                                return PStrCmpResult::Ordered(str1.cmp(str2));
                             }
                         }
                     }
                 }
-
-                r1 = i1.next();
-                r2 = i2.next();
-
-                continue;
             }
         }
 
-        let ordering = if r1.is_none() {
-            mem::swap(&mut r1, &mut r2);
-            Ordering::Less
-        } else {
-            Ordering::Greater
-        };
-
-        let machine_st = i1.machine_st;
+        break;
+    }
 
-        let check_focuses = || match (i1.focus(), i2.focus()) {
-            (Addr::EmptyList, Addr::EmptyList) => Some(Ordering::Equal),
-            (Addr::EmptyList, _) => Some(Ordering::Less),
-            (_, Addr::EmptyList) => Some(Ordering::Greater),
-            _ => None,
-        };
+    // to have a cyclic term, the cell at i1.focus must be:
+    //
+    // 1) 'continuable' as a cell in a string traversal, and,
+    // 2) matchable by compare_pstr_prefixes to the cell at i2.focus.
+    //
+    // If both cells are continuable they must have been encountered
+    // and thus matched by the compare_pstr_prefixes loop previously,
+    // so here it suffices to check if they are both continuable.
+
+    if i1.focus == i2.focus {
+        PStrCmpResult::Ordered(Ordering::Equal)
+    } else if i1.focus == empty_list_as_cell!() {
+        PStrCmpResult::Ordered(Ordering::Less)
+    } else if i2.focus == empty_list_as_cell!() {
+        PStrCmpResult::Ordered(Ordering::Greater)
+    } else if i1.is_continuable() {
+        if i2.is_continuable() {
+            return PStrCmpResult::Ordered(Ordering::Equal);
+        }
 
-        return match r1 {
-            Some(PStrIteratee::PStrSegment(h, n)) => {
-                if let &HeapCellValue::PartialString(ref pstr, _) = &machine_st.heap[h] {
-                    if pstr.as_str_from(n).chars().next().is_some() {
-                        Some(ordering)
-                    } else {
-                        check_focuses()
-                    }
-                } else {
-                    unreachable!()
-                }
-            }
-            Some(PStrIteratee::Char(_)) => Some(ordering),
-            None => check_focuses(),
-        };
+        PStrCmpResult::FirstIterContinuable(r1.unwrap().iteratee)
+    } else if i2.is_continuable() {
+        PStrCmpResult::SecondIterContinuable(r2.unwrap().iteratee)
+    } else {
+        PStrCmpResult::Unordered
     }
 }
 
-#[inline]
-pub(super) fn compare_pstr_to_string<'a>(
-    heap_pstr_iter: &mut HeapPStrIter<'a>,
-    s: &String,
-) -> Option<usize> {
-    let mut s_offset = 0;
-
-    while let Some(iteratee) = heap_pstr_iter.next() {
-        match iteratee {
-            PStrIteratee::Char(c1) => {
-                if let Some(c2) = s[s_offset..].chars().next() {
-                    if c1 != c2 {
-                        return None;
-                    } else {
-                        s_offset += c1.len_utf8();
-                    }
-                } else {
-                    return Some(s_offset);
-                }
-            }
-            PStrIteratee::PStrSegment(h, n) => match heap_pstr_iter.machine_st.heap[h] {
-                HeapCellValue::PartialString(ref pstr, _) => {
-                    let t = pstr.as_str_from(n);
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::machine::mock_wam::*;
 
-                    if s[s_offset..].starts_with(t) {
-                        s_offset += t.len();
-                    } else if t.starts_with(&s[s_offset..]) {
-                        heap_pstr_iter.focus = Addr::PStrLocation(h, n + s[s_offset..].len());
+    #[test]
+    fn pstr_iter_tests() {
+        let mut wam = MockWAM::new();
 
-                        s_offset += s[s_offset..].len();
-                        return Some(s_offset);
-                    } else {
-                        return None;
-                    }
-                }
-                _ => {
-                    unreachable!()
-                }
-            },
+        let pstr_var_cell = put_partial_string(
+            &mut wam.machine_st.heap,
+            "abc ",
+            &mut wam.machine_st.atom_tbl,
+        );
+
+        let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize];
+
+        {
+            let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0);
+
+            assert_eq!(
+                iter.next(),
+                Some(PStrIteratee::PStrSegment(0, cell_as_atom!(pstr_cell), 0))
+            );
+            assert_eq!(iter.next(), None);
+
+            assert!(!iter.at_string_terminator());
+        }
+
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(pstr_loc_as_cell!(2));
+
+        let pstr_second_var_cell = put_partial_string(
+            &mut wam.machine_st.heap,
+            "def",
+            &mut wam.machine_st.atom_tbl,
+        );
+
+        let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize];
+
+        {
+            let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0);
+
+            assert_eq!(
+                iter.next(),
+                Some(PStrIteratee::PStrSegment(0, cell_as_atom!(pstr_cell), 0))
+            );
+            assert_eq!(
+                iter.next(),
+                Some(PStrIteratee::PStrSegment(2, cell_as_atom!(pstr_second_cell), 0))
+            );
+
+            assert_eq!(iter.next(), None);
+            assert!(!iter.at_string_terminator());
         }
 
-        if s[s_offset..].is_empty() {
-            return Some(s_offset);
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        {
+            let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0);
+
+            assert_eq!(
+                iter.next(),
+                Some(PStrIteratee::PStrSegment(0, cell_as_atom!(pstr_cell), 0))
+            );
+            assert_eq!(
+                iter.next(),
+                Some(PStrIteratee::PStrSegment(2, cell_as_atom!(pstr_second_cell), 0))
+            );
+
+            assert_eq!(iter.next(), None);
+            assert!(iter.at_string_terminator());
         }
-    }
 
-    Some(s_offset)
+        wam.machine_st.heap.pop();
+        wam.machine_st.heap.push(pstr_loc_as_cell!(wam.machine_st.heap.len() + 1));
+
+        wam.machine_st.heap.push(pstr_offset_as_cell!(0));
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0)));
+
+        {
+            let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0);
+
+            while let Some(_) = iter.next() {}
+
+            assert!(!iter.at_string_terminator());
+        }
+
+        {
+            let mut iter1 = HeapPStrIter::new(&wam.machine_st.heap, 0);
+            let mut iter2 = HeapPStrIter::new(&wam.machine_st.heap, 0);
+
+            assert_eq!(
+                compare_pstr_prefixes(&mut iter1, &mut iter2),
+                PStrCmpResult::Ordered(Ordering::Equal)
+            );
+        }
+
+        {
+            let second_h = wam.machine_st.heap.len();
+
+            // construct a structurally similar but different cyclic partial string
+            // matching the one beginning at wam.machine_st.heap[0].
+
+            put_partial_string(
+                &mut wam.machine_st.heap,
+                "ab",
+                &mut wam.machine_st.atom_tbl,
+            );
+
+            wam.machine_st.heap.pop();
+
+            wam.machine_st.heap.push(pstr_loc_as_cell!(second_h+2));
+
+            put_partial_string(
+                &mut wam.machine_st.heap,
+                "c ",
+                &mut wam.machine_st.atom_tbl,
+            );
+
+            wam.machine_st.heap.pop();
+
+            wam.machine_st.heap.push(pstr_loc_as_cell!(second_h+4));
+
+            wam.machine_st.heap.push(pstr_second_cell);
+            wam.machine_st.heap.push(pstr_loc_as_cell!(second_h+6));
+
+            wam.machine_st.heap.push(pstr_offset_as_cell!(second_h));
+            wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0)));
+
+            let mut iter1 = HeapPStrIter::new(&wam.machine_st.heap, 0);
+            let mut iter2 = HeapPStrIter::new(&wam.machine_st.heap, second_h);
+
+            assert_eq!(
+                compare_pstr_prefixes(&mut iter1, &mut iter2),
+                PStrCmpResult::Ordered(Ordering::Equal)
+            );
+        }
+
+        wam.machine_st.heap.clear();
+
+        put_partial_string(
+            &mut wam.machine_st.heap,
+            "abc ",
+            &mut wam.machine_st.atom_tbl,
+        );
+
+        let pstr_cell = wam.machine_st.heap[0];
+
+        wam.machine_st.heap[1] = list_loc_as_cell!(2);
+
+        wam.machine_st.heap.push(char_as_cell!('a'));
+        wam.machine_st.heap.push(list_loc_as_cell!(4));
+        wam.machine_st.heap.push(char_as_cell!('b'));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        wam.machine_st.heap.push(pstr_cell);
+        wam.machine_st.heap.push(heap_loc_as_cell!(7));
+
+        {
+            let mut iter1 = HeapPStrIter::new(&wam.machine_st.heap, 0);
+            let mut iter2 = HeapPStrIter::new(&wam.machine_st.heap, 6);
+
+            assert_eq!(
+                compare_pstr_prefixes(&mut iter1, &mut iter2),
+                PStrCmpResult::FirstIterContinuable(PStrIteratee::Char(1, 'a')),
+            );
+
+            assert_eq!(iter2.focus, heap_loc_as_cell!(7));
+        }
+
+        // test "abc" = [X,Y,Z].
+
+        wam.machine_st.heap.clear();
+
+        let cstr_var_cell = put_complete_string(
+            &mut wam.machine_st.heap,
+            "abc",
+            &mut wam.machine_st.atom_tbl,
+        );
+
+        wam.machine_st.heap.push(list_loc_as_cell!(2));
+        wam.machine_st.heap.push(heap_loc_as_cell!(2));
+
+        wam.machine_st.heap.push(list_loc_as_cell!(4));
+        wam.machine_st.heap.push(heap_loc_as_cell!(4));
+
+        wam.machine_st.heap.push(list_loc_as_cell!(6));
+        wam.machine_st.heap.push(heap_loc_as_cell!(6));
+
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1));
+
+        assert_eq!(
+            wam.machine_st.heap[2],
+            char_as_cell!('a'),
+        );
+
+        assert_eq!(
+            wam.machine_st.heap[4],
+            char_as_cell!('b'),
+        );
+
+        assert_eq!(
+            wam.machine_st.heap[6],
+            char_as_cell!('c'),
+        );
+
+        // test "abc" = [X,Y,Z|D].
+
+        wam.machine_st.heap.clear();
+
+        let cstr_var_cell = put_complete_string(
+            &mut wam.machine_st.heap,
+            "abc",
+            &mut wam.machine_st.atom_tbl,
+        );
+
+        wam.machine_st.heap.push(list_loc_as_cell!(2));
+        wam.machine_st.heap.push(heap_loc_as_cell!(2)); // X
+
+        wam.machine_st.heap.push(list_loc_as_cell!(4));
+        wam.machine_st.heap.push(heap_loc_as_cell!(4)); // Y
+
+        wam.machine_st.heap.push(list_loc_as_cell!(6));
+        wam.machine_st.heap.push(heap_loc_as_cell!(6)); // Z
+
+        wam.machine_st.heap.push(heap_loc_as_cell!(7)); // D
+
+        unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1));
+
+        assert_eq!(wam.machine_st.fail, false);
+
+        assert_eq!(
+            wam.machine_st.heap[2],
+            char_as_cell!('a'),
+        );
+
+        assert_eq!(
+            wam.machine_st.heap[4],
+            char_as_cell!('b'),
+        );
+
+        assert_eq!(
+            wam.machine_st.heap[6],
+            char_as_cell!('c'),
+        );
+
+        assert_eq!(
+            wam.machine_st.heap[7],
+            empty_list_as_cell!(),
+        );
+
+        // test "d" = [d].
+
+        wam.machine_st.heap.clear();
+
+        let cstr_var_cell = put_complete_string(
+            &mut wam.machine_st.heap,
+            "d",
+            &mut wam.machine_st.atom_tbl,
+        );
+
+        wam.machine_st.heap.push(list_loc_as_cell!(2));
+        wam.machine_st.heap.push(char_as_cell!('d'));
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1));
+
+        assert_eq!(wam.machine_st.fail, false);
+
+        // test "abc" = [X,b,Z].
+
+        wam.machine_st.heap.clear();
+
+        let cstr_var_cell = put_complete_string(
+            &mut wam.machine_st.heap,
+            "abc",
+            &mut wam.machine_st.atom_tbl,
+        );
+
+        wam.machine_st.heap.push(list_loc_as_cell!(2));
+        wam.machine_st.heap.push(heap_loc_as_cell!(2));
+
+        wam.machine_st.heap.push(list_loc_as_cell!(4));
+        wam.machine_st.heap.push(char_as_cell!('b'));
+
+        wam.machine_st.heap.push(list_loc_as_cell!(6));
+        wam.machine_st.heap.push(heap_loc_as_cell!(6));
+
+        wam.machine_st.heap.push(empty_list_as_cell!());
+
+        unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1));
+
+        assert_eq!(wam.machine_st.fail, false);
+
+        assert_eq!(
+            wam.machine_st.heap[2],
+            char_as_cell!('a'),
+        );
+
+        assert_eq!(
+            wam.machine_st.heap[4],
+            char_as_cell!('b'),
+        );
+
+        assert_eq!(
+            wam.machine_st.heap[6],
+            char_as_cell!('c'),
+        );
+
+        // test "abcdef" = [a,b,c|X].
+
+        wam.machine_st.heap.clear();
+
+        put_complete_string(
+            &mut wam.machine_st.heap,
+            "abcdef",
+            &mut wam.machine_st.atom_tbl,
+        );
+
+        wam.machine_st.heap.push(pstr_as_cell!(atom!("abc")));
+        wam.machine_st.heap.push(heap_loc_as_cell!(2));
+
+        unify!(wam.machine_st, heap_loc_as_cell!(0), pstr_loc_as_cell!(1));
+
+        print_heap_terms(wam.machine_st.heap.iter(), 0);
+
+        assert_eq!(wam.machine_st.fail, false);
+
+        assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(5));
+        assert_eq!(wam.machine_st.heap[3], pstr_loc_as_cell!(1));
+        assert_eq!(wam.machine_st.heap[4], atom_as_cstr_cell!(atom!("abcdef")));
+        assert_eq!(wam.machine_st.heap[5], pstr_offset_as_cell!(4));
+        assert_eq!(wam.machine_st.heap[6], fixnum_as_cell!(Fixnum::build_with("abc".len() as i64)));
+
+        // test iteration on X = [b,c,b,c,b,c,b,c|...] as an offset.
+
+        wam.machine_st.heap.clear();
+
+        wam.machine_st.heap.push(pstr_as_cell!(atom!("abc")));
+        wam.machine_st.heap.push(pstr_loc_as_cell!(2));
+        wam.machine_st.heap.push(pstr_offset_as_cell!(0));
+        wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(1)));
+
+        {
+            let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 2);
+
+            assert_eq!(
+                iter.next(),
+                Some(PStrIteratee::PStrSegment(2, atom!("abc"), 1))
+            );
+
+            // assert!(iter.next().is_none());
+
+            while let Some(_) = iter.next() {}
+        }
+    }
 }
index b93f270ef590e02e18dbb802b4a2e32c20559113..e2c74be1702c899eefddd52693eb564e7ea0aa19 100644 (file)
@@ -1,12 +1,10 @@
-use prolog_parser::ast::*;
-use prolog_parser::tabled_rc::*;
-use prolog_parser::{atom, clause_name, rc_atom};
-
+use crate::atom_table::*;
+use crate::clause_types::*;
 use crate::forms::*;
 use crate::iterators::*;
-use crate::machine::load_state::*;
+use crate::machine::loader::*;
 use crate::machine::machine_errors::*;
-use crate::machine::*;
+use crate::parser::ast::*;
 
 use indexmap::IndexSet;
 
@@ -30,89 +28,81 @@ pub(crate) enum CutContext {
     HasCutVariable,
 }
 
-pub(crate) fn fold_by_str<I>(terms: I, mut term: Term, sym: ClauseName) -> Term
+pub(crate) fn fold_by_str<I>(terms: I, mut term: Term, sym: Atom) -> Term
 where
     I: DoubleEndedIterator<Item = Term>,
 {
     for prec in terms.rev() {
-        term = Term::Clause(
-            Cell::default(),
-            sym.clone(),
-            vec![Box::new(prec), Box::new(term)],
-            None,
-        );
+        term = Term::Clause(Cell::default(), sym, vec![prec, term]);
     }
 
     term
 }
 
 pub(crate) fn to_op_decl(
-    prec: usize,
-    spec: &str,
-    name: ClauseName,
+    prec: u16,
+    spec: Atom,
+    name: Atom,
 ) -> Result<OpDecl, CompilationError> {
     match spec {
-        "xfx" => Ok(OpDecl::new(prec, XFX, name)),
-        "xfy" => Ok(OpDecl::new(prec, XFY, name)),
-        "yfx" => Ok(OpDecl::new(prec, YFX, name)),
-        "fx" => Ok(OpDecl::new(prec, FX, name)),
-        "fy" => Ok(OpDecl::new(prec, FY, name)),
-        "xf" => Ok(OpDecl::new(prec, XF, name)),
-        "yf" => Ok(OpDecl::new(prec, YF, name)),
+        atom!("xfx") => Ok(OpDecl::new(OpDesc::build_with(prec, XFX as u8), name)),
+        atom!("xfy") => Ok(OpDecl::new(OpDesc::build_with(prec, XFY as u8), name)),
+        atom!("yfx") => Ok(OpDecl::new(OpDesc::build_with(prec, YFX as u8), name)),
+        atom!("fx") => Ok(OpDecl::new(OpDesc::build_with(prec, FX as u8), name)),
+        atom!("fy") => Ok(OpDecl::new(OpDesc::build_with(prec, FY as u8), name)),
+        atom!("xf") => Ok(OpDecl::new(OpDesc::build_with(prec, XF as u8), name)),
+        atom!("yf") => Ok(OpDecl::new(OpDesc::build_with(prec, YF as u8), name)),
         _ => Err(CompilationError::InconsistentEntry),
     }
 }
 
 fn setup_op_decl(
-    mut terms: Vec<Box<Term>>,
-    atom_tbl: TabledData<Atom>,
+    mut terms: Vec<Term>,
+    atom_tbl: &mut AtomTable,
 ) -> Result<OpDecl, CompilationError> {
-    let name = match *terms.pop().unwrap() {
-        Term::Constant(_, Constant::Atom(name, _)) => name,
-        Term::Constant(_, Constant::Char(c)) => clause_name!(c.to_string(), atom_tbl),
+    let name = match terms.pop().unwrap() {
+        Term::Literal(_, Literal::Atom(name)) => name,
+        Term::Literal(_, Literal::Char(c)) => atom_tbl.build_with(&c.to_string()),
         _ => return Err(CompilationError::InconsistentEntry),
     };
 
-    let spec = match *terms.pop().unwrap() {
-        Term::Constant(_, Constant::Atom(name, _)) => name,
-        Term::Constant(_, Constant::Char(c)) => clause_name!(c.to_string(), atom_tbl),
+    let spec = match terms.pop().unwrap() {
+        Term::Literal(_, Literal::Atom(name)) => name,
+        Term::Literal(_, Literal::Char(c)) => atom_tbl.build_with(&c.to_string()),
         _ => return Err(CompilationError::InconsistentEntry),
     };
 
-    let prec = match *terms.pop().unwrap() {
-        Term::Constant(_, Constant::Fixnum(bi)) => match usize::try_from(bi) {
+    let prec = match terms.pop().unwrap() {
+        Term::Literal(_, Literal::Fixnum(bi)) => match u16::try_from(bi.get_num()) {
             Ok(n) if n <= 1200 => n,
             _ => return Err(CompilationError::InconsistentEntry),
         },
         _ => return Err(CompilationError::InconsistentEntry),
     };
 
-    to_op_decl(prec, spec.as_str(), name)
+    to_op_decl(prec, spec, name)
 }
 
 fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, CompilationError> {
     match term {
-        Term::Clause(_, ref slash, ref mut terms, Some(_))
-            if (slash.as_str() == "/" || slash.as_str() == "//") && terms.len() == 2 =>
+        Term::Clause(_, slash, ref mut terms)
+            if (*slash == atom!("/") || *slash == atom!("//")) && terms.len() == 2 =>
         {
-            let arity = *terms.pop().unwrap();
-            let name = *terms.pop().unwrap();
-
-            let arity = arity
-                .into_constant()
-                .and_then(|c| match c {
-                    Constant::Integer(n) => n.to_usize(),
-                    Constant::Fixnum(n) => usize::try_from(n).ok(),
-                    _ => None,
-                })
-                .ok_or(CompilationError::InvalidModuleExport)?;
+            let arity = terms.pop().unwrap();
+            let name = terms.pop().unwrap();
 
-            let name = name
-                .into_constant()
-                .and_then(|c| c.to_atom())
-                .ok_or(CompilationError::InvalidModuleExport)?;
+            let arity = match arity {
+                Term::Literal(_, Literal::Integer(n)) => n.to_usize(),
+                Term::Literal(_, Literal::Fixnum(n)) => usize::try_from(n.get_num()).ok(),
+                _ => None,
+            }.ok_or(CompilationError::InvalidModuleExport)?;
+
+            let name = match name {
+                Term::Literal(_, Literal::Atom(name)) => Some(name),
+                _ => None,
+            }.ok_or(CompilationError::InvalidModuleExport)?;
 
-            if slash.as_str() == "/" {
+            if *slash == atom!("/") {
                 Ok((name, arity))
             } else {
                 Ok((name, arity + 2))
@@ -148,13 +138,13 @@ fn setup_scoped_predicate_indicator(term: &mut Term) -> Result<ScopedPredicateKe
 
 fn setup_module_export(
     mut term: Term,
-    atom_tbl: TabledData<Atom>,
+    atom_tbl: &mut AtomTable,
 ) -> Result<ModuleExport, CompilationError> {
     setup_predicate_indicator(&mut term)
         .map(ModuleExport::PredicateKey)
         .or_else(|_| {
-            if let Term::Clause(_, name, terms, _) = term {
-                if terms.len() == 3 && name.as_str() == "op" {
+            if let Term::Clause(_, name, terms) = term {
+                if terms.len() == 3 && name == atom!("op") {
                     Ok(ModuleExport::OpDecl(setup_op_decl(terms, atom_tbl)?))
                 } else {
                     Err(CompilationError::InvalidModuleDecl)
@@ -167,18 +157,18 @@ fn setup_module_export(
 
 pub(super) fn setup_module_export_list(
     mut export_list: Term,
-    atom_tbl: TabledData<Atom>,
+    atom_tbl: &mut AtomTable,
 ) -> Result<Vec<ModuleExport>, CompilationError> {
     let mut exports = vec![];
 
     while let Term::Cons(_, t1, t2) = export_list {
-        let module_export = setup_module_export(*t1, atom_tbl.clone())?;
+        let module_export = setup_module_export(*t1, atom_tbl)?;
 
         exports.push(module_export);
         export_list = *t2;
     }
 
-    if let Term::Constant(_, Constant::EmptyList) = export_list {
+    if let Term::Literal(_, Literal::Atom(atom!("[]"))) = export_list {
         Ok(exports)
     } else {
         Err(CompilationError::InvalidModuleDecl)
@@ -186,35 +176,33 @@ pub(super) fn setup_module_export_list(
 }
 
 fn setup_module_decl(
-    mut terms: Vec<Box<Term>>,
-    atom_tbl: TabledData<Atom>,
+    mut terms: Vec<Term>,
+    atom_tbl: &mut AtomTable,
 ) -> Result<ModuleDecl, CompilationError> {
-    let export_list = *terms.pop().unwrap();
-    let name = terms
-        .pop()
-        .unwrap()
-        .into_constant()
-        .and_then(|c| c.to_atom())
-        .ok_or(CompilationError::InvalidModuleDecl)?;
+    let export_list = terms.pop().unwrap();
+    let name = terms.pop().unwrap();
+
+    let name = match name {
+        Term::Literal(_, Literal::Atom(name)) => Some(name),
+        _ => None,
+    }.ok_or(CompilationError::InvalidModuleDecl)?;
 
     let exports = setup_module_export_list(export_list, atom_tbl)?;
+
     Ok(ModuleDecl { name, exports })
 }
 
-fn setup_use_module_decl(mut terms: Vec<Box<Term>>) -> Result<ModuleSource, CompilationError> {
-    match *terms.pop().unwrap() {
-        Term::Clause(_, ref name, ref mut terms, None)
-            if name.as_str() == "library" && terms.len() == 1 =>
+fn setup_use_module_decl(mut terms: Vec<Term>) -> Result<ModuleSource, CompilationError> {
+    match terms.pop().unwrap() {
+        Term::Clause(_, name, mut terms)
+            if name == atom!("library") && terms.len() == 1 =>
         {
-            terms
-                .pop()
-                .unwrap()
-                .into_constant()
-                .and_then(|c| c.to_atom())
-                .map(|c| ModuleSource::Library(c))
-                .ok_or(CompilationError::InvalidUseModuleDecl)
+            match terms.pop().unwrap() {
+                Term::Literal(_, Literal::Atom(name)) => Ok(ModuleSource::Library(name)),
+                _ => Err(CompilationError::InvalidModuleDecl),
+            }
         }
-        Term::Constant(_, Constant::Atom(ref name, _)) => Ok(ModuleSource::File(name.clone())),
+        Term::Literal(_, Literal::Atom(name)) => Ok(ModuleSource::File(name)),
         _ => Err(CompilationError::InvalidUseModuleDecl),
     }
 }
@@ -224,10 +212,10 @@ fn setup_double_quotes(mut terms: Vec<Box<Term>>) -> Result<DoubleQuotes, Compil
     let dbl_quotes = *terms.pop().unwrap();
 
     match terms[0].as_ref() {
-        Term::Constant(_, Constant::Atom(ref name, _))
+        Term::Literal(_, Literal::Atom(ref name, _))
             if name.as_str() == "double_quotes" => {
                 match dbl_quotes {
-                    Term::Constant(_, Constant::Atom(name, _)) => {
+                    Term::Literal(_, Literal::Atom(name, _)) => {
                         match name.as_str() {
                             "atom"  => Ok(DoubleQuotes::Atom),
                             "chars" => Ok(DoubleQuotes::Chars),
@@ -250,34 +238,31 @@ fn setup_double_quotes(mut terms: Vec<Box<Term>>) -> Result<DoubleQuotes, Compil
 type UseModuleExport = (ModuleSource, IndexSet<ModuleExport>);
 
 fn setup_qualified_import(
-    mut terms: Vec<Box<Term>>,
-    atom_tbl: TabledData<Atom>,
+    mut terms: Vec<Term>,
+    atom_tbl: &mut AtomTable,
 ) -> Result<UseModuleExport, CompilationError> {
-    let mut export_list = *terms.pop().unwrap();
-    let module_src = match *terms.pop().unwrap() {
-        Term::Clause(_, ref name, ref mut terms, None)
-            if name.as_str() == "library" && terms.len() == 1 =>
+    let mut export_list = terms.pop().unwrap();
+    let module_src = match terms.pop().unwrap() {
+        Term::Clause(_, name, mut terms)
+            if name == atom!("library") && terms.len() == 1 =>
         {
-            terms
-                .pop()
-                .unwrap()
-                .into_constant()
-                .and_then(|c| c.to_atom())
-                .map(|c| ModuleSource::Library(c))
-                .ok_or(CompilationError::InvalidUseModuleDecl)
+            match terms.pop().unwrap() {
+                Term::Literal(_, Literal::Atom(name)) => Ok(ModuleSource::Library(name)),
+                _ => Err(CompilationError::InvalidModuleDecl),
+            }
         }
-        Term::Constant(_, Constant::Atom(ref name, _)) => Ok(ModuleSource::File(name.clone())),
+        Term::Literal(_, Literal::Atom(name)) => Ok(ModuleSource::File(name)),
         _ => Err(CompilationError::InvalidUseModuleDecl),
     }?;
 
     let mut exports = IndexSet::new();
 
     while let Term::Cons(_, t1, t2) = export_list {
-        exports.insert(setup_module_export(*t1, atom_tbl.clone())?);
+        exports.insert(setup_module_export(*t1, atom_tbl)?);
         export_list = *t2;
     }
 
-    if let Term::Constant(_, Constant::EmptyList) = export_list {
+    if let Term::Literal(_, Literal::Atom(atom!("[]"))) = export_list {
         Ok((module_src, exports))
     } else {
         Err(CompilationError::InvalidModuleDecl)
@@ -322,29 +307,29 @@ fn setup_qualified_import(
  * -
  * ?
  */
-fn setup_meta_predicate<'a>(
-    mut terms: Vec<Box<Term>>,
-    load_state: &LoadState<'a>,
-) -> Result<(ClauseName, ClauseName, Vec<MetaSpec>), CompilationError> {
+fn setup_meta_predicate<'a, LS: LoadState<'a>>(
+    mut terms: Vec<Term>,
+    loader: &mut Loader<'a, LS>,
+) -> Result<(Atom, Atom, Vec<MetaSpec>), CompilationError> {
     fn get_name_and_meta_specs(
-        name: ClauseName,
-        terms: &mut [Box<Term>],
-    ) -> Result<(ClauseName, Vec<MetaSpec>), CompilationError> {
+        name: Atom,
+        terms: &mut [Term],
+    ) -> Result<(Atom, Vec<MetaSpec>), CompilationError> {
         let mut meta_specs = vec![];
 
         for meta_spec in terms.into_iter() {
-            match &**meta_spec {
-                Term::Constant(_, Constant::Atom(meta_spec, _)) => {
-                    let meta_spec = match meta_spec.as_str() {
-                        "+" => MetaSpec::Plus,
-                        "-" => MetaSpec::Minus,
-                        "?" => MetaSpec::Either,
+            match meta_spec {
+                Term::Literal(_, Literal::Atom(meta_spec)) => {
+                    let meta_spec = match meta_spec {
+                        atom!("+") => MetaSpec::Plus,
+                        atom!("-") => MetaSpec::Minus,
+                        atom!("?") => MetaSpec::Either,
                         _ => return Err(CompilationError::InvalidMetaPredicateDecl),
                     };
 
                     meta_specs.push(meta_spec);
                 }
-                Term::Constant(_, Constant::Fixnum(n)) => match usize::try_from(*n) {
+                Term::Literal(_, Literal::Fixnum(n)) => match usize::try_from(n.get_num()) {
                     Ok(n) if n <= MAX_ARITY => {
                         meta_specs.push(MetaSpec::RequiresExpansionWithArgument(n));
                     }
@@ -361,16 +346,15 @@ fn setup_meta_predicate<'a>(
         Ok((name, meta_specs))
     }
 
-    match *terms.pop().unwrap() {
-        Term::Clause(_, name, mut terms, _) if name.as_str() == ":" && terms.len() == 2 => {
-            let spec = *terms.pop().unwrap();
-            let module_name = *terms.pop().unwrap();
+    match terms.pop().unwrap() {
+        Term::Clause(_, name, mut terms) if name == atom!(":") && terms.len() == 2 => {
+            let spec = terms.pop().unwrap();
+            let module_name = terms.pop().unwrap();
 
             match module_name {
-                Term::Constant(_, Constant::Atom(module_name, _)) => match spec {
-                    Term::Clause(_, name, mut terms, _) => {
+                Term::Literal(_, Literal::Atom(module_name)) => match spec {
+                    Term::Clause(_, name, mut terms) => {
                         let (name, meta_specs) = get_name_and_meta_specs(name, &mut terms)?;
-
                         Ok((module_name, name, meta_specs))
                     }
                     _ => Err(CompilationError::InvalidMetaPredicateDecl),
@@ -378,10 +362,10 @@ fn setup_meta_predicate<'a>(
                 _ => Err(CompilationError::InvalidMetaPredicateDecl),
             }
         }
-        Term::Clause(_, name, mut terms, _) => {
+        Term::Clause(_, name, mut terms) => {
             let (name, meta_specs) = get_name_and_meta_specs(name, &mut terms)?;
             Ok((
-                load_state.compilation_target.module_name(),
+                loader.payload.compilation_target.module_name(),
                 name,
                 meta_specs,
             ))
@@ -420,11 +404,11 @@ fn merge_clauses(tls: &mut VecDeque<TopLevel>) -> Result<TopLevel, CompilationEr
     }
 }
 
-fn mark_cut_variables_as(terms: &mut Vec<Term>, name: ClauseName) {
+fn mark_cut_variables_as(terms: &mut Vec<Term>, name: Atom) {
     for term in terms.iter_mut() {
         match term {
-            &mut Term::Constant(_, Constant::Atom(ref mut var, _)) if var.as_str() == "!" => {
-                *var = name.clone()
+            &mut Term::Literal(_, Literal::Atom(ref mut var)) if *var == atom!("!") => {
+                *var = name;
             }
             _ => {}
         }
@@ -433,12 +417,12 @@ fn mark_cut_variables_as(terms: &mut Vec<Term>, name: ClauseName) {
 
 fn mark_cut_variable(term: &mut Term) -> bool {
     let cut_var_found = match term {
-        &mut Term::Constant(_, Constant::Atom(ref var, _)) if var.as_str() == "!" => true,
+        &mut Term::Literal(_, Literal::Atom(ref var)) if *var == atom!("!") => true,
         _ => false,
     };
 
     if cut_var_found {
-        *term = Term::Var(Cell::default(), rc_atom!("!"));
+        *term = Term::Var(Cell::default(), Rc::new(String::from("!")));
         true
     } else {
         false
@@ -463,21 +447,21 @@ fn check_for_internal_if_then(terms: &mut Vec<Term>) {
         return;
     }
 
-    if let Some(Term::Clause(_, ref name, ref subterms, _)) = terms.last() {
-        if name.as_str() != "->" || subterms.len() != 2 {
+    if let Some(Term::Clause(_, name, ref subterms)) = terms.last() {
+        if *name != atom!("->") || subterms.len() != 2 {
             return;
         }
     } else {
         return;
     }
 
-    if let Some(Term::Clause(_, _, mut subterms, _)) = terms.pop() {
-        let mut conq_terms = VecDeque::from(unfold_by_str(*subterms.pop().unwrap(), ","));
-        let mut pre_cut_terms = VecDeque::from(unfold_by_str(*subterms.pop().unwrap(), ","));
+    if let Some(Term::Clause(_, _, mut subterms)) = terms.pop() {
+        let mut conq_terms = VecDeque::from(unfold_by_str(subterms.pop().unwrap(), atom!(",")));
+        let mut pre_cut_terms = VecDeque::from(unfold_by_str(subterms.pop().unwrap(), atom!(",")));
 
-        conq_terms.push_front(Term::Constant(
+        conq_terms.push_front(Term::Literal(
             Cell::default(),
-            Constant::Atom(clause_name!("blocked_!"), None),
+            Literal::Atom(atom!("blocked_!")),
         ));
 
         while let Some(term) = pre_cut_terms.pop_back() {
@@ -489,37 +473,44 @@ fn check_for_internal_if_then(terms: &mut Vec<Term>) {
         terms.push(fold_by_str(
             conq_terms.into_iter(),
             tail_term,
-            clause_name!(","),
+            atom!(","),
         ));
     }
 }
 
-pub(super) fn setup_declaration<'a>(
-    load_state: &LoadState<'a>,
-    mut terms: Vec<Box<Term>>,
+pub(super) fn setup_declaration<'a, LS: LoadState<'a>>(
+    loader: &mut Loader<'a, LS>,
+    mut terms: Vec<Term>,
 ) -> Result<Declaration, CompilationError> {
-    let term = *terms.pop().unwrap();
-    let atom_tbl = load_state.wam.machine_st.atom_tbl.clone();
+    let term = terms.pop().unwrap();
 
     match term {
-        Term::Clause(_, name, mut terms, _) => match (name.as_str(), terms.len()) {
-            ("dynamic", 1) => {
-                let (name, arity) = setup_predicate_indicator(&mut *terms.pop().unwrap())?;
+        Term::Clause(_, name, mut terms) => match (name, terms.len()) {
+            (atom!("dynamic"), 1) => {
+                let (name, arity) = setup_predicate_indicator(&mut terms.pop().unwrap())?;
                 Ok(Declaration::Dynamic(name, arity))
             }
-            ("module", 2) => Ok(Declaration::Module(setup_module_decl(terms, atom_tbl)?)),
-            ("op", 3) => Ok(Declaration::Op(setup_op_decl(terms, atom_tbl)?)),
-            ("non_counted_backtracking", 1) => {
-                let (name, arity) = setup_predicate_indicator(&mut *terms.pop().unwrap())?;
+            (atom!("module"), 2) => {
+                let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl;
+                Ok(Declaration::Module(setup_module_decl(terms, atom_tbl)?))
+            }
+            (atom!("op"), 3) => {
+                let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl;
+                Ok(Declaration::Op(setup_op_decl(terms, atom_tbl)?))
+            }
+            (atom!("non_counted_backtracking"), 1) => {
+                let (name, arity) = setup_predicate_indicator(&mut terms.pop().unwrap())?;
                 Ok(Declaration::NonCountedBacktracking(name, arity))
             }
-            ("use_module", 1) => Ok(Declaration::UseModule(setup_use_module_decl(terms)?)),
-            ("use_module", 2) => {
+            (atom!("use_module"), 1) => Ok(Declaration::UseModule(setup_use_module_decl(terms)?)),
+            (atom!("use_module"), 2) => {
+                let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl;
                 let (name, exports) = setup_qualified_import(terms, atom_tbl)?;
+
                 Ok(Declaration::UseQualifiedModule(name, exports))
             }
-            ("meta_predicate", 1) => {
-                let (module_name, name, meta_specs) = setup_meta_predicate(terms, load_state)?;
+            (atom!("meta_predicate"), 1) => {
+                let (module_name, name, meta_specs) = setup_meta_predicate(terms, loader)?;
                 Ok(Declaration::MetaPredicate(module_name, name, meta_specs))
             }
             _ => Err(CompilationError::InconsistentEntry),
@@ -529,43 +520,43 @@ pub(super) fn setup_declaration<'a>(
 }
 
 #[inline]
-fn clause_to_query_term<'a>(
-    load_state: &mut LoadState<'a>,
-    name: ClauseName,
-    terms: Vec<Box<Term>>,
-    fixity: Option<SharedOpDesc>,
+fn clause_to_query_term<'a, LS: LoadState<'a>>(
+    loader: &mut Loader<'a, LS>,
+    name: Atom,
+    terms: Vec<Term>,
 ) -> QueryTerm {
-    let ct = load_state.get_clause_type(name, terms.len(), fixity);
+    let ct = loader.get_clause_type(name, terms.len());
     QueryTerm::Clause(Cell::default(), ct, terms, false)
 }
 
 #[inline]
-fn qualified_clause_to_query_term<'a>(
-    load_state: &mut LoadState<'a>,
-    module_name: ClauseName,
-    name: ClauseName,
-    terms: Vec<Box<Term>>,
-    fixity: Option<SharedOpDesc>,
+fn qualified_clause_to_query_term<'a, LS: LoadState<'a>>(
+    loader: &mut Loader<'a, LS>,
+    module_name: Atom,
+    name: Atom,
+    terms: Vec<Term>,
 ) -> QueryTerm {
-    let ct = load_state.get_qualified_clause_type(module_name, name, terms.len(), fixity);
+    let ct = loader.get_qualified_clause_type(module_name, name, terms.len());
     QueryTerm::Clause(Cell::default(), ct, terms, false)
 }
 
 #[derive(Debug)]
 pub(crate) struct Preprocessor {
+    flags: MachineFlags,
     queue: VecDeque<VecDeque<Term>>,
 }
 
 impl Preprocessor {
-    pub(super) fn new() -> Self {
+    pub(super) fn new(flags: MachineFlags) -> Self {
         Preprocessor {
+            flags,
             queue: VecDeque::new(),
         }
     }
 
     fn setup_fact(&mut self, term: Term) -> Result<Term, CompilationError> {
         match term {
-            Term::Clause(..) | Term::Constant(_, Constant::Atom(..)) => Ok(term),
+            Term::Clause(..) | Term::Literal(_, Literal::Atom(..)) => Ok(term),
             _ => Err(CompilationError::InadmissibleFact),
         }
     }
@@ -579,20 +570,17 @@ impl Preprocessor {
             }
         }
 
-        vars.insert(rc_atom!("!"));
+        vars.insert(Rc::new(String::from("!")));
         vars.into_iter()
             .map(|v| Term::Var(Cell::default(), v))
             .collect()
     }
 
     fn fabricate_rule_body(&self, vars: &Vec<Term>, body_term: Term) -> Term {
-        let vars_of_head = vars.iter().cloned().map(Box::new).collect();
-        let head_term = Term::Clause(Cell::default(), clause_name!(""), vars_of_head, None);
-
-        let rule = vec![Box::new(head_term), Box::new(body_term)];
-        let turnstile = clause_name!(":-");
+        let head_term = Term::Clause(Cell::default(), atom!(""), vars.clone());
+        let rule = vec![head_term, body_term];
 
-        Term::Clause(Cell::default(), turnstile, rule, None)
+        Term::Clause(Cell::default(), atom!(":-"), rule)
     }
 
     // the terms form the body of the rule. We create a head, by
@@ -609,16 +597,16 @@ impl Preprocessor {
 
     fn fabricate_disjunct(&self, body_term: Term) -> (JumpStub, VecDeque<Term>) {
         let vars = self.compute_head(&body_term);
-        let results = unfold_by_str(body_term, ";")
+        let results = unfold_by_str(body_term, atom!(";"))
             .into_iter()
             .map(|term| {
-                let mut subterms = unfold_by_str(term, ",");
+                let mut subterms = unfold_by_str(term, atom!(","));
                 mark_cut_variables(&mut subterms);
 
                 check_for_internal_if_then(&mut subterms);
 
                 let term = subterms.pop().unwrap();
-                let clause = fold_by_str(subterms.into_iter(), term, clause_name!(","));
+                let clause = fold_by_str(subterms.into_iter(), term, atom!(","));
 
                 self.fabricate_rule_body(&vars, clause)
             })
@@ -628,80 +616,78 @@ impl Preprocessor {
     }
 
     fn fabricate_if_then(&self, prec: Term, conq: Term) -> (JumpStub, VecDeque<Term>) {
-        let mut prec_seq = unfold_by_str(prec, ",");
-        let comma_sym = clause_name!(",");
-        let cut_sym = atom!("!");
+        let mut prec_seq = unfold_by_str(prec, atom!(","));
+        let comma_sym = atom!(",");
+        let cut_sym = Literal::Atom(atom!("!"));
 
-        prec_seq.push(Term::Constant(Cell::default(), cut_sym));
+        prec_seq.push(Term::Literal(Cell::default(), cut_sym));
 
-        mark_cut_variables_as(&mut prec_seq, clause_name!("blocked_!"));
+        mark_cut_variables_as(&mut prec_seq, atom!("blocked_!"));
 
-        let mut conq_seq = unfold_by_str(conq, ",");
+        let mut conq_seq = unfold_by_str(conq, atom!(","));
 
         mark_cut_variables(&mut conq_seq);
         prec_seq.extend(conq_seq.into_iter());
 
-        let back_term = Box::new(prec_seq.pop().unwrap());
-        let front_term = Box::new(prec_seq.pop().unwrap());
+        let back_term = prec_seq.pop().unwrap();
+        let front_term = prec_seq.pop().unwrap();
 
         let body_term = Term::Clause(
             Cell::default(),
-            comma_sym.clone(),
+            comma_sym,
             vec![front_term, back_term],
-            None,
         );
 
         self.fabricate_rule(fold_by_str(prec_seq.into_iter(), body_term, comma_sym))
     }
 
-    fn to_query_term<'a>(
+    fn to_query_term<'a, LS: LoadState<'a>>(
         &mut self,
-        load_state: &mut LoadState<'a>,
+        loader: &mut Loader<'a, LS>,
         term: Term,
     ) -> Result<QueryTerm, CompilationError> {
         match term {
-            Term::Constant(_, Constant::Atom(name, fixity)) => {
-                if name.as_str() == "!" || name.as_str() == "blocked_!" {
+            Term::Literal(_, Literal::Atom(name)) => {
+                if name == atom!("!") || name == atom!("blocked_!") {
                     Ok(QueryTerm::BlockedCut)
                 } else {
-                    Ok(clause_to_query_term(load_state, name, vec![], fixity))
+                    Ok(clause_to_query_term(loader, name, vec![]))
                 }
             }
-            Term::Constant(_, Constant::Char('!')) => Ok(QueryTerm::BlockedCut),
+            Term::Literal(_, Literal::Char('!')) => Ok(QueryTerm::BlockedCut),
             Term::Var(_, ref v) if v.as_str() == "!" => {
                 Ok(QueryTerm::UnblockedCut(Cell::default()))
             }
-            Term::Clause(r, name, mut terms, fixity) => match (name.as_str(), terms.len()) {
-                (";", 2) => {
-                    let term = Term::Clause(r, name.clone(), terms, fixity);
+            Term::Clause(r, name, mut terms) => match (name, terms.len()) {
+                (atom!(";"), 2) => {
+                    let term = Term::Clause(r, name, terms);
 
                     let (stub, clauses) = self.fabricate_disjunct(term);
                     self.queue.push_back(clauses);
 
                     Ok(QueryTerm::Jump(stub))
                 }
-                ("->", 2) => {
-                    let conq = *terms.pop().unwrap();
-                    let prec = *terms.pop().unwrap();
+                (atom!("->"), 2) => {
+                    let conq = terms.pop().unwrap();
+                    let prec = terms.pop().unwrap();
 
                     let (stub, clauses) = self.fabricate_if_then(prec, conq);
                     self.queue.push_back(clauses);
 
                     Ok(QueryTerm::Jump(stub))
                 }
-                ("\\+", 1) => {
-                    terms.push(Box::new(Term::Constant(
+                (atom!("\\+"), 1) => {
+                    terms.push(Term::Literal(
                         Cell::default(),
-                        Constant::Atom(clause_name!("$fail"), None),
-                    )));
+                        Literal::Atom(atom!("$fail")),
+                    ));
 
-                    let conq =
-                        Term::Constant(Cell::default(), Constant::Atom(clause_name!("true"), None));
+                    let conq = Term::Literal(Cell::default(), Literal::Atom(atom!("true")));
 
-                    let prec = Term::Clause(Cell::default(), clause_name!("->"), terms, None);
-                    let terms = vec![Box::new(prec), Box::new(conq)];
+                    let prec = Term::Clause(Cell::default(), atom!("->"), terms);
+                    let terms = vec![prec, conq];
 
-                    let term = Term::Clause(Cell::default(), clause_name!(";"), terms, None);
+                    let term = Term::Clause(Cell::default(), atom!(";"), terms);
                     let (stub, clauses) = self.fabricate_disjunct(term);
 
                     debug_assert!(clauses.len() > 0);
@@ -709,104 +695,102 @@ impl Preprocessor {
 
                     Ok(QueryTerm::Jump(stub))
                 }
-                ("$get_level", 1) => {
-                    if let Term::Var(_, ref var) = *terms[0] {
+                (atom!("$get_level"), 1) => {
+                    if let Term::Var(_, ref var) = &terms[0] {
                         Ok(QueryTerm::GetLevelAndUnify(Cell::default(), var.clone()))
                     } else {
                         Err(CompilationError::InadmissibleQueryTerm)
                     }
                 }
-                (":", 2) => {
-                    let predicate_name = *terms.pop().unwrap();
-                    let module_name = *terms.pop().unwrap();
+                (atom!(":"), 2) => {
+                    let predicate_name = terms.pop().unwrap();
+                    let module_name = terms.pop().unwrap();
 
                     match (module_name, predicate_name) {
                         (
-                            Term::Constant(_, Constant::Atom(module_name, _)),
-                            Term::Constant(_, Constant::Atom(predicate_name, fixity)),
+                            Term::Literal(_, Literal::Atom(module_name)),
+                            Term::Literal(_, Literal::Atom(predicate_name)),
                         ) => Ok(qualified_clause_to_query_term(
-                            load_state,
+                            loader,
                             module_name,
                             predicate_name,
                             vec![],
-                            fixity,
                         )),
                         (
-                            Term::Constant(_, Constant::Atom(module_name, _)),
-                            Term::Clause(_, name, terms, fixity),
+                            Term::Literal(_, Literal::Atom(module_name)),
+                            Term::Clause(_, name, terms),
                         ) => Ok(qualified_clause_to_query_term(
-                            load_state,
+                            loader,
                             module_name,
                             name,
                             terms,
-                            fixity,
                         )),
                         (module_name, predicate_name) => {
-                            terms.push(Box::new(module_name));
-                            terms.push(Box::new(predicate_name));
+                            terms.push(module_name);
+                            terms.push(predicate_name);
 
-                            Ok(clause_to_query_term(load_state, name, terms, fixity))
+                            Ok(clause_to_query_term(loader, name, terms))
                         }
                     }
                 }
-                _ => Ok(clause_to_query_term(load_state, name, terms, fixity)),
+                _ => Ok(clause_to_query_term(loader, name, terms)),
             },
             Term::Var(..) => Ok(QueryTerm::Clause(
                 Cell::default(),
                 ClauseType::CallN,
-                vec![Box::new(term)],
+                vec![term],
                 false,
             )),
             _ => Err(CompilationError::InadmissibleQueryTerm),
         }
     }
 
-    fn pre_query_term<'a>(
+    fn pre_query_term<'a, LS: LoadState<'a>>(
         &mut self,
-        load_state: &mut LoadState<'a>,
+        loader: &mut Loader<'a, LS>,
         term: Term,
     ) -> Result<QueryTerm, CompilationError> {
         match term {
-            Term::Clause(r, name, mut subterms, fixity) => {
-                if subterms.len() == 1 && name.as_str() == "$call_with_default_policy" {
-                    self.to_query_term(load_state, *subterms.pop().unwrap())
+            Term::Clause(r, name, mut subterms) => {
+                if subterms.len() == 1 && name == atom!("$call_with_default_policy") {
+                    self.to_query_term(loader, subterms.pop().unwrap())
                         .map(|mut query_term| {
                             query_term.set_default_caller();
                             query_term
                         })
                 } else {
-                    let clause = Term::Clause(r, name, subterms, fixity);
-                    self.to_query_term(load_state, clause)
+                    let clause = Term::Clause(r, name, subterms);
+                    self.to_query_term(loader, clause)
                 }
             }
-            _ => self.to_query_term(load_state, term),
+            _ => self.to_query_term(loader, term),
         }
     }
 
-    fn setup_query<'a>(
+    fn setup_query<'a, LS: LoadState<'a>>(
         &mut self,
-        load_state: &mut LoadState<'a>,
-        terms: Vec<Box<Term>>,
+        loader: &mut Loader<'a, LS>,
+        terms: Vec<Term>,
         cut_context: CutContext,
     ) -> Result<Vec<QueryTerm>, CompilationError> {
         let mut query_terms = vec![];
         let mut work_queue = VecDeque::from(terms);
 
         while let Some(term) = work_queue.pop_front() {
-            let mut term = *term;
+            let mut term = term;
 
-            if let Term::Clause(cell, name, terms, op_spec) = term {
-                if name.as_str() == "," && terms.len() == 2 {
-                    let term = Term::Clause(cell, name, terms, op_spec);
-                    let mut subterms = unfold_by_str(term, ",");
+            if let Term::Clause(cell, name, terms) = term {
+                if name == atom!(",") && terms.len() == 2 {
+                    let term = Term::Clause(cell, name, terms);
+                    let mut subterms = unfold_by_str(term, atom!(","));
 
                     while let Some(subterm) = subterms.pop() {
-                        work_queue.push_front(Box::new(subterm));
+                        work_queue.push_front(subterm);
                     }
 
                     continue;
                 } else {
-                    term = Term::Clause(cell, name, terms, op_spec);
+                    term = Term::Clause(cell, name, terms);
                 }
             }
 
@@ -814,30 +798,30 @@ impl Preprocessor {
                 mark_cut_variable(&mut term);
             }
 
-            query_terms.push(self.pre_query_term(load_state, term)?);
+            query_terms.push(self.pre_query_term(loader, term)?);
         }
 
         Ok(query_terms)
     }
 
-    fn setup_rule<'a>(
+    fn setup_rule<'a, LS: LoadState<'a>>(
         &mut self,
-        load_state: &mut LoadState<'a>,
-        mut terms: Vec<Box<Term>>,
+        loader: &mut Loader<'a, LS>,
+        mut terms: Vec<Term>,
         cut_context: CutContext,
     ) -> Result<Rule, CompilationError> {
         let post_head_terms: Vec<_> = terms.drain(1..).collect();
-        let mut query_terms = self.setup_query(load_state, post_head_terms, cut_context)?;
+        let mut query_terms = self.setup_query(loader, post_head_terms, cut_context)?;
 
         let clauses = query_terms.drain(1..).collect();
         let qt = query_terms.pop().unwrap();
 
-        match *terms.pop().unwrap() {
-            Term::Clause(_, name, terms, _) => Ok(Rule {
+        match terms.pop().unwrap() {
+            Term::Clause(_, name, terms) => Ok(Rule {
                 head: (name, terms, qt),
                 clauses,
             }),
-            Term::Constant(_, Constant::Atom(name, _)) => Ok(Rule {
+            Term::Literal(_, Literal::Atom(name)) => Ok(Rule {
                 head: (name, vec![], qt),
                 clauses,
             }),
@@ -845,37 +829,37 @@ impl Preprocessor {
         }
     }
 
-    fn try_term_to_query<'a>(
+    fn try_term_to_query<'a, LS: LoadState<'a>>(
         &mut self,
-        load_state: &mut LoadState<'a>,
-        terms: Vec<Box<Term>>,
+        loader: &mut Loader<'a, LS>,
+        terms: Vec<Term>,
         cut_context: CutContext,
     ) -> Result<TopLevel, CompilationError> {
         Ok(TopLevel::Query(self.setup_query(
-            load_state,
+            loader,
             terms,
             cut_context,
         )?))
     }
 
-    pub(super) fn try_term_to_tl<'a>(
+    pub(super) fn try_term_to_tl<'a, LS: LoadState<'a>>(
         &mut self,
-        load_state: &mut LoadState<'a>,
+        loader: &mut Loader<'a, LS>,
         term: Term,
         cut_context: CutContext,
     ) -> Result<TopLevel, CompilationError> {
         match term {
-            Term::Clause(r, name, terms, fixity) => {
-                if name.as_str() == "?-" {
-                    self.try_term_to_query(load_state, terms, cut_context)
-                } else if name.as_str() == ":-" && terms.len() == 2 {
+            Term::Clause(r, name, terms) => {
+                if name == atom!("?-") {
+                    self.try_term_to_query(loader, terms, cut_context)
+                } else if name == atom!(":-") && terms.len() == 2 {
                     Ok(TopLevel::Rule(self.setup_rule(
-                        load_state,
+                        loader,
                         terms,
                         cut_context,
                     )?))
                 } else {
-                    let term = Term::Clause(r, name, terms, fixity);
+                    let term = Term::Clause(r, name, terms);
                     Ok(TopLevel::Fact(self.setup_fact(term)?))
                 }
             }
@@ -883,30 +867,30 @@ impl Preprocessor {
         }
     }
 
-    fn try_terms_to_tls<'a, I: IntoIterator<Item = Term>>(
+    fn try_terms_to_tls<'a, I: IntoIterator<Item = Term>, LS: LoadState<'a>>(
         &mut self,
-        load_state: &mut LoadState<'a>,
+        loader: &mut Loader<'a, LS>,
         terms: I,
         cut_context: CutContext,
     ) -> Result<VecDeque<TopLevel>, CompilationError> {
         let mut results = VecDeque::new();
 
         for term in terms.into_iter() {
-            results.push_back(self.try_term_to_tl(load_state, term, cut_context)?);
+            results.push_back(self.try_term_to_tl(loader, term, cut_context)?);
         }
 
         Ok(results)
     }
 
-    pub(super) fn parse_queue<'a>(
+    pub(super) fn parse_queue<'a, LS: LoadState<'a>>(
         &mut self,
-        load_state: &mut LoadState<'a>,
+        loader: &mut Loader<'a, LS>,
     ) -> Result<VecDeque<TopLevel>, CompilationError> {
         let mut queue = VecDeque::new();
 
         while let Some(terms) = self.queue.pop_front() {
             let clauses = merge_clauses(&mut self.try_terms_to_tls(
-                load_state,
+                loader,
                 terms,
                 CutContext::HasCutVariable,
             )?)?;
index a3c917136653e7bd9dd5e6f445c2a23a31ac0f3a..1ab988cdd2620976209fee8264957a597d24809c 100644 (file)
@@ -1,16 +1,15 @@
 use core::marker::PhantomData;
 
+use crate::types::*;
+
 use crate::machine::machine_indices::*;
-use crate::machine::raw_block::*;
+use crate::raw_block::*;
 
 use std::mem;
 use std::ops::{Index, IndexMut};
 use std::ptr;
 
-#[derive(Debug)]
-struct StackTraits {}
-
-impl RawBlockTraits for StackTraits {
+impl RawBlockTraits for Stack {
     #[inline]
     fn init_size() -> usize {
         10 * 1024 * 1024
@@ -18,31 +17,23 @@ impl RawBlockTraits for StackTraits {
 
     #[inline]
     fn align() -> usize {
-        mem::align_of::<Addr>()
-    }
-
-    #[inline]
-    fn base_offset(base: *const u8) -> *const u8 {
-        unsafe { base.offset(Self::align() as isize) }
+        mem::align_of::<HeapCellValue>()
     }
 }
 
-const fn prelude_size<Prelude>() -> usize {
-    let size = mem::size_of::<Prelude>();
-    let align = mem::align_of::<Addr>();
-
-    (size & !(align - 1)) + align
+#[inline(always)]
+pub const fn prelude_size<Prelude>() -> usize {
+    mem::size_of::<Prelude>()
 }
 
 #[derive(Debug)]
-pub(crate) struct Stack {
-    buf: RawBlock<StackTraits>,
-    _marker: PhantomData<Addr>,
+pub struct Stack {
+    buf: RawBlock<Stack>,
+    _marker: PhantomData<HeapCellValue>,
 }
 
 impl Drop for Stack {
     fn drop(&mut self) {
-        self.drop_in_place();
         self.buf.deallocate();
     }
 }
@@ -57,7 +48,7 @@ pub(crate) struct AndFramePrelude {
     pub(crate) univ_prelude: FramePrelude,
     pub(crate) e: usize,
     pub(crate) cp: LocalCodePtr,
-    pub(crate) interrupt_cp: LocalCodePtr,
+    pub(crate) interrupt_cp: LocalCodePtr, // TODO: get rid of it!
 }
 
 #[derive(Debug)]
@@ -67,22 +58,22 @@ pub(crate) struct AndFrame {
 
 impl AndFrame {
     pub(crate) fn size_of(num_cells: usize) -> usize {
-        prelude_size::<AndFramePrelude>() + num_cells * mem::size_of::<Addr>()
+        prelude_size::<AndFramePrelude>() + num_cells * mem::size_of::<HeapCellValue>()
     }
 }
 
 impl Index<usize> for AndFrame {
-    type Output = Addr;
+    type Output = HeapCellValue;
 
     fn index(&self, index: usize) -> &Self::Output {
         let prelude_offset = prelude_size::<AndFramePrelude>();
-        let index_offset = (index - 1) * mem::size_of::<Addr>();
+        let index_offset = (index - 1) * mem::size_of::<HeapCellValue>();
 
         unsafe {
             let ptr = mem::transmute::<&AndFrame, *const u8>(self);
             let ptr = ptr as usize + prelude_offset + index_offset;
 
-            &*(ptr as *const Addr)
+            &*(ptr as *const HeapCellValue)
         }
     }
 }
@@ -90,13 +81,35 @@ impl Index<usize> for AndFrame {
 impl IndexMut<usize> for AndFrame {
     fn index_mut(&mut self, index: usize) -> &mut Self::Output {
         let prelude_offset = prelude_size::<AndFramePrelude>();
-        let index_offset = (index - 1) * mem::size_of::<Addr>();
+        let index_offset = (index - 1) * mem::size_of::<HeapCellValue>();
 
         unsafe {
             let ptr = mem::transmute::<&mut AndFrame, *const u8>(self);
             let ptr = ptr as usize + prelude_offset + index_offset;
 
-            &mut *(ptr as *mut Addr)
+            &mut *(ptr as *mut HeapCellValue)
+        }
+    }
+}
+
+impl Index<usize> for Stack {
+    type Output = HeapCellValue;
+
+    #[inline]
+    fn index(&self, index: usize) -> &Self::Output {
+        unsafe {
+            let ptr = self.buf.base as usize + index;
+            &*(ptr as *const HeapCellValue)
+        }
+    }
+}
+
+impl IndexMut<usize> for Stack {
+    #[inline]
+    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+        unsafe {
+            let ptr = self.buf.base as usize + index;
+            &mut *(ptr as *mut HeapCellValue)
         }
     }
 }
@@ -119,18 +132,18 @@ pub(crate) struct OrFrame {
 }
 
 impl Index<usize> for OrFrame {
-    type Output = Addr;
+    type Output = HeapCellValue;
 
     #[inline]
     fn index(&self, index: usize) -> &Self::Output {
         let prelude_offset = prelude_size::<OrFramePrelude>();
-        let index_offset = index * mem::size_of::<Addr>();
+        let index_offset = index * mem::size_of::<HeapCellValue>();
 
         unsafe {
             let ptr = mem::transmute::<&OrFrame, *const u8>(self);
             let ptr = ptr as usize + prelude_offset + index_offset;
 
-            &*(ptr as *const Addr)
+            &*(ptr as *const HeapCellValue)
         }
     }
 }
@@ -139,20 +152,20 @@ impl IndexMut<usize> for OrFrame {
     #[inline]
     fn index_mut(&mut self, index: usize) -> &mut Self::Output {
         let prelude_offset = prelude_size::<OrFramePrelude>();
-        let index_offset = index * mem::size_of::<Addr>();
+        let index_offset = index * mem::size_of::<HeapCellValue>();
 
         unsafe {
             let ptr = mem::transmute::<&mut OrFrame, *const u8>(self);
             let ptr = ptr as usize + prelude_offset + index_offset;
 
-            &mut *(ptr as *mut Addr)
+            &mut *(ptr as *mut HeapCellValue)
         }
     }
 }
 
 impl OrFrame {
     pub(crate) fn size_of(num_cells: usize) -> usize {
-        prelude_size::<OrFramePrelude>() + num_cells * mem::size_of::<Addr>()
+        prelude_size::<OrFramePrelude>() + num_cells * mem::size_of::<HeapCellValue>()
     }
 }
 
@@ -164,26 +177,39 @@ impl Stack {
         }
     }
 
+    #[inline(always)]
+    unsafe fn alloc(&mut self, frame_size: usize) -> *mut u8 {
+        loop {
+            let ptr = self.buf.alloc(frame_size);
+
+            if ptr.is_null() {
+                self.buf.grow();
+            } else {
+                return ptr;
+            }
+        }
+    }
+
     pub(crate) fn allocate_and_frame(&mut self, num_cells: usize) -> usize {
         let frame_size = AndFrame::size_of(num_cells);
 
         unsafe {
-            let new_top = self.buf.new_block(frame_size);
-            let e = self.buf.top as usize - self.buf.base as usize;
+            let e = self.buf.ptr as usize - self.buf.base as usize;
+            let new_ptr = self.alloc(frame_size);
+            let mut offset = prelude_size::<AndFramePrelude>();
 
             for idx in 0..num_cells {
-                let offset = prelude_size::<AndFramePrelude>() + idx * mem::size_of::<Addr>();
                 ptr::write(
-                    (self.buf.top as usize + offset) as *mut Addr,
-                    Addr::StackCell(e, idx + 1),
+                    (new_ptr as usize + offset) as *mut HeapCellValue,
+                    stack_loc_as_cell!(AndFrame, e, idx + 1),
                 );
+
+                offset += mem::size_of::<HeapCellValue>();
             }
 
-            let and_frame = &mut *(self.buf.top as *mut AndFrame);
+            let and_frame = &mut *(new_ptr as *mut AndFrame);
             and_frame.prelude.univ_prelude.num_cells = num_cells;
 
-            self.buf.top = new_top;
-
             e
         }
     }
@@ -192,27 +218,27 @@ impl Stack {
         let frame_size = OrFrame::size_of(num_cells);
 
         unsafe {
-            let new_top = self.buf.new_block(frame_size);
-            let b = self.buf.top as usize - self.buf.base as usize;
+            let b = self.buf.ptr as usize - self.buf.base as usize;
+            let new_ptr = self.alloc(frame_size);
+            let mut offset = prelude_size::<OrFramePrelude>();
 
             for idx in 0..num_cells {
-                let offset = prelude_size::<OrFramePrelude>() + idx * mem::size_of::<Addr>();
                 ptr::write(
-                    (self.buf.top as usize + offset) as *mut Addr,
-                    Addr::StackCell(b, idx),
+                    (new_ptr as usize + offset) as *mut HeapCellValue,
+                    stack_loc_as_cell!(OrFrame, b, idx),
                 );
+
+                offset += mem::size_of::<HeapCellValue>();
             }
 
-            let or_frame = &mut *(self.buf.top as *mut OrFrame);
+            let or_frame = &mut *(new_ptr as *mut OrFrame);
             or_frame.prelude.univ_prelude.num_cells = num_cells;
 
-            self.buf.top = new_top;
-
             b
         }
     }
 
-    #[inline]
+    #[inline(always)]
     pub(crate) fn index_and_frame(&self, e: usize) -> &AndFrame {
         unsafe {
             let ptr = self.buf.base as usize + e;
@@ -220,7 +246,7 @@ impl Stack {
         }
     }
 
-    #[inline]
+    #[inline(always)]
     pub(crate) fn index_and_frame_mut(&mut self, e: usize) -> &mut AndFrame {
         unsafe {
             let ptr = self.buf.base as usize + e;
@@ -228,7 +254,7 @@ impl Stack {
         }
     }
 
-    #[inline]
+    #[inline(always)]
     pub(crate) fn index_or_frame(&self, b: usize) -> &OrFrame {
         unsafe {
             let ptr = self.buf.base as usize + b;
@@ -236,7 +262,7 @@ impl Stack {
         }
     }
 
-    #[inline]
+    #[inline(always)]
     pub(crate) fn index_or_frame_mut(&mut self, b: usize) -> &mut OrFrame {
         unsafe {
             let ptr = self.buf.base as usize + b;
@@ -244,31 +270,65 @@ impl Stack {
         }
     }
 
-    #[inline]
+    #[inline(always)]
     pub(crate) fn truncate(&mut self, b: usize) {
-        if b == 0 {
-            self.inner_truncate(mem::align_of::<Addr>());
-        } else {
-            self.inner_truncate(b);
+        let base = self.buf.base as usize + b;
+
+        if base < self.buf.ptr as usize {
+            self.buf.ptr = base as *mut _;
         }
     }
+}
 
-    #[inline]
-    fn inner_truncate(&mut self, b: usize) {
-        let base = b + self.buf.base as usize;
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use crate::machine::mock_wam::*;
+
+    #[test]
+    fn stack_tests() {
+        let mut wam = MockWAM::new();
 
-        if base < self.buf.top as usize {
-            self.buf.top = base as *const _;
+        let e = wam.machine_st.stack.allocate_and_frame(10); // create an AND frame!
+        let and_frame = wam.machine_st.stack.index_and_frame_mut(e);
+
+        assert_eq!(
+            e,
+            0// 10 * mem::size_of::<HeapCellValue>() + prelude_size::<AndFrame>()
+        );
+
+        assert_eq!(and_frame.prelude.univ_prelude.num_cells, 10);
+
+        for idx in 0..10 {
+            assert_eq!(and_frame[idx + 1], stack_loc_as_cell!(AndFrame, e, idx + 1));
+        }
+
+        and_frame[5] = empty_list_as_cell!();
+
+        assert_eq!(and_frame[5], empty_list_as_cell!());
+
+        let b = wam.machine_st.stack.allocate_or_frame(5);
+
+        let or_frame = wam.machine_st.stack.index_or_frame_mut(b);
+
+        for idx in 0..5 {
+            assert_eq!(or_frame[idx], stack_loc_as_cell!(OrFrame, b, idx));
+        }
+
+        let next_e = wam.machine_st.stack.allocate_and_frame(9); // create an AND frame!
+        let and_frame = wam.machine_st.stack.index_and_frame_mut(next_e);
+
+        for idx in 0..9 {
+            assert_eq!(and_frame[idx + 1], stack_loc_as_cell!(AndFrame, next_e, idx + 1));
         }
-    }
 
-    pub(crate) fn drop_in_place(&mut self) {
-        self.truncate(mem::align_of::<Addr>());
+        let and_frame = wam.machine_st.stack.index_and_frame(e);
+        assert_eq!(and_frame[5], empty_list_as_cell!());
 
-        debug_assert!(if self.buf.top.is_null() {
-            self.buf.top == self.buf.base
-        } else {
-            self.buf.top as usize == self.buf.base as usize + mem::align_of::<Addr>()
-        });
+        assert_eq!(
+            wam.machine_st.stack[stack_loc!(AndFrame, e, 5)],
+            empty_list_as_cell!()
+        );
     }
 }
index 69f660228e794700a0bd3b57cc8d98e549a6e356..064d9fa8d01841eee5ac2efa4d86441bbe3cf530 100644 (file)
@@ -1,47 +1,52 @@
-use prolog_parser::ast::*;
-use prolog_parser::clause_name;
+use crate::arena::*;
+use crate::atom_table::*;
+use crate::parser::ast::*;
+use crate::parser::char_reader::*;
+use crate::read::*;
 
+use crate::machine::heap::*;
 use crate::machine::machine_errors::*;
 use crate::machine::machine_indices::*;
 use crate::machine::machine_state::*;
-use crate::read::readline::*;
-use crate::read::PrologStream;
+use crate::types::*;
+
+pub use modular_bitfield::prelude::*;
 
-use std::cell::RefCell;
 use std::cmp::Ordering;
 use std::error::Error;
 use std::fmt;
-use std::fs::File;
-use std::hash::{Hash, Hasher};
+use std::fs::{File, OpenOptions};
+use std::hash::{Hash};
 use std::io;
-use std::io::{stderr, stdout, Cursor, ErrorKind, Read, Seek, SeekFrom, Write};
+use std::io::{Cursor, ErrorKind, Read, Seek, SeekFrom, Write};
 use std::mem;
-use std::net::{Shutdown, TcpStream};
-use std::ops::DerefMut;
-use std::rc::Rc;
+use std::net::{TcpStream, Shutdown};
+use std::ops::{Deref, DerefMut};
+use std::ptr;
 
 use native_tls::TlsStream;
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub(crate) enum StreamType {
+#[derive(Debug, BitfieldSpecifier, Clone, Copy, PartialEq, Eq, Hash)]
+#[bits = 1]
+pub enum StreamType {
     Binary,
     Text,
 }
 
 impl StreamType {
     #[inline]
-    pub(crate) fn as_str(&self) -> &'static str {
+    pub(crate) fn as_atom(&self) -> Atom {
         match self {
-            StreamType::Binary => "binary_stream",
-            StreamType::Text => "text_stream",
+            StreamType::Binary => atom!("binary_stream"),
+            StreamType::Text => atom!("text_stream"),
         }
     }
 
     #[inline]
-    pub(crate) fn as_property_str(&self) -> &'static str {
+    pub(crate) fn as_property_atom(&self) -> Atom {
         match self {
-            StreamType::Binary => "binary",
-            StreamType::Text => "text",
+            StreamType::Binary => atom!("binary"),
+            StreamType::Text => atom!("text"),
         }
     }
 
@@ -54,14 +59,16 @@ impl StreamType {
     }
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub(crate) enum EOFAction {
+#[derive(Debug, BitfieldSpecifier, Clone, Copy, PartialEq, Eq, Hash)]
+#[bits = 2]
+pub enum EOFAction {
     EOFCode,
     Error,
     Reset,
 }
 
-#[derive(Debug, PartialEq)]
+#[derive(Debug, BitfieldSpecifier, Copy, Clone, PartialEq)]
+#[bits = 2]
 pub(crate) enum AtEndOfStream {
     Not,
     At,
@@ -70,165 +77,706 @@ pub(crate) enum AtEndOfStream {
 
 impl AtEndOfStream {
     #[inline]
-    pub(crate) fn as_str(&self) -> &'static str {
+    pub(crate) fn as_atom(&self) -> Atom {
         match self {
-            AtEndOfStream::Not => "not",
-            AtEndOfStream::Past => "past",
-            AtEndOfStream::At => "at",
+            AtEndOfStream::Not => atom!("not"),
+            AtEndOfStream::Past => atom!("past"),
+            AtEndOfStream::At => atom!("at"),
         }
     }
 }
 
 impl EOFAction {
     #[inline]
-    pub(crate) fn as_str(&self) -> &'static str {
+    pub(crate) fn as_atom(&self) -> Atom {
         match self {
-            EOFAction::EOFCode => "eof_code",
-            EOFAction::Error => "error",
-            EOFAction::Reset => "reset",
+            EOFAction::EOFCode => atom!("eof_code"),
+            EOFAction::Error => atom!("error"),
+            EOFAction::Reset => atom!("reset"),
         }
     }
 }
 
-fn parser_top_to_bytes(mut buf: Vec<io::Result<char>>) -> io::Result<Vec<u8>> {
-    let mut str_buf = String::new();
+#[derive(Debug)]
+pub struct ByteStream(Cursor<Vec<u8>>);
 
-    while let Some(c) = buf.pop() {
-        str_buf.push(c?);
+impl Read for ByteStream {
+    #[inline]
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        self.0.read(buf)
+    }
+}
+
+impl Write for ByteStream {
+    #[inline]
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        self.0.write(buf)
     }
 
-    unsafe {
-        let array = str_buf.as_bytes_mut();
-        array.reverse();
-        Ok(Vec::from(array))
+    #[inline]
+    fn flush(&mut self) -> std::io::Result<()> {
+        self.0.flush()
     }
 }
 
-/* all these streams are closed automatically when the instance is
- * dropped. */
-enum StreamInstance {
-    Bytes(Cursor<Vec<u8>>),
-    InputFile(ClauseName, File),
-    OutputFile(ClauseName, File, bool), // File, append.
-    Null,
-    PausedPrologStream(Vec<u8>, Box<StreamInstance>),
-    ReadlineStream(ReadlineStream),
-    StaticStr(Cursor<&'static str>),
-    Stderr,
-    Stdout,
-    TcpStream(ClauseName, TcpStream),
-    TlsStream(ClauseName, TlsStream<Stream>),
+#[derive(Debug)]
+pub struct InputFileStream {
+    file_name: Atom,
+    file: File,
 }
 
-impl StreamInstance {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        match self {
-            StreamInstance::PausedPrologStream(ref mut put_back, ref mut stream) => {
-                let mut index = 0;
+impl Read for InputFileStream {
+    #[inline]
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        self.file.read(buf)
+    }
+}
 
-                while index < buf.len() {
-                    if let Some(b) = put_back.pop() {
-                        buf[index] = b;
-                        index += 1;
-                    } else {
-                        break;
-                    }
-                }
+#[derive(Debug)]
+pub struct OutputFileStream {
+    file_name: Atom,
+    file: File,
+    is_append: bool,
+}
 
-                if index == buf.len() {
-                    Ok(buf.len())
-                } else {
-                    stream
-                        .read(&mut buf[index..])
-                        .map(|bytes_read| bytes_read + index)
-                }
-            }
-            StreamInstance::InputFile(_, ref mut file) => file.read(buf),
-            StreamInstance::TcpStream(_, ref mut tcp_stream) => tcp_stream.read(buf),
-            StreamInstance::TlsStream(_, ref mut tls_stream) => tls_stream.read(buf),
-            StreamInstance::ReadlineStream(ref mut rl_stream) => rl_stream.read(buf),
-            StreamInstance::StaticStr(ref mut src) => src.read(buf),
-            StreamInstance::Bytes(ref mut cursor) => cursor.read(buf),
-            StreamInstance::OutputFile(..)
-            | StreamInstance::Stderr
-            | StreamInstance::Stdout
-            | StreamInstance::Null => Err(std::io::Error::new(
-                ErrorKind::PermissionDenied,
-                StreamError::ReadFromOutputStream,
-            )),
+impl Write for OutputFileStream {
+    #[inline]
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        self.file.write(buf)
+    }
+
+    #[inline]
+    fn flush(&mut self) -> std::io::Result<()> {
+        self.file.flush()
+    }
+}
+
+#[derive(Debug)]
+pub struct StaticStringStream {
+    stream: Cursor<&'static str>,
+}
+
+impl Read for StaticStringStream {
+    #[inline(always)]
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        self.stream.read(buf)
+    }
+}
+
+impl CharRead for StaticStringStream {
+    #[inline(always)]
+    fn peek_char(&mut self) -> Option<std::io::Result<char>> {
+        let pos = self.stream.position() as usize;
+        self.stream.get_ref()[pos ..].chars().next().map(Ok)
+    }
+
+    #[inline(always)]
+    fn consume(&mut self, nread: usize) {
+        self.stream.seek(SeekFrom::Current(nread as i64)).unwrap();
+    }
+
+    #[inline(always)]
+    fn put_back_char(&mut self, c: char) {
+        self.stream.seek(SeekFrom::Current(- (c.len_utf8() as i64))).unwrap();
+    }
+}
+
+#[derive(Debug)]
+pub struct NamedTcpStream {
+    address: Atom,
+    tcp_stream: TcpStream,
+}
+
+impl Read for NamedTcpStream {
+    #[inline]
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        self.tcp_stream.read(buf)
+    }
+}
+
+impl Write for NamedTcpStream {
+    #[inline]
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        self.tcp_stream.write(buf)
+    }
+
+    #[inline]
+    fn flush(&mut self) -> std::io::Result<()> {
+        self.tcp_stream.flush()
+    }
+}
+
+#[derive(Debug)]
+pub struct NamedTlsStream {
+    address: Atom,
+    tls_stream: TlsStream<Stream>,
+}
+
+impl Read for NamedTlsStream {
+    #[inline]
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        self.tls_stream.read(buf)
+    }
+}
+
+impl Write for NamedTlsStream {
+    #[inline]
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        self.tls_stream.write(buf)
+    }
+
+    #[inline]
+    fn flush(&mut self) -> std::io::Result<()> {
+        self.tls_stream.flush()
+    }
+}
+
+/*
+#[derive(Debug)]
+pub struct NullStream {}
+*/
+
+#[derive(Debug)]
+pub struct StandardOutputStream {}
+
+impl Write for StandardOutputStream {
+    #[inline]
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        io::stdout().write(buf)
+    }
+
+    #[inline]
+    fn flush(&mut self) -> std::io::Result<()> {
+        io::stdout().flush()
+    }
+}
+
+#[derive(Debug)]
+pub struct StandardErrorStream {}
+
+impl Write for StandardErrorStream {
+    #[inline]
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        io::stderr().write(buf)
+    }
+
+    #[inline]
+    fn flush(&mut self) -> std::io::Result<()> {
+        io::stderr().flush()
+    }
+}
+
+#[bitfield]
+#[repr(u64)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct StreamOptions {
+    pub stream_type: StreamType,
+    pub reposition: bool,
+    pub eof_action: EOFAction,
+    pub has_alias: bool,
+    pub alias: B59,
+}
+
+impl StreamOptions {
+    #[inline]
+    pub fn get_alias(self) -> Option<Atom> {
+        if self.has_alias() {
+            Some(Atom::from((self.alias() << 3) as usize))
+        } else {
+            None
+        }
+    }
+
+    #[inline]
+    pub fn set_alias_to_atom_opt(&mut self, alias: Option<Atom>) {
+        self.set_has_alias(alias.is_some());
+
+        if let Some(alias) = alias {
+            self.set_alias(alias.flat_index());
         }
     }
 }
 
-impl fmt::Debug for StreamInstance {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            &StreamInstance::Bytes(ref bytes) => write!(fmt, "Bytes({:?})", bytes),
-            &StreamInstance::StaticStr(_) => write!(fmt, "StaticStr(_)"), // Hacky solution.
-            &StreamInstance::InputFile(_, ref file) => write!(fmt, "InputFile({:?})", file),
-            &StreamInstance::OutputFile(_, ref file, _) => write!(fmt, "OutputFile({:?})", file),
-            &StreamInstance::Null => write!(fmt, "Null"),
-            &StreamInstance::PausedPrologStream(ref put_back, ref stream) => {
-                write!(fmt, "PausedPrologStream({:?}, {:?})", put_back, stream)
-            }
-            &StreamInstance::ReadlineStream(ref readline_stream) => {
-                write!(fmt, "ReadlineStream({:?})", readline_stream)
+impl Default for StreamOptions {
+    #[inline]
+    fn default() -> Self {
+        StreamOptions::new()
+            .with_stream_type(StreamType::Text)
+            .with_reposition(false)
+            .with_eof_action(EOFAction::EOFCode)
+            .with_has_alias(false)
+            .with_alias(0)
+    }
+}
+
+#[derive(Debug, Copy, Clone)]
+pub struct StreamLayout<T> {
+    pub options: StreamOptions,
+    pub lines_read: usize,
+    past_end_of_stream: bool,
+    stream: T,
+}
+
+impl<T> StreamLayout<T> {
+    #[inline]
+    pub fn new(stream: T) -> Self {
+        Self {
+            options: StreamOptions::default(),
+            lines_read: 0,
+            past_end_of_stream: false,
+            stream,
+        }
+    }
+}
+
+impl<T> Deref for StreamLayout<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.stream
+    }
+}
+
+impl<T> DerefMut for StreamLayout<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.stream
+    }
+}
+
+macro_rules! arena_allocated_impl_for_stream {
+    ($stream_type:ty, $stream_tag:ident) => {
+        impl ArenaAllocated for StreamLayout<$stream_type> {
+            type PtrToAllocated = TypedArenaPtr<StreamLayout<$stream_type>>;
+
+            #[inline]
+            fn tag() -> ArenaHeaderTag {
+                ArenaHeaderTag::$stream_tag
             }
-            &StreamInstance::Stderr => write!(fmt, "Stderr"),
-            &StreamInstance::Stdout => write!(fmt, "Stdout"),
-            &StreamInstance::TcpStream(_, ref tcp_stream) => {
-                write!(fmt, "TcpStream({:?})", tcp_stream)
+
+            #[inline]
+            fn size(&self) -> usize {
+                mem::size_of::<StreamLayout<$stream_type>>()
             }
-            &StreamInstance::TlsStream(_, ref tls_stream) => {
-                write!(fmt, "TlsStream({:?})", tls_stream)
+
+            #[inline]
+            fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
+                unsafe {
+                    ptr::write(dst, self);
+                    TypedArenaPtr::new(dst as *mut Self)
+                }
             }
         }
+    };
+}
+
+/*
+pub mod testing {
+    use super::PausedPrologStream;
+
+    impl PausedPrologStream {
+        #[allow(dead_code)]
+        pub fn write_test_input(&mut self, string: &str) {
+            self.bytes.extend(string.as_bytes().iter().rev());
+        }
+    }
+}
+*/
+/*
+impl ArenaAllocated for PausedPrologStream {
+    type PtrToAllocated = TypedArenaPtr<PausedPrologStream>;
+
+    #[inline]
+    fn tag() -> ArenaHeaderTag {
+        ArenaHeaderTag::PausedPrologStream
+    }
+
+    #[inline]
+    fn size(&self) -> usize {
+        mem::size_of::<PausedPrologStream>()
+    }
+
+    #[inline]
+    fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
+        unsafe {
+            ptr::write(dst, self);
+            TypedArenaPtr::new(dst as *mut Self)
+        }
     }
 }
 
 #[derive(Debug)]
-pub(crate) struct InnerStream {
-    options: StreamOptions,
-    stream_inst: StreamInstance,
-    past_end_of_stream: bool,
-    lines_read: usize,
+pub struct PausedPrologStream {
+    bytes: Vec<u8>,
+    paused_stream: Stream,
 }
 
-#[derive(Debug, Clone)]
-struct WrappedStreamInstance(Rc<RefCell<InnerStream>>);
+impl PausedPrologStream {
+    #[inline]
+    pub fn new() -> Self {
+        PausedPrologStream {
+            bytes: vec![],
+            paused_stream: Stream::Null(StreamOptions::default()),
+        }
+    }
+}
 
-impl WrappedStreamInstance {
+impl Read for PausedPrologStream {
     #[inline]
-    fn new(stream_inst: StreamInstance, past_end_of_stream: bool) -> Self {
-        WrappedStreamInstance(Rc::new(RefCell::new(InnerStream {
-            options: StreamOptions::default(),
-            stream_inst,
-            past_end_of_stream,
-            lines_read: 0,
-        })))
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        self.paused_stream.read(buf)
     }
 }
+*/
+
+arena_allocated_impl_for_stream!(CharReader<ByteStream>, ByteStream);
+arena_allocated_impl_for_stream!(CharReader<InputFileStream>, InputFileStream);
+arena_allocated_impl_for_stream!(OutputFileStream, OutputFileStream);
+arena_allocated_impl_for_stream!(CharReader<NamedTcpStream>, NamedTcpStream);
+arena_allocated_impl_for_stream!(CharReader<NamedTlsStream>, NamedTlsStream);
+arena_allocated_impl_for_stream!(ReadlineStream, ReadlineStream);
+arena_allocated_impl_for_stream!(StaticStringStream, StaticStringStream);
+arena_allocated_impl_for_stream!(StandardOutputStream, StandardOutputStream);
+arena_allocated_impl_for_stream!(StandardErrorStream, StandardErrorStream);
+
+#[derive(Debug, Copy, Clone)]
+pub enum Stream {
+    Byte(TypedArenaPtr<StreamLayout<CharReader<ByteStream>>>),
+    InputFile(TypedArenaPtr<StreamLayout<CharReader<InputFileStream>>>),
+    OutputFile(TypedArenaPtr<StreamLayout<OutputFileStream>>),
+    StaticString(TypedArenaPtr<StreamLayout<StaticStringStream>>),
+    NamedTcp(TypedArenaPtr<StreamLayout<CharReader<NamedTcpStream>>>),
+    NamedTls(TypedArenaPtr<StreamLayout<CharReader<NamedTlsStream>>>),
+    Null(StreamOptions),
+    Readline(TypedArenaPtr<StreamLayout<ReadlineStream>>),
+    StandardOutput(TypedArenaPtr<StreamLayout<StandardOutputStream>>),
+    StandardError(TypedArenaPtr<StreamLayout<StandardErrorStream>>),
+}
+
+impl From<TypedArenaPtr<StreamLayout<ReadlineStream>>> for Stream {
+    #[inline]
+    fn from(stream: TypedArenaPtr<StreamLayout<ReadlineStream>>) -> Stream {
+        Stream::Readline(stream)
+    }
+}
+
+impl Stream {
+    #[inline]
+    pub fn from_readline_stream(stream: ReadlineStream, arena: &mut Arena) -> Stream {
+        Stream::Readline(arena_alloc!(StreamLayout::new(stream), arena))
+    }
+
+    #[inline]
+    pub fn from_owned_string(string: String, arena: &mut Arena) -> Stream {
+        Stream::Byte(arena_alloc!(
+            StreamLayout::new(CharReader::new(ByteStream(Cursor::new(string.into_bytes())))),
+            arena
+        ))
+    }
+
+    #[inline]
+    pub fn from_static_string(src: &'static str, arena: &mut Arena) -> Stream {
+        Stream::StaticString(arena_alloc!(
+            StreamLayout::new(StaticStringStream {
+                stream: Cursor::new(src)
+            }),
+            arena
+        ))
+    }
+
+    #[inline]
+    pub fn stdin(arena: &mut Arena) -> Stream {
+        Stream::Readline(arena_alloc!(
+            StreamLayout::new(ReadlineStream::new("")),
+            arena
+        ))
+    }
+
+    pub fn from_tag(tag: ArenaHeaderTag, ptr: *const u8) -> Self {
+        match tag {
+            ArenaHeaderTag::ByteStream => Stream::Byte(TypedArenaPtr::new(ptr as *mut _)),
+            ArenaHeaderTag::InputFileStream => Stream::InputFile(TypedArenaPtr::new(ptr as *mut _)),
+            ArenaHeaderTag::OutputFileStream => {
+                Stream::OutputFile(TypedArenaPtr::new(ptr as *mut _))
+            }
+            ArenaHeaderTag::NamedTcpStream => Stream::NamedTcp(TypedArenaPtr::new(ptr as *mut _)),
+            ArenaHeaderTag::NamedTlsStream => Stream::NamedTls(TypedArenaPtr::new(ptr as *mut _)),
+            ArenaHeaderTag::ReadlineStream => Stream::Readline(TypedArenaPtr::new(ptr as *mut _)),
+            ArenaHeaderTag::StaticStringStream => {
+                Stream::StaticString(TypedArenaPtr::new(ptr as *mut _))
+            }
+            ArenaHeaderTag::StandardOutputStream => {
+                Stream::StandardOutput(TypedArenaPtr::new(ptr as *mut _))
+            }
+            ArenaHeaderTag::StandardErrorStream => {
+                Stream::StandardError(TypedArenaPtr::new(ptr as *mut _))
+            }
+            ArenaHeaderTag::NullStream => Stream::Null(StreamOptions::default()),
+            _ => unreachable!(),
+        }
+    }
+
+    #[inline]
+    pub fn is_stderr(&self) -> bool {
+        if let Stream::StandardError(_) = self {
+            true
+        } else {
+            false
+        }
+    }
+
+    #[inline]
+    pub fn is_stdout(&self) -> bool {
+        if let Stream::StandardOutput(_) = self {
+            true
+        } else {
+            false
+        }
+    }
+
+    #[inline]
+    pub fn is_stdin(&self) -> bool {
+        if let Stream::Readline(_) = self {
+            true
+        } else {
+            false
+        }
+    }
+
+    pub fn as_ptr(&self) -> *const ArenaHeader {
+        match self {
+            Stream::Byte(ptr) => ptr.header_ptr(),
+            Stream::InputFile(ptr) => ptr.header_ptr(),
+            Stream::OutputFile(ptr) => ptr.header_ptr(),
+            Stream::StaticString(ptr) => ptr.header_ptr(),
+            Stream::NamedTcp(ptr) => ptr.header_ptr(),
+            Stream::NamedTls(ptr) => ptr.header_ptr(),
+            Stream::Null(_) => ptr::null(),
+            Stream::Readline(ptr) => ptr.header_ptr(),
+            Stream::StandardOutput(ptr) => ptr.header_ptr(),
+            Stream::StandardError(ptr) => ptr.header_ptr(),
+        }
+    }
+
+    pub fn options(&self) -> &StreamOptions {
+        match self {
+            Stream::Byte(ref ptr) => &ptr.options,
+            Stream::InputFile(ref ptr) => &ptr.options,
+            Stream::OutputFile(ref ptr) => &ptr.options,
+            Stream::StaticString(ref ptr) => &ptr.options,
+            Stream::NamedTcp(ref ptr) => &ptr.options,
+            Stream::NamedTls(ref ptr) => &ptr.options,
+            Stream::Null(ref options) => options,
+            Stream::Readline(ref ptr) => &ptr.options,
+            Stream::StandardOutput(ref ptr) => &ptr.options,
+            Stream::StandardError(ref ptr) => &ptr.options,
+        }
+    }
+
+    pub fn options_mut(&mut self) -> &mut StreamOptions {
+        match self {
+            Stream::Byte(ref mut ptr) => &mut ptr.options,
+            Stream::InputFile(ref mut ptr) => &mut ptr.options,
+            Stream::OutputFile(ref mut ptr) => &mut ptr.options,
+            Stream::StaticString(ref mut ptr) => &mut ptr.options,
+            Stream::NamedTcp(ref mut ptr) => &mut ptr.options,
+            Stream::NamedTls(ref mut ptr) => &mut ptr.options,
+            Stream::Null(ref mut options) => options,
+            Stream::Readline(ref mut ptr) => &mut ptr.options,
+            Stream::StandardOutput(ref mut ptr) => &mut ptr.options,
+            Stream::StandardError(ref mut ptr) => &mut ptr.options,
+        }
+    }
+
+    /*
+    fn unpause_stream(&mut self) {
+        let stream_inst = match self {
+            Stream::PausedProlog(paused) if paused.bytes.is_empty() => {
+                mem::replace(&mut paused.paused_stream, Stream::Null(StreamOptions::default()))
+            }
+            _ => {
+                return;
+            }
+        };
+
+        *self = stream_inst;
+    }
+    */
+
+    #[inline]
+    pub(crate) fn add_lines_read(&mut self, incr_num_lines_read: usize) {
+        match self {
+            Stream::Byte(ptr) => ptr.lines_read += incr_num_lines_read,
+            Stream::InputFile(ptr) => ptr.lines_read += incr_num_lines_read,
+            Stream::OutputFile(ptr) => ptr.lines_read += incr_num_lines_read,
+            Stream::StaticString(ptr) => ptr.lines_read += incr_num_lines_read,
+            Stream::NamedTcp(ptr) => ptr.lines_read += incr_num_lines_read,
+            Stream::NamedTls(ptr) => ptr.lines_read += incr_num_lines_read,
+            Stream::Null(_) => {}
+            Stream::Readline(ptr) => ptr.lines_read += incr_num_lines_read,
+            Stream::StandardOutput(ptr) => ptr.lines_read += incr_num_lines_read,
+            Stream::StandardError(ptr) => ptr.lines_read += incr_num_lines_read,
+        }
+    }
+
+    #[inline]
+    pub(crate) fn set_lines_read(&mut self, value: usize) {
+        match self {
+            Stream::Byte(ptr) => ptr.lines_read = value,
+            Stream::InputFile(ptr) => ptr.lines_read = value,
+            Stream::OutputFile(ptr) => ptr.lines_read = value,
+            Stream::StaticString(ptr) => ptr.lines_read = value,
+            Stream::NamedTcp(ptr) => ptr.lines_read = value,
+            Stream::NamedTls(ptr) => ptr.lines_read = value,
+            Stream::Null(_) => {}
+            Stream::Readline(ptr) => ptr.lines_read = value,
+            Stream::StandardOutput(ptr) => ptr.lines_read = value,
+            Stream::StandardError(ptr) => ptr.lines_read = value,
+        }
+    }
+
+    #[inline]
+    pub(crate) fn lines_read(&self) -> usize {
+        match self {
+            Stream::Byte(ptr) => ptr.lines_read,
+            Stream::InputFile(ptr) => ptr.lines_read,
+            Stream::OutputFile(ptr) => ptr.lines_read,
+            Stream::StaticString(ptr) => ptr.lines_read,
+            Stream::NamedTcp(ptr) => ptr.lines_read,
+            Stream::NamedTls(ptr) => ptr.lines_read,
+            Stream::Null(_) => 0,
+            Stream::Readline(ptr) => ptr.lines_read,
+            Stream::StandardOutput(ptr) => ptr.lines_read,
+            Stream::StandardError(ptr) => ptr.lines_read,
+        }
+    }
+}
+
+impl CharRead for Stream {
+    fn peek_char(&mut self) -> Option<std::io::Result<char>> {
+        match self {
+            Stream::InputFile(file) => (*file).peek_char(),
+            Stream::NamedTcp(tcp_stream) => (*tcp_stream).peek_char(),
+            Stream::NamedTls(tls_stream) => (*tls_stream).peek_char(),
+            Stream::Readline(rl_stream) => (*rl_stream).peek_char(),
+            Stream::StaticString(src) => (*src).peek_char(),
+            Stream::Byte(cursor) => (*cursor).peek_char(),
+            Stream::OutputFile(_) |
+            Stream::StandardError(_) |
+            Stream::StandardOutput(_) |
+            Stream::Null(_) => Some(Err(std::io::Error::new(
+                ErrorKind::PermissionDenied,
+                StreamError::ReadFromOutputStream,
+            ))),
+        }
+    }
+
+    fn read_char(&mut self) -> Option<std::io::Result<char>> {
+        match self {
+            Stream::InputFile(file) => (*file).read_char(),
+            Stream::NamedTcp(tcp_stream) => (*tcp_stream).read_char(),
+            Stream::NamedTls(tls_stream) => (*tls_stream).read_char(),
+            Stream::Readline(rl_stream) => (*rl_stream).read_char(),
+            Stream::StaticString(src) => (*src).read_char(),
+            Stream::Byte(cursor) => (*cursor).read_char(),
+            Stream::OutputFile(_) |
+            Stream::StandardError(_) |
+            Stream::StandardOutput(_) |
+            Stream::Null(_) => Some(Err(std::io::Error::new(
+                ErrorKind::PermissionDenied,
+                StreamError::ReadFromOutputStream,
+            ))),
+        }
+    }
+
+    fn put_back_char(&mut self, c: char) {
+        match self {
+            Stream::InputFile(file) => file.put_back_char(c),
+            Stream::NamedTcp(tcp_stream) => tcp_stream.put_back_char(c),
+            Stream::NamedTls(tls_stream) => tls_stream.put_back_char(c),
+            Stream::Readline(rl_stream) => rl_stream.put_back_char(c),
+            Stream::StaticString(src) => src.put_back_char(c),
+            Stream::Byte(cursor) => cursor.put_back_char(c),
+            Stream::OutputFile(_) |
+            Stream::StandardError(_) |
+            Stream::StandardOutput(_) |
+            Stream::Null(_) => {}
+        }
+    }
+
+    fn consume(&mut self, nread: usize) {
+        match self {
+            Stream::InputFile(ref mut file) => file.consume(nread),
+            Stream::NamedTcp(ref mut tcp_stream) => tcp_stream.consume(nread),
+            Stream::NamedTls(ref mut tls_stream) => tls_stream.consume(nread),
+            Stream::Readline(ref mut rl_stream) => rl_stream.consume(nread),
+            Stream::StaticString(ref mut src) => src.consume(nread),
+            Stream::Byte(ref mut cursor) => cursor.consume(nread),
+            Stream::OutputFile(_) |
+            Stream::StandardError(_) |
+            Stream::StandardOutput(_) |
+            Stream::Null(_) => {}
+        }
+    }
+}
+
+impl Read for Stream {
+    #[inline]
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        let bytes_read = match self {
+            Stream::InputFile(file) => (*file).read(buf),
+            Stream::NamedTcp(tcp_stream) => (*tcp_stream).read(buf),
+            Stream::NamedTls(tls_stream) => (*tls_stream).read(buf),
+            Stream::Readline(rl_stream) => (*rl_stream).read(buf),
+            Stream::StaticString(src) => (*src).read(buf),
+            Stream::Byte(cursor) => (*cursor).read(buf),
+            Stream::OutputFile(_)
+            | Stream::StandardError(_)
+            | Stream::StandardOutput(_)
+            | Stream::Null(_) => Err(std::io::Error::new(
+                ErrorKind::PermissionDenied,
+                StreamError::ReadFromOutputStream,
+            )),
+        };
 
-impl PartialEq for WrappedStreamInstance {
-    #[inline]
-    fn eq(&self, other: &Self) -> bool {
-        Rc::ptr_eq(&self.0, &other.0)
+        bytes_read
     }
 }
 
-impl Eq for WrappedStreamInstance {}
-
-impl Hash for WrappedStreamInstance {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        let rc = &self.0;
-        let ptr = Rc::into_raw(rc.clone());
-
-        state.write_usize(ptr as usize);
+impl Write for Stream {
+    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+        match self {
+            Stream::OutputFile(ref mut file) => file.write(buf),
+            Stream::NamedTcp(ref mut tcp_stream) => tcp_stream.get_mut().write(buf),
+            Stream::NamedTls(ref mut tls_stream) => tls_stream.get_mut().write(buf),
+            Stream::Byte(ref mut cursor) => cursor.get_mut().write(buf),
+            Stream::StandardOutput(stream) => stream.write(buf),
+            Stream::StandardError(stream) => stream.write(buf),
+            Stream::StaticString(_) |
+            Stream::Readline(_) |
+            Stream::InputFile(..) |
+            Stream::Null(_) => Err(std::io::Error::new(
+                ErrorKind::PermissionDenied,
+                StreamError::WriteToInputStream,
+            )),
+        }
+    }
 
-        unsafe {
-            // necessary to avoid memory leak.
-            let _ = Rc::from_raw(ptr);
-        };
+    fn flush(&mut self) -> std::io::Result<()> {
+        match self {
+            Stream::OutputFile(ref mut file) => file.stream.flush(),
+            Stream::NamedTcp(ref mut tcp_stream) => tcp_stream.stream.get_mut().flush(),
+            Stream::NamedTls(ref mut tls_stream) => tls_stream.stream.get_mut().flush(),
+            Stream::Byte(ref mut cursor) => cursor.stream.get_mut().flush(),
+            Stream::StandardError(stream) => stream.stream.flush(),
+            Stream::StandardOutput(stream) => stream.stream.flush(),
+            Stream::StaticString(_) |
+            Stream::Readline(_) |
+            Stream::InputFile(_) |
+            Stream::Null(_) => Err(std::io::Error::new(
+                ErrorKind::PermissionDenied,
+                StreamError::FlushToInputStream,
+            )),
+        }
     }
 }
 
@@ -236,8 +784,8 @@ impl Hash for WrappedStreamInstance {
 enum StreamError {
     PeekByteFailed,
     PeekByteFromNonPeekableStream,
-    PeekCharFailed,
-    PeekCharFromNonPeekableStream,
+    #[allow(unused)] PeekCharFailed,
+    #[allow(unused)] PeekCharFromNonPeekableStream,
     ReadFromOutputStream,
     WriteToInputStream,
     FlushToInputStream,
@@ -273,31 +821,6 @@ impl fmt::Display for StreamError {
 
 impl Error for StreamError {}
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub(crate) struct StreamOptions {
-    pub(crate) stream_type: StreamType,
-    pub(crate) reposition: bool,
-    pub(crate) alias: Option<ClauseName>,
-    pub(crate) eof_action: EOFAction,
-}
-
-impl Default for StreamOptions {
-    #[inline]
-    fn default() -> Self {
-        StreamOptions {
-            stream_type: StreamType::Text,
-            reposition: false,
-            alias: None,
-            eof_action: EOFAction::EOFCode,
-        }
-    }
-}
-
-#[derive(Debug, Clone, Hash)]
-pub struct Stream {
-    stream_inst: WrappedStreamInstance,
-}
-
 impl PartialOrd for Stream {
     #[inline]
     fn partial_cmp(&self, other: &Stream) -> Option<Ordering> {
@@ -315,123 +838,44 @@ impl Ord for Stream {
 impl PartialEq for Stream {
     #[inline]
     fn eq(&self, other: &Self) -> bool {
-        self.stream_inst == other.stream_inst
+        self.as_ptr() == other.as_ptr()
     }
 }
 
 impl Eq for Stream {}
 
-impl From<String> for Stream {
-    fn from(string: String) -> Self {
-        Stream::from_inst(StreamInstance::Bytes(Cursor::new(string.into_bytes())))
-    }
-}
-
-impl From<ReadlineStream> for Stream {
-    fn from(rl_stream: ReadlineStream) -> Self {
-        Stream::from_inst(StreamInstance::ReadlineStream(rl_stream))
-    }
-}
-
-impl From<&'static str> for Stream {
-    fn from(src: &'static str) -> Stream {
-        Stream::from_inst(StreamInstance::StaticStr(Cursor::new(src)))
-    }
-}
-
 impl Stream {
-    #[inline]
-    pub(crate) fn as_ptr(&self) -> *const u8 {
-        let rc = self.stream_inst.0.clone();
-        let ptr = Rc::into_raw(rc);
-
-        unsafe {
-            // must be done to avoid memory leak.
-            let _ = Rc::from_raw(ptr);
-        }
-
-        ptr as *const u8
-    }
-
-    pub fn bytes(&self) -> Option<std::cell::Ref<Vec<u8>>> {
-        /*
-        // Replacement of workaround for when we have stable https://github.com/rust-lang/rust/issues/81061
-        std::cell::Ref::filter_map(
-            self.stream_inst.0.borrow(),
-            |inner_stream| match inner_stream.stream_inst {
-                StreamInstance::Bytes(cursor) => Some(cursor.get_ref()),
-                _ => None,
-            },
-        )
-        .ok()
-        */
-        let val = std::cell::Ref::map(self.stream_inst.0.borrow(), |inner_stream| {
-            &inner_stream.stream_inst
-        });
-        match std::ops::Deref::deref(&val) {
-            StreamInstance::Bytes(_) => Some(std::cell::Ref::map(
-                std::cell::Ref::clone(&val),
-                |instance| match instance {
-                    StreamInstance::Bytes(cursor) => cursor.get_ref(),
-                    _ => unreachable!(),
-                },
-            )),
-            _ => None,
-        }
-    }
-
-    #[inline]
-    pub(crate) fn lines_read(&mut self) -> usize {
-        self.stream_inst.0.borrow_mut().lines_read
-    }
-
-    #[inline]
-    pub(crate) fn add_lines_read(&mut self, incr_num_lines_read: usize) {
-        self.stream_inst.0.borrow_mut().lines_read += incr_num_lines_read;
-    }
-
-    #[inline]
-    pub(crate) fn options(&self) -> std::cell::Ref<'_, StreamOptions> {
-        std::cell::Ref::map(self.stream_inst.0.borrow(), |inner_stream| {
-            &inner_stream.options
-        })
-    }
-
-    #[inline]
-    pub(crate) fn options_mut(&mut self) -> std::cell::RefMut<'_, StreamOptions> {
-        std::cell::RefMut::map(self.stream_inst.0.borrow_mut(), |inner_stream| {
-            &mut inner_stream.options
-        })
-    }
-
     #[inline]
     pub(crate) fn position(&mut self) -> Option<(u64, usize)> {
         // returns lines_read, position.
-        let result = match self.stream_inst.0.borrow_mut().stream_inst {
-            StreamInstance::InputFile(_, ref mut file) => file.seek(SeekFrom::Current(0)).ok(),
-            StreamInstance::TcpStream(..)
-            | StreamInstance::TlsStream(..)
-            | StreamInstance::ReadlineStream(..)
-            | StreamInstance::StaticStr(..)
-            | StreamInstance::PausedPrologStream(..)
-            | StreamInstance::Bytes(..) => Some(0),
+        let result = match self {
+            Stream::InputFile(ref mut file_stream) => {
+                file_stream.get_mut().file.seek(SeekFrom::Current(0)).ok()
+            }
+            Stream::NamedTcp(..)
+            | Stream::NamedTls(..)
+            | Stream::Readline(..)
+            | Stream::StaticString(..)
+            | Stream::Byte(..) => Some(0),
             _ => None,
         };
 
-        result.map(|position| (position, self.stream_inst.0.borrow().lines_read))
+        result.map(|position| (position, self.lines_read()))
     }
 
     #[inline]
     pub(crate) fn set_position(&mut self, position: u64) {
-        match self.stream_inst.0.borrow_mut().deref_mut() {
-            InnerStream {
-                past_end_of_stream,
-                stream_inst: StreamInstance::InputFile(_, ref mut file),
-                ..
-            } => {
-                file.seek(SeekFrom::Start(position)).unwrap();
+        match self {
+            Stream::InputFile(stream_layout) => {
+                let StreamLayout {
+                    past_end_of_stream,
+                    stream,
+                    ..
+                } = &mut **stream_layout;
 
-                if let Ok(metadata) = file.metadata() {
+                stream.get_mut().file.seek(SeekFrom::Start(position)).unwrap();
+
+                if let Ok(metadata) = stream.get_ref().file.metadata() {
                     *past_end_of_stream = position > metadata.len();
                 }
             }
@@ -441,7 +885,19 @@ impl Stream {
 
     #[inline]
     pub(crate) fn past_end_of_stream(&self) -> bool {
-        self.stream_inst.0.borrow_mut().past_end_of_stream
+        match self {
+            Stream::Byte(stream) => stream.past_end_of_stream,
+            Stream::InputFile(stream) => stream.past_end_of_stream,
+            Stream::OutputFile(stream) => stream.past_end_of_stream,
+            // Stream::PausedProlog(stream) => stream.paused_stream.past_end_of_stream(),
+            Stream::StaticString(stream) => stream.past_end_of_stream,
+            Stream::NamedTcp(stream) => stream.past_end_of_stream,
+            Stream::NamedTls(stream) => stream.past_end_of_stream,
+            Stream::Null(_) => false,
+            Stream::Readline(stream) => stream.past_end_of_stream,
+            Stream::StandardOutput(stream) => stream.past_end_of_stream,
+            Stream::StandardError(stream) => stream.past_end_of_stream,
+        }
     }
 
     #[inline]
@@ -450,8 +906,19 @@ impl Stream {
     }
 
     #[inline]
-    pub(crate) fn set_past_end_of_stream(&mut self) {
-        self.stream_inst.0.borrow_mut().past_end_of_stream = true;
+    pub(crate) fn set_past_end_of_stream(&mut self, value: bool) {
+        match self {
+            Stream::Byte(stream) => stream.past_end_of_stream = value,
+            Stream::InputFile(stream) => stream.past_end_of_stream = value,
+            Stream::OutputFile(stream) => stream.past_end_of_stream = value,
+            Stream::StaticString(stream) => stream.past_end_of_stream = value,
+            Stream::NamedTcp(stream) => stream.past_end_of_stream = value,
+            Stream::NamedTls(stream) => stream.past_end_of_stream = value,
+            Stream::Null(_) => {}
+            Stream::Readline(stream) => stream.past_end_of_stream = value,
+            Stream::StandardOutput(stream) => stream.past_end_of_stream = value,
+            Stream::StandardError(stream) => stream.past_end_of_stream = value,
+        }
     }
 
     #[inline]
@@ -460,14 +927,16 @@ impl Stream {
             return AtEndOfStream::Past;
         }
 
-        match self.stream_inst.0.borrow_mut().deref_mut() {
-            InnerStream {
+        if let Stream::InputFile(stream_layout) = self {
+            let StreamLayout {
                 past_end_of_stream,
-                stream_inst: StreamInstance::InputFile(_, ref mut file),
+                stream,
                 ..
-            } => match file.metadata() {
+            } = &mut **stream_layout;
+
+            match stream.get_ref().file.metadata() {
                 Ok(metadata) => {
-                    if let Ok(position) = file.seek(SeekFrom::Current(0)) {
+                    if let Ok(position) = stream.get_mut().file.seek(SeekFrom::Current(0)) {
                         return match position.cmp(&metadata.len()) {
                             Ordering::Equal => AtEndOfStream::At,
                             Ordering::Less => AtEndOfStream::Not,
@@ -485,120 +954,130 @@ impl Stream {
                     *past_end_of_stream = true;
                     AtEndOfStream::Past
                 }
-            },
-            _ => AtEndOfStream::Not,
+            }
+        } else {
+            AtEndOfStream::Not
         }
     }
 
     #[inline]
-    pub(crate) fn file_name(&self) -> Option<ClauseName> {
-        match self.stream_inst.0.borrow().stream_inst {
-            StreamInstance::InputFile(ref name, _) => Some(name.clone()),
-            StreamInstance::OutputFile(ref name, ..) => Some(name.clone()),
-            StreamInstance::TcpStream(ref name, _) => Some(name.clone()),
+    pub(crate) fn file_name(&self) -> Option<Atom> {
+        match self {
+            Stream::InputFile(file) => Some(file.stream.get_ref().file_name),
+            Stream::OutputFile(file) => Some(file.stream.file_name),
+            Stream::NamedTcp(tcp) => Some(tcp.stream.get_ref().address),
+            Stream::NamedTls(tls) => Some(tls.stream.get_ref().address),
             _ => None,
         }
     }
 
     #[inline]
-    pub(crate) fn mode(&self) -> &'static str {
-        match self.stream_inst.0.borrow().stream_inst {
-            StreamInstance::Bytes(_)
-            | StreamInstance::PausedPrologStream(..)
-            | StreamInstance::ReadlineStream(_)
-            | StreamInstance::StaticStr(_)
-            | StreamInstance::InputFile(..) => "read",
-            StreamInstance::TcpStream(..) | StreamInstance::TlsStream(..) => "read_append",
-            StreamInstance::OutputFile(_, _, true) => "append",
-            StreamInstance::Stderr
-            | StreamInstance::Stdout
-            | StreamInstance::OutputFile(_, _, false) => "write",
-            StreamInstance::Null => "",
-        }
-    }
-
-    #[inline]
-    fn from_inst(stream_inst: StreamInstance) -> Self {
-        Stream {
-            stream_inst: WrappedStreamInstance::new(stream_inst, false),
+    pub(crate) fn mode(&self) -> Atom {
+        match self {
+            Stream::Byte(_)
+            | Stream::Readline(_)
+            | Stream::StaticString(_)
+            | Stream::InputFile(..) => atom!("read"),
+            Stream::NamedTcp(..) | Stream::NamedTls(..) => atom!("read_append"),
+            Stream::OutputFile(file) if file.is_append => atom!("append"),
+            Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) => atom!("write"),
+            Stream::Null(_) => atom!(""),
         }
     }
 
     #[inline]
-    pub fn stdout() -> Self {
-        Stream::from_inst(StreamInstance::Stdout)
+    pub fn stdout(arena: &mut Arena) -> Self {
+        Stream::StandardOutput(arena_alloc!(
+            StreamLayout::new(StandardOutputStream {}),
+            arena
+        ))
     }
 
     #[inline]
-    pub fn stderr() -> Self {
-        Stream::from_inst(StreamInstance::Stderr)
+    pub fn stderr(arena: &mut Arena) -> Self {
+        Stream::StandardError(arena_alloc!(
+            StreamLayout::new(StandardErrorStream {}),
+            arena
+        ))
     }
 
     #[inline]
-    pub(crate) fn from_tcp_stream(address: ClauseName, tcp_stream: TcpStream) -> Self {
+    pub(crate) fn from_tcp_stream(
+        address: Atom,
+        tcp_stream: TcpStream,
+        arena: &mut Arena,
+    ) -> Self {
         tcp_stream.set_read_timeout(None).unwrap();
         tcp_stream.set_write_timeout(None).unwrap();
 
-        Stream::from_inst(StreamInstance::TcpStream(address, tcp_stream))
-    }
-
-    #[inline]
-    pub(crate) fn from_tls_stream(address: ClauseName, tls_stream: TlsStream<Stream>) -> Self {
-        Stream::from_inst(StreamInstance::TlsStream(address, tls_stream))
-    }
-
-    #[inline]
-    pub(crate) fn from_file_as_output(name: ClauseName, file: File, in_append_mode: bool) -> Self {
-        Stream::from_inst(StreamInstance::OutputFile(name, file, in_append_mode))
-    }
-
-    #[inline]
-    pub(crate) fn from_file_as_input(name: ClauseName, file: File) -> Self {
-        Stream::from_inst(StreamInstance::InputFile(name, file))
+        Stream::NamedTcp(arena_alloc!(
+            StreamLayout::new(CharReader::new(NamedTcpStream {
+                address,
+                tcp_stream
+            })),
+            arena
+        ))
     }
 
     #[inline]
-    pub(crate) fn is_stderr(&self) -> bool {
-        match self.stream_inst.0.borrow().stream_inst {
-            StreamInstance::Stderr => true,
-            _ => false,
-        }
+    pub(crate) fn from_tls_stream(
+        address: Atom,
+        tls_stream: TlsStream<Stream>,
+        arena: &mut Arena,
+    ) -> Self {
+        Stream::NamedTls(arena_alloc!(
+            StreamLayout::new(CharReader::new(NamedTlsStream {
+                address,
+                tls_stream
+            })),
+            arena
+        ))
     }
 
     #[inline]
-    pub(crate) fn is_stdout(&self) -> bool {
-        match self.stream_inst.0.borrow().stream_inst {
-            StreamInstance::Stdout => true,
-            _ => false,
-        }
+    pub(crate) fn from_file_as_output(
+        file_name: Atom,
+        file: File,
+        is_append: bool,
+        arena: &mut Arena,
+    ) -> Self {
+        Stream::OutputFile(arena_alloc!(
+            StreamLayout::new(OutputFileStream {
+                file_name,
+                file,
+                is_append
+            }),
+            arena
+        ))
     }
 
     #[inline]
-    pub(crate) fn is_stdin(&self) -> bool {
-        match self.stream_inst.0.borrow().stream_inst {
-            StreamInstance::ReadlineStream(_) => true,
-            _ => false,
-        }
+    pub(crate) fn from_file_as_input(file_name: Atom, file: File, arena: &mut Arena) -> Self {
+        Stream::InputFile(arena_alloc!(
+            StreamLayout::new(CharReader::new(InputFileStream { file_name, file })),
+            arena
+        ))
     }
 
     #[inline]
     pub(crate) fn close(&mut self) -> Result<(), std::io::Error> {
-        let result = match self.stream_inst.0.borrow_mut().stream_inst {
-            StreamInstance::TcpStream(_, ref mut tcp_stream) => {
-                tcp_stream.shutdown(Shutdown::Both)
+        let result = match self {
+            Stream::NamedTcp(ref mut tcp_stream) => {
+                tcp_stream.inner_mut().tcp_stream.shutdown(Shutdown::Both)
             },
-            StreamInstance::TlsStream(_, ref mut tls_stream) => {
-                tls_stream.shutdown()
+            Stream::NamedTls(ref mut tls_stream) => {
+                tls_stream.inner_mut().tls_stream.shutdown()
             }
             _ => Ok(())
         };
-        self.stream_inst.0.borrow_mut().stream_inst = StreamInstance::Null;
+
+        *self = Stream::Null(StreamOptions::default());
         result
     }
 
     #[inline]
     pub(crate) fn is_null_stream(&self) -> bool {
-        if let StreamInstance::Null = self.stream_inst.0.borrow().stream_inst {
+        if let Stream::Null(_) = self {
             true
         } else {
             false
@@ -607,98 +1086,77 @@ impl Stream {
 
     #[inline]
     pub(crate) fn is_input_stream(&self) -> bool {
-        match self.stream_inst.0.borrow().stream_inst {
-            StreamInstance::TcpStream(..)
-            | StreamInstance::TlsStream(..)
-            | StreamInstance::Bytes(_)
-            | StreamInstance::PausedPrologStream(..)
-            | StreamInstance::ReadlineStream(_)
-            | StreamInstance::StaticStr(_)
-            | StreamInstance::InputFile(..) => true,
+        match self {
+            Stream::NamedTcp(..)
+            | Stream::NamedTls(..)
+            | Stream::Byte(_)
+            | Stream::Readline(_)
+            | Stream::StaticString(_)
+            | Stream::InputFile(..) => true,
             _ => false,
         }
     }
 
     #[inline]
     pub(crate) fn is_output_stream(&self) -> bool {
-        match self.stream_inst.0.borrow().stream_inst {
-            StreamInstance::Stderr
-            | StreamInstance::Stdout
-            | StreamInstance::TcpStream(..)
-            | StreamInstance::TlsStream(..)
-            | StreamInstance::Bytes(_)
-            | StreamInstance::OutputFile(..) => true,
+        match self {
+            Stream::StandardError(_)
+            | Stream::StandardOutput(_)
+            | Stream::NamedTcp(..)
+            | Stream::NamedTls(..)
+            | Stream::Byte(_)
+            | Stream::OutputFile(..) => true,
             _ => false,
         }
     }
 
-    fn unpause_stream(&mut self) {
-        let stream_inst = match self.stream_inst.0.borrow_mut().stream_inst {
-            StreamInstance::PausedPrologStream(ref put_back, ref mut stream_inst)
-                if put_back.is_empty() =>
-            {
-                mem::replace(&mut **stream_inst, StreamInstance::Null)
-            }
-            _ => {
-                return;
-            }
-        };
-
-        self.stream_inst.0.borrow_mut().stream_inst = stream_inst;
-    }
-
     // returns true on success.
     #[inline]
     pub(super) fn reset(&mut self) -> bool {
-        self.stream_inst.0.borrow_mut().lines_read = 0;
-        self.stream_inst.0.borrow_mut().past_end_of_stream = false;
+        self.set_lines_read(0);
+        self.set_past_end_of_stream(false);
 
         loop {
-            match self.stream_inst.0.borrow_mut().stream_inst {
-                StreamInstance::Bytes(ref mut cursor) => {
-                    cursor.set_position(0);
+            match self {
+                Stream::Byte(ref mut cursor) => {
+                    cursor.stream.get_mut().0.set_position(0);
                     return true;
                 }
-                StreamInstance::InputFile(_, ref mut file) => {
-                    file.seek(SeekFrom::Start(0)).unwrap();
+                Stream::InputFile(ref mut file_stream) => {
+                    file_stream.stream.get_mut().file.seek(SeekFrom::Start(0)).unwrap();
                     return true;
                 }
-                StreamInstance::PausedPrologStream(ref mut put_back, _) => {
-                    put_back.clear();
-                }
-                StreamInstance::ReadlineStream(_) => {
+                Stream::Readline(_) => {
                     return true;
                 }
                 _ => {
                     return false;
                 }
             }
-
-            self.unpause_stream();
         }
     }
 
     #[inline]
     pub(crate) fn peek_byte(&mut self) -> std::io::Result<u8> {
-        match self.stream_inst.0.borrow_mut().stream_inst {
-            StreamInstance::Bytes(ref mut cursor) => {
+        match self {
+            Stream::Byte(ref mut cursor) => {
                 let mut b = [0u8; 1];
-                let pos = cursor.position();
+                let pos = cursor.stream.get_mut().0.position();
 
                 match cursor.read(&mut b)? {
                     1 => {
-                        cursor.set_position(pos);
+                        cursor.stream.get_mut().0.set_position(pos);
                         Ok(b[0])
                     }
                     _ => Err(std::io::Error::new(ErrorKind::UnexpectedEof, "end of file")),
                 }
             }
-            StreamInstance::InputFile(_, ref mut file) => {
+            Stream::InputFile(ref mut file) => {
                 let mut b = [0u8; 1];
 
                 match file.read(&mut b)? {
                     1 => {
-                        file.seek(SeekFrom::Current(-1))?;
+                        file.stream.get_mut().file.seek(SeekFrom::Current(-1))?;
                         Ok(b[0])
                     }
                     _ => Err(std::io::Error::new(
@@ -707,10 +1165,10 @@ impl Stream {
                     )),
                 }
             }
-            StreamInstance::ReadlineStream(ref mut stream) => stream.peek_byte(),
-            StreamInstance::TcpStream(_, ref mut tcp_stream) => {
+            Stream::Readline(ref mut stream) => stream.stream.peek_byte(),
+            Stream::NamedTcp(ref mut stream) => {
                 let mut b = [0u8; 1];
-                tcp_stream.peek(&mut b)?;
+                stream.stream.get_mut().tcp_stream.peek(&mut b)?;
                 Ok(b[0])
             }
             _ => Err(std::io::Error::new(
@@ -719,113 +1177,37 @@ impl Stream {
             )),
         }
     }
-
-    #[inline]
-    pub(crate) fn peek_char(&mut self) -> std::io::Result<char> {
-        use unicode_reader::CodePoints;
-
-        match self.stream_inst.0.borrow_mut().stream_inst {
-            StreamInstance::InputFile(_, ref mut file) => {
-                let c = {
-                    let mut iter = CodePoints::from(&*file);
-
-                    if let Some(Ok(c)) = iter.next() {
-                        c
-                    } else {
-                        return Err(std::io::Error::new(
-                            ErrorKind::UnexpectedEof,
-                            StreamError::PeekCharFailed,
-                        ));
-                    }
-                };
-
-                file.seek(SeekFrom::Current(-(c.len_utf8() as i64)))?;
-
-                Ok(c)
-            }
-            StreamInstance::ReadlineStream(ref mut stream) => stream.peek_char(),
-            StreamInstance::TcpStream(_, ref tcp_stream) => {
-                let c = {
-                    let mut buf = [0u8; 8];
-                    tcp_stream.peek(&mut buf)?;
-
-                    let mut iter = CodePoints::from(buf.bytes());
-
-                    if let Some(Ok(c)) = iter.next() {
-                        c
-                    } else {
-                        return Err(std::io::Error::new(
-                            ErrorKind::UnexpectedEof,
-                            StreamError::PeekCharFailed,
-                        ));
-                    }
-                };
-
-                Ok(c)
-            }
-            _ => Err(std::io::Error::new(
-                ErrorKind::PermissionDenied,
-                StreamError::PeekCharFromNonPeekableStream,
-            )),
-        }
-    }
-
-    #[inline]
-    pub(crate) fn pause_stream(&mut self, buf: Vec<io::Result<char>>) -> io::Result<()> {
-        match self.stream_inst.0.borrow_mut().stream_inst {
-            StreamInstance::PausedPrologStream(ref mut inner_buf, _) => {
-                inner_buf.extend(parser_top_to_bytes(buf)?.into_iter());
-                return Ok(());
-            }
-            _ => {}
-        }
-
-        if !buf.is_empty() {
-            let stream_inst = mem::replace(
-                &mut self.stream_inst.0.borrow_mut().stream_inst,
-                StreamInstance::Null,
-            );
-
-            self.stream_inst.0.borrow_mut().stream_inst = StreamInstance::PausedPrologStream(
-                parser_top_to_bytes(buf)?,
-                Box::new(stream_inst),
-            );
-        }
-
-        Ok(())
-    }
 }
 
 impl MachineState {
     #[inline]
     pub(crate) fn eof_action(
         &mut self,
-        result: Addr,
-        stream: &mut Stream,
-        caller: ClauseName,
+        result: HeapCellValue,
+        mut stream: Stream,
+        caller: Atom,
         arity: usize,
     ) -> CallResult {
-        let eof_action = stream.options().eof_action;
+        let eof_action = stream.options().eof_action();
 
         match eof_action {
             EOFAction::Error => {
-                stream.set_past_end_of_stream();
-                return Err(self.open_past_eos_error(stream.clone(), caller, arity));
+                stream.set_past_end_of_stream(true);
+                return Err(self.open_past_eos_error(stream, caller, arity));
             }
             EOFAction::EOFCode => {
-                let end_of_stream = if stream.options().stream_type == StreamType::Binary {
-                    Addr::Fixnum(-1)
+                let end_of_stream = if stream.options().stream_type() == StreamType::Binary {
+                    fixnum_as_cell!(Fixnum::build_with(-1))
                 } else {
-                    self.heap
-                        .to_unifiable(HeapCellValue::Atom(clause_name!("end_of_file"), None))
+                    atom_as_cell!(atom!("end_of_file"))
                 };
 
-                stream.set_past_end_of_stream();
-                Ok(self.unify(result, end_of_stream))
+                stream.set_past_end_of_stream(true);
+                Ok(unify!(self, result, end_of_stream))
             }
             EOFAction::Reset => {
                 if !stream.reset() {
-                    stream.set_past_end_of_stream();
+                    stream.set_past_end_of_stream(true);
                 }
 
                 Ok(self.fail = stream.past_end_of_stream())
@@ -834,182 +1216,176 @@ impl MachineState {
     }
 
     pub(crate) fn to_stream_options(
-        &self,
-        alias: Addr,
-        eof_action: Addr,
-        reposition: Addr,
-        stream_type: Addr,
+        &mut self,
+        alias: HeapCellValue,
+        eof_action: HeapCellValue,
+        reposition: HeapCellValue,
+        stream_type: HeapCellValue,
     ) -> StreamOptions {
-        let alias = match self.store(self.deref(alias)) {
-            Addr::Con(h) if self.heap.atom_at(h) => {
-                if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
-                    Some(name.clone())
-                } else {
-                    unreachable!()
-                }
+        let alias = read_heap_cell!(self.store(MachineState::deref(self, alias)),
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                debug_assert_eq!(arity, 0);
+                Some(name)
             }
-            _ => None,
-        };
+            _ => {
+                None
+            }
+        );
 
-        let eof_action = match self.store(self.deref(eof_action)) {
-            Addr::Con(h) if self.heap.atom_at(h) => {
-                if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
-                    match name.as_str() {
-                        "eof_code" => EOFAction::EOFCode,
-                        "error" => EOFAction::Error,
-                        "reset" => EOFAction::Reset,
-                        _ => unreachable!(),
-                    }
-                } else {
-                    unreachable!()
+        let eof_action = read_heap_cell!(self.store(MachineState::deref(self, eof_action)),
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                debug_assert_eq!(arity, 0);
+
+                match name  {
+                    atom!("eof_code") => EOFAction::EOFCode,
+                    atom!("error") => EOFAction::Error,
+                    atom!("reset") => EOFAction::Reset,
+                    _ => unreachable!(),
                 }
             }
             _ => {
                 unreachable!()
             }
-        };
+        );
 
-        let reposition = match self.store(self.deref(reposition)) {
-            Addr::Con(h) if self.heap.atom_at(h) => {
-                if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
-                    name.as_str() == "true"
-                } else {
-                    unreachable!()
-                }
+        let reposition = read_heap_cell!(self.store(MachineState::deref(self, reposition)),
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                debug_assert_eq!(arity, 0);
+                name == atom!("true")
             }
             _ => {
                 unreachable!()
             }
-        };
+        );
 
-        let stream_type = match self.store(self.deref(stream_type)) {
-            Addr::Con(h) if self.heap.atom_at(h) => {
-                if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
-                    match name.as_str() {
-                        "text" => StreamType::Text,
-                        "binary" => StreamType::Binary,
-                        _ => unreachable!(),
-                    }
-                } else {
-                    unreachable!()
+        let stream_type = read_heap_cell!(self.store(MachineState::deref(self, stream_type)),
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                debug_assert_eq!(arity, 0);
+                match name {
+                    atom!("text") => StreamType::Text,
+                    atom!("binary") => StreamType::Binary,
+                    _ => unreachable!(),
                 }
             }
             _ => {
                 unreachable!()
             }
-        };
+        );
 
         let mut options = StreamOptions::default();
 
-        options.stream_type = stream_type;
-        options.reposition = reposition;
-        options.alias = alias;
-        options.eof_action = eof_action;
+        options.set_stream_type(stream_type);
+        options.set_reposition(reposition);
+        options.set_alias_to_atom_opt(alias);
+        options.set_eof_action(eof_action);
 
         options
     }
 
     pub(crate) fn get_stream_or_alias(
         &mut self,
-        addr: Addr,
+        addr: HeapCellValue,
         stream_aliases: &StreamAliasDir,
-        caller: &'static str,
+        caller: Atom,
         arity: usize,
     ) -> Result<Stream, MachineStub> {
-        Ok(match self.store(self.deref(addr)) {
-            Addr::Con(h) if self.heap.atom_at(h) => {
-                if let HeapCellValue::Atom(ref atom, ref spec) = self.heap.clone(h) {
-                    match stream_aliases.get(atom) {
-                        Some(stream) if !stream.is_null_stream() => stream.clone(),
-                        _ => {
-                            let stub = MachineError::functor_stub(clause_name!(caller), arity);
-
-                            let addr = self
-                                .heap
-                                .to_unifiable(HeapCellValue::Atom(atom.clone(), spec.clone()));
-
-                            return Err(self.error_form(
-                                MachineError::existence_error(
-                                    self.heap.h(),
-                                    ExistenceError::Stream(addr),
-                                ),
-                                stub,
-                            ));
-                        }
+        let addr = self.store(MachineState::deref(self, addr));
+
+        read_heap_cell!(addr,
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                debug_assert_eq!(arity, 0);
+
+                return match stream_aliases.get(&name) {
+                    Some(stream) if !stream.is_null_stream() => Ok(*stream),
+                    _ => {
+                        let stub = functor_stub(caller, arity);
+                        let addr = atom_as_cell!(name);
+
+                        let existence_error = self.existence_error(ExistenceError::Stream(addr));
+
+                        Err(self.error_form(existence_error, stub))
                     }
-                } else {
-                    unreachable!()
-                }
+                };
             }
-            Addr::Stream(h) => {
-                if let HeapCellValue::Stream(ref stream) = &self.heap[h] {
-                    if stream.is_null_stream() {
-                        return Err(self.open_permission_error(Addr::Stream(h), caller, arity));
-                    } else {
-                        stream.clone()
-                    }
-                } else {
-                    unreachable!()
-                }
+            (HeapCellValueTag::Cons, ptr) => {
+                match_untyped_arena_ptr!(ptr,
+                     (ArenaHeaderTag::Stream, stream) => {
+                         return if stream.is_null_stream() {
+                             Err(self.open_permission_error(stream_as_cell!(stream), caller, arity))
+                         } else {
+                             Ok(stream)
+                         };
+                     }
+                     _ => {
+                     }
+                );
             }
-            addr => {
-                let stub = MachineError::functor_stub(clause_name!(caller), arity);
-
-                if addr.is_ref() {
-                    return Err(self.error_form(MachineError::instantiation_error(), stub));
-                } else {
-                    return Err(self.error_form(
-                        MachineError::domain_error(DomainErrorType::StreamOrAlias, addr),
-                        stub,
-                    ));
-                }
+            _ => {
             }
-        })
+        );
+
+        let stub = functor_stub(caller, arity);
+
+        if addr.is_var() {
+            let instantiation_error = self.instantiation_error();
+            Err(self.error_form(instantiation_error, stub))
+        } else {
+            let domain_error = self.domain_error(DomainErrorType::StreamOrAlias, addr);
+            Err(self.error_form(domain_error, stub))
+        }
     }
 
     pub(crate) fn open_parsing_stream(
-        &self,
-        stream: Stream,
-        stub_name: &'static str,
+        &mut self,
+        mut stream: Stream,
+        stub_name: Atom,
         stub_arity: usize,
-    ) -> Result<PrologStream, MachineStub> {
-        match parsing_stream(stream) {
-            Ok(parsing_stream) => Ok(parsing_stream),
-            Err(e) => {
-                let stub = MachineError::functor_stub(clause_name!(stub_name), stub_arity);
-                let err = MachineError::session_error(self.heap.h(), SessionError::from(e));
+    ) -> Result<Stream, MachineStub> {
+        match stream.peek_char() {
+            None => Ok(stream), // empty stream is handled gracefully by Lexer::eof
+            Some(Err(e)) => {
+                let err = self.session_error(SessionError::from(e));
+                let stub = functor_stub(stub_name, stub_arity);
 
                 Err(self.error_form(err, stub))
             }
+            Some(Ok(c)) => {
+                if c == '\u{feff}' {
+                    // skip UTF-8 BOM
+                    stream.consume(c.len_utf8());
+                }
+
+                Ok(stream)
+            }
         }
     }
 
     pub(crate) fn stream_permission_error(
-        &self,
+        &mut self,
         perm: Permission,
-        err_string: &'static str,
+        err_atom: Atom,
         stream: Stream,
-        caller: ClauseName,
+        caller: Atom,
         arity: usize,
     ) -> MachineStub {
-        let stub = MachineError::functor_stub(caller, arity);
-        let payload = vec![HeapCellValue::Stream(stream)];
+        let stub = functor_stub(caller, arity);
+        let payload = vec![stream_as_cell!(stream)];
 
-        let err = MachineError::permission_error(self.heap.h(), perm, err_string, payload);
+        let err = self.permission_error(perm, err_atom, payload);
 
         return self.error_form(err, stub);
     }
 
     #[inline]
     pub(crate) fn open_past_eos_error(
-        &self,
+        &mut self,
         stream: Stream,
-        caller: ClauseName,
+        caller: Atom,
         arity: usize,
     ) -> MachineStub {
         self.stream_permission_error(
             Permission::InputStream,
-            "past_end_of_stream",
+            atom!("past_end_of_stream"),
             stream,
             caller,
             arity,
@@ -1017,67 +1393,58 @@ impl MachineState {
     }
 
     pub(crate) fn open_permission_error<T: PermissionError>(
-        &self,
+        &mut self,
         culprit: T,
-        stub_name: &'static str,
+        stub_name: Atom,
         stub_arity: usize,
     ) -> MachineStub {
-        let stub = MachineError::functor_stub(clause_name!(stub_name), stub_arity);
-        let err =
-            MachineError::permission_error(self.heap.h(), Permission::Open, "source_sink", culprit);
+        let stub = functor_stub(stub_name, stub_arity);
+        let err = self.permission_error(Permission::Open, atom!("source_sink"), culprit);
 
         return self.error_form(err, stub);
     }
 
     pub(crate) fn occupied_alias_permission_error(
-        &self,
-        alias: ClauseName,
-        stub_name: &'static str,
+        &mut self,
+        alias: Atom,
+        stub_name: Atom,
         stub_arity: usize,
     ) -> MachineStub {
-        let stub = MachineError::functor_stub(clause_name!(stub_name), stub_arity);
-        let err = MachineError::permission_error(
-            self.heap.h(),
+        let stub = functor_stub(stub_name, stub_arity);
+        let alias_name = atom!("alias");
+
+        let err = self.permission_error(
             Permission::Open,
-            "source_sink",
-            functor!("alias", [clause_name(alias)]),
+            atom!("source_sink"),
+            functor!(alias_name, [atom(alias)]),
         );
 
         return self.error_form(err, stub);
     }
 
-    pub(crate) fn reposition_error(
-        &self,
-        stub_name: &'static str,
-        stub_arity: usize,
-    ) -> MachineStub {
-        let stub = MachineError::functor_stub(clause_name!(stub_name), stub_arity);
-        let rep_stub = functor!("reposition", [atom("true")]);
+    pub(crate) fn reposition_error(&mut self, stub_name: Atom, stub_arity: usize) -> MachineStub {
+        let stub = functor_stub(stub_name, stub_arity);
 
-        let err = MachineError::permission_error(
-            self.heap.h(),
-            Permission::Open,
-            "source_sink",
-            rep_stub,
-        );
+        let rep_stub = functor!(atom!("reposition"), [atom(atom!("true"))]);
+        let err = self.permission_error(Permission::Open, atom!("source_sink"), rep_stub);
 
         return self.error_form(err, stub);
     }
 
     pub(crate) fn check_stream_properties(
         &mut self,
-        stream: &mut Stream,
+        stream: Stream,
         expected_type: StreamType,
-        input: Option<Addr>,
-        caller: ClauseName,
+        input: Option<HeapCellValue>,
+        caller: Atom,
         arity: usize,
     ) -> CallResult {
         let opt_err = if input.is_some() && !stream.is_input_stream() {
-            Some("stream") // 8.14.2.3 g)
+            Some(atom!("stream")) // 8.14.2.3 g)
         } else if input.is_none() && !stream.is_output_stream() {
-            Some("stream") // 8.14.2.3 g)
-        } else if stream.options().stream_type != expected_type {
-            Some(expected_type.other().as_str()) // 8.14.2.3 h)
+            Some(atom!("stream")) // 8.14.2.3 g)
+        } else if stream.options().stream_type() != expected_type {
+            Some(expected_type.other().as_atom()) // 8.14.2.3 h)
         } else {
             None
         };
@@ -1088,14 +1455,8 @@ impl MachineState {
             Permission::OutputStream
         };
 
-        if let Some(err_string) = opt_err {
-            return Err(self.stream_permission_error(
-                permission,
-                err_string,
-                stream.clone(),
-                caller,
-                arity,
-            ));
+        if let Some(err_atom) = opt_err {
+            return Err(self.stream_permission_error(permission, err_atom, stream, caller, arity));
         }
 
         if let Some(input) = input {
@@ -1106,53 +1467,96 @@ impl MachineState {
 
         Ok(())
     }
-}
 
-impl Read for Stream {
-    #[inline]
-    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
-        let bytes_read = self.stream_inst.0.borrow_mut().stream_inst.read(buf)?;
-        self.unpause_stream();
-        Ok(bytes_read)
-    }
-}
+    pub(crate) fn stream_from_file_spec(
+        &mut self,
+        file_spec: Atom,
+        indices: &mut IndexStore,
+        options: &StreamOptions,
+    ) -> Result<Stream, MachineStub> {
+        if file_spec == atom!("") {
+            let stub = functor_stub(atom!("open"), 4);
+            let err = self.domain_error(DomainErrorType::SourceSink, self[temp_v!(1)]);
 
-impl Write for Stream {
-    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
-        match self.stream_inst.0.borrow_mut().stream_inst {
-            StreamInstance::OutputFile(_, ref mut file, _) => file.write(buf),
-            StreamInstance::TcpStream(_, ref mut tcp_stream) => tcp_stream.write(buf),
-            StreamInstance::TlsStream(_, ref mut tls_stream) => tls_stream.write(buf),
-            StreamInstance::Bytes(ref mut cursor) => cursor.write(buf),
-            StreamInstance::Stdout => stdout().write(buf),
-            StreamInstance::Stderr => stderr().write(buf),
-            StreamInstance::PausedPrologStream(..)
-            | StreamInstance::StaticStr(_)
-            | StreamInstance::ReadlineStream(_)
-            | StreamInstance::InputFile(..)
-            | StreamInstance::Null => Err(std::io::Error::new(
-                ErrorKind::PermissionDenied,
-                StreamError::WriteToInputStream,
-            )),
+            return Err(self.error_form(err, stub));
         }
-    }
 
-    fn flush(&mut self) -> std::io::Result<()> {
-        match self.stream_inst.0.borrow_mut().stream_inst {
-            StreamInstance::OutputFile(_, ref mut file, _) => file.flush(),
-            StreamInstance::TcpStream(_, ref mut tcp_stream) => tcp_stream.flush(),
-            StreamInstance::TlsStream(_, ref mut tls_stream) => tls_stream.flush(),
-            StreamInstance::Bytes(ref mut cursor) => cursor.flush(),
-            StreamInstance::Stderr => stderr().flush(),
-            StreamInstance::Stdout => stdout().flush(),
-            StreamInstance::PausedPrologStream(..)
-            | StreamInstance::StaticStr(_)
-            | StreamInstance::ReadlineStream(_)
-            | StreamInstance::InputFile(..)
-            | StreamInstance::Null => Err(std::io::Error::new(
-                ErrorKind::PermissionDenied,
-                StreamError::FlushToInputStream,
-            )),
+        // 8.11.5.3l)
+        if let Some(alias) = options.get_alias() {
+            if indices.stream_aliases.contains_key(&alias) {
+                return Err(self.occupied_alias_permission_error(alias, atom!("open"), 4));
+            }
         }
+
+        let mode = MachineState::deref(self, self[temp_v!(2)]);
+        let mode = cell_as_atom!(self.store(mode));
+
+        let mut open_options = OpenOptions::new();
+
+        let (is_input_file, in_append_mode) = match mode {
+            atom!("read") => {
+                open_options.read(true).write(false).create(false);
+                (true, false)
+            }
+            atom!("write") => {
+                open_options
+                    .read(false)
+                    .write(true)
+                    .truncate(true)
+                    .create(true);
+
+                (false, false)
+            }
+            atom!("append") => {
+                open_options
+                    .read(false)
+                    .write(true)
+                    .create(true)
+                    .append(true);
+
+                (false, true)
+            }
+            _ => {
+                let stub = functor_stub(atom!("open"), 4);
+                let err = self.domain_error(DomainErrorType::IOMode, self[temp_v!(2)]);
+
+                // 8.11.5.3h)
+                return Err(self.error_form(err, stub));
+            }
+        };
+
+        let file = match open_options.open(file_spec.as_str()) {
+            Ok(file) => file,
+            Err(err) => {
+                match err.kind() {
+                    ErrorKind::NotFound => {
+                        // 8.11.5.3j)
+                        let stub = functor_stub(atom!("open"), 4);
+
+                        let err = self.existence_error(
+                            ExistenceError::SourceSink(self[temp_v!(1)]),
+                        );
+
+                        return Err(self.error_form(err, stub));
+                    }
+                    ErrorKind::PermissionDenied => {
+                        // 8.11.5.3k)
+                        return Err(self.open_permission_error(self[temp_v!(1)], atom!("open"), 4));
+                    }
+                    _ => {
+                        let stub = functor_stub(atom!("open"), 4);
+                        let err = self.syntax_error(ParserError::IO(err));
+
+                        return Err(self.error_form(err, stub));
+                    }
+                }
+            }
+        };
+
+        Ok(if is_input_file {
+            Stream::from_file_as_input(file_spec, file, &mut self.arena)
+        } else {
+            Stream::from_file_as_output(file_spec, file, in_append_mode, &mut self.arena)
+        })
     }
 }
index 0505e13831c9749f42a006d59898886a8b11e529..9f3dd089539ffc0a3bea7fdd9ee9e47fdab726d6 100644 (file)
@@ -1,30 +1,32 @@
-use prolog_parser::ast::*;
-use prolog_parser::parser::*;
-use prolog_parser::{
-    alpha_char, alpha_numeric_char, binary_digit_char, clause_name, decimal_digit_char,
-    exponent_char, graphic_char, graphic_token_char, hexadecimal_digit_char, layout_char,
-    meta_char, new_line_char, octal_digit_char, octet_char, prolog_char, sign_char, solo_char,
-    symbolic_control_char, symbolic_hexadecimal_char, temp_v,
-};
+use crate::parser::ast::*;
+use crate::parser::parser::*;
 
 use lazy_static::lazy_static;
 
+use crate::arena::*;
+use crate::atom_table::*;
 use crate::clause_types::*;
 use crate::forms::*;
+use crate::heap_iter::*;
 use crate::heap_print::*;
 use crate::instructions::*;
 use crate::machine;
 use crate::machine::code_repo::CodeRepo;
 use crate::machine::code_walker::*;
 use crate::machine::copier::*;
+use crate::machine::heap::*;
 use crate::machine::machine_errors::*;
 use crate::machine::machine_indices::*;
 use crate::machine::machine_state::*;
+use crate::machine::partial_string::*;
 use crate::machine::preprocessor::to_op_decl;
+use crate::machine::stack::*;
 use crate::machine::streams::*;
+use crate::parser::char_reader::*;
+use crate::parser::rug::Integer;
+use crate::read::*;
+use crate::types::*;
 
-use crate::read::readline;
-use crate::rug::Integer;
 use ordered_float::OrderedFloat;
 
 use indexmap::IndexSet;
@@ -40,7 +42,6 @@ use std::iter::{once, FromIterator};
 use std::net::{TcpListener, TcpStream};
 use std::num::NonZeroU32;
 use std::ops::Sub;
-use std::rc::Rc;
 use std::process;
 
 use chrono::{offset::Local, DateTime};
@@ -92,147 +93,287 @@ pub(crate) fn get_key() -> KeyEvent {
     key
 }
 
-#[derive(Debug)]
-struct BrentAlgState {
-    hare: Addr,
-    tortoise: Addr,
-    power: usize,
-    steps: usize,
+#[derive(Debug, Clone, Copy)]
+pub struct BrentAlgState {
+    pub hare: usize,
+    pub tortoise: usize,
+    pub power: usize,
+    pub lam: usize,
+    pub pstr_chars: usize,
 }
 
 impl BrentAlgState {
-    fn new(hare: Addr) -> Self {
-        BrentAlgState {
-            hare: hare,
+    pub fn new(hare: usize) -> Self {
+        Self {
+            hare,
             tortoise: hare,
-            power: 2,
-            steps: 0,
+            power: 1,
+            lam: 0,
+            pstr_chars: 0,
         }
     }
 
-    #[inline]
-    fn conclude_or_move_tortoise(&mut self) -> Option<CycleSearchResult> {
+    #[inline(always)]
+    pub fn step(&mut self, hare: usize) -> Option<CycleSearchResult> {
+        self.hare = hare;
+        self.lam += 1;
+
         if self.tortoise == self.hare {
             return Some(CycleSearchResult::NotList);
-        } else if self.steps == self.power {
+        } else if self.lam == self.power {
             self.tortoise = self.hare;
             self.power <<= 1;
+            self.lam = 0;
         }
 
         None
     }
 
-    #[inline]
-    fn step(&mut self, hare: Addr) -> Option<CycleSearchResult> {
-        self.hare = hare;
-        self.steps += 1;
-
-        self.conclude_or_move_tortoise()
+    #[inline(always)]
+    pub fn num_steps(&self) -> usize {
+        return self.lam + self.pstr_chars + self.power - 1;
     }
 
-    fn to_result(self) -> CycleSearchResult {
-        match self.hare {
-            addr @ Addr::HeapCell(_) | addr @ Addr::StackCell(..) | addr @ Addr::AttrVar(_) => {
-                CycleSearchResult::PartialList(self.steps, addr.as_var().unwrap())
-            }
-            Addr::PStrLocation(h, n) => CycleSearchResult::PStrLocation(self.steps, h, n),
-            Addr::EmptyList => CycleSearchResult::ProperList(self.steps),
-            _ => CycleSearchResult::NotList,
+    pub fn to_result(mut self, heap: &[HeapCellValue]) -> CycleSearchResult {
+        if let Some(var) = heap[self.hare].as_var() {
+            return CycleSearchResult::PartialList(self.num_steps(), var);
         }
+
+        read_heap_cell!(heap[self.hare],
+            (HeapCellValueTag::PStrOffset) => {
+                let n = cell_as_fixnum!(heap[self.hare+1]).get_num() as usize;
+
+                let pstr = cell_as_string!(heap[self.hare]);
+                self.pstr_chars += pstr.as_str_from(n).chars().count();
+
+                CycleSearchResult::PStrLocation(self.num_steps(), n)
+            }
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                if name == atom!("[]") && arity == 0 {
+                    CycleSearchResult::ProperList(self.num_steps())
+                } else {
+                    CycleSearchResult::NotList
+                }
+            }
+            _ => {
+                CycleSearchResult::NotList
+            }
+        )
     }
-}
 
-fn is_builtin_predicate(name: &ClauseName) -> bool {
-    let in_builtins = name.owning_module().as_str() == "builtins";
-    let hidden_name = name.as_str().starts_with("$");
+    fn add_pstr_chars_and_step(&mut self, heap: &[HeapCellValue], h: usize) -> Option<CycleSearchResult> {
+        read_heap_cell!(heap[h],
+            (HeapCellValueTag::CStr, cstr_atom) => {
+                let cstr = PartialString::from(cstr_atom);
+
+                self.pstr_chars += cstr.as_str_from(0).chars().count();
+                Some(CycleSearchResult::ProperList(self.num_steps()))
+            }
+            (HeapCellValueTag::PStr, pstr_atom) => {
+                let pstr = PartialString::from(pstr_atom);
+
+                self.pstr_chars += pstr.as_str_from(0).chars().count() - 1;
+                self.step(h+1)
+            }
+            (HeapCellValueTag::PStrOffset, offset) => {
+                let pstr = cell_as_string!(heap[offset]);
+                let n = cell_as_fixnum!(heap[h+1]).get_num() as usize;
+
+                self.pstr_chars += pstr.as_str_from(n).chars().count();
 
-    in_builtins || hidden_name
+                if let HeapCellValueTag::PStr = heap[offset].get_tag() {
+                    self.pstr_chars -= 1;
+                    self.step(offset+1)
+                } else {
+                    debug_assert!(heap[offset].get_tag() == HeapCellValueTag::CStr);
+                    Some(CycleSearchResult::ProperList(self.num_steps()))
+                }
+            }
+            _ => {
+                unreachable!()
+            }
+        )
+    }
 }
 
 impl MachineState {
-    // a step in Brent's algorithm.
-    fn brents_alg_step(&self, brent_st: &mut BrentAlgState) -> Option<CycleSearchResult> {
-        match self.store(self.deref(brent_st.hare)) {
-            Addr::EmptyList => Some(CycleSearchResult::ProperList(brent_st.steps)),
-            addr @ Addr::HeapCell(_) | addr @ Addr::StackCell(..) | addr @ Addr::AttrVar(_) => {
-                Some(CycleSearchResult::PartialList(
-                    brent_st.steps,
-                    addr.as_var().unwrap(),
-                ))
-            }
-            Addr::PStrLocation(h, n) => match &self.heap[h] {
-                HeapCellValue::PartialString(ref pstr, _) => {
-                    if let Some(c) = pstr.range_from(n..).next() {
-                        brent_st.step(Addr::PStrLocation(h, n + c.len_utf8()))
-                    } else {
-                        unreachable!()
-                    }
-                }
-                _ => {
-                    unreachable!()
-                }
-            },
-            Addr::Lis(l) => brent_st.step(Addr::HeapCell(l + 1)),
-            _ => Some(CycleSearchResult::NotList),
+    #[inline(always)]
+    pub fn brents_alg_step(&self, brent_st: &mut BrentAlgState) -> Option<CycleSearchResult> {
+        let deref_v = self.deref(self.heap[brent_st.hare]);
+        let store_v = self.store(deref_v);
+
+        if let Some(var) = store_v.as_var() {
+            return Some(CycleSearchResult::PartialList(brent_st.num_steps(), var));
+        }
+
+        if store_v == empty_list_as_cell!() {
+            return Some(CycleSearchResult::ProperList(brent_st.num_steps()));
         }
+
+        read_heap_cell!(store_v,
+            (HeapCellValueTag::PStrLoc, h) => {
+                brent_st.add_pstr_chars_and_step(&self.heap, h)
+            }
+            (HeapCellValueTag::PStrOffset) => {
+                brent_st.add_pstr_chars_and_step(&self.heap, brent_st.hare)
+            }
+            (HeapCellValueTag::CStr, cstr_atom) => {
+                let cstr = PartialString::from(cstr_atom);
+
+                brent_st.pstr_chars += cstr.as_str_from(0).chars().count();
+                Some(CycleSearchResult::ProperList(brent_st.num_steps()))
+            }
+            (HeapCellValueTag::Lis, h) => {
+                brent_st.step(h+1)
+            }
+            (HeapCellValueTag::Str, s) => {
+                let (name, arity) = cell_as_atom_cell!(self.heap[s]).get_name_and_arity();
+
+                if name == atom!(".") && arity == 2 {
+                    brent_st.step(s+2)
+                } else {
+                    Some(CycleSearchResult::NotList)
+                }
+            }
+            (HeapCellValueTag::Atom, (_name, arity)) => {
+                debug_assert!(arity == 0);
+                Some(CycleSearchResult::NotList)
+            }
+            _ => {
+                Some(CycleSearchResult::NotList)
+            }
+        )
     }
 
-    pub(super) fn detect_cycles_with_max(&self, max_steps: usize, addr: Addr) -> CycleSearchResult {
-        let hare = match self.store(self.deref(addr)) {
-            Addr::Lis(offset) if max_steps > 0 => Addr::Lis(offset),
-            Addr::Lis(offset) => {
-                return CycleSearchResult::UntouchedList(offset);
+    pub fn detect_cycles(&self, value: HeapCellValue) -> CycleSearchResult {
+        let deref_v = self.deref(value);
+        let store_v = self.store(deref_v);
+
+        let mut pstr_chars = 0;
+
+        let hare = read_heap_cell!(store_v,
+            (HeapCellValueTag::Lis, offset) => {
+                offset+1
             }
-            Addr::PStrLocation(h, n) if max_steps > 0 => Addr::PStrLocation(h, n),
-            Addr::PStrLocation(h, _) => {
-                return CycleSearchResult::UntouchedList(h);
+            (HeapCellValueTag::PStrLoc, h) => {
+                let (h_offset, n) = pstr_loc_and_offset(&self.heap, h);
+                let n = n.get_num() as usize;
+                let pstr = cell_as_string!(self.heap[h_offset]);
+
+                pstr_chars = pstr.as_str_from(n).chars().count() - 1;
+
+                if self.heap[h].get_tag() == HeapCellValueTag::PStrOffset {
+                    debug_assert!(self.heap[h].get_tag() == HeapCellValueTag::PStrOffset);
+
+                    if self.heap[h_offset].get_tag() == HeapCellValueTag::CStr {
+                        return CycleSearchResult::ProperList(pstr_chars + 1);
+                    }
+                }
+
+                h_offset+1
+            }
+            (HeapCellValueTag::PStrOffset) => {
+                unreachable!()
             }
-            Addr::EmptyList => {
-                return CycleSearchResult::EmptyList;
+            (HeapCellValueTag::CStr, cstr_atom) => {
+                let cstr = PartialString::from(cstr_atom);
+                return CycleSearchResult::ProperList(cstr.as_str_from(0).chars().count());
             }
-            Addr::Con(h) if max_steps > 0 => {
-                if let HeapCellValue::PartialString(..) = &self.heap[h] {
-                    Addr::PStrLocation(h, 0)
+            (HeapCellValueTag::Str, s) => {
+                let (name, arity) = cell_as_atom_cell!(self.heap[s])
+                    .get_name_and_arity();
+
+                if name == atom!("[]") && arity == 0 {
+                    return CycleSearchResult::EmptyList;
+                } else if name == atom!(".") && arity == 2 {
+                    s + 2
                 } else {
                     return CycleSearchResult::NotList;
                 }
             }
-            Addr::Con(h) => {
-                if let HeapCellValue::PartialString(..) = &self.heap[h] {
-                    return CycleSearchResult::UntouchedList(h);
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                if name == atom!("[]") && arity == 0 {
+                    return CycleSearchResult::EmptyList;
+                } else {
+                    return CycleSearchResult::NotList;
                 }
-
-                return CycleSearchResult::NotList;
             }
             _ => {
                 return CycleSearchResult::NotList;
             }
-        };
+        );
 
         let mut brent_st = BrentAlgState::new(hare);
 
-        loop {
-            if brent_st.steps == max_steps {
-                return brent_st.to_result();
-            }
+        brent_st.power += 1; // advance a step.
+        brent_st.pstr_chars = pstr_chars;
 
+        loop {
             if let Some(result) = self.brents_alg_step(&mut brent_st) {
                 return result;
             }
         }
     }
 
-    pub(super) fn detect_cycles(&self, addr: Addr) -> CycleSearchResult {
-        let addr = self.store(self.deref(addr));
-        let hare = match addr {
-            Addr::Lis(offset) => Addr::Lis(offset),
-            Addr::EmptyList => {
-                return CycleSearchResult::EmptyList;
-            }
-            Addr::PStrLocation(h, n) => Addr::PStrLocation(h, n),
-            Addr::Con(h) => {
-                if let HeapCellValue::PartialString(..) = &self.heap[h] {
-                    Addr::PStrLocation(h, 0)
+    pub fn detect_cycles_with_max(&self, max_steps: usize, value: HeapCellValue) -> CycleSearchResult {
+        let deref_v = self.deref(value);
+        let store_v = self.store(deref_v);
+
+        // let mut pstr_chars = 0;
+
+        let hare = read_heap_cell!(store_v,
+            (HeapCellValueTag::Lis, offset) => {
+                if max_steps > 0 {
+                    offset+1
+                } else {
+                    return CycleSearchResult::UntouchedList(offset);
+                }
+            }
+            (HeapCellValueTag::PStrLoc, h) => {
+                let (h_offset, _n) = pstr_loc_and_offset(&self.heap, h);
+
+                if self.heap[h].get_tag() == HeapCellValueTag::PStr {
+                    h_offset+1
+                } else {
+                    debug_assert!(self.heap[h].get_tag() == HeapCellValueTag::PStrOffset);
+                    h
+                }
+            }
+            (HeapCellValueTag::PStrOffset) => {
+                unreachable!()
+            }
+            (HeapCellValueTag::CStr, cstr_atom) => {
+                return if max_steps > 0 {
+                    let cstr = PartialString::from(cstr_atom);
+                    let pstr_chars = cstr.as_str_from(0).chars().count();
+
+                    if pstr_chars < max_steps {
+                        CycleSearchResult::ProperList(pstr_chars)
+                    } else {
+                        CycleSearchResult::UntouchedCStr(cstr_atom, max_steps)
+                    }
+                } else {
+                    CycleSearchResult::UntouchedCStr(cstr_atom, 0)
+                };
+            }
+            (HeapCellValueTag::Str, s) => {
+                let (name, arity) = cell_as_atom_cell!(self.heap[s]).get_name_and_arity();
+
+                if name == atom!("[]") && arity == 0 {
+                    return CycleSearchResult::EmptyList;
+                } else if name == atom!(".") && arity == 2 {
+                    if max_steps > 0 {
+                        s + 2
+                    } else {
+                        return CycleSearchResult::UntouchedList(s + 1);
+                    }
+                } else {
+                    return CycleSearchResult::NotList;
+                }
+            }
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                if name == atom!("[]") && arity == 0 {
+                    return CycleSearchResult::EmptyList;
                 } else {
                     return CycleSearchResult::NotList;
                 }
@@ -240,221 +381,179 @@ impl MachineState {
             _ => {
                 return CycleSearchResult::NotList;
             }
-        };
+        );
 
         let mut brent_st = BrentAlgState::new(hare);
 
+        brent_st.power += 1; // advance a step.
+        // brent_st.pstr_chars = pstr_chars;
+
         loop {
+            if brent_st.num_steps() == max_steps {
+                return brent_st.to_result(&self.heap);
+            }
+
             if let Some(result) = self.brents_alg_step(&mut brent_st) {
                 return result;
             }
         }
     }
 
-    fn finalize_skip_max_list(&mut self, n: usize, addr: Addr) {
-        let target_n = self[temp_v!(1)];
-        self.unify(Addr::Usize(n), target_n);
+    fn term_variables_under_max_depth(
+           &mut self,
+           term: HeapCellValue,
+           max_depth: usize,
+           list_of_vars: HeapCellValue,
+    ) {
+           let mut seen_set = IndexSet::new();
+
+           {
+               let mut iter = stackful_post_order_iter(&mut self.heap, term);
+
+            while let Some(value) = iter.next() {
+                       if iter.parent_stack_len() >= max_depth {
+                           iter.pop_stack();
+                           continue;
+                       }
+
+                       let value = unmark_cell_bits!(value);
+
+                       if value.is_var() && !seen_set.contains(&value) {
+                           seen_set.insert(value);
+                       }
+            }
+           }
+
+        let outcome = heap_loc_as_cell!(
+            iter_to_heap_list(
+                &mut self.heap,
+                seen_set.into_iter().rev(),
+            )
+        );
+
+        unify_fn!(self, list_of_vars, outcome);
+    }
+
+    fn finalize_skip_max_list(&mut self, n: usize, value: HeapCellValue) {
+        let target_n = self.registers[1];
+        self.unify_fixnum(Fixnum::build_with(n as i64), target_n);
 
         if !self.fail {
-            let xs = self[temp_v!(4)];
-            self.unify(addr, xs);
+            let xs = self.registers[4];
+            unify!(self, value, xs);
         }
     }
 
-    fn skip_max_list_result(&mut self, max_steps: Option<isize>) {
+    fn skip_max_list_result(&mut self, max_steps: Option<i64>) {
         let search_result = if let Some(max_steps) = max_steps {
             if max_steps == -1 {
-                self.detect_cycles(self[temp_v!(3)])
+                self.detect_cycles(self.registers[3])
             } else {
-                self.detect_cycles_with_max(max_steps as usize, self[temp_v!(3)])
+                self.detect_cycles_with_max(max_steps as usize, self.registers[3])
             }
         } else {
-            self.detect_cycles(self[temp_v!(3)])
+            self.detect_cycles(self.registers[3])
         };
 
         match search_result {
-            CycleSearchResult::PStrLocation(steps, h, n) => {
-                self.finalize_skip_max_list(steps, Addr::PStrLocation(h, n));
+            CycleSearchResult::PStrLocation(steps, pstr_loc) => {
+                self.finalize_skip_max_list(steps, heap_loc_as_cell!(pstr_loc));
+            }
+            CycleSearchResult::UntouchedList(l) => {
+                self.finalize_skip_max_list(0, list_loc_as_cell!(l));
+            }
+            CycleSearchResult::UntouchedCStr(cstr_atom, n) => {
+                self.finalize_skip_max_list(n, string_as_cstr_cell!(cstr_atom));
+            }
+            CycleSearchResult::EmptyList => {
+                self.finalize_skip_max_list(0, empty_list_as_cell!());
+            }
+            CycleSearchResult::PartialList(n, r) => {
+                self.finalize_skip_max_list(n, r.as_heap_cell_value());
             }
-            CycleSearchResult::UntouchedList(l) => self.finalize_skip_max_list(0, Addr::Lis(l)),
-            CycleSearchResult::EmptyList => self.finalize_skip_max_list(0, Addr::EmptyList),
-            CycleSearchResult::PartialList(n, r) => self.finalize_skip_max_list(n, r.as_addr()),
             CycleSearchResult::ProperList(steps) => {
-                self.finalize_skip_max_list(steps, Addr::EmptyList)
+                self.finalize_skip_max_list(steps, empty_list_as_cell!())
             }
             CycleSearchResult::NotList => {
-                let xs0 = self[temp_v!(3)];
+                let xs0 = self.registers[3];
                 self.finalize_skip_max_list(0, xs0);
             }
         };
     }
 
-    pub(super) fn skip_max_list(&mut self) -> CallResult {
-        let max_steps = self.store(self.deref(self[temp_v!(2)]));
-
-        match max_steps {
-            Addr::HeapCell(_) | Addr::StackCell(..) | Addr::AttrVar(_) => {
-                let stub = MachineError::functor_stub(clause_name!("$skip_max_list"), 4);
-                return Err(self.error_form(MachineError::instantiation_error(), stub));
-            }
-            addr => {
-                let max_steps_n = match Number::try_from((max_steps, &self.heap)) {
-                    Ok(Number::Integer(n)) => n.to_isize(),
-                    Ok(Number::Fixnum(n)) => Some(n),
-                    _ => None,
-                };
-
-                if max_steps_n.map(|i| i >= -1).unwrap_or(false) {
-                    let n = self.store(self.deref(self[temp_v!(1)]));
-
-                    match Number::try_from((n, &self.heap)) {
-                        Ok(Number::Integer(n)) => {
-                            if n.as_ref() == &0 {
-                                let xs0 = self[temp_v!(3)];
-                                let xs = self[temp_v!(4)];
-
-                                self.unify(xs0, xs);
-                            } else {
-                                self.skip_max_list_result(max_steps_n);
-                            }
-                        }
-                        Ok(Number::Fixnum(n)) => {
-                            if n == 0 {
-                                let xs0 = self[temp_v!(3)];
-                                let xs = self[temp_v!(4)];
-
-                                self.unify(xs0, xs);
-                            } else {
-                                self.skip_max_list_result(max_steps_n);
-                            }
-                        }
-                        _ => {
-                            self.skip_max_list_result(max_steps_n);
-                        }
-                    }
-                } else {
-                    let stub = MachineError::functor_stub(clause_name!("$skip_max_list"), 4);
-                    return Err(self.error_form(
-                        MachineError::type_error(self.heap.h(), ValidType::Integer, addr),
-                        stub,
-                    ));
-                }
-            }
-        }
-
-        Ok(())
-    }
+    pub fn skip_max_list(&mut self) -> CallResult {
+        let max_steps = self.store(self.deref(self.registers[2]));
 
-    fn stream_from_file_spec(
-        &self,
-        file_spec: ClauseName,
-        indices: &mut IndexStore,
-        options: &StreamOptions,
-    ) -> Result<Stream, MachineStub> {
-        if file_spec.as_str().is_empty() {
-            let stub = MachineError::functor_stub(clause_name!("open"), 4);
-            let err = MachineError::domain_error(DomainErrorType::SourceSink, self[temp_v!(1)]);
+        if max_steps.is_var() {
+            let stub = functor_stub(atom!("$skip_max_list"), 4);
+            let err = self.instantiation_error();
 
             return Err(self.error_form(err, stub));
         }
 
-        // 8.11.5.3l)
-        if let Some(ref alias) = &options.alias {
-            if indices.stream_aliases.contains_key(alias) {
-                return Err(self.occupied_alias_permission_error(alias.clone(), "open", 4));
-            }
-        }
-
-        let mode = atom_from!(self, self.store(self.deref(self[temp_v!(2)])));
-        let mut open_options = fs::OpenOptions::new();
-
-        let (is_input_file, in_append_mode) = match mode.as_str() {
-            "read" => {
-                open_options.read(true).write(false).create(false);
-                (true, false)
-            }
-            "write" => {
-                open_options
-                    .read(false)
-                    .write(true)
-                    .truncate(true)
-                    .create(true);
-                (false, false)
-            }
-            "append" => {
-                open_options
-                    .read(false)
-                    .write(true)
-                    .create(true)
-                    .append(true);
-                (false, true)
-            }
-            _ => {
-                let stub = MachineError::functor_stub(clause_name!("open"), 4);
-                let err = MachineError::domain_error(DomainErrorType::IOMode, self[temp_v!(2)]);
-
-                // 8.11.5.3h)
-                return Err(self.error_form(err, stub));
-            }
+        let max_steps_n = match Number::try_from(max_steps) {
+            Ok(Number::Fixnum(n)) => Some(n.get_num()),
+            Ok(Number::Integer(n)) => n.to_i64(),
+            _ => None,
         };
 
-        let file = match open_options.open(file_spec.as_str()) {
-            Ok(file) => file,
-            Err(err) => {
-                match err.kind() {
-                    ErrorKind::NotFound => {
-                        // 8.11.5.3j)
-                        let stub = MachineError::functor_stub(clause_name!("open"), 4);
-
-                        let err = MachineError::existence_error(
-                            self.heap.h(),
-                            ExistenceError::SourceSink(self[temp_v!(1)]),
-                        );
+        if max_steps_n.map(|i| i >= -1).unwrap_or(false) {
+            let n = self.store(self.deref(self.registers[1]));
 
-                        return Err(self.error_form(err, stub));
-                    }
-                    ErrorKind::PermissionDenied => {
-                        // 8.11.5.3k)
-                        return Err(self.open_permission_error(self[temp_v!(1)], "open", 4));
-                    }
-                    _ => {
-                        let stub = MachineError::functor_stub(clause_name!("open"), 4);
+            match Number::try_from(n) {
+                Ok(Number::Integer(n)) => {
+                    if &*n == &0 {
+                        let xs0 = self.registers[3];
+                        let xs = self.registers[4];
 
-                        let err = MachineError::syntax_error(self.heap.h(), ParserError::IO(err));
+                        unify!(self, xs0, xs);
+                    } else {
+                        self.skip_max_list_result(max_steps_n);
+                    }
+                }
+                Ok(Number::Fixnum(n)) => {
+                    if n.get_num() == 0 {
+                        let xs0 = self.registers[3];
+                        let xs = self.registers[4];
 
-                        return Err(self.error_form(err, stub));
+                        unify!(self, xs0, xs);
+                    } else {
+                        self.skip_max_list_result(max_steps_n);
                     }
                 }
+                _ => {
+                    self.skip_max_list_result(max_steps_n);
+                }
             }
-        };
-
-        Ok(if is_input_file {
-            Stream::from_file_as_input(file_spec, file)
         } else {
-            Stream::from_file_as_output(file_spec, file, in_append_mode)
-        })
+            let stub = functor_stub(atom!("$skip_max_list"), 4);
+            let err = self.type_error(ValidType::Integer, max_steps);
+
+            return Err(self.error_form(err, stub));
+        }
+
+        Ok(())
     }
+}
 
+impl MachineState {
     #[inline]
-    fn install_new_block(&mut self, r: RegType) -> usize {
+    fn install_new_block(&mut self, value: HeapCellValue) -> usize {
         self.block = self.b;
+        self.unify_fixnum(Fixnum::build_with(self.block as i64), value);
 
-        let c = Constant::Usize(self.block);
-        let addr = self[r];
-
-        self.write_constant_to_var(addr, &c);
         self.block
     }
 
-    fn copy_findall_solution(&mut self, lh_offset: usize, copy_target: Addr) -> usize {
-        let threshold = self.lifted_heap.h() - lh_offset;
+    fn copy_findall_solution(&mut self, lh_offset: usize, copy_target: HeapCellValue) -> usize {
+        let threshold = self.lifted_heap.len() - lh_offset;
 
         let mut copy_ball_term =
             CopyBallTerm::new(&mut self.stack, &mut self.heap, &mut self.lifted_heap);
 
-        copy_ball_term.push(HeapCellValue::Addr(Addr::Lis(threshold + 1)));
-        copy_ball_term.push(HeapCellValue::Addr(Addr::HeapCell(threshold + 3)));
-        copy_ball_term.push(HeapCellValue::Addr(Addr::HeapCell(threshold + 2)));
+        copy_ball_term.push(list_loc_as_cell!(threshold + 1));
+        copy_ball_term.push(heap_loc_as_cell!(threshold + 3));
+        copy_ball_term.push(heap_loc_as_cell!(threshold + 2));
 
         copy_term(copy_ball_term, copy_target, AttrVarPolicy::DeepCopy);
 
@@ -471,124 +570,70 @@ impl MachineState {
         Ok(self.p = CodePtr::REPL(repl_code_ptr, p))
     }
 
-    fn truncate_if_no_lifted_heap_diff<AddrConstr>(&mut self, addr_constr: AddrConstr)
-    where
-        AddrConstr: Fn(usize) -> Addr,
-    {
-        match self.store(self.deref(self[temp_v!(1)])) {
-            Addr::Usize(lh_offset) => {
-                if lh_offset >= self.lifted_heap.h() {
+    #[inline(always)]
+    fn truncate_if_no_lifted_heap_diff(
+        &mut self,
+        addr_constr: impl Fn(usize) -> HeapCellValue,
+    ) {
+        read_heap_cell!(self.store(self.deref(self.registers[1])),
+            (HeapCellValueTag::Fixnum, n) => {
+                let lh_offset = n.get_num() as usize;
+
+                if lh_offset >= self.lifted_heap.len() {
                     self.lifted_heap.truncate(lh_offset);
                 } else {
-                    let threshold = self.lifted_heap.h() - lh_offset;
-                    self.lifted_heap
-                        .push(HeapCellValue::Addr(addr_constr(threshold)));
+                    let threshold = self.lifted_heap.len() - lh_offset;
+                    self.lifted_heap.push(addr_constr(threshold));
                 }
             }
-            _ => self.fail = true,
-        }
+            _ => {
+                self.fail = true;
+            }
+        );
     }
 
-    fn get_next_db_ref(&mut self, indices: &IndexStore, db_ref: &DBRef) {
+    fn get_next_db_ref(&self, indices: &IndexStore, db_ref: &DBRef) -> Option<DBRef> {
         match db_ref {
-            &DBRef::NamedPred(ref name, arity, _) => {
-                let key = (name.clone(), arity);
-                let mut iter = indices.code_dir.range(key..).skip(1);
-
-                while let Some(((name, arity), idx)) = iter.next() {
-                    if idx.is_undefined() {
-                        self.fail = true;
-                        return;
-                    }
-
-                    if is_builtin_predicate(&name) {
-                        continue;
-                    }
+            DBRef::NamedPred(name, arity) => {
+                let key = (*name, *arity);
 
-                    let a2 = self[temp_v!(2)];
+                if let Some((last_idx, _, _)) = indices.code_dir.get_full(&key) {
+                    for idx in last_idx + 1 .. indices.code_dir.len() {
+                        let ((name, arity), idx) = indices.code_dir.get_index(idx).unwrap();
 
-                    if let Some(r) = a2.as_var() {
-                        let spec = get_clause_spec(
-                            name.clone(),
-                            *arity,
-                            &CompositeOpDir::new(&indices.op_dir, None),
-                        );
-
-                        let addr = self
-                            .heap
-                            .to_unifiable(HeapCellValue::DBRef(DBRef::NamedPred(
-                                name.clone(),
-                                *arity,
-                                spec,
-                            )));
+                        if idx.is_undefined() {
+                            return None;
+                        }
 
-                        self.bind(r, addr);
+                        if SystemClauseType::from(*name, *arity).is_some() {
+                            continue;
+                        }
 
-                        return;
+                        return Some(DBRef::NamedPred(*name, *arity));
                     }
                 }
-
-                self.fail = true;
             }
-            &DBRef::Op(_, spec, ref name, ref op_dir, _) => {
-                let fixity = match spec {
-                    XF | YF => Fixity::Post,
-                    FX | FY => Fixity::Pre,
-                    _ => Fixity::In,
-                };
-
-                let key = OrderedOpDirKey(name.clone(), fixity);
+            DBRef::Op(name, fixity, op_dir) => {
+                let key = (*name, *fixity);
 
-                match op_dir.range(key..).skip(1).next() {
-                    Some((OrderedOpDirKey(name, _), (priority, spec))) => {
-                        let a2 = self[temp_v!(2)];
-
-                        if let Some(r) = a2.as_var() {
-                            let addr = self.heap.to_unifiable(HeapCellValue::DBRef(DBRef::Op(
-                                *priority,
-                                *spec,
-                                name.clone(),
-                                op_dir.clone(),
-                                SharedOpDesc::new(*priority, *spec),
-                            )));
-
-                            self.bind(r, addr);
-                        } else {
-                            self.fail = true;
-                        }
+                if let Some((last_idx, _, _)) = op_dir.get_full(&key) {
+                    if let Some(((name, fixity), _)) = op_dir.get_index(last_idx+1) {
+                        return Some(DBRef::Op(*name, *fixity, *op_dir));
                     }
-                    None => self.fail = true,
                 }
             }
         }
-    }
-
-    fn int_to_char(
-        &self,
-        n: &Integer,
-        stub: &'static str,
-        arity: usize,
-    ) -> Result<char, MachineStub> {
-        let c = n.to_u32().and_then(std::char::from_u32);
 
-        if let Some(c) = c {
-            Ok(c)
-        } else {
-            let stub = MachineError::functor_stub(clause_name!(stub), arity);
-            let err = MachineError::representation_error(RepFlag::CharacterCode);
-            let err = self.error_form(err, stub);
-
-            Err(err)
-        }
+        None
     }
 
     fn parse_number_from_string(
         &mut self,
         mut string: String,
         indices: &IndexStore,
-        stub: MachineStub,
+        stub_gen: impl Fn() -> FunctorStub,
     ) -> CallResult {
-        let nx = self[temp_v!(2)];
+        let nx = self.registers[2];
 
         if let Some(c) = string.chars().last() {
             if layout_char!(c) {
@@ -600,106 +645,159 @@ impl MachineState {
                     }
                 });
                 let err = ParserError::UnexpectedChar(c, line_num, col_num);
+                let err = self.syntax_error(err);
 
-                let h = self.heap.h();
-                let err = MachineError::syntax_error(h, err);
-
-                return Err(self.error_form(err, stub));
+                return Err(self.error_form(err, stub_gen()));
             }
         }
 
         string.push('.');
 
-        let mut stream = match parsing_stream(std::io::Cursor::new(string)) {
-            Ok(stream) => stream,
-            Err(e) => {
-                let err = MachineError::session_error(self.heap.h(), SessionError::from(e));
-
-                return Err(self.error_form(err, stub));
-            }
-        };
-
-        let mut parser = Parser::new(&mut stream, self.atom_tbl.clone(), self.machine_flags());
+        let stream = CharReader::new(std::io::Cursor::new(string));
+        let mut parser = Parser::new(stream, self);
 
         match parser.read_term(&CompositeOpDir::new(&indices.op_dir, None)) {
             Err(err) => {
-                let h = self.heap.h();
-                let err = MachineError::syntax_error(h, err);
-
-                return Err(self.error_form(err, stub));
+                let err = self.syntax_error(err);
+                return Err(self.error_form(err, stub_gen()));
             }
-            Ok(Term::Constant(_, Constant::Rational(n))) => {
-                let addr = self.heap.put_constant(Constant::Rational(n));
-                (self.unify_fn)(self, nx, addr);
+            Ok(Term::Literal(_, Literal::Rational(n))) => {
+                self.unify_rational(n, nx);
             }
-            Ok(Term::Constant(_, Constant::Float(n))) => {
-                let addr = self.heap.put_constant(Constant::Float(n));
-                (self.unify_fn)(self, nx, addr);
+            Ok(Term::Literal(_, Literal::Float(n))) => {
+                self.unify_f64(n, nx);
             }
-            Ok(Term::Constant(_, Constant::Integer(n))) => {
-                let addr = self.heap.put_constant(Constant::Integer(n));
-                (self.unify_fn)(self, nx, addr);
+            Ok(Term::Literal(_, Literal::Integer(n))) => {
+                self.unify_big_int(n, nx);
             }
-            Ok(Term::Constant(_, Constant::Fixnum(n))) => {
-                let addr = self.heap.put_constant(Constant::Fixnum(n));
-                (self.unify_fn)(self, nx, addr);
+            Ok(Term::Literal(_, Literal::Fixnum(n))) => {
+                self.unify_fixnum(n, nx);
             }
             _ => {
                 let err = ParserError::ParseBigInt(0, 0);
+                let err = self.syntax_error(err);
 
-                let h = self.heap.h();
-                let err = MachineError::syntax_error(h, err);
-
-                return Err(self.error_form(err, stub));
+                return Err(self.error_form(err, stub_gen()));
             }
         }
 
         Ok(())
     }
 
-    fn call_continuation_chunk(&mut self, chunk: Addr, return_p: LocalCodePtr) -> LocalCodePtr {
+    fn call_continuation_chunk(&mut self, chunk: HeapCellValue, return_p: LocalCodePtr) -> LocalCodePtr {
         let chunk = self.store(self.deref(chunk));
 
-        match chunk {
-            Addr::Str(s) => {
-                match &self.heap[s] {
-                    HeapCellValue::NamedStr(arity, ..) => {
-                        let num_cells = arity - 1;
-                        let p_functor = self.heap[s + 1].as_addr(s + 1);
+        let s = chunk.get_value();
+        let arity = cell_as_atom_cell!(self.heap[s]).get_arity();
 
-                        let cp = self.heap.to_local_code_ptr(&p_functor).unwrap();
-                        let prev_e = self.e;
+        let num_cells = arity - 1;
+        let p_functor = self.heap[s + 1];
 
-                        let e = self.stack.allocate_and_frame(num_cells);
-                        let and_frame = self.stack.index_and_frame_mut(e);
+        let cp = to_local_code_ptr(&self.heap, p_functor).unwrap();
+        let prev_e = self.e;
 
-                        and_frame.prelude.e = prev_e;
-                        and_frame.prelude.cp = return_p;
+        let e = self.stack.allocate_and_frame(num_cells);
+        let and_frame = self.stack.index_and_frame_mut(e);
 
-                        self.p = CodePtr::Local(cp + 1);
+        and_frame.prelude.e = prev_e;
+        and_frame.prelude.cp = return_p;
 
-                        // adjust cut point to occur after call_continuation.
-                        if num_cells > 0 {
-                            if let Addr::CutPoint(_) = self.heap[s + 2].as_addr(s + 2) {
-                                and_frame[1] = Addr::CutPoint(self.b);
-                            } else {
-                                and_frame[1] = self.heap[s + 2].as_addr(s + 2);
-                            }
-                        }
+        self.p = CodePtr::Local(cp + 1);
 
-                        for index in s + 3..s + 2 + num_cells {
-                            and_frame[index - (s + 1)] = self.heap[index].as_addr(index);
-                        }
+        // adjust cut point to occur after call_continuation.
+        if num_cells > 0 {
+            if let HeapCellValueTag::Fixnum = self.heap[s + 2].get_tag() {
+                and_frame[1] = fixnum_as_cell!(Fixnum::build_with(self.b as i64));
+            } else {
+                and_frame[1] = self.heap[s + 2];
+            }
+        }
 
-                        self.e = e;
+        for index in s + 3..s + 2 + num_cells {
+            and_frame[index - (s + 1)] = self.heap[index];
+        }
 
-                        self.p.local()
+        self.e = e;
+        self.p.local()
+    }
+
+    pub fn value_to_str_like(&mut self, value: HeapCellValue) -> Option<AtomOrString> {
+        read_heap_cell!(value,
+            (HeapCellValueTag::CStr, cstr_atom) => {
+                // avoid allocating a String if possible ...
+                Some(AtomOrString::Atom(cstr_atom))
+            }
+            (HeapCellValueTag::Atom, (atom, arity)) => {
+                if arity == 0 {
+                    // ... likewise.
+                    Some(AtomOrString::Atom(atom))
+                } else {
+                    None
+                }
+            }
+            _ => {
+                if value.is_constant() {
+                    return None;
+                }
+
+                let h = self.heap.len();
+                self.heap.push(value);
+
+                let mut iter = HeapPStrIter::new(&self.heap, h);
+                let string = iter.to_string();
+                let at_terminator = iter.at_string_terminator();
+
+                self.heap.pop();
+
+                // if the iteration doesn't terminate like a string
+                // (i.e. with the [] atom or a CStr), it is not
+                // "str_like" so return None.
+                if at_terminator {
+                    Some(AtomOrString::String(string))
+                } else {
+                    None
+                }
+            }
+        )
+    }
+
+    fn codes_to_string(
+        &mut self,
+        addrs: impl Iterator<Item = HeapCellValue>,
+        stub_gen: impl Fn() -> FunctorStub,
+    ) -> Result<String, MachineStub> {
+        let mut string = String::new();
+
+        for addr in addrs {
+            match Number::try_from(addr) {
+                Ok(Number::Fixnum(n)) => {
+                    match u32::try_from(n.get_num()) {
+                        Ok(n) => {
+                            if let Some(c) = std::char::from_u32(n) {
+                                string.push(c);
+                                continue;
+                            }
+                        }
+                        _ => {}
+                    }
+                }
+                Ok(Number::Integer(n)) => {
+                    if let Some(c) = n.to_u32().and_then(std::char::from_u32) {
+                        string.push(c);
+                        continue;
                     }
-                    _ => unreachable!(),
+                }
+                _ => {
+                    let err = self.type_error(ValidType::Integer, addr);
+                    return Err(self.error_form(err, stub_gen()));
                 }
             }
-            _ => unreachable!(),
+
+            let err = self.representation_error(RepFlag::CharacterCode);
+            return Err(self.error_form(err, stub_gen()));
         }
+
+        Ok(string)
     }
 
     pub(super) fn system_call(
@@ -714,10 +812,10 @@ impl MachineState {
     ) -> CallResult {
         match ct {
             &SystemClauseType::BindFromRegister => {
-                let reg = self.store(self.deref(self[temp_v!(2)]));
-                let n = match Number::try_from((reg, &self.heap)) {
+                let reg = self.store(self.deref(self.registers[2]));
+                let n = match Number::try_from(reg) {
+                    Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).ok(),
                     Ok(Number::Integer(n)) => n.to_usize(),
-                    Ok(Number::Fixnum(n)) => usize::try_from(n).ok(),
                     _ => {
                         unreachable!()
                     }
@@ -725,10 +823,10 @@ impl MachineState {
 
                 if let Some(n) = n {
                     if n <= MAX_ARITY {
-                        let target = self[temp_v!(n)];
-                        let addr = self[temp_v!(1)];
+                        let target = self.registers[n];
+                        let addr = self.registers[1];
 
-                        (self.unify_fn)(self, addr, target);
+                        unify_fn!(self, addr, target);
                         return return_from_clause!(self.last_call, self);
                     }
                 }
@@ -737,14 +835,11 @@ impl MachineState {
             }
             &SystemClauseType::CurrentHostname => {
                 match hostname::get().ok() {
-                    Some(host) => match host.into_string().ok() {
+                    Some(host) => match host.to_str() {
                         Some(host) => {
-                            let hostname = self.heap.to_unifiable(HeapCellValue::Atom(
-                                clause_name!(host, self.atom_tbl),
-                                None,
-                            ));
+                            let hostname = self.atom_tbl.build_with(host);
 
-                            (self.unify_fn)(self, self[temp_v!(1)], hostname);
+                            self.unify_atom(hostname, self.store(self.deref(self.registers[1])));
                             return return_from_clause!(self.last_call, self);
                         }
                         None => {}
@@ -756,171 +851,202 @@ impl MachineState {
                 return Ok(());
             }
             &SystemClauseType::CurrentInput => {
-                let addr = self.store(self.deref(self[temp_v!(1)]));
+                let addr = self.store(self.deref(self.registers[1]));
+                let stream = *current_input_stream;
 
-                let stream = current_input_stream.clone();
+                if let Some(var) = addr.as_var() {
+                    self.bind(var, stream_as_cell!(stream));
+                    return return_from_clause!(self.last_call, self);
+                }
 
-                match addr {
-                    addr if addr.is_ref() => {
-                        let stream = self.heap.to_unifiable(HeapCellValue::Stream(stream));
-                        (self.unify_fn)(self, stream, addr);
-                    }
-                    Addr::Stream(other_stream) => {
-                        if let HeapCellValue::Stream(ref other_stream) = &self.heap[other_stream] {
-                            self.fail = current_input_stream != other_stream;
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    addr => {
-                        let stub = MachineError::functor_stub(clause_name!("current_input"), 1);
+                read_heap_cell!(addr,
+                    (HeapCellValueTag::Cons, cons_ptr) => {
+                        match_untyped_arena_ptr!(cons_ptr,
+                             (ArenaHeaderTag::Stream, other_stream) => {
+                                 self.fail = stream != other_stream;
+                             }
+                             _ => {
+                                 let stub = functor_stub(atom!("current_input"), 1);
+                                 let err = self.domain_error(DomainErrorType::Stream, addr);
 
-                        let err = MachineError::domain_error(DomainErrorType::Stream, addr);
+                                 return Err(self.error_form(err, stub));
+                             }
+                        );
+                    }
+                    _ => {
+                        let stub = functor_stub(atom!("current_input"), 1);
+                        let err = self.domain_error(DomainErrorType::Stream, addr);
 
                         return Err(self.error_form(err, stub));
                     }
-                }
+                );
             }
             &SystemClauseType::CurrentOutput => {
-                let addr = self.store(self.deref(self[temp_v!(1)]));
-                let stream = current_output_stream.clone();
+                let addr = self.store(self.deref(self.registers[1]));
+                let stream = *current_output_stream;
 
-                match addr {
-                    addr if addr.is_ref() => {
-                        let stream = self.heap.to_unifiable(HeapCellValue::Stream(stream));
-                        (self.unify_fn)(self, stream, addr);
-                    }
-                    Addr::Stream(other_stream) => {
-                        if let HeapCellValue::Stream(ref other_stream) = &self.heap[other_stream] {
-                            self.fail = current_output_stream != other_stream;
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    addr => {
-                        let stub = MachineError::functor_stub(clause_name!("current_input"), 1);
+                if let Some(var) = addr.as_var() {
+                    self.bind(var, stream_as_cell!(stream));
+                    return return_from_clause!(self.last_call, self);
+                }
 
-                        let err = MachineError::domain_error(DomainErrorType::Stream, addr);
+                read_heap_cell!(addr,
+                    (HeapCellValueTag::Cons, cons_ptr) => {
+                        match_untyped_arena_ptr!(cons_ptr,
+                             (ArenaHeaderTag::Stream, other_stream) => {
+                                 self.fail = stream != other_stream;
+                             }
+                             _ => {
+                                 let stub = functor_stub(atom!("current_output"), 1);
+                                 let err = self.domain_error(DomainErrorType::Stream, addr);
+
+                                 return Err(self.error_form(err, stub));
+                             }
+                        );
+                    }
+                    _ => {
+                        let stub = functor_stub(atom!("current_output"), 1);
+                        let err = self.domain_error(DomainErrorType::Stream, addr);
 
                         return Err(self.error_form(err, stub));
                     }
-                }
+                );
             }
             &SystemClauseType::DirectoryFiles => {
-                let dir = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
-                let path = std::path::Path::new(&dir);
-                let mut files = Vec::new();
-
-                if let Ok(entries) = fs::read_dir(path) {
-                    for entry in entries {
-                        if let Ok(entry) = entry {
-                            match entry.file_name().into_string() {
-                                Ok(name) => {
-                                    files.push(self.heap.put_complete_string(&name));
-                                }
-                                _ => {
-                                    let stub = MachineError::functor_stub(
-                                        clause_name!("directory_files"),
-                                        2,
-                                    );
-                                    let err =
-                                        MachineError::representation_error(RepFlag::Character);
-                                    let err = self.error_form(err, stub);
-
-                                    return Err(err);
+                if let Some(dir) = self.value_to_str_like(self.registers[1]) {
+                    let path = std::path::Path::new(dir.as_str());
+                    let mut files = Vec::new();
+
+                    if let Ok(entries) = fs::read_dir(path) {
+                        for entry in entries {
+                            if let Ok(entry) = entry {
+                                if let Some(name) = entry.file_name().to_str() {
+                                    let name = self.atom_tbl.build_with(name);
+                                    files.push(atom_as_cstr_cell!(name));
+
+                                    continue;
                                 }
                             }
+
+                            let stub = functor_stub(atom!("directory_files"), 2);
+                            let err = self.representation_error(RepFlag::Character);
+                            let err = self.error_form(err, stub);
+
+                            return Err(err);
                         }
+
+                        let files_list = heap_loc_as_cell!(
+                            iter_to_heap_list(&mut self.heap, files.into_iter())
+                        );
+
+                        unify!(self, self.registers[2], files_list);
+                        return return_from_clause!(self.last_call, self);
                     }
                 }
 
-                let files_list = Addr::HeapCell(self.heap.to_list(files.into_iter()));
-                (self.unify_fn)(self, self[temp_v!(2)], files_list);
+                self.fail = true;
             }
             &SystemClauseType::FileSize => {
-                let file = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
-                let len = Integer::from(fs::metadata(&file).unwrap().len());
-
-                let len = self.heap.to_unifiable(HeapCellValue::Integer(Rc::new(len)));
+                if let Some(file) = self.value_to_str_like(self.registers[1]) {
+                    let len = Number::arena_from(
+                        fs::metadata(file.as_str()).unwrap().len(),
+                        &mut self.arena,
+                    );
 
-                (self.unify_fn)(self, self[temp_v!(2)], len);
+                    match len {
+                        Number::Fixnum(n) => self.unify_fixnum(n, self.registers[2]),
+                        Number::Integer(n) => self.unify_big_int(n, self.registers[2]),
+                        _ => unreachable!(),
+                    }
+                } else {
+                    self.fail = true;
+                }
             }
             &SystemClauseType::FileExists => {
-                let file = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
-                if !std::path::Path::new(&file).exists() || !fs::metadata(&file).unwrap().is_file()
-                {
+                if let Some(file) = self.value_to_str_like(self.registers[1]) {
+                    let file_str = file.as_str();
+
+                    if !std::path::Path::new(file_str).exists() || !fs::metadata(file_str).unwrap().is_file() {
+                        self.fail = true;
+                    }
+                } else {
                     self.fail = true;
-                    return Ok(());
                 }
             }
             &SystemClauseType::DirectoryExists => {
-                let directory = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
-                if !std::path::Path::new(&directory).exists()
-                    || !fs::metadata(&directory).unwrap().is_dir()
-                {
+                if let Some(dir) = self.value_to_str_like(self.registers[1]) {
+                    let dir_str = dir.as_str();
+
+                    if !std::path::Path::new(dir_str).exists()
+                        || !fs::metadata(dir_str).unwrap().is_dir()
+                    {
+                        self.fail = true;
+                        return Ok(());
+                    }
+                } else {
                     self.fail = true;
-                    return Ok(());
                 }
             }
             &SystemClauseType::DirectorySeparator => {
-                let addr = self
-                    .heap
-                    .put_constant(Constant::Char(std::path::MAIN_SEPARATOR));
-                (self.unify_fn)(self, self[temp_v!(1)], addr);
+                self.unify_char(std::path::MAIN_SEPARATOR, self.registers[1]);
             }
             &SystemClauseType::MakeDirectory => {
-                let directory = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
-
-                match fs::create_dir(directory) {
-                    Ok(_) => {}
-                    _ => {
-                        self.fail = true;
-                        return Ok(());
+                if let Some(dir) = self.value_to_str_like(self.registers[1]) {
+                    match fs::create_dir(dir.as_str()) {
+                        Ok(_) => {}
+                        _ => {
+                            self.fail = true;
+                            return Ok(());
+                        }
                     }
+                } else {
+                    self.fail = true;
                 }
             }
             &SystemClauseType::MakeDirectoryPath => {
-                let directory = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
+                if let Some(dir) = self.value_to_str_like(self.registers[1]) {
 
-                match fs::create_dir_all(directory) {
-                    Ok(_) => {}
-                    _ => {
-                        self.fail = true;
-                        return Ok(());
+                    match fs::create_dir_all(dir.as_str()) {
+                        Ok(_) => {}
+                        _ => {
+                            self.fail = true;
+                            return Ok(());
+                        }
                     }
+                } else {
+                    self.fail = true;
                 }
             }
             &SystemClauseType::DeleteFile => {
-                let file = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
-
-                match fs::remove_file(file) {
-                    Ok(_) => {}
-                    _ => {
-                        self.fail = true;
-                        return Ok(());
+                if let Some(file) = self.value_to_str_like(self.registers[1]) {
+                    match fs::remove_file(file.as_str()) {
+                        Ok(_) => {}
+                        _ => {
+                            self.fail = true;
+                            return Ok(());
+                        }
                     }
                 }
             }
             &SystemClauseType::RenameFile => {
-                let file = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
-                let renamed = self.heap_pstr_iter(self[temp_v!(2)]).to_string();
-
-                match fs::rename(file, renamed) {
-                    Ok(_) => {}
-                    _ => {
-                        self.fail = true;
-                        return Ok(());
+                if let Some(file) = self.value_to_str_like(self.registers[1]) {
+                    if let Some(renamed) = self.value_to_str_like(self.registers[2]) {
+                        if fs::rename(file.as_str(), renamed.as_str()).is_ok() {
+                            return return_from_clause!(self.last_call, self);
+                        }
                     }
                 }
+
+                self.fail = true;
             }
             &SystemClauseType::DeleteDirectory => {
-                let directory = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
-
-                match fs::remove_dir(directory) {
-                    Ok(_) => {}
-                    _ => {
-                        self.fail = true;
-                        return Ok(());
+                if let Some(dir) = self.value_to_str_like(self.registers[1]) {
+                    match fs::remove_dir(dir.as_str()) {
+                        Ok(_) => {}
+                        _ => {
+                            self.fail = true;
+                            return Ok(());
+                        }
                     }
                 }
             }
@@ -929,283 +1055,211 @@ impl MachineState {
                     let current = match dir.to_str() {
                         Some(d) => d,
                         _ => {
-                            let stub =
-                                MachineError::functor_stub(clause_name!("working_directory"), 2);
-                            let err = MachineError::representation_error(RepFlag::Character);
+                            let stub = functor_stub(atom!("working_directory"), 2);
+                            let err = self.representation_error(RepFlag::Character);
                             let err = self.error_form(err, stub);
 
                             return Err(err);
                         }
                     };
 
-                    let chars = self.heap.put_complete_string(current);
-                    (self.unify_fn)(self, self[temp_v!(1)], chars);
+                    let current_atom = self.atom_tbl.build_with(&current);
 
-                    let next = self.heap_pstr_iter(self[temp_v!(2)]).to_string();
+                    self.unify_complete_string(
+                        current_atom,
+                        self.store(self.deref(self.registers[1])),
+                    );
 
-                    match env::set_current_dir(std::path::Path::new(&next)) {
-                        Ok(_) => {}
-                        _ => {
-                            self.fail = true;
-                            return Ok(());
+                    if self.fail {
+                        return Ok(());
+                    }
+
+                    if let Some(next) = self.value_to_str_like(self.registers[2]) {
+                        if env::set_current_dir(std::path::Path::new(next.as_str())).is_ok() {
+                            return return_from_clause!(self.last_call, self);
                         }
                     }
-                } else {
-                    self.fail = true;
-                    return Ok(());
                 }
+
+                self.fail = true;
             }
             &SystemClauseType::PathCanonical => {
-                let path = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
+                if let Some(path) = self.value_to_str_like(self.registers[1]) {
+                    match fs::canonicalize(path.as_str()) {
+                        Ok(canonical) => {
+                            let cs = match canonical.to_str() {
+                                Some(s) => s,
+                                _ => {
+                                    let stub = functor_stub(atom!("path_canonical"), 2);
+                                    let err = self.representation_error(RepFlag::Character);
+                                    let err = self.error_form(err, stub);
 
-                match fs::canonicalize(path) {
-                    Ok(canonical) => {
-                        let cs = match canonical.to_str() {
-                            Some(s) => s,
-                            _ => {
-                                let stub =
-                                    MachineError::functor_stub(clause_name!("path_canonical"), 2);
-                                let err = MachineError::representation_error(RepFlag::Character);
-                                let err = self.error_form(err, stub);
+                                    return Err(err);
+                                }
+                            };
 
-                                return Err(err);
-                            }
-                        };
-                        let chars = self.heap.put_complete_string(cs);
-                        (self.unify_fn)(self, self[temp_v!(2)], chars);
-                    }
-                    _ => {
-                        self.fail = true;
-                        return Ok(());
+                            let canonical_atom = self.atom_tbl.build_with(cs);
+
+                            self.unify_complete_string(
+                                canonical_atom,
+                                self.store(self.deref(self.registers[2])),
+                            );
+
+                            return return_from_clause!(self.last_call, self);
+                        }
+                        _ => {
+                        }
                     }
                 }
+
+                self.fail = true;
             }
             &SystemClauseType::FileTime => {
-                let file = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
+                if let Some(file) = self.value_to_str_like(self.registers[1]) {
+                    let which = cell_as_atom!(self.store(self.deref(self.registers[2])));
+
+                    if let Ok(md) = fs::metadata(file.as_str()) {
+                        if let Ok(time) = match which {
+                            atom!("modification") => md.modified(),
+                            atom!("access") => md.accessed(),
+                            atom!("creation") => md.created(),
+                            _ => {
+                                unreachable!()
+                            }
+                        } {
+                            let chars_atom = self.systemtime_to_timestamp(time);
 
-                let which = match self.store(self.deref(self[temp_v!(2)])) {
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
-                            atom.as_str()
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    _ => {
-                        unreachable!()
-                    }
-                };
+                            self.unify_complete_string(
+                                chars_atom,
+                                self.registers[3],
+                            );
 
-                if let Ok(md) = fs::metadata(file) {
-                    if let Ok(time) = match which {
-                        "modification" => md.modified(),
-                        "access" => md.accessed(),
-                        "creation" => md.created(),
-                        _ => {
-                            unreachable!()
+                            return return_from_clause!(self.last_call, self);
                         }
-                    } {
-                        let chars = self.systemtime_to_timestamp(time);
-                        (self.unify_fn)(self, self[temp_v!(3)], chars);
-                    } else {
-                        self.fail = true;
-                        return Ok(());
                     }
-                } else {
-                    self.fail = true;
-                    return Ok(());
                 }
+
+                self.fail = true;
             }
             &SystemClauseType::AtomChars => {
-                let a1 = self[temp_v!(1)];
+                let a1 = self.store(self.deref(self.registers[1]));
 
-                match self.store(self.deref(a1)) {
-                    Addr::Char(c) => {
-                        let iter = once(Addr::Char(c));
-                        let list_of_chars = Addr::HeapCell(self.heap.to_list(iter));
+                read_heap_cell!(a1,
+                    (HeapCellValueTag::Char) => {
+                        let h = self.heap.len();
 
-                        let a2 = self[temp_v!(2)];
-                        (self.unify_fn)(self, a2, list_of_chars);
-                    }
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(name, _) = self.heap.clone(h) {
-                            let s = self.heap.put_complete_string(name.as_str());
-                            let a2 = self[temp_v!(2)];
+                        self.heap.push(a1);
+                        self.heap.push(empty_list_as_cell!());
 
-                            (self.unify_fn)(self, s, a2);
+                        unify!(self, self.registers[2], list_loc_as_cell!(h));
+                    }
+                    (HeapCellValueTag::Atom, (name, arity)) => {
+                        if arity == 0 {
+                            self.unify_complete_string(
+                                name,
+                                self.store(self.deref(self.registers[2])),
+                            );
                         } else {
-                            unreachable!()
+                            self.fail = true;
                         }
                     }
-                    Addr::EmptyList => {
-                        let a2 = self[temp_v!(2)];
-                        let chars = vec![Addr::Char('['), Addr::Char(']')];
-
-                        let list_of_chars = Addr::HeapCell(self.heap.to_list(chars.into_iter()));
-
-                        (self.unify_fn)(self, a2, list_of_chars);
-                    }
-                    addr if addr.is_ref() => {
-                        let mut iter = self.heap_pstr_iter(self[temp_v!(2)]);
-                        let string = iter.to_string();
-
-                        match iter.focus() {
-                            Addr::EmptyList => {
-                                if &string == "[]" {
-                                    (self.unify_fn)(self, addr, Addr::EmptyList);
-                                } else {
-                                    let chars = clause_name!(string, self.atom_tbl);
-                                    let atom =
-                                        self.heap.to_unifiable(HeapCellValue::Atom(chars, None));
+                    (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
+                        let a2 = self.store(self.deref(self.registers[2]));
 
-                                    (self.unify_fn)(self, addr, atom);
+                        if let Some(str_like) = self.value_to_str_like(a2) {
+                            let atom = match str_like {
+                                AtomOrString::Atom(atom) => {
+                                    atom
                                 }
-                            }
-                            focus => {
-                                if let Addr::Lis(l) = focus {
-                                    let stub =
-                                        MachineError::functor_stub(clause_name!("atom_chars"), 2);
-
-                                    let err = MachineError::type_error(
-                                        self.heap.h(),
-                                        ValidType::Character,
-                                        Addr::HeapCell(l),
-                                    );
-
-                                    return Err(self.error_form(err, stub));
-                                } else {
-                                    unreachable!()
+                                AtomOrString::String(string) => {
+                                    self.atom_tbl.build_with(&string)
                                 }
-                            }
+                            };
+
+                            self.bind(a1.as_var().unwrap(), atom_as_cell!(atom));
+                            return return_from_clause!(self.last_call, self);
                         }
+
+                        self.fail = true;
                     }
-                    _ => unreachable!(),
-                };
+                    _ => {
+                        unreachable!();
+                    }
+                );
             }
             &SystemClauseType::AtomCodes => {
-                let a1 = self[temp_v!(1)];
-
-                match self.store(self.deref(a1)) {
-                    Addr::Char(c) => {
-                        let iter = once(Addr::Fixnum(c as isize));
-                        let list_of_codes = Addr::HeapCell(self.heap.to_list(iter));
+                let a1 = self.store(self.deref(self.registers[1]));
 
-                        let a2 = self[temp_v!(2)];
-                        (self.unify_fn)(self, a2, list_of_codes);
-                    }
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(name, _) = self.heap.clone(h) {
-                            let a2 = self.store(self.deref(self[temp_v!(2)]));
+                read_heap_cell!(a1,
+                    (HeapCellValueTag::Char, c) => {
+                        let h = self.heap.len();
 
-                            let iter = name.as_str().chars().map(|c| Addr::Fixnum(c as isize));
+                        self.heap.push(fixnum_as_cell!(Fixnum::build_with(c as i64)));
+                        self.heap.push(empty_list_as_cell!());
 
-                            let list_of_codes = Addr::HeapCell(self.heap.to_list(iter));
+                        unify!(self, list_loc_as_cell!(h), self.registers[2]);
+                    }
+                    (HeapCellValueTag::Atom, (name, arity)) => {
+                        if arity == 0 {
+                            let iter = name.chars()
+                                .map(|c| fixnum_as_cell!(Fixnum::build_with(c as i64)));
 
-                            (self.unify_fn)(self, a2, list_of_codes);
+                            let h = iter_to_heap_list(&mut self.heap, iter);
+                            unify!(self, heap_loc_as_cell!(h), self.registers[2]);
                         } else {
-                            unreachable!()
+                            self.fail = true;
                         }
                     }
-                    Addr::EmptyList => {
-                        let chars = vec![Addr::Fixnum('[' as isize), Addr::Fixnum(']' as isize)];
-
-                        let list_of_codes = Addr::HeapCell(self.heap.to_list(chars.into_iter()));
-                        let a2 = self[temp_v!(2)];
+                    (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
+                        let stub_gen = || functor_stub(atom!("atom_codes"), 2);
 
-                        (self.unify_fn)(self, a2, list_of_codes);
-                    }
-                    addr if addr.is_ref() => {
-                        let stub = MachineError::functor_stub(clause_name!("atom_codes"), 2);
-
-                        match self.try_from_list(temp_v!(2), stub) {
-                            Err(e) => return Err(e),
+                        match self.try_from_list(self.registers[2], stub_gen) {
                             Ok(addrs) => {
-                                let mut chars = String::new();
-
-                                for addr in addrs {
-                                    let addr = self.store(self.deref(addr));
+                                let string = self.codes_to_string(addrs.into_iter(), stub_gen)?;
+                                let atom = self.atom_tbl.build_with(&string);
 
-                                    match Number::try_from((addr, &self.heap)) {
-                                        Ok(Number::Fixnum(n)) => {
-                                            let c = self.int_to_char(
-                                                &Integer::from(n),
-                                                "atom_codes",
-                                                2,
-                                            )?;
-
-                                            chars.push(c);
-                                            continue;
-                                        }
-                                        Ok(Number::Integer(n)) => {
-                                            let c = self.int_to_char(&n, "atom_codes", 2)?;
-                                            chars.push(c);
-
-                                            continue;
-                                        }
-                                        _ => {
-                                            let stub = MachineError::functor_stub(
-                                                clause_name!("atom_codes"),
-                                                2,
-                                            );
-
-                                            let err = MachineError::type_error(
-                                                self.heap.h(),
-                                                ValidType::Integer,
-                                                addr,
-                                            );
-
-                                            return Err(self.error_form(err, stub));
-                                        }
-                                    }
-                                }
-
-                                let string = self.heap.to_unifiable(HeapCellValue::Atom(
-                                    clause_name!(chars, self.atom_tbl),
-                                    None,
-                                ));
-
-                                self.bind(addr.as_var().unwrap(), string);
+                                self.bind(a1.as_var().unwrap(), atom_as_cell!(atom));
+                            }
+                            Err(e) => {
+                                return Err(e);
                             }
                         }
                     }
                     _ => {
-                        unreachable!()
+                        unreachable!();
                     }
-                };
+                );
             }
             &SystemClauseType::AtomLength => {
-                let a1 = self.store(self.deref(self[temp_v!(1)]));
+                let a1 = self.store(self.deref(self.registers[1]));
 
-                let atom = match self.store(self.deref(a1)) {
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
-                            name.clone()
+                let len: i64 = read_heap_cell!(a1,
+                    (HeapCellValueTag::Atom, (name, arity)) => {
+                        if arity == 0 {
+                            name.chars().count() as i64
                         } else {
-                            unreachable!()
+                            self.fail = true;
+                            return Ok(());
                         }
                     }
-                    Addr::EmptyList => {
-                        clause_name!("[]")
-                    }
-                    Addr::Char(c) => {
-                        clause_name!(c.to_string(), self.atom_tbl)
+                    (HeapCellValueTag::Char) => {
+                        1
                     }
                     _ => {
                         unreachable!()
                     }
-                };
-
-                let len = Integer::from(atom.as_str().chars().count());
-                let len = self.heap.to_unifiable(HeapCellValue::Integer(Rc::new(len)));
-
-                let a2 = self[temp_v!(2)];
+                );
 
-                (self.unify_fn)(self, a2, len);
+                self.unify_fixnum(
+                    Fixnum::build_with(len),
+                    self.store(self.deref(self.registers[2])),
+                );
             }
             &SystemClauseType::CallContinuation => {
-                let stub = MachineError::functor_stub(clause_name!("call_continuation"), 1);
+                let stub_gen = || functor_stub(atom!("call_continuation"), 1);
+                let a1 = self.store(self.deref(self.registers[1]));
 
-                match self.try_from_list(temp_v!(1), stub) {
+                match self.try_from_list(a1, stub_gen) {
                     Err(e) => return Err(e),
                     Ok(cont_chunks) => {
                         let mut return_p = if self.last_call {
@@ -1225,125 +1279,97 @@ impl MachineState {
                 return Ok(());
             }
             &SystemClauseType::CharsToNumber => {
-                let stub = MachineError::functor_stub(clause_name!("number_chars"), 2);
+                let stub_gen = || functor_stub(atom!("number_chars"), 2);
+                let a1 = self.store(self.deref(self.registers[1]));
 
-                match self.try_from_list(temp_v!(1), stub) {
-                    Err(e) => {
-                        return Err(e);
-                    }
-                    Ok(addrs) => match self.try_char_list(addrs) {
-                        Ok(string) => {
-                            let stub = MachineError::functor_stub(clause_name!("number_chars"), 2);
-                            self.parse_number_from_string(string, indices, stub)?;
-                        }
-                        Err(err) => {
-                            let stub = MachineError::functor_stub(clause_name!("number_chars"), 2);
-
-                            return Err(self.error_form(err, stub));
-                        }
-                    },
+                if let Some(atom_or_string) = self.value_to_str_like(a1) {
+                    self.parse_number_from_string(atom_or_string.to_string(), indices, stub_gen)?;
+                } else {
+                    // a1 is a ground list at the call site within
+                    // number_chars/2, so failure of value_to_str_like
+                    // means the list contains a non-character.
+                    let err = self.type_error(ValidType::Character, a1);
+                    return Err(self.error_form(err, stub_gen()));
                 }
             }
             &SystemClauseType::CreatePartialString => {
-                let atom = match self.store(self.deref(self[temp_v!(1)])) {
-                    Addr::Con(h) => {
-                        if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
-                            name.clone()
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    _ => {
-                        unreachable!()
-                    }
-                };
+                let atom = cell_as_atom!(self.store(self.deref(self.registers[1])));
 
-                if atom.as_str().is_empty() {
+                if atom == atom!("") {
                     self.fail = true;
                     return Ok(());
                 }
 
-                let pstr = self.heap.allocate_pstr(atom.as_str());
-                (self.unify_fn)(self, self[temp_v!(2)], pstr);
+                let pstr_h = self.heap.len();
 
-                if !self.fail {
-                    let h = self.heap.h();
-                    let pstr_tail = self.heap[h - 1].as_addr(h - 1);
+                self.heap.push(pstr_as_cell!(atom));
+                self.heap.push(heap_loc_as_cell!(pstr_h+1));
 
-                    (self.unify_fn)(self, self[temp_v!(3)], pstr_tail);
+                unify!(self, self.registers[2], pstr_loc_as_cell!(pstr_h));
+
+                if !self.fail {
+                    self.bind(Ref::heap_cell(pstr_h+1), self.registers[3]);
                 }
             }
             &SystemClauseType::IsPartialString => {
-                let addr = self.store(self.deref(self[temp_v!(1)]));
+                let value = self.store(self.deref(self.registers[1]));
 
-                match addr {
-                    Addr::EmptyList => {
-                        return return_from_clause!(self.last_call, self);
-                    }
-                    Addr::AttrVar(_) | Addr::HeapCell(_) | Addr::StackCell(..) => {
-                        self.fail = true;
-                        return Ok(());
-                    }
-                    _ => {}
-                }
+                let h = self.heap.len();
+                self.heap.push(value);
 
-                let mut heap_pstr_iter = self.heap_pstr_iter(addr);
+                let mut iter = HeapPStrIter::new(&self.heap, h);
 
-                while let Some(_) = heap_pstr_iter.next() {}
+                while let Some(_) = iter.next() {}
 
-                self.fail = match heap_pstr_iter.focus() {
-                    Addr::AttrVar(_)
-                    | Addr::HeapCell(_)
-                    | Addr::StackCell(..)
-                    | Addr::EmptyList => false,
-                    _ => true,
-                };
+                let at_end_of_pstr = iter.focus.is_var() || iter.at_string_terminator();
+                self.fail = !at_end_of_pstr;
+
+                self.heap.pop();
             }
             &SystemClauseType::PartialStringTail => {
-                let pstr = self.store(self.deref(self[temp_v!(1)]));
+                let pstr = self.store(self.deref(self.registers[1]));
 
-                match pstr {
-                    Addr::PStrLocation(h, _) => {
-                        if let HeapCellValue::PartialString(_, true) = &self.heap[h] {
-                            let tail = self.heap[h + 1].as_addr(h + 1);
-                            let target = self[temp_v!(2)];
+                read_heap_cell!(pstr,
+                    (HeapCellValueTag::PStrLoc, h) => {
+                        let (h, _) = pstr_loc_and_offset(&self.heap, h);
 
-                            (self.unify_fn)(self, tail, target);
+                        if HeapCellValueTag::CStr == self.heap[h].get_tag() {
+                            self.unify_atom(atom!("[]"), self.store(self.deref(self.registers[2])));
                         } else {
-                            self.fail = true;
-                            return Ok(());
+                            unify_fn!(self, heap_loc_as_cell!(h+1), self.registers[2]);
                         }
                     }
-                    Addr::Lis(h) => {
-                        (self.unify_fn)(self, Addr::HeapCell(h + 1), self[temp_v!(2)]);
+                    (HeapCellValueTag::CStr) => {
+                        self.unify_atom(atom!("[]"), self.store(self.deref(self.registers[2])));
                     }
-                    Addr::EmptyList => {
-                        self.fail = true;
-                        return Ok(());
+                    (HeapCellValueTag::Lis, h) => {
+                        unify_fn!(self, heap_loc_as_cell!(h+1), self.registers[2]);
                     }
                     _ => {
-                        unreachable!()
+                        self.fail = true;
                     }
-                }
+                );
             }
             &SystemClauseType::PeekByte => {
+                let stub_gen = || functor_stub(atom!("peek_byte"), 2);
+
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "peek_byte",
+                    atom!("peek_byte"),
                     2,
                 )?;
 
                 self.check_stream_properties(
-                    &mut stream,
+                    stream,
                     StreamType::Binary,
-                    Some(self[temp_v!(2)]),
-                    clause_name!("peek_byte"),
+                    Some(self.registers[2]),
+                    atom!("peek_byte"),
                     2,
                 )?;
 
                 if stream.past_end_of_stream() {
-                    if EOFAction::Reset != stream.options().eof_action {
+                    if EOFAction::Reset != stream.options().eof_action() {
                         return return_from_clause!(self.last_call, self);
                     } else if self.fail {
                         return Ok(());
@@ -1351,45 +1377,38 @@ impl MachineState {
                 }
 
                 if stream.at_end_of_stream() {
-                    stream.set_past_end_of_stream();
-                    (self.unify_fn)(self, self[temp_v!(2)], Addr::Fixnum(-1));
+                    stream.set_past_end_of_stream(true);
+
+                    self.unify_fixnum(
+                        Fixnum::build_with(-1),
+                        self.store(self.deref(self.registers[2])),
+                    );
+
                     return return_from_clause!(self.last_call, self);
                 }
 
-                let addr = match self.store(self.deref(self[temp_v!(2)])) {
-                    addr if addr.is_ref() => addr,
-                    addr => match Number::try_from((addr, &self.heap)) {
+                let addr = match self.store(self.deref(self.registers[2])) {
+                    addr if addr.is_var() => addr,
+                    addr => match Number::try_from(addr) {
                         Ok(Number::Integer(n)) => {
                             if let Some(nb) = n.to_u8() {
-                                Addr::Usize(nb as usize)
+                                fixnum_as_cell!(Fixnum::build_with(nb as i64))
                             } else {
-                                return Err(self.type_error(
-                                    ValidType::InByte,
-                                    addr,
-                                    clause_name!("peek_byte"),
-                                    2,
-                                ));
+                                let err = self.type_error(ValidType::InByte, addr);
+                                return Err(self.error_form(err, stub_gen()));
                             }
                         }
                         Ok(Number::Fixnum(n)) => {
-                            if let Ok(nb) = u8::try_from(n) {
-                                Addr::Usize(nb as usize)
+                            if let Ok(nb) = u8::try_from(n.get_num()) {
+                                fixnum_as_cell!(Fixnum::build_with(nb as i64))
                             } else {
-                                return Err(self.type_error(
-                                    ValidType::InByte,
-                                    addr,
-                                    clause_name!("peek_byte"),
-                                    2,
-                                ));
+                                let err = self.type_error(ValidType::InByte, addr);
+                                return Err(self.error_form(err, stub_gen()));
                             }
                         }
                         _ => {
-                            return Err(self.type_error(
-                                ValidType::InByte,
-                                addr,
-                                clause_name!("peek_byte"),
-                                2,
-                            ));
+                            let err = self.type_error(ValidType::InByte, addr);
+                            return Err(self.error_form(err, stub_gen()));
                         }
                     },
                 };
@@ -1397,15 +1416,7 @@ impl MachineState {
                 loop {
                     match stream.peek_byte().map_err(|e| e.kind()) {
                         Ok(b) => {
-                            if let Some(var) = addr.as_var() {
-                                self.bind(var, Addr::Usize(b as usize));
-                                break;
-                            } else if addr == Addr::Usize(b as usize) {
-                                break;
-                            } else {
-                                self.fail = true;
-                                return Ok(());
-                            }
+                            self.unify_fixnum(Fixnum::build_with(b as i64), addr);
                         }
                         Err(ErrorKind::PermissionDenied) => {
                             self.fail = true;
@@ -1413,13 +1424,13 @@ impl MachineState {
                         }
                         _ => {
                             self.eof_action(
-                                self[temp_v!(2)],
-                                &mut stream,
-                                clause_name!("peek_byte"),
+                                self.registers[2],
+                                stream,
+                                atom!("peek_byte"),
                                 2,
                             )?;
 
-                            if EOFAction::Reset != stream.options().eof_action {
+                            if EOFAction::Reset != stream.options().eof_action() {
                                 return return_from_clause!(self.last_call, self);
                             } else if self.fail {
                                 return Ok(());
@@ -1429,23 +1440,25 @@ impl MachineState {
                 }
             }
             &SystemClauseType::PeekChar => {
+                let stub_gen = || functor_stub(atom!("peek_char"), 2);
+
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "peek_char",
+                    atom!("peek_char"),
                     2,
                 )?;
 
                 self.check_stream_properties(
-                    &mut stream,
+                    stream,
                     StreamType::Text,
-                    Some(self[temp_v!(2)]),
-                    clause_name!("peek_char"),
+                    Some(self.registers[2]),
+                    atom!("peek_char"),
                     2,
                 )?;
 
                 if stream.past_end_of_stream() {
-                    if EOFAction::Reset != stream.options().eof_action {
+                    if EOFAction::Reset != stream.options().eof_action() {
                         return return_from_clause!(self.last_call, self);
                     } else if self.fail {
                         return Ok(());
@@ -1453,106 +1466,87 @@ impl MachineState {
                 }
 
                 if stream.at_end_of_stream() {
-                    let end_of_file = clause_name!("end_of_file");
-                    let end_of_file = self
-                        .heap
-                        .to_unifiable(HeapCellValue::Atom(end_of_file, None));
-
-                    stream.set_past_end_of_stream();
+                    let end_of_file = atom!("end_of_file");
+                    stream.set_past_end_of_stream(true);
 
-                    (self.unify_fn)(self, self[temp_v!(2)], end_of_file);
+                    self.unify_atom(end_of_file, self.store(self.deref(self.registers[2])));
                     return return_from_clause!(self.last_call, self);
                 }
 
-                let addr = match self.store(self.deref(self[temp_v!(2)])) {
-                    addr if addr.is_ref() => addr,
-                    Addr::Con(h) if self.heap.atom_at(h) => match &self.heap[h] {
-                        HeapCellValue::Atom(ref atom, _) if atom.is_char() => {
-                            if let Some(c) = atom.as_str().chars().next() {
-                                Addr::Char(c)
+                let a2 = self.store(self.deref(self.registers[2]));
+
+                let a2 = read_heap_cell!(a2,
+                    (HeapCellValueTag::Char) => {
+                        a2
+                    }
+                    (HeapCellValueTag::Atom, (name, arity)) => {
+                        if arity == 0 {
+                            if let Some(c) = name.as_char() {
+                                char_as_cell!(c)
                             } else {
-                                unreachable!()
+                                let err = self.type_error(ValidType::InCharacter, a2);
+                                return Err(self.error_form(err, stub_gen()));
                             }
+                        } else {
+                            let err = self.type_error(ValidType::InCharacter, a2);
+                            return Err(self.error_form(err, stub_gen()));
                         }
-                        culprit => {
-                            return Err(self.type_error(
-                                ValidType::InCharacter,
-                                culprit.as_addr(h),
-                                clause_name!("peek_char"),
-                                2,
-                            ));
-                        }
-                    },
-                    Addr::Char(d) => Addr::Char(d),
-                    culprit => {
-                        return Err(self.type_error(
-                            ValidType::InCharacter,
-                            culprit,
-                            clause_name!("peek_char"),
-                            2,
-                        ));
                     }
-                };
+                    (HeapCellValueTag::Var | HeapCellValueTag::StackVar | HeapCellValueTag::AttrVar) => {
+                        a2
+                    }
+                    _ => {
+                        let err = self.type_error(ValidType::InCharacter, a2);
+                        return Err(self.error_form(err, stub_gen()));
+                    }
+                );
 
                 loop {
-                    match stream.peek_char().map_err(|e| e.kind()) {
-                        Ok(d) => {
-                            if let Some(var) = addr.as_var() {
-                                self.bind(var, Addr::Char(d));
-                                break;
-                            } else if addr == Addr::Char(d) {
-                                break;
-                            } else {
-                                self.fail = true;
-                                return Ok(());
-                            }
+                    match stream.peek_char().map(|result| result.map_err(|e| e.kind())) {
+                        Some(Ok(d)) => {
+                            self.unify_char(d, a2);
                         }
-                        Err(ErrorKind::PermissionDenied) => {
+                        Some(Err(ErrorKind::PermissionDenied)) => {
                             self.fail = true;
                             break;
                         }
                         _ => {
                             self.eof_action(
-                                self[temp_v!(2)],
-                                &mut stream,
-                                clause_name!("peek_char"),
+                                self.registers[2],
+                                stream,
+                                atom!("peek_char"),
                                 2,
                             )?;
 
-                            if EOFAction::Reset != stream.options().eof_action {
+                            if EOFAction::Reset != stream.options().eof_action() {
                                 return return_from_clause!(self.last_call, self);
                             } else if self.fail {
                                 return Ok(());
                             }
-                        } /*
-                          _ => {
-                              let stub = MachineError::functor_stub(clause_name!("peek_char"), 2);
-                              let err = MachineError::representation_error(RepFlag::Character);
-                              let err = self.error_form(err, stub);
-
-                              return Err(err);
-                          }*/
+                        }
                     }
                 }
             }
             &SystemClauseType::PeekCode => {
+                let stub_gen = || functor_stub(atom!("peek_code"), 2);
+
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "peek_code",
+                    atom!("peek_code"),
                     2,
                 )?;
 
                 self.check_stream_properties(
-                    &mut stream,
+                    stream,
                     StreamType::Text,
-                    Some(self[temp_v!(2)]),
-                    clause_name!("peek_code"),
+                    Some(self.registers[2]),
+                    atom!("peek_code"),
                     2,
                 )?;
 
                 if stream.past_end_of_stream() {
-                    if EOFAction::Reset != stream.options().eof_action {
+                    if EOFAction::Reset != stream.options().eof_action() {
                         return return_from_clause!(self.last_call, self);
                     } else if self.fail {
                         return Ok(());
@@ -1560,89 +1554,73 @@ impl MachineState {
                 }
 
                 if stream.at_end_of_stream() {
-                    let end_of_file = clause_name!("end_of_file");
-                    let end_of_file = self
-                        .heap
-                        .to_unifiable(HeapCellValue::Atom(end_of_file, None));
-
-                    stream.set_past_end_of_stream();
+                    let end_of_file = atom!("end_of_file");
+                    stream.set_past_end_of_stream(true);
 
-                    (self.unify_fn)(self, self[temp_v!(2)], end_of_file);
+                    self.unify_atom(end_of_file, self.store(self.deref(self.registers[2])));
                     return return_from_clause!(self.last_call, self);
                 }
 
-                let addr = match self.store(self.deref(self[temp_v!(2)])) {
-                    addr if addr.is_ref() => addr,
-                    addr => match Number::try_from((addr, &self.heap)) {
-                        Ok(Number::Integer(n)) => {
-                            let n = n
-                                .to_u32()
-                                .and_then(|n| std::char::from_u32(n).and_then(|_| Some(n)));
+                let a2 = self.store(self.deref(self.registers[2]));
 
-                            if let Some(n) = n {
-                                Addr::Fixnum(n as isize)
-                            } else {
-                                return Err(self.representation_error(
-                                    RepFlag::InCharacterCode,
-                                    clause_name!("peek_code"),
-                                    2,
-                                ));
+                let addr = read_heap_cell!(a2,
+                    (HeapCellValueTag::Var | HeapCellValueTag::StackVar | HeapCellValueTag::AttrVar) => {
+                        a2
+                    }
+                    _ => {
+                        match Number::try_from(a2) {
+                            Ok(Number::Integer(n)) => {
+                                let n = n
+                                    .to_u32()
+                                    .and_then(|n| std::char::from_u32(n).and_then(|_| Some(n)));
+
+                                if let Some(n) = n {
+                                    fixnum_as_cell!(Fixnum::build_with(n as i64))
+                                } else {
+                                    let err = self.representation_error(RepFlag::InCharacterCode);
+                                    return Err(self.error_form(err, stub_gen()));
+                                }
                             }
-                        }
-                        Ok(Number::Fixnum(n)) => {
-                            let n = u32::try_from(n)
-                                .ok()
-                                .and_then(|n| std::char::from_u32(n).and_then(|_| Some(n)));
+                            Ok(Number::Fixnum(n)) => {
+                                let n = u32::try_from(n.get_num())
+                                    .ok()
+                                    .and_then(|n| std::char::from_u32(n).and_then(|_| Some(n)));
 
-                            if let Some(n) = n {
-                                Addr::Fixnum(n as isize)
-                            } else {
-                                return Err(self.representation_error(
-                                    RepFlag::InCharacterCode,
-                                    clause_name!("peek_code"),
-                                    2,
-                                ));
+                                if let Some(n) = n {
+                                    fixnum_as_cell!(Fixnum::build_with(n as i64))
+                                } else {
+                                    let err = self.representation_error(RepFlag::InCharacterCode);
+                                    return Err(self.error_form(err, stub_gen()));
+                                }
+                            }
+                            _ => {
+                                let err = self.type_error(ValidType::Integer, self.registers[2]);
+                                return Err(self.error_form(err, stub_gen()));
                             }
                         }
-                        _ => {
-                            return Err(self.type_error(
-                                ValidType::Integer,
-                                self[temp_v!(2)],
-                                clause_name!("peek_code"),
-                                2,
-                            ));
-                        }
-                    },
-                };
+                    }
+                );
 
                 loop {
                     let result = stream.peek_char();
 
-                    match result.map_err(|e| e.kind()) {
-                        Ok(c) => {
-                            if let Some(var) = addr.as_var() {
-                                self.bind(var, Addr::Fixnum(c as isize));
-                                break;
-                            } else if addr == Addr::Fixnum(c as isize) {
-                                break;
-                            } else {
-                                self.fail = true;
-                                return Ok(());
-                            }
+                    match result.map(|result| result.map_err(|e| e.kind())) {
+                        Some(Ok(c)) => {
+                            self.unify_fixnum(Fixnum::build_with(c as i64), addr);
                         }
-                        Err(ErrorKind::PermissionDenied) => {
+                        Some(Err(ErrorKind::PermissionDenied)) => {
                             self.fail = true;
                             break;
                         }
                         _ => {
                             self.eof_action(
-                                self[temp_v!(2)],
-                                &mut stream,
-                                clause_name!("peek_code"),
+                                self.registers[2],
+                                stream,
+                                atom!("peek_code"),
                                 2,
                             )?;
 
-                            if EOFAction::Reset != stream.options().eof_action {
+                            if EOFAction::Reset != stream.options().eof_action() {
                                 return return_from_clause!(self.last_call, self);
                             } else if self.fail {
                                 return Ok(());
@@ -1652,16 +1630,16 @@ impl MachineState {
                 }
             }
             &SystemClauseType::NumberToChars => {
-                let n = self[temp_v!(1)];
-                let chs = self[temp_v!(2)];
+                let n = self.registers[1];
+                let chs = self.registers[2];
 
                 let n = self.store(self.deref(n));
 
-                let string = match Number::try_from((n, &self.heap)) {
+                let string = match Number::try_from(n) {
                     Ok(Number::Float(OrderedFloat(n))) => {
                         format!("{0:<20?}", n)
                     }
-                    Ok(Number::Fixnum(n)) => n.to_string(),
+                    Ok(Number::Fixnum(n)) => n.get_num().to_string(),
                     Ok(Number::Integer(n)) => n.to_string(),
                     Ok(Number::Rational(r)) => {
                         // n has already been confirmed as an integer, and
@@ -1674,20 +1652,18 @@ impl MachineState {
                     }
                 };
 
-                let chars = string.trim().chars().map(|c| Addr::Char(c));
-                let char_list = Addr::HeapCell(self.heap.to_list(chars));
-
-                (self.unify_fn)(self, char_list, chs);
+                let chars_atom = self.atom_tbl.build_with(&string.trim());
+                self.unify_complete_string(chars_atom, self.store(self.deref(chs)));
             }
             &SystemClauseType::NumberToCodes => {
-                let n = self[temp_v!(1)];
-                let chs = self[temp_v!(2)];
+                let n = self.store(self.deref(self.registers[1]));
+                let chs = self.registers[2];
 
-                let string = match Number::try_from((n, &self.heap)) {
+                let string = match Number::try_from(n) {
                     Ok(Number::Float(OrderedFloat(n))) => {
                         format!("{0:<20?}", n)
                     }
-                    Ok(Number::Fixnum(n)) => n.to_string(),
+                    Ok(Number::Fixnum(n)) => n.get_num().to_string(),
                     Ok(Number::Integer(n)) => n.to_string(),
                     Ok(Number::Rational(r)) => {
                         // n has already been confirmed as an integer, and
@@ -1700,219 +1676,199 @@ impl MachineState {
                     }
                 };
 
-                let codes = string.trim().chars().map(|c| Addr::Fixnum(c as isize));
-
-                let codes_list = Addr::HeapCell(self.heap.to_list(codes));
+                let codes = string.trim().chars().map(|c| {
+                    fixnum_as_cell!(Fixnum::build_with(c as i64))
+                });
 
-                (self.unify_fn)(self, codes_list, chs);
+                let h = iter_to_heap_list(&mut self.heap, codes);
+                unify!(self, heap_loc_as_cell!(h), chs);
             }
             &SystemClauseType::CodesToNumber => {
-                let stub = MachineError::functor_stub(clause_name!("number_codes"), 2);
+                let stub_gen = || functor_stub(atom!("number_codes"), 2);
 
-                match self.try_from_list(temp_v!(1), stub) {
+                match self.try_from_list(self.registers[1], stub_gen) {
                     Err(e) => {
                         return Err(e);
                     }
-                    Ok(addrs) => match self.try_char_list(addrs) {
-                        Ok(chars) => {
-                            let stub = MachineError::functor_stub(clause_name!("number_codes"), 2);
-                            self.parse_number_from_string(chars, indices, stub)?;
-                        }
-                        Err(err) => {
-                            let stub = MachineError::functor_stub(clause_name!("number_codes"), 2);
-
-                            return Err(self.error_form(err, stub));
-                        }
-                    },
+                    Ok(addrs) => {
+                        let string = self.codes_to_string(addrs.into_iter(), stub_gen)?;
+                        self.parse_number_from_string(string, indices, stub_gen)?;
+                    }
                 }
             }
             &SystemClauseType::LiftedHeapLength => {
-                let a1 = self[temp_v!(1)];
-                let lh_len = Addr::Usize(self.lifted_heap.h());
+                let a1 = self.registers[1];
+                let lh_len = Fixnum::build_with(self.lifted_heap.len() as i64);
 
-                (self.unify_fn)(self, a1, lh_len);
+                self.unify_fixnum(lh_len, a1);
             }
             &SystemClauseType::CharCode => {
-                let a1 = self[temp_v!(1)];
-
-                match self.store(self.deref(a1)) {
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        let c = if let HeapCellValue::Atom(name, _) = &self.heap[h] {
-                            if name.is_char() {
-                                name.as_str().chars().next().unwrap()
-                            } else {
-                                self.fail = true;
-                                return Ok(());
-                            }
-                        } else {
-                            unreachable!()
-                        };
+                let stub_gen = || functor_stub(atom!("char_code"), 2);
+                let a1 = self.store(self.deref(self.registers[1]));
 
-                        let a2 = self[temp_v!(2)];
-                        (self.unify_fn)(self, Addr::Fixnum(c as isize), a2);
+                let c = read_heap_cell!(a1,
+                    (HeapCellValueTag::Atom, (name, _arity)) => {
+                        name.as_char().unwrap()
                     }
-                    Addr::Char(c) => {
-                        let a2 = self[temp_v!(2)];
-                        (self.unify_fn)(self, Addr::Fixnum(c as isize), a2);
+                    (HeapCellValueTag::Char, c) => {
+                        c
                     }
-                    addr if addr.is_ref() => {
-                        let a2 = self[temp_v!(2)];
-                        let a2 = self.store(self.deref(a2));
+                    _ => {
+                        let a2 = self.store(self.deref(self.registers[2]));
+
+                        match Number::try_from(a2) {
+                            Ok(Number::Integer(n)) => {
+                                let c = match n.to_u32().and_then(std::char::from_u32) {
+                                    Some(c) => c,
+                                    _ => {
+                                        let err = self.representation_error(RepFlag::CharacterCode);
+                                        return Err(self.error_form(err, stub_gen()));
+                                    }
+                                };
 
-                        let c = match Number::try_from((a2, &self.heap)) {
-                            Ok(Number::Integer(n)) => self.int_to_char(&n, "char_code", 2)?,
+                                self.unify_char(c, a2);
+                                return return_from_clause!(self.last_call, self);
+                            }
                             Ok(Number::Fixnum(n)) => {
-                                self.int_to_char(&Integer::from(n), "char_code", 2)?
+                                match u32::try_from(n.get_num()) {
+                                    Ok(n) => {
+                                        if let Some(c) = std::char::from_u32(n) {
+                                            self.unify_char(c, a1);
+                                            return return_from_clause!(self.last_call, self);
+                                        }
+                                    }
+                                    _ => {}
+                                }
+
+                                let err = self.representation_error(RepFlag::CharacterCode);
+                                return Err(self.error_form(err, stub_gen()));
                             }
                             _ => {
                                 self.fail = true;
                                 return Ok(());
                             }
-                        };
-
-                        (self.unify_fn)(self, Addr::Char(c), addr);
-                    }
-                    _ => {
-                        unreachable!();
+                        }
                     }
-                };
+                );
+
+                self.unify_fixnum(
+                    Fixnum::build_with(c as i64),
+                    self.store(self.deref(self.registers[2])),
+                );
             }
             &SystemClauseType::CharType => {
-                let a1 = self.store(self.deref(self[temp_v!(1)]));
-                let a2 = self.store(self.deref(self[temp_v!(2)]));
-
-                let c = match a1 {
-                    Addr::Char(c) => c,
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(name, _) = &self.heap[h] {
-                            name.as_str().chars().next().unwrap()
-                        } else {
-                            unreachable!()
-                        }
+                let a1 = self.store(self.deref(self.registers[1]));
+                let a2 = self.store(self.deref(self.registers[2]));
+
+                let c = read_heap_cell!(a1,
+                    (HeapCellValueTag::Char, c) => {
+                        c
                     }
-                    _ => unreachable!(),
-                };
-                let chars = match a2 {
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(name, _) = &self.heap[h] {
-                            name.as_str().to_string()
-                        } else {
-                            unreachable!()
-                        }
+                    (HeapCellValueTag::Atom, (name, _arity)) => {
+                        name.as_char().unwrap()
                     }
-                    Addr::Char(c) => c.to_string(),
-                    _ => unreachable!(),
-                };
+                    _ => {
+                        unreachable!()
+                    }
+                );
+
+                let chars = cell_as_atom!(a2);
                 self.fail = true; // This predicate fails by default.
+
                 macro_rules! macro_check {
-                    ($id:ident, $name:tt) => {
+                    ($id:ident, $name:expr) => {
                         if $id!(c) && chars == $name {
                             self.fail = false;
-
                             return return_from_clause!(self.last_call, self);
                         }
                     };
                 }
+
                 macro_rules! method_check {
-                    ($id:ident, $name:tt) => {
+                    ($id:ident, $name:expr) => {
                         if c.$id() && chars == $name {
                             self.fail = false;
-
                             return return_from_clause!(self.last_call, self);
                         }
                     };
                 }
-                macro_check!(alpha_char, "alpha");
-                method_check!(is_alphabetic, "alphabetic");
-                method_check!(is_alphanumeric, "alphanumeric");
-                macro_check!(alpha_numeric_char, "alnum");
-                method_check!(is_ascii, "ascii");
-                method_check!(is_ascii_punctuation, "ascii_ponctuaction");
-                method_check!(is_ascii_graphic, "ascii_graphic");
-                // macro_check!(backslash_char, "backslash");
-                // macro_check!(back_quote_char, "back_quote");
-                macro_check!(binary_digit_char, "binary_digit");
-                // macro_check!(capital_letter_char, "upper");
+
+                macro_check!(alpha_char, atom!("alpha"));
+                method_check!(is_alphabetic, atom!("alphabetic"));
+                method_check!(is_alphanumeric, atom!("alphanumeric"));
+                macro_check!(alpha_numeric_char, atom!("alnum"));
+                method_check!(is_ascii, atom!("ascii"));
+                method_check!(is_ascii_punctuation, atom!("ascii_ponctuaction"));
+                method_check!(is_ascii_graphic, atom!("ascii_graphic"));
+                // macro_check!(backslash_char, atom!("backslash"));
+                // macro_check!(back_quote_char, atom!("back_quote"));
+                macro_check!(binary_digit_char, atom!("binary_digit"));
+                // macro_check!(capital_letter_char, atom!("upper"));
                 // macro_check!(comment_1_char, "comment_1");
                 // macro_check!(comment_2_char, "comment_2");
-                method_check!(is_control, "control");
-                // macro_check!(cut_char, "cut");
-                macro_check!(decimal_digit_char, "decimal_digit");
-                // macro_check!(decimal_point_char, "decimal_point");
-                // macro_check!(double_quote_char, "double_quote");
-                macro_check!(exponent_char, "exponent");
-                macro_check!(graphic_char, "graphic");
-                macro_check!(graphic_token_char, "graphic_token");
-                macro_check!(hexadecimal_digit_char, "hexadecimal_digit");
-                macro_check!(layout_char, "layout");
-                method_check!(is_lowercase, "lower");
-                macro_check!(meta_char, "meta");
-                // macro_check!(new_line_char, "new_line");
-                method_check!(is_numeric, "numeric");
-                macro_check!(octal_digit_char, "octal_digit");
-                macro_check!(octet_char, "octet");
-                macro_check!(prolog_char, "prolog");
-                // macro_check!(semicolon_char, "semicolon");
-                macro_check!(sign_char, "sign");
-                // macro_check!(single_quote_char, "single_quote");
-                // macro_check!(small_letter_char, "lower");
-                macro_check!(solo_char, "solo");
-                // macro_check!(space_char, "space");
-                macro_check!(symbolic_hexadecimal_char, "symbolic_hexadecimal");
-                macro_check!(symbolic_control_char, "symbolic_control");
-                method_check!(is_uppercase, "upper");
-                // macro_check!(variable_indicator_char, "variable_indicator");
-                method_check!(is_whitespace, "whitespace");
+                method_check!(is_control, atom!("control"));
+                // macro_check!(cut_char, atom!("cut"));
+                macro_check!(decimal_digit_char, atom!("decimal_digit"));
+                // macro_check!(decimal_point_char, atom!("decimal_point"));
+                // macro_check!(double_quote_char, atom!("double_quote"));
+                macro_check!(exponent_char, atom!("exponent"));
+                macro_check!(graphic_char, atom!("graphic"));
+                macro_check!(graphic_token_char, atom!("graphic_token"));
+                macro_check!(hexadecimal_digit_char, atom!("hexadecimal_digit"));
+                macro_check!(layout_char, atom!("layout"));
+                method_check!(is_lowercase, atom!("lower"));
+                macro_check!(meta_char, atom!("meta"));
+                // macro_check!(new_line_char, atom!("new_line"));
+                method_check!(is_numeric, atom!("numeric"));
+                macro_check!(octal_digit_char, atom!("octal_digit"));
+                macro_check!(octet_char, atom!("octet"));
+                macro_check!(prolog_char, atom!("prolog"));
+                // macro_check!(semicolon_char, atom!("semicolon"));
+                macro_check!(sign_char, atom!("sign"));
+                // macro_check!(single_quote_char, atom!("single_quote"));
+                // macro_check!(small_letter_char, atom!("lower"));
+                macro_check!(solo_char, atom!("solo"));
+                // macro_check!(space_char, atom!("space"));
+                macro_check!(symbolic_hexadecimal_char, atom!("symbolic_hexadecimal"));
+                macro_check!(symbolic_control_char, atom!("symbolic_control"));
+                method_check!(is_uppercase, atom!("upper"));
+                // macro_check!(variable_indicator_char, atom!("variable_indicator"));
+                method_check!(is_whitespace, atom!("whitespace"));
             }
             &SystemClauseType::CheckCutPoint => {
-                let addr = self.store(self.deref(self[temp_v!(1)]));
+                let addr = self.store(self.deref(self.registers[1]));
+                let old_b = cell_as_fixnum!(addr).get_num() as usize;
 
-                match addr {
-                    Addr::Usize(old_b) | Addr::CutPoint(old_b) => {
-                        let prev_b = self.stack.index_or_frame(self.b).prelude.b;
-                        let prev_b = self.stack.index_or_frame(prev_b).prelude.b;
+                let prev_b = self.stack.index_or_frame(self.b).prelude.b;
+                let prev_b = self.stack.index_or_frame(prev_b).prelude.b;
 
-                        if prev_b > old_b {
-                            self.fail = true;
-                        }
-                    }
-                    _ => self.fail = true,
-                };
+                if prev_b > old_b {
+                    self.fail = true;
+                }
             }
             &SystemClauseType::CopyTermWithoutAttrVars => {
                 self.copy_term(AttrVarPolicy::StripAttributes);
             }
             &SystemClauseType::FetchGlobalVar => {
-                let (key_h, key) = match self.store(self.deref(self[temp_v!(1)])) {
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
-                            (h, atom.clone())
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    _ => {
-                        unreachable!()
-                    }
-                };
-
-                let addr = self[temp_v!(2)];
+                let key = cell_as_atom!(self.store(self.deref(self.registers[1])));
+                let addr = self.registers[2];
 
                 match indices.global_variables.get_mut(&key) {
                     Some((ref ball, ref mut loc)) => match loc {
-                        Some(ref value_addr) => {
-                            (self.unify_fn)(self, addr, *value_addr);
+                        Some(value_loc) => {
+                            unify_fn!(self, addr, *value_loc);
                         }
-                        loc @ None if !ball.stub.is_empty() => {
-                            let h = self.heap.h();
+                        None if !ball.stub.is_empty() => {
+                            let h = self.heap.len();
                             let stub = ball.copy_and_align(h);
 
                             self.heap.extend(stub.into_iter());
-                            (self.unify_fn)(self, addr, Addr::HeapCell(h));
+
+                            unify_fn!(self, addr, heap_loc_as_cell!(h));
 
                             if !self.fail {
-                                *loc = Some(Addr::HeapCell(h));
-                                self.trail(TrailRef::BlackboardEntry(key_h));
+                                *loc = Some(heap_loc_as_cell!(h));
+                                self.trail(TrailRef::BlackboardEntry(key));
                             }
                         }
                         _ => self.fail = true,
@@ -1922,308 +1878,252 @@ impl MachineState {
             }
             &SystemClauseType::PutCode => {
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "put_code",
+                    atom!("put_code"),
                     2,
                 )?;
 
                 self.check_stream_properties(
-                    &mut stream,
+                    stream,
                     StreamType::Text,
                     None,
-                    clause_name!("put_code"),
+                    atom!("put_code"),
                     2,
                 )?;
 
-                match self.store(self.deref(self[temp_v!(2)])) {
-                    addr if addr.is_ref() => {
-                        let stub = MachineError::functor_stub(clause_name!("put_code"), 2);
-                        let err = MachineError::instantiation_error();
+                let stub_gen = || functor_stub(atom!("put_code"), 2);
 
-                        return Err(self.error_form(err, stub));
-                    }
-                    addr => {
-                        match Number::try_from((addr, &self.heap)) {
-                            Ok(Number::Integer(n)) => {
-                                if let Some(c) = n.to_u32().and_then(|c| char::try_from(c).ok()) {
-                                    write!(&mut stream, "{}", c).unwrap();
-                                    return return_from_clause!(self.last_call, self);
-                                }
-                            }
-                            Ok(Number::Fixnum(n)) => {
-                                if let Some(c) =
-                                    u32::try_from(n).ok().and_then(|c| char::try_from(c).ok())
-                                {
-                                    write!(&mut stream, "{}", c).unwrap();
-                                    return return_from_clause!(self.last_call, self);
-                                }
-                            }
-                            _ => {
-                                let stub = MachineError::functor_stub(clause_name!("put_code"), 2);
-                                let err = MachineError::type_error(
-                                    self.heap.h(),
-                                    ValidType::Integer,
-                                    self[temp_v!(2)],
-                                );
-
-                                return Err(self.error_form(err, stub));
+                let addr = self.store(self.deref(self.registers[2]));
+
+                if addr.is_var() {
+                    let err = self.instantiation_error();
+                    return Err(self.error_form(err, stub_gen()));
+                } else {
+                    match Number::try_from(addr) {
+                        Ok(Number::Integer(n)) => {
+                            if let Some(c) = n.to_u32().and_then(|c| char::try_from(c).ok()) {
+                                write!(&mut stream, "{}", c).unwrap();
+                                return return_from_clause!(self.last_call, self);
                             }
                         }
+                        Ok(Number::Fixnum(n)) => {
+                            let n = n.get_num();
 
-                        let stub = MachineError::functor_stub(clause_name!("put_code"), 2);
-                        let err = MachineError::representation_error(RepFlag::CharacterCode);
-
-                        return Err(self.error_form(err, stub));
+                            if let Some(c) = u32::try_from(n).ok().and_then(|c| char::from_u32(c)) {
+                                write!(&mut stream, "{}", c).unwrap();
+                                return return_from_clause!(self.last_call, self);
+                            }
+                        }
+                        _ => {
+                            let err = self.type_error(ValidType::Integer, addr);
+                            return Err(self.error_form(err, stub_gen()));
+                        }
                     }
+
+                    let err = self.representation_error(RepFlag::CharacterCode);
+                    return Err(self.error_form(err, stub_gen()));
                 }
             }
             &SystemClauseType::PutChar => {
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "put_char",
+                    atom!("put_char"),
                     2,
                 )?;
 
                 self.check_stream_properties(
-                    &mut stream,
+                    stream,
                     StreamType::Text,
                     None,
-                    clause_name!("put_char"),
+                    atom!("put_char"),
                     2,
                 )?;
 
-                match self.store(self.deref(self[temp_v!(2)])) {
-                    addr if addr.is_ref() => {
-                        let stub = MachineError::functor_stub(clause_name!("put_char"), 2);
-                        let err = MachineError::instantiation_error();
+                let stub_gen = || functor_stub(atom!("put_char"), 2);
+                let addr = self.store(self.deref(self.registers[2]));
 
-                        return Err(self.error_form(err, stub));
-                    }
-                    addr => {
-                        match self.store(self.deref(self[temp_v!(2)])) {
-                            Addr::Con(h) if self.heap.atom_at(h) => match &self.heap[h] {
-                                HeapCellValue::Atom(ref atom, _) if atom.is_char() => {
-                                    if let Some(c) = atom.as_str().chars().next() {
-                                        write!(&mut stream, "{}", c).unwrap();
-                                        return return_from_clause!(self.last_call, self);
-                                    } else {
-                                        unreachable!()
-                                    }
-                                }
-                                _ => {}
-                            },
-                            Addr::Char(c) => {
-                                write!(&mut stream, "{}", c).unwrap();
-                                return return_from_clause!(self.last_call, self);
-                            }
-                            _ => {}
+                if addr.is_var() {
+                    let err = self.instantiation_error();
+                    return Err(self.error_form(err, stub_gen()));
+                } else {
+                    read_heap_cell!(addr,
+                        (HeapCellValueTag::Atom, (name, _arity)) => {
+                            let c = name.as_char().unwrap();
+                            write!(&mut stream, "{}", c).unwrap();
+                            return return_from_clause!(self.last_call, self);
                         }
+                        (HeapCellValueTag::Char, c) => {
+                            write!(&mut stream, "{}", c).unwrap();
+                            return return_from_clause!(self.last_call, self);
+                        }
+                        _ => {
+                        }
+                    );
 
-                        let stub = MachineError::functor_stub(clause_name!("put_char"), 2);
-                        let err =
-                            MachineError::type_error(self.heap.h(), ValidType::Character, addr);
-
-                        return Err(self.error_form(err, stub));
-                    }
+                    let err = self.type_error(ValidType::Character, addr);
+                    return Err(self.error_form(err, stub_gen()));
                 }
             }
             &SystemClauseType::PutChars => {
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "$put_chars",
+                    atom!("$put_chars"),
                     2,
                 )?;
 
                 let mut bytes = Vec::new();
-                let string = self.heap_pstr_iter(self[temp_v!(2)]).to_string();
+                let stub_gen = || functor_stub(atom!("$put_chars"), 2);
+
+                if let Some(string) = self.value_to_str_like(self.registers[2]) {
+                    if stream.options().stream_type() == StreamType::Binary {
+                        for c in string.as_str().chars() {
+                            if c as u32 > 255 {
+                                let err = self.type_error(ValidType::Byte, char_as_cell!(c));
+                                return Err(self.error_form(err, stub_gen()));
+                            }
 
-                if stream.options().stream_type == StreamType::Binary {
-                    for c in string.chars() {
-                        bytes.push(c as u8);
+                            bytes.push(c as u8);
+                        }
+                    } else {
+                        bytes = string.as_str().bytes().collect();
                     }
-                } else {
-                    bytes = string.into_bytes();
-                }
 
-                match stream.write_all(&bytes) {
-                    Ok(_) => {
-                        return return_from_clause!(self.last_call, self);
-                    }
-                    _ => {
-                        let stub = MachineError::functor_stub(clause_name!("$put_chars"), 2);
-
-                        let addr = self
-                            .heap
-                            .to_unifiable(HeapCellValue::Stream(stream.clone()));
-
-                        return Err(self.error_form(
-                            MachineError::existence_error(
-                                self.heap.h(),
-                                ExistenceError::Stream(addr),
-                            ),
-                            stub,
-                        ));
+                    match stream.write_all(&bytes) {
+                        Ok(_) => {
+                            return return_from_clause!(self.last_call, self);
+                        }
+                        _ => {
+                            let addr = stream_as_cell!(stream);
+                            let err = self.existence_error(ExistenceError::Stream(addr));
+
+                            return Err(self.error_form(err, stub_gen()));
+                        }
                     }
+                } else {
+                    self.fail = true;
                 }
             }
             &SystemClauseType::PutByte => {
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "put_byte",
+                    atom!("put_byte"),
                     2,
                 )?;
 
                 self.check_stream_properties(
-                    &mut stream,
+                    stream,
                     StreamType::Binary,
                     None,
-                    clause_name!("put_byte"),
+                    atom!("put_byte"),
                     2,
                 )?;
 
-                match self.store(self.deref(self[temp_v!(2)])) {
-                    addr if addr.is_ref() => {
-                        let stub = MachineError::functor_stub(clause_name!("put_byte"), 2);
-                        let err = MachineError::instantiation_error();
+                let stub_gen = || functor_stub(atom!("put_byte"), 2);
+                let addr = self.store(self.deref(self.registers[2]));
 
-                        return Err(self.error_form(err, stub));
-                    }
-                    addr => {
-                        match Number::try_from((addr, &self.heap)) {
-                            Ok(Number::Integer(n)) => {
-                                if let Some(nb) = n.to_u8() {
-                                    match stream.write(&mut [nb]) {
-                                        Ok(1) => {
-                                            return return_from_clause!(self.last_call, self);
-                                        }
-                                        _ => {
-                                            let stub = MachineError::functor_stub(
-                                                clause_name!("put_byte"),
-                                                2,
-                                            );
-
-                                            let addr = self.heap.to_unifiable(
-                                                HeapCellValue::Stream(stream.clone()),
-                                            );
-
-                                            return Err(self.error_form(
-                                                MachineError::existence_error(
-                                                    self.heap.h(),
-                                                    ExistenceError::Stream(addr),
-                                                ),
-                                                stub,
-                                            ));
-                                        }
+                if addr.is_var() {
+                    let err = self.instantiation_error();
+                    return Err(self.error_form(err, stub_gen()));
+                } else {
+                    match Number::try_from(addr) {
+                        Ok(Number::Integer(n)) => {
+                            if let Some(nb) = n.to_u8() {
+                                match stream.write(&mut [nb]) {
+                                    Ok(1) => {
+                                        return return_from_clause!(self.last_call, self);
+                                    }
+                                    _ => {
+                                        let err = self.existence_error(
+                                            ExistenceError::Stream(stream_as_cell!(stream))
+                                        );
+
+                                        return Err(self.error_form(err, stub_gen()));
                                     }
                                 }
                             }
-                            Ok(Number::Fixnum(n)) => {
-                                if let Ok(nb) = u8::try_from(n) {
-                                    match stream.write(&mut [nb]) {
-                                        Ok(1) => {
-                                            return return_from_clause!(self.last_call, self);
-                                        }
-                                        _ => {
-                                            let stub = MachineError::functor_stub(
-                                                clause_name!("put_byte"),
-                                                2,
-                                            );
-
-                                            let addr = self.heap.to_unifiable(
-                                                HeapCellValue::Stream(stream.clone()),
-                                            );
-
-                                            return Err(self.error_form(
-                                                MachineError::existence_error(
-                                                    self.heap.h(),
-                                                    ExistenceError::Stream(addr),
-                                                ),
-                                                stub,
-                                            ));
-                                        }
+                        }
+                        Ok(Number::Fixnum(n)) => {
+                            if let Ok(nb) = u8::try_from(n.get_num()) {
+                                match stream.write(&mut [nb]) {
+                                    Ok(1) => {
+                                        return return_from_clause!(self.last_call, self);
+                                    }
+                                    _ => {
+                                        let err = self.existence_error(
+                                            ExistenceError::Stream(stream_as_cell!(stream))
+                                        );
+
+                                        return Err(self.error_form(err, stub_gen()));
                                     }
                                 }
                             }
-                            _ => {}
                         }
-
-                        let stub = MachineError::functor_stub(clause_name!("put_byte"), 2);
-                        let err = MachineError::type_error(
-                            self.heap.h(),
-                            ValidType::Byte,
-                            self[temp_v!(2)],
-                        );
-
-                        return Err(self.error_form(err, stub));
+                        _ => {
+                        }
                     }
                 }
+
+                let err = self.type_error(ValidType::Byte, self.registers[2]);
+                return Err(self.error_form(err, stub_gen()));
             }
             &SystemClauseType::GetByte => {
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "get_byte",
+                    atom!("get_byte"),
                     2,
                 )?;
 
                 self.check_stream_properties(
-                    &mut stream,
+                    stream,
                     StreamType::Binary,
-                    Some(self[temp_v!(2)]),
-                    clause_name!("get_byte"),
+                    Some(self.registers[2]),
+                    atom!("get_byte"),
                     2,
                 )?;
 
                 if stream.past_end_of_stream() {
-                    self.eof_action(self[temp_v!(2)], &mut stream, clause_name!("get_byte"), 2)?;
+                    self.eof_action(self.registers[2], stream, atom!("get_byte"), 2)?;
 
-                    if EOFAction::Reset != stream.options().eof_action {
+                    if EOFAction::Reset != stream.options().eof_action() {
                         return return_from_clause!(self.last_call, self);
                     } else if self.fail {
                         return Ok(());
                     }
                 }
 
-                let addr = match self.store(self.deref(self[temp_v!(2)])) {
-                    addr if addr.is_ref() => addr,
-                    addr => match Number::try_from((addr, &self.heap)) {
+                let stub_gen = || functor_stub(atom!("get_byte"), 2);
+                let addr = self.store(self.deref(self.registers[2]));
+
+                let addr = if addr.is_var() {
+                    addr
+                } else {
+                    match Number::try_from(addr) {
                         Ok(Number::Integer(n)) => {
                             if let Some(nb) = n.to_u8() {
-                                Addr::Usize(nb as usize)
+                                fixnum_as_cell!(Fixnum::build_with(nb as i64))
                             } else {
-                                return Err(self.type_error(
-                                    ValidType::InByte,
-                                    addr,
-                                    clause_name!("get_byte"),
-                                    2,
-                                ));
+                                let err = self.type_error(ValidType::InByte, addr);
+                                return Err(self.error_form(err, stub_gen()));
                             }
                         }
                         Ok(Number::Fixnum(n)) => {
-                            if let Ok(nb) = u8::try_from(n) {
-                                Addr::Usize(nb as usize)
+                            if let Ok(nb) = u8::try_from(n.get_num()) {
+                                fixnum_as_cell!(Fixnum::build_with(nb as i64))
                             } else {
-                                return Err(self.type_error(
-                                    ValidType::InByte,
-                                    addr,
-                                    clause_name!("get_byte"),
-                                    2,
-                                ));
+                                let err = self.type_error(ValidType::InByte, addr);
+                                return Err(self.error_form(err, stub_gen()));
                             }
                         }
                         _ => {
-                            return Err(self.type_error(
-                                ValidType::InByte,
-                                addr,
-                                clause_name!("get_byte"),
-                                2,
-                            ));
+                            let err = self.type_error(ValidType::InByte, addr);
+                            return Err(self.error_form(err, stub_gen()));
                         }
-                    },
+                    }
                 };
 
                 loop {
@@ -2231,19 +2131,12 @@ impl MachineState {
 
                     match stream.read(&mut b) {
                         Ok(1) => {
-                            if let Some(var) = addr.as_var() {
-                                self.bind(var, Addr::Usize(b[0] as usize));
-                                break;
-                            } else if addr == Addr::Usize(b[0] as usize) {
-                                break;
-                            } else {
-                                self.fail = true;
-                                return Ok(());
-                            }
+                            self.unify_fixnum(Fixnum::build_with(b[0] as i64), addr);
+                            break;
                         }
                         _ => {
-                            stream.set_past_end_of_stream();
-                            (self.unify_fn)(self, self[temp_v!(2)], Addr::Fixnum(-1));
+                            stream.set_past_end_of_stream(true);
+                            self.unify_fixnum(Fixnum::build_with(-1), self.registers[2]);
                             return return_from_clause!(self.last_call, self);
                         }
                     }
@@ -2251,22 +2144,22 @@ impl MachineState {
             }
             &SystemClauseType::GetChar => {
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "get_char",
+                    atom!("get_char"),
                     2,
                 )?;
 
                 self.check_stream_properties(
-                    &mut stream,
+                    stream,
                     StreamType::Text,
-                    Some(self[temp_v!(2)]),
-                    clause_name!("get_char"),
+                    Some(self.registers[2]),
+                    atom!("get_char"),
                     2,
                 )?;
 
                 if stream.past_end_of_stream() {
-                    if EOFAction::Reset != stream.options().eof_action {
+                    if EOFAction::Reset != stream.options().eof_action() {
                         return return_from_clause!(self.last_call, self);
                     } else if self.fail {
                         return Ok(());
@@ -2274,98 +2167,73 @@ impl MachineState {
                 }
 
                 if stream.at_end_of_stream() {
-                    let end_of_file = clause_name!("end_of_file");
-                    let end_of_file = self
-                        .heap
-                        .to_unifiable(HeapCellValue::Atom(end_of_file, None));
-
-                    stream.set_past_end_of_stream();
+                    let end_of_file = atom!("end_of_file");
+                    stream.set_past_end_of_stream(true);
 
-                    (self.unify_fn)(self, self[temp_v!(2)], end_of_file);
+                    self.unify_atom(end_of_file, self.store(self.deref(self.registers[2])));
                     return return_from_clause!(self.last_call, self);
                 }
 
-                let mut iter = self.open_parsing_stream(stream.clone(), "get_char", 2)?;
+                let stub_gen = || functor_stub(atom!("get_char"), 2);
+                let mut iter = self.open_parsing_stream(stream, atom!("get_char"), 2)?;
 
-                let addr = match self.store(self.deref(self[temp_v!(2)])) {
-                    addr if addr.is_ref() => addr,
-                    Addr::Con(h) if self.heap.atom_at(h) => match &self.heap[h] {
-                        HeapCellValue::Atom(ref atom, _) if atom.is_char() => {
-                            if let Some(c) = atom.as_str().chars().next() {
-                                Addr::Char(c)
-                            } else {
-                                unreachable!()
-                            }
+                let addr = self.store(self.deref(self.registers[2]));
+
+                let addr = if addr.is_var() {
+                    addr
+                } else {
+                    read_heap_cell!(addr,
+                        (HeapCellValueTag::Atom, (atom, _arity)) => {
+                            char_as_cell!(atom.as_char().unwrap())
                         }
-                        culprit => {
-                            return Err(self.type_error(
-                                ValidType::InCharacter,
-                                culprit.as_addr(h),
-                                clause_name!("get_char"),
-                                2,
-                            ));
+                        (HeapCellValueTag::Char) => {
+                            addr
                         }
-                    },
-                    Addr::Char(d) => Addr::Char(d),
-                    culprit => {
-                        return Err(self.type_error(
-                            ValidType::InCharacter,
-                            culprit,
-                            clause_name!("get_char"),
-                            2,
-                        ));
-                    }
+                        _ => {
+                            let err = self.type_error(ValidType::InCharacter, addr);
+                            return Err(self.error_form(err, stub_gen()));
+                        }
+                    )
                 };
 
                 loop {
-                    let result = iter.next();
+                    let result = iter.read_char();
 
                     match result {
-                        Some(Ok(d)) => {
-                            if let Some(var) = addr.as_var() {
-                                self.bind(var, Addr::Char(d));
-                                break;
-                            } else if addr == Addr::Char(d) {
-                                break;
-                            } else {
-                                self.fail = true;
+                        Some(Ok(c)) => {
+                            self.unify_char(c, addr);
+
+                            if self.fail {
                                 return Ok(());
                             }
                         }
                         _ => {
                             self.eof_action(
-                                self[temp_v!(2)],
-                                &mut stream,
-                                clause_name!("get_char"),
+                                self.registers[2],
+                                stream,
+                                atom!("get_char"),
                                 2,
                             )?;
 
-                            if EOFAction::Reset != stream.options().eof_action {
+                            if EOFAction::Reset != stream.options().eof_action() {
                                 return return_from_clause!(self.last_call, self);
                             } else if self.fail {
                                 return Ok(());
                             }
-                        } /*
-                          _ => {
-                              let stub = MachineError::functor_stub(clause_name!("get_char"), 2);
-                              let err = MachineError::representation_error(RepFlag::Character);
-                              let err = self.error_form(err, stub);
-
-                              return Err(err);
-                          }*/
+                        }
                     }
                 }
             }
             &SystemClauseType::GetNChars => {
                 let stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "get_n_chars",
+                    atom!("get_n_chars"),
                     3,
                 )?;
 
-                let num = match Number::try_from((self[temp_v!(2)], &self.heap)) {
-                    Ok(Number::Fixnum(n)) => usize::try_from(n).unwrap(),
+                let num = match Number::try_from(self.store(self.deref(self.registers[2]))) {
+                    Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).unwrap(),
                     Ok(Number::Integer(n)) => match n.to_usize() {
                         Some(u) => u,
                         _ => {
@@ -2380,18 +2248,20 @@ impl MachineState {
 
                 let mut string = String::new();
 
-                if stream.options().stream_type == StreamType::Binary {
+                if stream.options().stream_type() == StreamType::Binary {
                     let mut buf = vec![];
                     let mut chunk = stream.take(num as u64);
+
                     chunk.read_to_end(&mut buf).ok();
+
                     for c in buf {
                         string.push(c as char);
                     }
                 } else {
-                    let mut iter = self.open_parsing_stream(stream.clone(), "get_n_chars", 2)?;
+                    let mut iter = self.open_parsing_stream(stream, atom!("get_n_chars"), 2)?;
 
                     for _ in 0..num {
-                        let result = iter.next();
+                        let result = iter.read_char();
 
                         match result {
                             Some(Ok(c)) => {
@@ -2403,27 +2273,28 @@ impl MachineState {
                         }
                     }
                 };
-                let string = self.heap.put_complete_string(&string);
-                (self.unify_fn)(self, self[temp_v!(3)], string);
+
+                let atom = self.atom_tbl.build_with(&string);
+                self.unify_complete_string(atom, self.store(self.deref(self.registers[3])));
             }
             &SystemClauseType::GetCode => {
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "get_code",
+                    atom!("get_code"),
                     2,
                 )?;
 
                 self.check_stream_properties(
-                    &mut stream,
+                    stream,
                     StreamType::Text,
-                    Some(self[temp_v!(2)]),
-                    clause_name!("get_code"),
+                    Some(self.registers[2]),
+                    atom!("get_code"),
                     2,
                 )?;
 
                 if stream.past_end_of_stream() {
-                    if EOFAction::Reset != stream.options().eof_action {
+                    if EOFAction::Reset != stream.options().eof_action() {
                         return return_from_clause!(self.last_call, self);
                     } else if self.fail {
                         return Ok(());
@@ -2431,87 +2302,74 @@ impl MachineState {
                 }
 
                 if stream.at_end_of_stream() {
-                    let end_of_file = Integer::from(-1);
-                    let end_of_file = self
-                        .heap
-                        .to_unifiable(HeapCellValue::Integer(Rc::new(end_of_file)));
+                    let end_of_file = atom!("end_of_file");
 
-                    stream.set_past_end_of_stream();
+                    stream.set_past_end_of_stream(true);
 
-                    (self.unify_fn)(self, self[temp_v!(2)], end_of_file);
+                    self.unify_atom(end_of_file, self.store(self.deref(self.registers[2])));
                     return return_from_clause!(self.last_call, self);
                 }
 
-                let addr = match self.store(self.deref(self[temp_v!(2)])) {
-                    addr if addr.is_ref() => addr,
-                    addr => match Number::try_from((addr, &self.heap)) {
+                let stub_gen = || functor_stub(atom!("get_code"), 2);
+                let addr = self.store(self.deref(self.registers[2]));
+
+                let addr = if addr.is_var() {
+                    addr
+                } else {
+                    match Number::try_from(addr) {
                         Ok(Number::Integer(n)) => {
                             let n = n
                                 .to_u32()
-                                .and_then(|n| std::char::from_u32(n).and_then(|_| Some(n)));
+                                .and_then(|n| std::char::from_u32(n));
 
                             if let Some(n) = n {
-                                Addr::Fixnum(n as isize)
+                                fixnum_as_cell!(Fixnum::build_with(n as i64))
                             } else {
-                                return Err(self.representation_error(
-                                    RepFlag::InCharacterCode,
-                                    clause_name!("get_code"),
-                                    2,
-                                ));
+                                let err = self.representation_error(RepFlag::InCharacterCode);
+                                return Err(self.error_form(err, stub_gen()));
                             }
                         }
                         Ok(Number::Fixnum(n)) => {
-                            let n = u32::try_from(n)
+                            let nf = u32::try_from(n.get_num())
                                 .ok()
-                                .and_then(|n| std::char::from_u32(n).and_then(|_| Some(n)));
+                                .and_then(|n| std::char::from_u32(n));
 
-                            if let Some(n) = n {
-                                Addr::Fixnum(n as isize)
+                            if nf.is_some() {
+                                fixnum_as_cell!(n)
                             } else {
-                                return Err(self.representation_error(
-                                    RepFlag::InCharacterCode,
-                                    clause_name!("get_code"),
-                                    2,
-                                ));
+                                let err = self.representation_error(RepFlag::InCharacterCode);
+                                return Err(self.error_form(err, stub_gen()));
                             }
                         }
                         _ => {
-                            return Err(self.type_error(
-                                ValidType::Integer,
-                                self[temp_v!(2)],
-                                clause_name!("get_code"),
-                                2,
-                            ));
+                            let err = self.type_error(ValidType::Integer, self.registers[2]);
+                            return Err(self.error_form(err, stub_gen()));
                         }
-                    },
+                    }
                 };
 
-                let mut iter = self.open_parsing_stream(stream.clone(), "get_code", 2)?;
+                let mut iter = self.open_parsing_stream(stream.clone(), atom!("get_code"), 2)?;
 
                 loop {
-                    let result = iter.next();
+                    let result = iter.read_char();
 
                     match result {
                         Some(Ok(c)) => {
-                            if let Some(var) = addr.as_var() {
-                                self.bind(var, Addr::Fixnum(c as isize));
-                                break;
-                            } else if addr == Addr::Fixnum(c as isize) {
-                                break;
-                            } else {
-                                self.fail = true;
+                            self.unify_fixnum(Fixnum::build_with(c as i64), addr);
+
+                            if self.fail {
                                 return Ok(());
                             }
                         }
                         _ => {
                             self.eof_action(
-                                self[temp_v!(2)],
-                                &mut stream,
-                                clause_name!("get_code"),
+                                self.registers[2],
+                                stream,
+                                atom!("get_code"),
                                 2,
                             )?;
 
-                            if EOFAction::Reset != stream.options().eof_action {
+                            if EOFAction::Reset != stream.options().eof_action() {
                                 return return_from_clause!(self.last_call, self);
                             } else if self.fail {
                                 return Ok(());
@@ -2536,9 +2394,9 @@ impl MachineState {
                 indices.streams = indices.streams.sub(&null_streams);
 
                 if let Some(first_stream) = first_stream {
-                    let stream = self.heap.to_unifiable(HeapCellValue::Stream(first_stream));
+                    let stream = stream_as_cell!(first_stream);
 
-                    let var = self.store(self.deref(self[temp_v!(1)])).as_var().unwrap();
+                    let var = self.store(self.deref(self.registers[1])).as_var().unwrap();
                     self.bind(var, stream);
                 } else {
                     self.fail = true;
@@ -2546,25 +2404,14 @@ impl MachineState {
                 }
             }
             &SystemClauseType::NextStream => {
-                let prev_stream = match self.store(self.deref(self[temp_v!(1)])) {
-                    Addr::Stream(h) => {
-                        if let HeapCellValue::Stream(ref stream) = &self.heap[h] {
-                            stream.clone()
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    _ => {
-                        unreachable!()
-                    }
-                };
+                let prev_stream = cell_as_stream!(self.store(self.deref(self.registers[1])));
 
                 let mut next_stream = None;
                 let mut null_streams = BTreeSet::new();
 
                 for stream in indices
                     .streams
-                    .range(prev_stream.clone()..)
+                    .range(prev_stream..)
                     .skip(1)
                     .cloned()
                 {
@@ -2579,8 +2426,8 @@ impl MachineState {
                 indices.streams = indices.streams.sub(&null_streams);
 
                 if let Some(next_stream) = next_stream {
-                    let var = self.store(self.deref(self[temp_v!(2)])).as_var().unwrap();
-                    let next_stream = self.heap.to_unifiable(HeapCellValue::Stream(next_stream));
+                    let var = self.store(self.deref(self.registers[2])).as_var().unwrap();
+                    let next_stream = stream_as_cell!(next_stream);
 
                     self.bind(var, next_stream);
                 } else {
@@ -2590,21 +2437,19 @@ impl MachineState {
             }
             &SystemClauseType::FlushOutput => {
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "flush_output",
+                    atom!("flush_output"),
                     1,
                 )?;
 
                 if !stream.is_output_stream() {
-                    let stub = MachineError::functor_stub(clause_name!("flush_output"), 1);
-
-                    let addr = vec![HeapCellValue::Stream(stream)];
+                    let stub = functor_stub(atom!("flush_output"), 1);
+                    let addr = stream_as_cell!(stream); // vec![HeapCellValue::Stream(stream)];
 
-                    let err = MachineError::permission_error(
-                        self.heap.h(),
+                    let err = self.permission_error(
                         Permission::OutputStream,
-                        "stream",
+                        atom!("stream"),
                         addr,
                     );
 
@@ -2618,14 +2463,17 @@ impl MachineState {
                     code: KeyCode::Char('c'),
                     modifiers: KeyModifiers::CONTROL,
                 };
+
                 let key = get_key();
+
                 if key == ctrl_c {
-                    let stub = MachineError::functor_stub(clause_name!("get_single_char"), 1);
-                    let err = MachineError::interrupt_error();
+                    let stub = functor_stub(atom!("get_single_char"), 1);
+                    let err = self.interrupt_error();
                     let err = self.error_form(err, stub);
 
                     return Err(err);
                 }
+
                 let c = match key.code {
                     KeyCode::Enter => '\n',
                     KeyCode::Tab => '\t',
@@ -2633,37 +2481,30 @@ impl MachineState {
                     _ => unreachable!(),
                 };
 
-                let a1 = self[temp_v!(1)];
-
-                (self.unify_fn)(self, Addr::Char(c), a1);
+                self.unify_char(c, self.store(self.deref(self.registers[1])));
             }
             &SystemClauseType::HeadIsDynamic => {
-                let module_name = atom_from!(self, self.store(self.deref(self[temp_v!(1)])));
-
-                self.fail = !match self.store(self.deref(self[temp_v!(2)])) {
-                    Addr::Str(s) => match &self.heap[s] {
-                        &HeapCellValue::NamedStr(arity, ref name, ..) => {
-                            indices.is_dynamic_predicate(module_name, (name.clone(), arity))
-                        }
-                        _ => unreachable!(),
-                    },
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(name, _) = &self.heap[h] {
-                            indices.is_dynamic_predicate(module_name, (name.clone(), 0))
-                        } else {
-                            unreachable!()
-                        }
-                    }
+                let module_name = cell_as_atom!(self.store(self.deref(self.registers[1])));
+
+               let (name, arity) = read_heap_cell!(self.store(self.deref(self.registers[2])),
+                   (HeapCellValueTag::Str, s) => {
+                       cell_as_atom_cell!(self.heap[s]).get_name_and_arity()
+                   }
+                    (HeapCellValueTag::Atom, (name, _arity)) => {
+                       (name, 0)
+                   }
                     _ => {
-                        unreachable!()
-                    }
-                };
+                       unreachable!()
+                   }
+               );
+
+                self.fail = !indices.is_dynamic_predicate(module_name, (name, arity));
             }
             &SystemClauseType::Close => {
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "close",
+                    atom!("close"),
                     2,
                 )?;
 
@@ -2676,404 +2517,423 @@ impl MachineState {
                 if stream == *current_input_stream {
                     *current_input_stream = indices
                         .stream_aliases
-                        .get(&clause_name!("user_input"))
+                        .get(&atom!("user_input"))
                         .cloned()
                         .unwrap();
 
-                    indices.streams.insert(current_input_stream.clone());
+                    indices.streams.insert(*current_input_stream);
                 } else if stream == *current_output_stream {
                     *current_output_stream = indices
                         .stream_aliases
-                        .get(&clause_name!("user_output"))
+                        .get(&atom!("user_output"))
                         .cloned()
                         .unwrap();
 
-                    indices.streams.insert(current_output_stream.clone());
+                    indices.streams.insert(*current_output_stream);
                 }
 
                 if !stream.is_stdin() && !stream.is_stdout() && !stream.is_stderr() {
                     let close_result = stream.close();
 
-                    if let Some(ref alias) = stream.options().alias {
-                        indices.stream_aliases.remove(alias);
+                    if let Some(alias) = stream.options().get_alias() {
+                        indices.stream_aliases.remove(&alias);
                     }
                     if let Err(_) = close_result {
-                        let stub = MachineError::functor_stub(
-                            clause_name!("close"),
-                            1,
-                        );
-
-                        let addr = self.heap.to_unifiable(
-                            HeapCellValue::Stream(stream.clone()),
-                        );
+                        let stub = functor_stub(atom!("close"), 1);
+                        let addr = stream_as_cell!(stream);
+                        let err  = self.existence_error(ExistenceError::Stream(addr));
 
-                        return Err(self.error_form(
-                            MachineError::existence_error(
-                                self.heap.h(),
-                                ExistenceError::Stream(addr),
-                            ),
-                            stub,
-                        ));
+                        return Err(self.error_form(err, stub));
                     }
                 }
             }
-            &SystemClauseType::CopyToLiftedHeap => match self.store(self.deref(self[temp_v!(1)])) {
-                Addr::Usize(lh_offset) => {
-                    let copy_target = self[temp_v!(2)];
+            &SystemClauseType::CopyToLiftedHeap => {
+                let lh_offset = cell_as_fixnum!(
+                    self.store(self.deref(self.registers[1]))
+                ).get_num() as usize;
 
-                    let old_threshold = self.copy_findall_solution(lh_offset, copy_target);
-                    let new_threshold = self.lifted_heap.h() - lh_offset;
+                let copy_target = self.registers[2];
 
-                    self.lifted_heap[old_threshold] =
-                        HeapCellValue::Addr(Addr::HeapCell(new_threshold));
+                let old_threshold = self.copy_findall_solution(lh_offset, copy_target);
+                let new_threshold = self.lifted_heap.len() - lh_offset;
 
-                    for addr in self.lifted_heap.iter_mut_from(old_threshold + 1) {
-                        match addr {
-                            HeapCellValue::Addr(ref mut addr) => {
-                                *addr -= self.heap.h() + lh_offset;
-                            }
-                            _ => {}
-                        }
-                    }
-                }
-                _ => {
-                    self.fail = true;
+                self.lifted_heap[old_threshold] = heap_loc_as_cell!(new_threshold);
+
+                for addr in self.lifted_heap[old_threshold + 1 ..].iter_mut() {
+                    *addr -= self.heap.len() + lh_offset;
                 }
             },
             &SystemClauseType::DeleteAttribute => {
-                let ls0 = self.store(self.deref(self[temp_v!(1)]));
+                let ls0 = self.store(self.deref(self.registers[1]));
 
-                if let Addr::Lis(l1) = ls0 {
-                    if let Addr::Lis(l2) = self.store(self.deref(Addr::HeapCell(l1 + 1))) {
-                        let old_addr = self.heap[l1 + 1].as_addr(l1 + 1);
-                        let tail = self.store(self.deref(Addr::HeapCell(l2 + 1)));
+                if let HeapCellValueTag::Lis = ls0.get_tag() {
+                    let l1 = ls0.get_value();
+                    let ls1 = self.store(self.deref(heap_loc_as_cell!(l1 + 1)));
 
-                        let tail = if tail.is_ref() {
-                            Addr::HeapCell(l1 + 1)
+                    if let HeapCellValueTag::Lis = ls1.get_tag() {
+                        let l2 = ls1.get_value();
+
+                        let old_addr = self.heap[l1+1];
+                        let tail = self.store(self.deref(heap_loc_as_cell!(l2 + 1)));
+
+                        let tail = if tail.is_var() {
+                            heap_loc_as_cell!(l1 + 1)
                         } else {
                             tail
                         };
 
-                        let trail_ref = match old_addr {
-                            Addr::HeapCell(h) => TrailRef::AttrVarHeapLink(h),
-                            Addr::Lis(l) => TrailRef::AttrVarListLink(l1 + 1, l),
-                            _ => unreachable!(),
-                        };
+                        let trail_ref = read_heap_cell!(old_addr,
+                            (HeapCellValueTag::Var, h) => {
+                                TrailRef::AttrVarHeapLink(h)
+                            }
+                            (HeapCellValueTag::Lis, l) => {
+                                TrailRef::AttrVarListLink(l1 + 1, l)
+                            }
+                            _ => {
+                                unreachable!()
+                            }
+                        );
 
-                        self.heap[l1 + 1] = HeapCellValue::Addr(tail);
+                        self.heap[l1 + 1] = tail;
                         self.trail(trail_ref);
                     }
                 }
             }
             &SystemClauseType::DeleteHeadAttribute => {
-                let addr = self.store(self.deref(self[temp_v!(1)]));
+                let addr = self.store(self.deref(self.registers[1]));
 
-                match addr {
-                    Addr::AttrVar(h) => {
-                        let addr = self.heap[h + 1].as_addr(h + 1);
-                        let addr = self.store(self.deref(addr));
+                debug_assert_eq!(addr.get_tag(), HeapCellValueTag::AttrVar);
 
-                        match addr {
-                            Addr::Lis(l) => {
-                                let tail = self.store(self.deref(Addr::HeapCell(l + 1)));
-                                let tail = if tail.is_ref() {
-                                    self.heap[h] = HeapCellValue::Addr(Addr::HeapCell(h));
-                                    self.trail(TrailRef::Ref(Ref::AttrVar(h)));
+                let h = addr.get_value();
+                let addr = self.store(self.deref(self.heap[h + 1]));
 
-                                    Addr::HeapCell(h + 1)
-                                } else {
-                                    tail
-                                };
+                debug_assert_eq!(addr.get_tag(), HeapCellValueTag::Lis);
 
-                                self.heap[h + 1] = HeapCellValue::Addr(tail);
-                                self.trail(TrailRef::AttrVarListLink(h + 1, l));
-                            }
-                            _ => {
-                                unreachable!();
-                            }
-                        }
-                    }
-                    _ => {
-                        unreachable!();
-                    }
-                }
+                let l = addr.get_value();
+                let tail = self.store(self.deref(heap_loc_as_cell!(l + 1)));
+
+                let tail = if tail.is_var() {
+                    self.heap[h] = heap_loc_as_cell!(h);
+                    self.trail(TrailRef::Ref(Ref::attr_var(h)));
+
+                    heap_loc_as_cell!(h + 1)
+                } else {
+                    tail
+                };
+
+                self.heap[h + 1] = tail;
+                self.trail(TrailRef::AttrVarListLink(h + 1, l));
             }
             &SystemClauseType::DynamicModuleResolution(narity) => {
-                let module_name = self.store(self.deref(self[temp_v!(1 + narity)]));
+                let module_name = self.store(self.deref(self.registers[1 + narity]));
+                let module_name = cell_as_atom!(module_name);
 
-                let module_name = match module_name {
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(ref module_name, _) = self.heap[h] {
-                            module_name.clone()
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    _ => {
-                        unreachable!()
-                    }
-                };
+                let addr = self.store(self.deref(self.registers[2 + narity]));
 
-                match self.store(self.deref(self[temp_v!(2 + narity)])) {
-                    Addr::Str(a) => {
-                        if let HeapCellValue::NamedStr(arity, name, _) = self.heap.clone(a) {
-                            for i in (arity + 1..arity + narity + 1).rev() {
-                                self.registers[i] = self.registers[i - arity];
-                            }
+                read_heap_cell!(addr,
+                    (HeapCellValueTag::Str, a) => {
+                        let (name, arity) = cell_as_atom_cell!(self.heap[a])
+                            .get_name_and_arity();
 
-                            for i in 1..arity + 1 {
-                                self.registers[i] = self.heap[a + i].as_addr(a + i);
-                            }
+                        for i in (arity + 1..arity + narity + 1).rev() {
+                            self.registers[i] = self.registers[i - arity];
+                        }
 
-                            return self.module_lookup(
-                                indices,
-                                call_policy,
-                                (name, arity + narity),
-                                module_name,
-                                true,
-                                &indices.stream_aliases,
-                            );
-                        } else {
-                            unreachable!()
+                        for i in 1..arity + 1 {
+                            self.registers[i] = self.heap[a + i];
                         }
+
+                        return self.module_lookup(
+                            indices,
+                            call_policy,
+                            (name, arity + narity),
+                            module_name,
+                            true,
+                            &indices.stream_aliases,
+                        );
                     }
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(name, _) = self.heap.clone(h) {
-                            return self.module_lookup(
-                                indices,
-                                call_policy,
-                                (name.clone(), narity),
-                                module_name,
-                                true,
-                                &indices.stream_aliases,
-                            );
-                        } else {
-                            unreachable!()
-                        }
+                    (HeapCellValueTag::Atom, (name, _arity)) => {
+                        return self.module_lookup(
+                            indices,
+                            call_policy,
+                            (name, narity),
+                            module_name,
+                            true,
+                            &indices.stream_aliases,
+                        );
                     }
-                    Addr::Char(c) => {
+                    (HeapCellValueTag::Char, c) => {
+                        let key = (self.atom_tbl.build_with(&c.to_string()), narity);
+
                         return self.module_lookup(
                             indices,
                             call_policy,
-                            (clause_name!(c.to_string(), self.atom_tbl), narity),
+                            key,
                             module_name,
                             true,
                             &indices.stream_aliases,
                         );
                     }
-                    addr => {
-                        let stub = MachineError::functor_stub(clause_name!("(:)"), 2);
-
-                        let type_error =
-                            MachineError::type_error(self.heap.h(), ValidType::Callable, addr);
+                    _ => {
+                        let stub = functor_stub(atom!("(:)"), 2);
+                        let err = self.type_error(ValidType::Callable, addr);
 
-                        let type_error = self.error_form(type_error, stub);
-                        return Err(type_error);
+                        return Err(self.error_form(err, stub));
                     }
-                }
+                );
             }
             &SystemClauseType::EnqueueAttributedVar => {
-                let addr = self[temp_v!(1)];
+                let addr = self.store(self.deref(self.registers[1]));
 
-                match self.store(self.deref(addr)) {
-                    Addr::AttrVar(h) => {
-                        self.attr_var_init.attr_var_queue.push(h);
-                    }
-                    _ => {}
-                }
+               read_heap_cell!(addr,
+                   (HeapCellValueTag::AttrVar, h) => {
+                       self.attr_var_init.attr_var_queue.push(h);
+                   }
+                   _ => {
+                   }
+               );
             }
             &SystemClauseType::GetNextDBRef => {
-                let a1 = self[temp_v!(1)];
+                let a1 = self.store(self.deref(self.registers[1]));
 
-                match self.store(self.deref(a1)) {
-                    addr @ Addr::HeapCell(_)
-                    | addr @ Addr::StackCell(..)
-                    | addr @ Addr::AttrVar(_) => {
-                        let mut iter = indices.code_dir.iter();
+                if let Some(name_var) = a1.as_var() {
+                    let mut iter = indices.code_dir.iter();
 
-                        while let Some(((name, arity), _)) = iter.next() {
-                            if is_builtin_predicate(&name) {
-                                continue;
-                            }
+                    while let Some(((name, arity), _)) = iter.next() {
+                        if SystemClauseType::from(*name, *arity).is_some() {
+                            continue;
+                        }
 
-                            let spec = get_clause_spec(
-                                name.clone(),
-                                *arity,
-                                &CompositeOpDir::new(&indices.op_dir, None),
-                            );
+                        let arity_var = self.deref(self.registers[2])
+                            .as_var().unwrap();
 
-                            let db_ref = DBRef::NamedPred(name.clone(), *arity, spec);
-                            let r = addr.as_var().unwrap();
+                        self.bind(name_var, atom_as_cell!(name));
+                        self.bind(arity_var, fixnum_as_cell!(Fixnum::build_with(*arity as i64)));
+
+                        return return_from_clause!(self.last_call, self);
+                    }
 
-                            let addr = self.heap.to_unifiable(HeapCellValue::DBRef(db_ref));
+                    self.fail = true;
+                } else if a1.get_tag() == HeapCellValueTag::Atom {
+                    let name = cell_as_atom!(a1);
+                    let arity = cell_as_fixnum!(self.store(self.deref(self.registers[2])))
+                        .get_num() as usize;
 
-                            self.bind(r, addr);
+                    match self.get_next_db_ref(indices, &DBRef::NamedPred(name, arity)) {
+                        Some(DBRef::NamedPred(name, arity)) => {
+                            let atom_var = self.deref(self.registers[3])
+                                .as_var().unwrap();
 
-                            return return_from_clause!(self.last_call, self);
-                        }
+                            let arity_var = self.deref(self.registers[4])
+                                .as_var().unwrap();
 
-                        self.fail = true;
-                    }
-                    Addr::Con(h) => match self.heap.clone(h) {
-                        HeapCellValue::DBRef(DBRef::Op(..)) => {
-                            self.fail = true;
-                        }
-                        HeapCellValue::DBRef(ref db_ref) => {
-                            self.get_next_db_ref(indices, db_ref);
+                            self.bind(atom_var, atom_as_cell!(name));
+                            self.bind(arity_var, fixnum_as_cell!(Fixnum::build_with(arity as i64)));
                         }
-                        _ => {
+                        Some(DBRef::Op(..)) | None => {
                             self.fail = true;
                         }
-                    },
-                    _ => {
-                        self.fail = true;
                     }
+                } else {
+                    self.fail = true;
+                    return Ok(());
                 }
             }
             &SystemClauseType::GetNextOpDBRef => {
-                let a1 = self[temp_v!(1)];
+                let prec = self.store(self.deref(self.registers[1]));
+
+                if let Some(prec_var) = prec.as_var() {
+                    let spec = self.store(self.deref(self.registers[2]));
+                    let op = self.store(self.deref(self.registers[3]));
+                    let orig_op = self.store(self.deref(self.registers[7]));
+
+                    let spec_num = if spec.get_tag() == HeapCellValueTag::Atom {
+                        (match cell_as_atom!(spec) {
+                            atom!("xfx") => XFX,
+                            atom!("xfy") => XFY,
+                            atom!("yfx") => YFX,
+                            atom!("fx") => FX,
+                            atom!("fy") => FY,
+                            atom!("xf") => XF,
+                            _ => unreachable!(),
+                        }) as u8
+                    } else {
+                        0
+                    };
+
+                    let unossified_op_dir = if !orig_op.is_var() {
+                        let orig_op = cell_as_atom!(orig_op);
+
+                        let op_descs = [
+                            indices.op_dir.get_key_value(&(orig_op, Fixity::In)),
+                            indices.op_dir.get_key_value(&(orig_op, Fixity::Pre)),
+                            indices.op_dir.get_key_value(&(orig_op, Fixity::Post)),
+                        ];
+
+                        let number_of_keys = op_descs[0].is_some() as usize +
+                            op_descs[1].is_some() as usize +
+                            op_descs[2].is_some() as usize;
+
+                        match number_of_keys {
+                            0 => {
+                                self.fail = true;
+                                return Ok(());
+                            }
+                            1 => {
+                                for op_desc in op_descs {
+                                    if let Some((_, op_desc)) = op_desc {
+                                        let (op_prec, op_spec) =
+                                            (op_desc.get_prec(), op_desc.get_spec());
+
+                                        let op_spec = match op_spec as u32 {
+                                            XFX => atom!("xfx"),
+                                            XFY => atom!("xfy"),
+                                            YFX => atom!("yfx"),
+                                            FX => atom!("fx"),
+                                            FY => atom!("fy"),
+                                            XF => atom!("xf"),
+                                            YF => atom!("yf"),
+                                            _ => unreachable!(),
+                                        };
+
+                                        let op_prec = Fixnum::build_with(op_prec as i64);
+
+                                        self.unify_fixnum(op_prec, prec);
+                                        self.unify_atom(op_spec, spec);
+                                    }
+                                }
+
+                                return return_from_clause!(self.last_call, self);
+                            }
+                            _ => {
+                                let mut unossified_op_dir = OssifiedOpDir::new();
+
+                                for op_desc in op_descs {
+                                    if let Some((key, op_desc)) = op_desc {
+                                        let (prec, spec) = (op_desc.get_prec(), op_desc.get_spec());
+                                        unossified_op_dir.insert(*key, (prec as usize, spec as Specifier));
+                                    }
+                                }
 
-                match self.store(self.deref(a1)) {
-                    addr @ Addr::HeapCell(_)
-                    | addr @ Addr::StackCell(..)
-                    | addr @ Addr::AttrVar(_) => {
+                                unossified_op_dir
+                            }
+                        }
+                    } else {
                         let mut unossified_op_dir = OssifiedOpDir::new();
 
                         unossified_op_dir.extend(indices.op_dir.iter().filter_map(
-                            |(key, op_dir_val)| {
-                                let (name, fixity) = key.clone();
-
-                                let prec = op_dir_val.shared_op_desc().prec();
+                            |(key, op_desc)| {
+                                let (other_prec, other_spec) = (op_desc.get_prec(), op_desc.get_spec());
+                                let name = key.0;
 
-                                if prec == 0 {
+                                if other_prec == 0 {
                                     return None;
                                 }
 
-                                let assoc = op_dir_val.shared_op_desc().assoc();
+                                if (!orig_op.is_var() && atom_as_cell!(name) != orig_op) ||
+                                    (!spec.is_var() && other_spec != spec_num) {
+                                        return None;
+                                    }
 
-                                Some((OrderedOpDirKey(name, fixity), (prec, assoc)))
-                            },
+                                Some((*key, (other_prec as usize, other_spec as Specifier)))
+                            }
                         ));
 
-                        let ossified_op_dir = Rc::new(unossified_op_dir);
+                        unossified_op_dir
+                    };
 
-                        match ossified_op_dir.iter().next() {
-                            Some((OrderedOpDirKey(name, _), (priority, spec))) => {
-                                let db_ref = DBRef::Op(
-                                    *priority,
-                                    *spec,
-                                    name.clone(),
-                                    ossified_op_dir.clone(),
-                                    SharedOpDesc::new(*priority, *spec),
-                                );
+                    let ossified_op_dir = arena_alloc!(unossified_op_dir, &mut self.arena);
+
+                    match ossified_op_dir.iter().next() {
+                        Some(((op_atom, _), (op_prec, op_spec))) => {
+                            let ossified_op_dir_var = self.store(self.deref(self.registers[4]))
+                                .as_var().unwrap();
+
+                            let spec_atom = match *op_spec {
+                                FX => atom!("fx"),
+                                FY => atom!("fy"),
+                                XF => atom!("xf"),
+                                YF => atom!("yf"),
+                                XFX => atom!("xfx"),
+                                XFY => atom!("xfy"),
+                                YFX => atom!("yfx"),
+                                _ => {
+                                    self.fail = true;
+                                    return Ok(());
+                                }
+                            };
 
-                                let r = addr.as_var().unwrap();
-                                let addr = self.heap.to_unifiable(HeapCellValue::DBRef(db_ref));
+                            let spec_var = spec.as_var().unwrap();
+                            let op_var = op.as_var().unwrap();
 
-                                self.bind(r, addr);
-                            }
-                            None => {
-                                self.fail = true;
-                                return Ok(());
-                            }
-                        }
-                    }
-                    Addr::Con(h) => match self.heap.clone(h) {
-                        HeapCellValue::DBRef(DBRef::NamedPred(..)) => {
-                            self.fail = true;
+                            self.bind(prec_var, fixnum_as_cell!(Fixnum::build_with(*op_prec as i64)));
+                            self.bind(spec_var, atom_as_cell!(spec_atom));
+                            self.bind(op_var, atom_as_cell!(op_atom));
+                            self.bind(ossified_op_dir_var, typed_arena_ptr_as_cell!(ossified_op_dir));
                         }
-                        HeapCellValue::DBRef(ref db_ref) => {
-                            self.get_next_db_ref(indices, db_ref);
-                        }
-                        _ => {
+                        None => {
                             self.fail = true;
+                            return Ok(());
                         }
-                    },
-                    _ => {
-                        self.fail = true;
                     }
-                }
-            }
-            &SystemClauseType::LookupDBRef => {
-                let a1 = self[temp_v!(1)];
-
-                match self.store(self.deref(a1)) {
-                    Addr::Con(h) => match self.heap.clone(h) {
-                        HeapCellValue::DBRef(DBRef::NamedPred(name, arity, spec)) => {
-                            let a2 = self[temp_v!(2)];
-                            let a3 = self[temp_v!(3)];
+                } else {
+                    let spec = cell_as_atom!(self.store(self.deref(self.registers[2])));
+                    let op_atom = cell_as_atom!(self.store(self.deref(self.registers[3])));
+                    let ossified_op_dir_cell = self.store(self.deref(self.registers[4]));
 
-                            let atom = self.heap.to_unifiable(HeapCellValue::Atom(name, spec));
+                    if ossified_op_dir_cell.is_var() {
+                        self.fail = true;
+                        return Ok(());
+                    }
 
-                            (self.unify_fn)(self, a2, atom);
+                    let ossified_op_dir = cell_as_ossified_op_dir!(
+                        ossified_op_dir_cell
+                    );
 
-                            if !self.fail {
-                                (self.unify_fn)(self, a3, Addr::Usize(arity));
-                            }
-                        }
+                    let fixity = match spec {
+                        atom!("xfy") | atom!("yfx") | atom!("xfx") => Fixity::In,
+                        atom!("xf") | atom!("yf") => Fixity::Post,
+                        atom!("fx") | atom!("fy") => Fixity::Pre,
                         _ => {
                             self.fail = true;
+                            return Ok(());
                         }
-                    },
-                    _ => {
-                        self.fail = true;
-                    }
-                }
-            }
-            &SystemClauseType::LookupOpDBRef => {
-                let a1 = self[temp_v!(1)];
-
-                match self.store(self.deref(a1)) {
-                    Addr::Con(h) => match self.heap.clone(h) {
-                        HeapCellValue::DBRef(DBRef::Op(
-                            priority,
-                            spec,
-                            name,
-                            _,
-                            shared_op_desc,
-                        )) => {
-                            let prec = self[temp_v!(2)];
-                            let specifier = self[temp_v!(3)];
-                            let op = self[temp_v!(4)];
-
-                            let spec = match spec {
-                                FX => "fx",
-                                FY => "fy",
-                                XF => "xf",
-                                YF => "yf",
-                                XFX => "xfx",
-                                XFY => "xfy",
-                                YFX => "yfx",
+                    };
+
+                    match self.get_next_db_ref(indices, &DBRef::Op(op_atom, fixity, ossified_op_dir)) {
+                        Some(DBRef::Op(op_atom, fixity, ossified_op_dir)) => {
+                            let (prec, spec) = ossified_op_dir.get(&(op_atom, fixity)).unwrap();
+
+                            let prec_var = self.deref(self.registers[5])
+                                .as_var().unwrap();
+
+                            let spec_var = self.deref(self.registers[6])
+                                .as_var().unwrap();
+
+                            let op_var = self.deref(self.registers[7])
+                                .as_var().unwrap();
+
+                            let spec_atom = match *spec {
+                                FX => atom!("fx"),
+                                FY => atom!("fy"),
+                                XF => atom!("xf"),
+                                YF => atom!("yf"),
+                                XFX => atom!("xfx"),
+                                XFY => atom!("xfy"),
+                                YFX => atom!("yfx"),
                                 _ => {
                                     self.fail = true;
                                     return Ok(());
                                 }
                             };
 
-                            let a3 = self
-                                .heap
-                                .to_unifiable(HeapCellValue::Atom(clause_name!(spec), None));
-
-                            let a4 = self
-                                .heap
-                                .to_unifiable(HeapCellValue::Atom(name, Some(shared_op_desc)));
-
-                            (self.unify_fn)(self, Addr::Usize(priority), prec);
-
-                            if !self.fail {
-                                (self.unify_fn)(self, a3, specifier);
-                            }
-
-                            if !self.fail {
-                                (self.unify_fn)(self, a4, op);
-                            }
+                            self.bind(prec_var, fixnum_as_cell!(Fixnum::build_with(*prec as i64)));
+                            self.bind(spec_var, atom_as_cell!(spec_atom));
+                            self.bind(op_var, atom_as_cell!(op_atom));
                         }
-                        _ => {
+                        Some(DBRef::NamedPred(..)) | None => {
                             self.fail = true;
                         }
-                    },
-                    _ => {
-                        self.fail = true;
                     }
                 }
             }
@@ -3086,62 +2946,82 @@ impl MachineState {
                 self.fail = result;
             }
             &SystemClauseType::CpuNow => {
-                let a1 = self[temp_v!(1)];
-                let a2 = ProcessTime::now().as_duration().as_secs_f64();
-                let addr = self.heap.put_constant(Constant::Float(OrderedFloat(a2)));
+                let secs = ProcessTime::now().as_duration().as_secs_f64();
+                let secs = arena_alloc!(OrderedFloat(secs), &mut self.arena);
 
-                (self.unify_fn)(self, a1, addr);
+                self.unify_f64(secs, self.registers[1]);
             }
             &SystemClauseType::CurrentTime => {
-                let str = self.systemtime_to_timestamp(SystemTime::now());
-                (self.unify_fn)(self, self[temp_v!(1)], str);
+                let timestamp = self.systemtime_to_timestamp(SystemTime::now());
+                self.unify_atom(timestamp, self.registers[1]);
+            }
+            &SystemClauseType::Open => {
+                let alias = self.registers[4];
+                let eof_action = self.registers[5];
+                let reposition = self.registers[6];
+                let stream_type = self.registers[7];
+
+                let options  = self.to_stream_options(alias, eof_action, reposition, stream_type);
+                let src_sink = self.store(self.deref(self.registers[1]));
+
+                if let Some(atom_or_string) = self.value_to_str_like(src_sink) {
+                    let file_spec  = self.atom_tbl.build_with(atom_or_string.as_str());
+                    let mut stream = self.stream_from_file_spec(file_spec, indices, &options)?;
+
+                    *stream.options_mut() = options;
+                    indices.streams.insert(stream);
+
+                    if let Some(alias) = stream.options().get_alias() {
+                        indices.stream_aliases.insert(alias, stream);
+                    }
+
+                    let stream_var = self.store(self.deref(self.registers[3]));
+                    self.bind(stream_var.as_var().unwrap(), stream_as_cell!(stream));
+                } else {
+                    let err = self.domain_error(DomainErrorType::SourceSink, src_sink);
+                    let stub = functor_stub(atom!("open"), 4);
+
+                    return Err(self.error_form(err, stub));
+                }
             }
             &SystemClauseType::OpDeclaration => {
-                let priority = self[temp_v!(1)];
-                let specifier = self[temp_v!(2)];
-                let op = self[temp_v!(3)];
+                let priority = self.registers[1];
+                let specifier = self.registers[2];
+                let op = self.registers[3];
 
                 let priority = self.store(self.deref(priority));
 
-                let priority = match Number::try_from((priority, &self.heap)) {
-                    Ok(Number::Integer(n)) => n.to_usize().unwrap(),
-                    Ok(Number::Fixnum(n)) => usize::try_from(n).unwrap(),
+                let priority = match Number::try_from(priority) {
+                    Ok(Number::Integer(n)) => n.to_u16().unwrap(),
+                    Ok(Number::Fixnum(n)) => u16::try_from(n.get_num()).unwrap(),
                     _ => {
                         unreachable!();
                     }
                 };
 
-                let specifier = match self.store(self.deref(specifier)) {
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(ref specifier, _) = &self.heap[h] {
-                            specifier.clone()
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    _ => unreachable!(),
-                };
+                let specifier = cell_as_atom_cell!(self.store(self.deref(specifier)))
+                    .get_name();
 
-                let op = match self.store(self.deref(op)) {
-                    Addr::Char(c) => clause_name!(c.to_string(), self.atom_tbl),
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
-                            name.clone()
-                        } else {
-                            unreachable!()
-                        }
+                let op = read_heap_cell!(self.store(self.deref(op)),
+                    (HeapCellValueTag::Char) => {
+                        self.atom_tbl.build_with(&op.to_string())
                     }
-                    _ => unreachable!(),
-                };
+                    (HeapCellValueTag::Atom, (name, _arity)) => {
+                        name
+                    }
+                    _ => {
+                        unreachable!()
+                    }
+                );
 
-                let result = to_op_decl(priority, specifier.as_str(), op)
+                let result = to_op_decl(priority, specifier, op)
                     .map_err(SessionError::from)
                     .and_then(|mut op_decl| {
-                        if op_decl.prec == 0 {
+                        if op_decl.op_desc.get_prec() == 0 {
                             Ok(op_decl.remove(&mut indices.op_dir))
                         } else {
                             let spec = get_op_desc(
-                                op_decl.name.clone(),
+                                op_decl.name,
                                 &CompositeOpDir::new(&indices.op_dir, None),
                             );
 
@@ -3153,128 +3033,96 @@ impl MachineState {
                     Ok(()) => {}
                     Err(e) => {
                         // 8.14.3.3 l)
-                        let e = MachineError::session_error(self.heap.h(), e);
-                        let stub = MachineError::functor_stub(clause_name!("op"), 3);
-                        let permission_error = self.error_form(e, stub);
+                        let err = self.session_error(e);
+                        let stub = functor_stub(atom!("op"), 3);
 
-                        return Err(permission_error);
+                        return Err(self.error_form(err, stub));
                     }
-                };
-            }
-            &SystemClauseType::Open => {
-                let alias = self[temp_v!(4)];
-                let eof_action = self[temp_v!(5)];
-                let reposition = self[temp_v!(6)];
-                let stream_type = self[temp_v!(7)];
-
-                let options = self.to_stream_options(alias, eof_action, reposition, stream_type);
-
-                let mut iter = self.heap_pstr_iter(self[temp_v!(1)]);
-                let file_spec = clause_name!(iter.to_string(), self.atom_tbl);
-
-                let mut stream = self.stream_from_file_spec(file_spec, indices, &options)?;
-
-                *stream.options_mut() = options;
-
-                indices.streams.insert(stream.clone());
-
-                if let Some(ref alias) = &stream.options().alias {
-                    indices.stream_aliases.insert(alias.clone(), stream.clone());
                 }
-
-                let stream = self.heap.to_unifiable(HeapCellValue::Stream(stream));
-                let stream_var = self.store(self.deref(self[temp_v!(3)]));
-
-                self.bind(stream_var.as_var().unwrap(), stream);
             }
             &SystemClauseType::SetStreamOptions => {
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "open",
+                    atom!("open"),
                     4,
                 )?;
 
-                let alias = self[temp_v!(2)];
-                let eof_action = self[temp_v!(3)];
-                let reposition = self[temp_v!(4)];
-                let stream_type = self[temp_v!(5)];
+                let alias = self.registers[2];
+                let eof_action = self.registers[3];
+                let reposition = self.registers[4];
+                let stream_type = self.registers[5];
 
                 let options = self.to_stream_options(alias, eof_action, reposition, stream_type);
                 *stream.options_mut() = options;
             }
             &SystemClauseType::TruncateIfNoLiftedHeapGrowthDiff => {
-                self.truncate_if_no_lifted_heap_diff(|h| Addr::HeapCell(h))
+                self.truncate_if_no_lifted_heap_diff(|h| heap_loc_as_cell!(h))
             }
             &SystemClauseType::TruncateIfNoLiftedHeapGrowth => {
-                self.truncate_if_no_lifted_heap_diff(|_| Addr::EmptyList)
+                self.truncate_if_no_lifted_heap_diff(|_| empty_list_as_cell!())
             }
             &SystemClauseType::GetAttributedVariableList => {
-                let attr_var = self.store(self.deref(self[temp_v!(1)]));
-                let attr_var_list = match attr_var {
-                    Addr::AttrVar(h) => h + 1,
-                    attr_var @ Addr::HeapCell(_) | attr_var @ Addr::StackCell(..) => {
+                let attr_var = self.store(self.deref(self.registers[1]));
+                let attr_var_list = read_heap_cell!(attr_var,
+                    (HeapCellValueTag::AttrVar, h) => {
+                        h + 1
+                    }
+                    (HeapCellValueTag::Var | HeapCellValueTag::StackVar) => {
                         // create an AttrVar in the heap.
-                        let h = self.heap.h();
+                        let h = self.heap.len();
 
-                        self.heap.push(HeapCellValue::Addr(Addr::AttrVar(h)));
-                        self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h + 1)));
+                        self.heap.push(attr_var_as_cell!(h));
+                        self.heap.push(heap_loc_as_cell!(h+1));
 
-                        self.bind(Ref::AttrVar(h), attr_var);
+                        self.bind(Ref::attr_var(h), attr_var);
                         h + 1
                     }
                     _ => {
                         self.fail = true;
                         return Ok(());
                     }
-                };
+                );
 
-                let list_addr = self[temp_v!(2)];
-                self.bind(Ref::HeapCell(attr_var_list), list_addr);
+                let list_addr = self.store(self.deref(self.registers[2]));
+                self.bind(Ref::heap_cell(attr_var_list), list_addr);
             }
             &SystemClauseType::GetAttrVarQueueDelimiter => {
-                let addr = self[temp_v!(1)];
-                let value = Addr::Usize(self.attr_var_init.attr_var_queue.len());
+                let addr = self.registers[1];
+                let value = Fixnum::build_with(self.attr_var_init.attr_var_queue.len() as i64);
 
-                (self.unify_fn)(self, addr, value);
+                self.unify_fixnum(value, self.store(self.deref(addr)));
             }
             &SystemClauseType::GetAttrVarQueueBeyond => {
-                let addr = self[temp_v!(1)];
+                let addr = self.registers[1];
                 let addr = self.store(self.deref(addr));
 
-                let b = match addr {
-                    Addr::Usize(b) => Some(b),
-                    _ => match Number::try_from((addr, &self.heap)) {
-                        Ok(Number::Integer(n)) => n.to_usize(),
-                        Ok(Number::Fixnum(n)) => usize::try_from(n).ok(),
-                        _ => {
-                            self.fail = true;
-                            return Ok(());
-                        }
-                    },
+                let b = match Number::try_from(addr) {
+                    Ok(Number::Integer(n)) => n.to_usize(),
+                    Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).ok(),
+                    _ => {
+                        self.fail = true;
+                        return Ok(());
+                    }
                 };
 
                 if let Some(b) = b {
                     let iter = self.gather_attr_vars_created_since(b);
 
-                    let var_list_addr = Addr::HeapCell(self.heap.to_list(iter));
-                    let list_addr = self[temp_v!(2)];
+                    let var_list_addr = heap_loc_as_cell!(
+                        iter_to_heap_list(&mut self.heap, iter)
+                    );
 
-                    (self.unify_fn)(self, var_list_addr, list_addr);
+                    let list_addr = self.registers[2];
+                    unify!(self, var_list_addr, list_addr);
                 }
             }
             &SystemClauseType::GetContinuationChunk => {
-                let e = self.store(self.deref(self[temp_v!(1)]));
-
-                let e = if let Addr::Usize(e) = e {
-                    e
-                } else {
-                    self.fail = true;
-                    return Ok(());
-                };
+                let e = self.store(self.deref(self.registers[1]));
+                let e = cell_as_fixnum!(e).get_num() as usize;
 
-                let p_functor = self.store(self.deref(self[temp_v!(2)]));
-                let p = self.heap.to_local_code_ptr(&p_functor).unwrap();
+                let p_functor = self.store(self.deref(self.registers[2]));
+                let p = to_local_code_ptr(&self.heap, p_functor).unwrap();
 
                 let num_cells = match code_repo.lookup_instr(self.last_call, &CodePtr::Local(p)) {
                     Some(line) => {
@@ -3290,137 +3138,81 @@ impl MachineState {
 
                 let mut addrs = vec![];
 
-                for index in 1..num_cells + 1 {
-                    addrs.push(self.stack.index_and_frame(e)[index]);
+                for idx in 1..num_cells + 1 {
+                    addrs.push(self.stack[stack_loc!(AndFrame, e, idx)]);
                 }
 
-                let chunk = Addr::HeapCell(self.heap.h());
+                let chunk = str_loc_as_cell!(self.heap.len());
 
-                self.heap.push(HeapCellValue::NamedStr(
-                    1 + num_cells,
-                    clause_name!("cont_chunk"),
-                    None,
-                ));
-
-                self.heap.push(HeapCellValue::Addr(p_functor));
-                self.heap.extend(addrs.into_iter().map(HeapCellValue::Addr));
+                self.heap.push(atom_as_cell!(atom!("cont_chunk"), 1 + num_cells));
+                self.heap.push(p_functor);
+                self.heap.extend(addrs);
 
-                (self.unify_fn)(self, self[temp_v!(3)], chunk);
+                unify!(self, self.registers[3], chunk);
             }
             &SystemClauseType::GetLiftedHeapFromOffsetDiff => {
-                let lh_offset = self[temp_v!(1)];
+                let lh_offset = self.registers[1];
+                let lh_offset = cell_as_fixnum!(self.store(self.deref(lh_offset))).get_num() as usize;
 
-                match self.store(self.deref(lh_offset)) {
-                    Addr::Usize(lh_offset) => {
-                        if lh_offset >= self.lifted_heap.h() {
-                            let solutions = self[temp_v!(2)];
-                            let diff = self[temp_v!(3)];
-
-                            (self.unify_fn)(self, solutions, diff);
-                        } else {
-                            let h = self.heap.h();
-                            let mut last_index = h;
+                if lh_offset >= self.lifted_heap.len() {
+                    let solutions = self.registers[2];
+                    let diff = self.registers[3];
 
-                            for value in self.lifted_heap.iter_from(lh_offset) {
-                                last_index = self.heap.h();
+                    unify_fn!(self, solutions, diff);
+                } else {
+                    let h = self.heap.len();
+                    let mut last_index = h;
 
-                                match value {
-                                    HeapCellValue::Addr(ref addr) => {
-                                        self.heap.push(HeapCellValue::Addr(*addr + h));
-                                    }
-                                    value => {
-                                        self.heap.push(value.context_free_clone());
-                                    }
-                                }
-                            }
+                    for value in self.lifted_heap[lh_offset ..].iter().cloned() {
+                        last_index = self.heap.len();
+                        self.heap.push(value + h);
+                    }
 
-                            if last_index < self.heap.h() {
-                                let addr_opt =
-                                    if let HeapCellValue::Addr(ref addr) = &self.heap[last_index] {
-                                        Some(*addr)
-                                    } else {
-                                        None
-                                    };
-
-                                addr_opt.map(|addr| {
-                                    let diff = self[temp_v!(3)];
-                                    (self.unify_fn)(self, diff, addr);
-                                });
-                            }
+                    if last_index < self.heap.len() {
+                        let diff = self.registers[3];
+                        unify_fn!(self, diff, self.heap[last_index]);
+                    }
 
-                            self.lifted_heap.truncate(lh_offset);
+                    self.lifted_heap.truncate(lh_offset);
 
-                            let solutions = self[temp_v!(2)];
-                            (self.unify_fn)(self, Addr::HeapCell(h), solutions);
-                        }
-                    }
-                    _ => {
-                        self.fail = true;
-                    }
+                    let solutions = self.registers[2];
+                    unify_fn!(self, heap_loc_as_cell!(h), solutions);
                 }
             }
             &SystemClauseType::GetLiftedHeapFromOffset => {
-                let lh_offset = self[temp_v!(1)];
+                let lh_offset = self.registers[1];
+                let lh_offset = cell_as_fixnum!(self.store(self.deref(lh_offset))).get_num() as usize;
 
-                match self.store(self.deref(lh_offset)) {
-                    Addr::Usize(lh_offset) => {
-                        if lh_offset >= self.lifted_heap.h() {
-                            let solutions = self[temp_v!(2)];
-                            (self.unify_fn)(self, solutions, Addr::EmptyList);
-                        } else {
-                            let h = self.heap.h();
+                if lh_offset >= self.lifted_heap.len() {
+                    let solutions = self.registers[2];
+                    unify_fn!(self, solutions, empty_list_as_cell!());
+                } else {
+                    let h = self.heap.len();
 
-                            for addr in self.lifted_heap.iter_from(lh_offset) {
-                                match addr {
-                                    HeapCellValue::Addr(ref addr) => {
-                                        self.heap.push(HeapCellValue::Addr(*addr + h));
-                                    }
-                                    value => {
-                                        self.heap.push(value.context_free_clone());
-                                    }
-                                }
-                            }
+                    for addr in self.lifted_heap[lh_offset..].iter().cloned() {
+                        self.heap.push(addr + h);
+                    }
 
-                            self.lifted_heap.truncate(lh_offset);
+                    self.lifted_heap.truncate(lh_offset);
 
-                            let solutions = self[temp_v!(2)];
-                            (self.unify_fn)(self, Addr::HeapCell(h), solutions);
-                        }
-                    }
-                    _ => {
-                        self.fail = true;
-                    }
+                    let solutions = self.registers[2];
+                    unify_fn!(self, heap_loc_as_cell!(h), solutions);
                 }
             }
             &SystemClauseType::GetDoubleQuotes => {
-                let a1 = self[temp_v!(1)];
-
-                match self.flags.double_quotes {
-                    DoubleQuotes::Chars => {
-                        let atom = self
-                            .heap
-                            .to_unifiable(HeapCellValue::Atom(clause_name!("chars"), None));
+                let a1 = self.store(self.deref(self.registers[1]));
 
-                        (self.unify_fn)(self, a1, atom);
-                    }
-                    DoubleQuotes::Atom => {
-                        let atom = self
-                            .heap
-                            .to_unifiable(HeapCellValue::Atom(clause_name!("atom"), None));
-
-                        (self.unify_fn)(self, a1, atom);
-                    }
-                    DoubleQuotes::Codes => {
-                        let atom = self
-                            .heap
-                            .to_unifiable(HeapCellValue::Atom(clause_name!("codes"), None));
-
-                        (self.unify_fn)(self, a1, atom);
-                    }
-                }
+                self.unify_atom(
+                    match self.flags.double_quotes {
+                        DoubleQuotes::Chars => atom!("chars"),
+                        DoubleQuotes::Atom => atom!("atom"),
+                        DoubleQuotes::Codes => atom!("codes"),
+                    },
+                    a1,
+                );
             }
             &SystemClauseType::GetSCCCleaner => {
-                let dest = self[temp_v!(1)];
+                let dest = self.registers[1];
 
                 match cut_policy.downcast_mut::<SCCCutPolicy>().ok() {
                     Some(sgc_policy) => {
@@ -3440,15 +3232,15 @@ impl MachineState {
                         }
                     }
                     None => {}
-                };
+                }
 
                 self.fail = true;
             }
             &SystemClauseType::Halt => {
-                let code = self.store(self.deref(self[temp_v!(1)]));
+                let code = self.store(self.deref(self.registers[1]));
 
-                let code = match Number::try_from((code, &self.heap)) {
-                    Ok(Number::Fixnum(n)) => n as i32,
+                let code = match Number::try_from(code) {
+                    Ok(Number::Fixnum(n)) => i32::try_from(n.get_num()).unwrap(),
                     Ok(Number::Integer(n)) => n.to_i32().unwrap(),
                     Ok(Number::Rational(r)) => {
                         // n has already been confirmed as an integer, and
@@ -3464,7 +3256,7 @@ impl MachineState {
                 std::process::exit(code);
             }
             &SystemClauseType::InstallSCCCleaner => {
-                let addr = self[temp_v!(1)];
+                let addr = self.registers[1];
                 let b = self.b;
                 let prev_block = self.block;
 
@@ -3475,7 +3267,7 @@ impl MachineState {
 
                 match cut_policy.downcast_mut::<SCCCutPolicy>().ok() {
                     Some(cut_policy) => {
-                        self.install_new_block(temp_v!(2));
+                        self.install_new_block(self.registers[2]);
                         cut_policy.push_cont_pt(addr, b, prev_block);
                     }
                     None => panic!(
@@ -3486,155 +3278,111 @@ impl MachineState {
             }
             &SystemClauseType::InstallInferenceCounter => {
                 // A1 = B, A2 = L
-                let a1 = self.store(self.deref(self[temp_v!(1)]));
-                let a2 = self.store(self.deref(self[temp_v!(2)]));
+                let a1 = self.store(self.deref(self.registers[1]));
+                let a2 = self.store(self.deref(self.registers[2]));
 
                 if call_policy.downcast_ref::<CWILCallPolicy>().is_err() {
                     CWILCallPolicy::new_in_place(call_policy);
                 }
 
-                let n = match Number::try_from((a2, &self.heap)) {
-                    Ok(Number::Integer(n)) => Integer::from(&*n.clone()),
-                    Ok(Number::Fixnum(n)) => Integer::from(n),
+                let n = match Number::try_from(a2) {
+                    Ok(Number::Fixnum(bp)) => bp.get_num() as usize,
+                    Ok(Number::Integer(n)) => n.to_usize().unwrap(),
                     _ => {
-                        let stub = MachineError::functor_stub(
-                            clause_name!("call_with_inference_limit"),
+                        let stub = functor_stub(
+                            atom!("call_with_inference_limit"),
                             3,
                         );
 
-                        return Err(self.error_form(
-                            MachineError::type_error(self.heap.h(), ValidType::Integer, a2),
-                            stub,
-                        ));
+                        let err = self.type_error(ValidType::Integer, a2);
+                        return Err(self.error_form(err, stub));
                     }
                 };
 
-                match a1 {
-                    Addr::Usize(bp) | Addr::CutPoint(bp) => {
-                        match call_policy.downcast_mut::<CWILCallPolicy>().ok() {
-                            Some(call_policy) => {
-                                let count = call_policy.add_limit(n, bp).clone();
-                                let count = self
-                                    .heap
-                                    .to_unifiable(HeapCellValue::Integer(Rc::new(count)));
-
-                                let a3 = self[temp_v!(3)];
-                                (self.unify_fn)(self, a3, count);
-                            }
-                            None => {
-                                panic!(
-                                    "install_inference_counter: should have installed \\
-                                     CWILCallPolicy."
-                                )
-                            }
-                        }
+                let bp = cell_as_fixnum!(a1).get_num() as usize;
+
+                match call_policy.downcast_mut::<CWILCallPolicy>().ok() {
+                    Some(call_policy) => {
+                        let count = call_policy.add_limit(n, bp);
+                        let count = arena_alloc!(count.clone(), &mut self.arena);
+
+                        let a3 = self.store(self.deref(self.registers[3]));
+                        self.unify_big_int(count, a3);
                     }
-                    _ => {
-                        unreachable!();
+                    None => {
+                        panic!(
+                            "install_inference_counter: should have installed \\
+                             CWILCallPolicy."
+                        )
                     }
                 }
             }
             &SystemClauseType::ModuleExists => {
-                let module = self.store(self.deref(self[temp_v!(1)]));
+                let module = self.store(self.deref(self.registers[1]));
+                let module_name = cell_as_atom!(module);
 
-                match module {
-                    Addr::Con(h) => {
-                        if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
-                            self.fail = !indices.modules.contains_key(name);
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    _ => {
-                        unreachable!()
-                    }
-                };
+                self.fail = !indices.modules.contains_key(&module_name);
             }
             &SystemClauseType::NoSuchPredicate => {
-                let module_name = atom_from!(self, self.store(self.deref(self[temp_v!(1)])));
-
-                self.fail = match self.store(self.deref(self[temp_v!(2)])) {
-                    Addr::Str(s) => match &self.heap[s] {
-                        &HeapCellValue::NamedStr(arity, ref name, ref spec) => {
-                            if CLAUSE_TYPE_FORMS
-                                .borrow()
-                                .get(&(name.as_str(), arity))
-                                .is_some()
-                            {
-                                true
-                            } else {
-                                let index = indices
-                                    .get_predicate_code_index(
-                                        name.clone(),
-                                        arity,
-                                        module_name,
-                                        spec.clone(),
-                                    )
-                                    .map(|index| index.get())
-                                    .unwrap_or(IndexPtr::DynamicUndefined);
-
-                                match index {
-                                    IndexPtr::DynamicUndefined => false,
-                                    _ => true,
-                                }
-                            }
-                        }
-                        _ => {
-                            unreachable!()
-                        }
-                    },
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let &HeapCellValue::Atom(ref name, ref spec) = &self.heap[h] {
-                            let spec =
-                                fetch_atom_op_spec(name.clone(), spec.clone(), &indices.op_dir);
-
-                            if CLAUSE_TYPE_FORMS
-                                .borrow()
-                                .get(&(name.as_str(), 0))
-                                .is_some()
-                            {
-                                true
-                            } else {
-                                let index = indices
-                                    .get_predicate_code_index(
-                                        name.clone(),
-                                        0,
-                                        module_name,
-                                        spec.clone(),
-                                    )
-                                    .map(|index| index.get())
-                                    .unwrap_or(IndexPtr::DynamicUndefined);
-
-                                match index {
-                                    IndexPtr::DynamicUndefined => false,
-                                    _ => true,
-                                }
+                let module_name = cell_as_atom!(self.store(self.deref(self.registers[1])));
+                let head = self.store(self.deref(self.registers[2]));
+
+                self.fail = read_heap_cell!(head,
+                    (HeapCellValueTag::Str, s) => {
+                        let (name, arity) = cell_as_atom_cell!(self.heap[s])
+                            .get_name_and_arity();
+
+                        if clause_type_form(name, arity).is_some() {
+                            true
+                        } else {
+                            let index = indices.get_predicate_code_index(
+                                name,
+                                arity,
+                                module_name,
+                            )
+                            .map(|index| index.get())
+                            .unwrap_or(IndexPtr::DynamicUndefined);
+
+                            match index {
+                                IndexPtr::DynamicUndefined => false,
+                                _ => true,
                             }
+                        }
+                    }
+                    (HeapCellValueTag::Atom, (name, arity)) => {
+                        debug_assert_eq!(arity, 0);
+
+                        if clause_type_form(name, 0).is_some() {
+                            true
                         } else {
-                            unreachable!()
+                            let index = indices.get_predicate_code_index(
+                                name,
+                                0,
+                                module_name,
+                            )
+                            .map(|index| index.get())
+                            .unwrap_or(IndexPtr::DynamicUndefined);
+
+                            match index {
+                                IndexPtr::DynamicUndefined => false,
+                                _ => true,
+                            }
                         }
                     }
-                    head => {
-                        let err =
-                            MachineError::type_error(self.heap.h(), ValidType::Callable, head);
-                        let stub = MachineError::functor_stub(clause_name!("clause"), 2);
+                    _ => {
+                        let err = self.type_error(ValidType::Callable, head);
+                        let stub = functor_stub(atom!("clause"), 2);
 
                         return Err(self.error_form(err, stub));
                     }
-                };
+                );
             }
             &SystemClauseType::RedoAttrVarBinding => {
-                let var = self.store(self.deref(self[temp_v!(1)]));
-                let value = self.store(self.deref(self[temp_v!(2)]));
+                let var = self.store(self.deref(self.registers[1]));
+                let value = self.store(self.deref(self.registers[2]));
 
-                match var {
-                    Addr::AttrVar(h) => {
-                        self.heap[h] = HeapCellValue::Addr(value);
-                    }
-                    _ => {
-                        unreachable!()
-                    }
-                }
+                debug_assert_eq!(HeapCellValueTag::AttrVar, var.get_tag());
+                self.heap[var.get_value()] = value;
             }
             &SystemClauseType::ResetAttrVarState => {
                 self.attr_var_init.reset();
@@ -3642,19 +3390,13 @@ impl MachineState {
             &SystemClauseType::RemoveCallPolicyCheck => {
                 let restore_default = match call_policy.downcast_mut::<CWILCallPolicy>().ok() {
                     Some(call_policy) => {
-                        let a1 = self.store(self.deref(self[temp_v!(1)]));
+                        let a1 = self.store(self.deref(self.registers[1]));
+                        let bp = cell_as_fixnum!(a1).get_num() as usize;
 
-                        match a1 {
-                            Addr::Usize(bp) | Addr::CutPoint(bp) => {
-                                if call_policy.is_empty() && bp == self.b {
-                                    Some(call_policy.into_inner())
-                                } else {
-                                    None
-                                }
-                            }
-                            _ => {
-                                panic!("remove_call_policy_check: expected Usize in A1.");
-                            }
+                        if call_policy.is_empty() && bp == self.b {
+                            Some(call_policy.into_inner())
+                        } else {
+                            None
                         }
                     }
                     None => panic!(
@@ -3670,23 +3412,15 @@ impl MachineState {
             &SystemClauseType::RemoveInferenceCounter => {
                 match call_policy.downcast_mut::<CWILCallPolicy>().ok() {
                     Some(call_policy) => {
-                        let a1 = self.store(self.deref(self[temp_v!(1)]));
+                        let a1 = self.store(self.deref(self.registers[1]));
+                        let bp = cell_as_fixnum!(a1).get_num() as usize;
 
-                        match a1 {
-                            Addr::Usize(bp) | Addr::CutPoint(bp) => {
-                                let count = call_policy.remove_limit(bp).clone();
-                                let count = self
-                                    .heap
-                                    .to_unifiable(HeapCellValue::Integer(Rc::new(count)));
+                        let count = call_policy.remove_limit(bp).clone();
+                        let count = arena_alloc!(count.clone(), &mut self.arena);
 
-                                let a2 = self[temp_v!(2)];
+                        let a2 = self.store(self.deref(self.registers[2]));
 
-                                (self.unify_fn)(self, a2, count);
-                            }
-                            _ => {
-                                panic!("remove_inference_counter: expected Usize in A1.");
-                            }
-                        }
+                        self.unify_big_int(count, a2);
                     }
                     None => panic!(
                         "remove_inference_counter: requires \\
@@ -3702,16 +3436,14 @@ impl MachineState {
                 let frame_len = self.stack.index_and_frame(e).prelude.univ_prelude.num_cells;
 
                 for i in 1..frame_len - 1 {
-                    self[RegType::Temp(i)] = self.stack.index_and_frame(e)[i];
+                    self.registers[i] = self.stack[stack_loc!(AndFrame, e, i)];
                 }
 
-                if let &Addr::CutPoint(b0) = &self.stack.index_and_frame(e)[frame_len - 1] {
-                    self.b0 = b0;
-                }
+                self.b0 = cell_as_fixnum!(self.stack[stack_loc!(AndFrame, e, frame_len - 1)])
+                    .get_num() as usize;
 
-                if let &Addr::Usize(num_of_args) = &self.stack.index_and_frame(e)[frame_len] {
-                    self.num_of_args = num_of_args;
-                }
+                self.num_of_args = cell_as_fixnum!(self.stack[stack_loc!(AndFrame, e, frame_len)])
+                    .get_num() as usize;
 
                 self.deallocate();
                 self.p = CodePtr::Local(self.stack.index_and_frame(e).prelude.interrupt_cp);
@@ -3737,21 +3469,18 @@ impl MachineState {
             }
             &SystemClauseType::SetCutPointByDefault(r) => deref_cut(self, r),
             &SystemClauseType::SetInput => {
-                let addr = self.store(self.deref(self[temp_v!(1)]));
+                let addr = self.store(self.deref(self.registers[1]));
                 let stream =
-                    self.get_stream_or_alias(addr, &indices.stream_aliases, "set_input", 1)?;
+                    self.get_stream_or_alias(addr, &indices.stream_aliases, atom!("set_input"), 1)?;
 
                 if !stream.is_input_stream() {
-                    let stub = MachineError::functor_stub(clause_name!("set_input"), 1);
+                    let stub = functor_stub(atom!("set_input"), 1);
 
-                    let user_alias = self
-                        .heap
-                        .to_unifiable(HeapCellValue::Atom(clause_name!("user"), None));
+                    let user_alias = atom_as_cell!(atom!("user"));
 
-                    let err = MachineError::permission_error(
-                        self.heap.h(),
+                    let err = self.permission_error(
                         Permission::InputStream,
-                        "stream",
+                        atom!("stream"),
                         user_alias,
                     );
 
@@ -3761,21 +3490,17 @@ impl MachineState {
                 *current_input_stream = stream;
             }
             &SystemClauseType::SetOutput => {
-                let addr = self.store(self.deref(self[temp_v!(1)]));
+                let addr = self.store(self.deref(self.registers[1]));
                 let stream =
-                    self.get_stream_or_alias(addr, &indices.stream_aliases, "set_output", 1)?;
+                    self.get_stream_or_alias(addr, &indices.stream_aliases, atom!("set_output"), 1)?;
 
                 if !stream.is_output_stream() {
-                    let stub = MachineError::functor_stub(clause_name!("set_input"), 1);
-
-                    let user_alias = self
-                        .heap
-                        .to_unifiable(HeapCellValue::Atom(clause_name!("user"), None));
+                    let stub = functor_stub(atom!("set_input"), 1);
 
-                    let err = MachineError::permission_error(
-                        self.heap.h(),
+                    let user_alias = atom_as_cell!(atom!("user"));
+                    let err = self.permission_error(
                         Permission::OutputStream,
-                        "stream",
+                        atom!("stream"),
                         user_alias,
                     );
 
@@ -3784,68 +3509,41 @@ impl MachineState {
 
                 *current_output_stream = stream;
             }
-            &SystemClauseType::SetDoubleQuotes => match self[temp_v!(1)] {
-                Addr::Con(h) if self.heap.atom_at(h) => {
-                    if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
-                        self.flags.double_quotes = match atom.as_str() {
-                            "atom" => DoubleQuotes::Atom,
-                            "chars" => DoubleQuotes::Chars,
-                            "codes" => DoubleQuotes::Codes,
-                            _ => {
-                                self.fail = true;
-                                return Ok(());
-                            }
-                        };
-                    } else {
-                        unreachable!()
-                    }
-                }
-                _ => {
-                    self.fail = true;
-                }
-            },
-            &SystemClauseType::InferenceLevel => {
-                let a1 = self[temp_v!(1)];
-                let a2 = self.store(self.deref(self[temp_v!(2)]));
-
-                match a2 {
-                    Addr::CutPoint(bp) | Addr::Usize(bp) => {
-                        let prev_b = self.stack.index_or_frame(self.b).prelude.b;
-
-                        if prev_b <= bp {
-                            let a2 = self
-                                .heap
-                                .to_unifiable(HeapCellValue::Atom(clause_name!("!"), None));
+            &SystemClauseType::SetDoubleQuotes => {
+                let atom = cell_as_atom!(self.registers[1]);
 
-                            (self.unify_fn)(self, a1, a2);
-                        } else {
-                            let a2 = self
-                                .heap
-                                .to_unifiable(HeapCellValue::Atom(clause_name!("true"), None));
-
-                            (self.unify_fn)(self, a1, a2);
-                        }
-                    }
+                self.flags.double_quotes = match atom {
+                    atom!("atom") => DoubleQuotes::Atom,
+                    atom!("chars") => DoubleQuotes::Chars,
+                    atom!("codes") => DoubleQuotes::Codes,
                     _ => {
                         self.fail = true;
+                        return Ok(());
                     }
+                };
+            }
+            &SystemClauseType::InferenceLevel => {
+                let a1 = self.registers[1];
+                let a2 = self.store(self.deref(self.registers[2]));
+
+                let bp = cell_as_fixnum!(a2).get_num() as usize;
+                let prev_b = self.stack.index_or_frame(self.b).prelude.b;
+
+                if prev_b <= bp {
+                    self.unify_atom(atom!("!"), a1)
+                } else {
+                    self.unify_atom(atom!("true"), a1);
                 }
             }
             &SystemClauseType::CleanUpBlock => {
-                let nb = self.store(self.deref(self[temp_v!(1)]));
+                let nb = self.store(self.deref(self.registers[1]));
+                let nb = cell_as_fixnum!(nb).get_num() as usize;
 
-                match nb {
-                    Addr::Usize(nb) => {
-                        let b = self.b;
+                let b = self.b;
 
-                        if nb > 0 && self.stack.index_or_frame(b).prelude.b == nb {
-                            self.b = self.stack.index_or_frame(nb).prelude.b;
-                        }
-                    }
-                    _ => {
-                        self.fail = true;
-                    }
-                };
+                if nb > 0 && self.stack.index_or_frame(b).prelude.b == nb {
+                    self.b = self.stack.index_or_frame(nb).prelude.b;
+                }
             }
             &SystemClauseType::EraseBall => {
                 self.ball.reset();
@@ -3854,10 +3552,10 @@ impl MachineState {
                 self.fail = true;
             }
             &SystemClauseType::GetBall => {
-                let addr = self.store(self.deref(self[temp_v!(1)]));
-                let h = self.heap.h();
+                let addr = self.store(self.deref(self.registers[1]));
+                let h = self.heap.len();
 
-                if self.ball.stub.h() > 0 {
+                if self.ball.stub.len() > 0 {
                     let stub = self.ball.copy_and_align(h);
                     self.heap.extend(stub.into_iter());
                 } else {
@@ -3865,96 +3563,87 @@ impl MachineState {
                     return Ok(());
                 }
 
-                let ball = self.heap[h].as_addr(h);
-
                 match addr.as_var() {
-                    Some(r) => self.bind(r, ball),
+                    Some(r) => self.bind(r, self.heap[h]),
                     _ => self.fail = true,
                 };
             }
             &SystemClauseType::GetCurrentBlock => {
-                let c = Constant::Usize(self.block);
-                let addr = self[temp_v!(1)];
-
-                self.write_constant_to_var(addr, &c);
+                let n = Fixnum::build_with(i64::try_from(self.block).unwrap());
+                self.unify_fixnum(n, self.registers[1]);
             }
             &SystemClauseType::GetBValue => {
-                let a1 = self[temp_v!(1)];
-                let a2 = Addr::Usize(self.b);
-
-                (self.unify_fn)(self, a1, a2);
+                let n = Fixnum::build_with(i64::try_from(self.b).unwrap());
+                self.unify_fixnum(n, self.registers[1]);
             }
             &SystemClauseType::GetCutPoint => {
-                let a1 = self[temp_v!(1)];
-                let a2 = Addr::CutPoint(self.b0);
-
-                (self.unify_fn)(self, a1, a2);
+                let n = Fixnum::build_with(i64::try_from(self.b0).unwrap());
+                self.unify_fixnum(n, self.registers[1]);
             }
             &SystemClauseType::InstallNewBlock => {
-                self.install_new_block(temp_v!(1));
+                self.install_new_block(self.registers[1]);
             }
             &SystemClauseType::NextEP => {
-                let first_arg = self.store(self.deref(self[temp_v!(1)]));
+                let first_arg = self.store(self.deref(self.registers[1]));
 
-                match first_arg {
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(ref name, _) = self.heap.clone(h) {
-                            if name.as_str() == "first" {
-                                if self.e == 0 {
-                                    self.fail = true;
-                                    return Ok(());
-                                }
+                read_heap_cell!(first_arg,
+                    (HeapCellValueTag::Atom, (name, arity)) => {
+                        debug_assert_eq!(name, atom!("first"));
+                        debug_assert_eq!(arity, 0);
+
+                        if self.e == 0 {
+                            self.fail = true;
+                            return Ok(());
+                        }
 
-                                let cp =
-                                    (self.stack.index_and_frame(self.e).prelude.cp - 1).unwrap();
+                        let and_frame = self.stack.index_and_frame(self.e);
+                        let cp = (and_frame.prelude.cp - 1).unwrap();
 
-                                let e = self.stack.index_and_frame(self.e).prelude.e;
-                                let e = Addr::Usize(e);
+                        let e = and_frame.prelude.e;
+                        let e = Fixnum::build_with(i64::try_from(e).unwrap());
 
-                                let p = cp.as_functor(&mut self.heap);
+                        let p = str_loc_as_cell!(self.heap.len());
 
-                                (self.unify_fn)(self, self[temp_v!(2)], e);
+                        self.heap.extend(cp.as_functor());
+                        self.unify_fixnum(e, self.registers[2]);
 
-                                if !self.fail {
-                                    (self.unify_fn)(self, self[temp_v!(3)], p);
-                                }
-                            } else {
-                                unreachable!()
-                            }
-                        } else {
-                            unreachable!()
+                        if !self.fail {
+                            unify!(self, p, self.registers[3]);
                         }
                     }
-                    Addr::Usize(e) => {
+                    (HeapCellValueTag::Fixnum, n) => {
+                        let e = n.get_num() as usize;
+
                         if e == 0 {
                             self.fail = true;
                             return Ok(());
                         }
 
-                        // get the call site so that the number of active permanent variables can be read
-                        // from it later.
-                        let cp = (self.stack.index_and_frame(e).prelude.cp - 1).unwrap();
+                        // get the call site so that the number of
+                        // active permanent variables can be read from
+                        // it later.
+                        let and_frame = self.stack.index_and_frame(e);
+                        let cp = (and_frame.prelude.cp - 1).unwrap();
 
-                        let p = cp.as_functor(&mut self.heap);
-                        let e = self.stack.index_and_frame(e).prelude.e;
+                        let p = str_loc_as_cell!(self.heap.len());
+                        self.heap.extend(cp.as_functor());
 
-                        let e = Addr::Usize(e);
-
-                        (self.unify_fn)(self, self[temp_v!(2)], e);
+                        let e = Fixnum::build_with(i64::try_from(and_frame.prelude.e).unwrap());
+                        self.unify_fixnum(e, self.registers[2]);
 
                         if !self.fail {
-                            (self.unify_fn)(self, self[temp_v!(3)], p);
+                            unify!(self, p, self.registers[3]);
                         }
                     }
                     _ => {
-                        unreachable!()
+                        unreachable!();
                     }
-                }
+                );
             }
             &SystemClauseType::PointsToContinuationResetMarker => {
-                let addr = self.store(self.deref(self[temp_v!(1)]));
+                let addr = self.store(self.deref(self.registers[1]));
 
-                let p = match self.heap.to_local_code_ptr(&addr) {
+                let p = match to_local_code_ptr(&self.heap, addr) {
                     Some(p) => p + 1,
                     None => {
                         self.fail = true;
@@ -3970,11 +3659,11 @@ impl MachineState {
                 return Ok(());
             }
             &SystemClauseType::QuotedToken => {
-                let addr = self.store(self.deref(self[temp_v!(1)]));
+                let addr = self.store(self.deref(self.registers[1]));
 
-                match addr {
-                    Addr::Fixnum(n) => {
-                        let n = u32::try_from(n).ok();
+                read_heap_cell!(addr,
+                    (HeapCellValueTag::Fixnum, n) => {
+                        let n = u32::try_from(n.get_num()).ok();
                         let n = n.and_then(std::char::from_u32);
 
                         self.fail = match n {
@@ -3982,111 +3671,96 @@ impl MachineState {
                             None => true,
                         };
                     }
-                    Addr::Char(c) => {
+                    (HeapCellValueTag::Char, c) => {
                         self.fail = non_quoted_token(once(c));
                     }
-                    Addr::Con(h) => {
-                        if let HeapCellValue::Atom(atom, _) = &self.heap[h] {
-                            self.fail = non_quoted_token(atom.as_str().chars());
-                        }
+                    (HeapCellValueTag::Atom, (name, arity)) => {
+                        debug_assert_eq!(arity, 0);
+                        self.fail = non_quoted_token(name.as_str().chars());
                     }
                     _ => {
                         self.fail = true;
                     }
-                }
+                );
             }
             &SystemClauseType::ReadQueryTerm => {
                 current_input_stream.reset();
 
-                readline::set_prompt(true);
-                let result = self.read_term(current_input_stream.clone(), indices);
-                readline::set_prompt(false);
+                set_prompt(true);
+                let result = self.read_term(*current_input_stream, indices);
+                set_prompt(false);
 
                 match result {
                     Ok(()) => {}
                     Err(e) => {
-                        *current_input_stream = readline::input_stream();
+                        *current_input_stream = input_stream(&mut self.arena);
                         return Err(e);
                     }
                 }
             }
             &SystemClauseType::ReadTerm => {
-                readline::set_prompt(false);
+                set_prompt(false);
 
                 let stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "read_term",
+                    atom!("read_term"),
                     3,
                 )?;
 
                 self.read_term(stream, indices)?;
             }
             &SystemClauseType::ReadTermFromChars => {
-                let mut heap_pstr_iter = self.heap_pstr_iter(self[temp_v!(1)]);
-                let chars = heap_pstr_iter.to_string();
-
-                if let Addr::EmptyList = heap_pstr_iter.focus() {
-                    let term_write_result = match self.read(
-                        Stream::from(chars),
-                        self.atom_tbl.clone(),
-                        &indices.op_dir,
-                    ) {
+                if let Some(atom_or_string) = self.value_to_str_like(self.registers[1]) {
+                    let chars = atom_or_string.to_string();
+                    let stream = Stream::from_owned_string(chars, &mut self.arena);
+
+                    let term_write_result = match self.read(stream, &indices.op_dir) {
                         Ok(term_write_result) => term_write_result,
                         Err(e) => {
-                            let stub =
-                                MachineError::functor_stub(clause_name!("read_term_from_chars"), 2);
-
-                            let h = self.heap.h();
-                            let e = MachineError::session_error(h, SessionError::from(e));
+                            let stub = functor_stub(atom!("read_term_from_chars"), 2);
+                            let e = self.session_error(SessionError::from(e));
 
                             return Err(self.error_form(e, stub));
                         }
                     };
 
-                    let result = Addr::HeapCell(term_write_result.heap_loc);
+                    let result = heap_loc_as_cell!(term_write_result.heap_loc);
+                    let var = self.store(self.deref(self.registers[2])).as_var().unwrap();
 
-                    if let Some(var) = self.store(self.deref(self[temp_v!(2)])).as_var() {
-                        self.bind(var, result);
-                    } else {
-                        unreachable!()
-                    }
+                    self.bind(var, result);
                 } else {
                     unreachable!()
                 }
             }
             &SystemClauseType::ResetBlock => {
-                let addr = self.deref(self[temp_v!(1)]);
+                let addr = self.deref(self.registers[1]);
                 self.reset_block(addr);
             }
             &SystemClauseType::ResetContinuationMarker => {
-                self[temp_v!(3)] = self
-                    .heap
-                    .to_unifiable(HeapCellValue::Atom(clause_name!("none"), None));
+                self.registers[3] = atom_as_cell!(atom!("none"));
 
-                let h = self.heap.h();
+                let h = self.heap.len();
+                self.heap.push(heap_loc_as_cell!(h));
 
-                self.heap.push(HeapCellValue::Addr(Addr::HeapCell(h)));
-                self[temp_v!(4)] = Addr::HeapCell(h);
+                self.registers[4] = heap_loc_as_cell!(h);
             }
             &SystemClauseType::SetBall => {
                 self.set_ball();
             }
             &SystemClauseType::SetSeed => {
-                let seed = self.store(self.deref(self[temp_v!(1)]));
+                let seed = self.store(self.deref(self.registers[1]));
+                let mut rand = RANDOM_STATE.borrow_mut();
 
-                let seed = match Number::try_from((seed, &self.heap)) {
-                    Ok(Number::Fixnum(n)) => Integer::from(n),
-                    Ok(Number::Integer(n)) => Integer::from(n.as_ref()),
-                    Ok(Number::Rational(n)) if n.denom() == &1 => n.numer().clone(),
+                match Number::try_from(seed) {
+                    Ok(Number::Fixnum(n)) => rand.seed(&Integer::from(n)),
+                    Ok(Number::Integer(n)) => rand.seed(&*n),
+                    Ok(Number::Rational(n)) if n.denom() == &1 => rand.seed(n.numer()),
                     _ => {
                         self.fail = true;
                         return Ok(());
                     }
-                };
-
-                let mut rand = RANDOM_STATE.borrow_mut();
-                rand.seed(&seed);
+                }
             }
             &SystemClauseType::SkipMaxList => {
                 if let Err(err) = self.skip_max_list() {
@@ -4094,11 +3768,11 @@ impl MachineState {
                 }
             }
             &SystemClauseType::Sleep => {
-                let time = self.store(self.deref(self[temp_v!(1)]));
+                let time = self.store(self.deref(self.registers[1]));
 
-                let time = match Number::try_from((time, &self.heap)) {
-                    Ok(Number::Float(OrderedFloat(n))) => n,
-                    Ok(Number::Fixnum(n)) => n as f64,
+                let time = match Number::try_from(time) {
+                    Ok(Number::Float(n)) => n.into_inner(),
+                    Ok(Number::Fixnum(n)) => n.get_num() as f64,
                     Ok(Number::Integer(n)) => n.to_f64(),
                     _ => {
                         unreachable!()
@@ -4107,96 +3781,78 @@ impl MachineState {
 
                 let duration = Duration::new(1, 0);
                 let duration = duration.mul_f64(time);
-                ::std::thread::sleep(duration);
+
+                std::thread::sleep(duration);
             }
             &SystemClauseType::SocketClientOpen => {
-                let addr = self.store(self.deref(self[temp_v!(1)]));
-                let port = self.store(self.deref(self[temp_v!(2)]));
+                let addr = self.store(self.deref(self.registers[1]));
+                let port = self.store(self.deref(self.registers[2]));
 
-                let socket_atom = match addr {
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(ref name, _) = &self.heap[h] {
-                            name.clone()
-                        } else {
-                            unreachable!()
-                        }
+                let socket_atom = cell_as_atom!(addr);
+
+                let _port = read_heap_cell!(port,
+                    (HeapCellValueTag::Atom, (name, arity)) => {
+                        debug_assert_eq!(arity, 0);
+                        name
                     }
                     _ => {
-                        unreachable!()
+                        self.atom_tbl.build_with(&match Number::try_from(port) {
+                            Ok(Number::Fixnum(n)) => n.get_num().to_string(),
+                            Ok(Number::Integer(n)) => n.to_string(),
+                            _ => {
+                                unreachable!()
+                            }
+                        })
                     }
-                };
+                );
 
-                let port = match port {
-                    Addr::Fixnum(n) => n.to_string(),
-                    Addr::Usize(n) => n.to_string(),
-                    Addr::Con(h) => match &self.heap[h] {
-                        HeapCellValue::Atom(ref name, _) => name.as_str().to_string(),
-                        HeapCellValue::Integer(ref n) => n.to_string(),
-                        _ => {
-                            unreachable!()
-                        }
-                    },
-                    _ => {
-                        unreachable!()
-                    }
+                let socket_addr = if socket_atom == atom!("") {
+                    atom!("127.0.0.1")
+                } else {
+                    socket_atom
                 };
 
-                let socket_addr = format!(
-                    "{}:{}",
-                    if socket_atom.as_str() == "" {
-                        "127.0.0.1"
-                    } else {
-                        socket_atom.as_str()
-                    },
-                    port,
-                );
-
-                let alias = self[temp_v!(4)];
-                let eof_action = self[temp_v!(5)];
-                let reposition = self[temp_v!(6)];
-                let stream_type = self[temp_v!(7)];
+                let alias = self.registers[4];
+                let eof_action = self.registers[5];
+                let reposition = self.registers[6];
+                let stream_type = self.registers[7];
 
                 let options = self.to_stream_options(alias, eof_action, reposition, stream_type);
 
-                if options.reposition {
-                    return Err(self.reposition_error("socket_client_open", 3));
+                if options.reposition() {
+                    return Err(self.reposition_error(atom!("socket_client_open"), 3));
                 }
 
-                if let Some(ref alias) = &options.alias {
-                    if indices.stream_aliases.contains_key(alias) {
+                if let Some(alias) = options.get_alias() {
+                    if indices.stream_aliases.contains_key(&alias) {
                         return Err(self.occupied_alias_permission_error(
-                            alias.clone(),
-                            "socket_client_open",
+                            alias,
+                            atom!("socket_client_open"),
                             3,
                         ));
                     }
                 }
 
-                let stream = match TcpStream::connect(&socket_addr).map_err(|e| e.kind()) {
+                let stream = match TcpStream::connect(socket_addr.as_str()).map_err(|e| e.kind()) {
                     Ok(tcp_stream) => {
-                        let socket_addr = clause_name!(socket_addr, self.atom_tbl);
-
-                        let mut stream = Stream::from_tcp_stream(socket_addr, tcp_stream);
+                        let mut stream = Stream::from_tcp_stream(socket_addr, tcp_stream, &mut self.arena);
 
                         *stream.options_mut() = options;
 
-                        if let Some(ref alias) = &stream.options().alias {
-                            indices.stream_aliases.insert(alias.clone(), stream.clone());
+                        if let Some(alias) = stream.options().get_alias() {
+                            indices.stream_aliases.insert(alias, stream);
                         }
 
-                        indices.streams.insert(stream.clone());
+                        indices.streams.insert(stream);
 
-                        self.heap.to_unifiable(HeapCellValue::Stream(stream))
+                        stream_as_cell!(stream)
                     }
                     Err(ErrorKind::PermissionDenied) => {
-                        return Err(self.open_permission_error(addr, "socket_client_open", 3));
+                        return Err(self.open_permission_error(addr, atom!("socket_client_open"), 3));
                     }
                     Err(ErrorKind::NotFound) => {
-                        let stub =
-                            MachineError::functor_stub(clause_name!("socket_client_open"), 3);
-
-                        let err = MachineError::existence_error(
-                            self.heap.h(),
+                        let stub = functor_stub(atom!("socket_client_open"), 3);
+                        let err = self.existence_error(
                             ExistenceError::SourceSink(addr),
                         );
 
@@ -4209,45 +3865,39 @@ impl MachineState {
                     }
                 };
 
-                let stream_addr = self.store(self.deref(self[temp_v!(3)]));
+                let stream_addr = self.store(self.deref(self.registers[3]));
                 self.bind(stream_addr.as_var().unwrap(), stream);
             }
             &SystemClauseType::SocketServerOpen => {
-                let addr = self.store(self.deref(self[temp_v!(1)]));
-                let socket_atom = match addr {
-                    Addr::EmptyList => "127.0.0.1".to_string(),
-                    Addr::Con(h) if self.heap.atom_at(h) => match &self.heap[h] {
-                        HeapCellValue::Atom(ref name, _) => name.as_str().to_string(),
-                        _ => {
-                            unreachable!()
-                        }
-                    },
-                    _ => {
-                        unreachable!()
-                    }
+                let addr = self.store(self.deref(self.registers[1]));
+                let socket_atom = cell_as_atom_cell!(addr).get_name();
+
+                let socket_atom = if socket_atom == atom!("[]") {
+                    atom!("127.0.0.1")
+                } else {
+                    socket_atom
                 };
 
-                let port = match self.store(self.deref(self[temp_v!(2)])) {
-                    Addr::Fixnum(n) => n.to_string(),
-                    Addr::Usize(n) => n.to_string(),
-                    Addr::Con(h) => match &self.heap[h] {
-                        HeapCellValue::Integer(ref n) => n.to_string(),
+                let port = self.store(self.deref(self.registers[2]));
+
+                let port = if port.is_var() {
+                    String::from("0")
+                } else {
+                    match Number::try_from(port) {
+                        Ok(Number::Fixnum(n)) => n.get_num().to_string(),
+                        Ok(Number::Integer(n)) => n.to_string(),
                         _ => {
                             unreachable!()
                         }
-                    },
-                    addr if addr.is_ref() => "0".to_string(),
-                    _ => {
-                        unreachable!()
                     }
                 };
 
                 let had_zero_port = &port == "0";
 
-                let server_addr = if socket_atom.is_empty() {
+                let server_addr = if socket_atom == atom!("") {
                     port
                 } else {
-                    format!("{}:{}", socket_atom, port)
+                    format!("{}:{}", socket_atom.as_str(), port)
                 };
 
                 let (tcp_listener, port) =
@@ -4256,18 +3906,14 @@ impl MachineState {
                             let port = tcp_listener.local_addr().map(|addr| addr.port()).ok();
 
                             if let Some(port) = port {
-                                (
-                                    self.heap
-                                        .to_unifiable(HeapCellValue::TcpListener(tcp_listener)),
-                                    port as usize,
-                                )
+                                (arena_alloc!(tcp_listener, &mut self.arena), port as usize)
                             } else {
                                 self.fail = true;
                                 return Ok(());
                             }
                         }
                         Err(ErrorKind::PermissionDenied) => {
-                            return Err(self.open_permission_error(addr, "socket_server_open", 2));
+                            return Err(self.open_permission_error(addr, atom!("socket_server_open"), 2));
                         }
                         _ => {
                             self.fail = true;
@@ -4275,334 +3921,313 @@ impl MachineState {
                         }
                     };
 
-                let addr = self.store(self.deref(self[temp_v!(3)]));
-                self.bind(addr.as_var().unwrap(), tcp_listener);
+                let addr = self.store(self.deref(self.registers[3]));
+                self.bind(addr.as_var().unwrap(), typed_arena_ptr_as_cell!(tcp_listener));
 
                 if had_zero_port {
-                    (self.unify_fn)(self, self[temp_v!(2)], Addr::Usize(port));
+                    self.unify_fixnum(Fixnum::build_with(port as i64), self.registers[2]);
                 }
             }
             &SystemClauseType::SocketServerAccept => {
-                let alias = self[temp_v!(4)];
-                let eof_action = self[temp_v!(5)];
-                let reposition = self[temp_v!(6)];
-                let stream_type = self[temp_v!(7)];
+                let alias = self.registers[4];
+                let eof_action = self.registers[5];
+                let reposition = self.registers[6];
+                let stream_type = self.registers[7];
 
                 let options = self.to_stream_options(alias, eof_action, reposition, stream_type);
 
-                if options.reposition {
-                    return Err(self.reposition_error("socket_server_accept", 4));
+                if options.reposition() {
+                    return Err(self.reposition_error(atom!("socket_server_accept"), 4));
                 }
 
-                if let Some(ref alias) = &options.alias {
-                    if indices.stream_aliases.contains_key(alias) {
+                if let Some(alias) = options.get_alias() {
+                    if indices.stream_aliases.contains_key(&alias) {
                         return Err(self.occupied_alias_permission_error(
-                            alias.clone(),
-                            "socket_server_accept",
+                            alias,
+                            atom!("socket_server_accept"),
                             4,
                         ));
                     }
                 }
 
-                match self.store(self.deref(self[temp_v!(1)])) {
-                    Addr::TcpListener(h) => match &mut self.heap[h] {
-                        HeapCellValue::TcpListener(ref mut tcp_listener) => {
-                            match tcp_listener.accept().ok() {
-                                Some((tcp_stream, socket_addr)) => {
-                                    let client =
-                                        clause_name!(format!("{}", socket_addr), self.atom_tbl);
+                let culprit = self.store(self.deref(self.registers[1]));
 
-                                    let mut tcp_stream =
-                                        Stream::from_tcp_stream(client.clone(), tcp_stream);
+                read_heap_cell!(culprit,
+                    (HeapCellValueTag::Cons, cons_ptr) => {
+                        match_untyped_arena_ptr!(cons_ptr,
+                             (ArenaHeaderTag::TcpListener, tcp_listener) => {
+                                 match tcp_listener.accept().ok() {
+                                     Some((tcp_stream, socket_addr)) => {
+                                         let client = self.atom_tbl.build_with(&socket_addr.to_string());
 
-                                    *tcp_stream.options_mut() = options;
+                                         let mut tcp_stream = Stream::from_tcp_stream(
+                                             client,
+                                             tcp_stream,
+                                             &mut self.arena,
+                                         );
 
-                                    if let Some(ref alias) = &tcp_stream.options().alias {
-                                        indices
-                                            .stream_aliases
-                                            .insert(alias.clone(), tcp_stream.clone());
-                                    }
+                                         *tcp_stream.options_mut() = options;
 
-                                    indices.streams.insert(tcp_stream.clone());
+                                         if let Some(alias) = &tcp_stream.options().get_alias() {
+                                             indices.stream_aliases.insert(*alias, tcp_stream);
+                                         }
 
-                                    let tcp_stream =
-                                        self.heap.to_unifiable(HeapCellValue::Stream(tcp_stream));
+                                         indices.streams.insert(tcp_stream);
 
-                                    let client =
-                                        self.heap.to_unifiable(HeapCellValue::Atom(client, None));
+                                         let tcp_stream = stream_as_cell!(tcp_stream);
+                                         let client = atom_as_cell!(client);
 
-                                    let client_addr = self.store(self.deref(self[temp_v!(2)]));
-                                    let stream_addr = self.store(self.deref(self[temp_v!(3)]));
+                                         let client_addr = self.store(self.deref(self.registers[2]));
+                                         let stream_addr = self.store(self.deref(self.registers[3]));
 
-                                    self.bind(client_addr.as_var().unwrap(), client);
-                                    self.bind(stream_addr.as_var().unwrap(), tcp_stream);
-                                }
-                                None => {
-                                    self.fail = true;
-                                    return Ok(());
-                                }
-                            }
-                        }
-                        culprit => {
-                            let culprit = culprit.as_addr(h);
-
-                            return Err(self.type_error(
-                                ValidType::TcpListener,
-                                culprit,
-                                clause_name!("socket_server_accept"),
-                                4,
-                            ));
-                        }
-                    },
-                    culprit => {
-                        return Err(self.type_error(
-                            ValidType::TcpListener,
-                            culprit,
-                            clause_name!("socket_server_accept"),
-                            4,
-                        ));
+                                         self.bind(client_addr.as_var().unwrap(), client);
+                                         self.bind(stream_addr.as_var().unwrap(), tcp_stream);
+
+                                         return return_from_clause!(self.last_call, self);
+                                     }
+                                     None => {
+                                         self.fail = true;
+                                         return Ok(());
+                                     }
+                                 }
+                             }
+                             _ => {
+                             }
+                        );
                     }
-                }
-            }
-            &SystemClauseType::SocketServerClose => {
-                match self.store(self.deref(self[temp_v!(1)])) {
-                    Addr::TcpListener(h) => {
-                        let closed_tcp_listener = clause_name!("$closed_tcp_listener");
-                        self.heap[h] = HeapCellValue::Atom(closed_tcp_listener, None);
-                    }
-                    culprit => {
-                        return Err(self.type_error(
-                            ValidType::TcpListener,
-                            culprit,
-                            clause_name!("socket_server_close"),
-                            1,
-                        ));
+                    _ => {
                     }
-                }
+                );
             }
             &SystemClauseType::TLSClientConnect => {
-                let hostname = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
-
-                let stream0 = self.get_stream_or_alias(
-                    self[temp_v!(2)],
-                    &indices.stream_aliases,
-                    "tls_client_negotiate",
-                    3,
-                )?;
-
-                let connector = TlsConnector::new().unwrap();
-                let stream =
-                    match connector.connect(&hostname, stream0) {
-                        Ok(tls_stream) => tls_stream,
-                        Err(_) => {
-                            return Err(self.open_permission_error(
-                                self[temp_v!(1)],
-                                "tls_client_negotiate",
-                                3,
-                            ));
-                        }
-                    };
+                if let Some(hostname) = self.value_to_str_like(self.registers[1]) {
+                    let stream0 = self.get_stream_or_alias(
+                        self.registers[2],
+                        &indices.stream_aliases,
+                        atom!("tls_client_negotiate"),
+                        3,
+                    )?;
+
+                    let connector = TlsConnector::new().unwrap();
+                    let stream =
+                        match connector.connect(hostname.as_str(), stream0) {
+                            Ok(tls_stream) => tls_stream,
+                            Err(_) => {
+                                return Err(self.open_permission_error(
+                                    self[temp_v!(1)],
+                                    atom!("tls_client_negotiate"),
+                                    3,
+                                ));
+                            }
+                        };
 
-                let addr = clause_name!("TLS".to_string(), self.atom_tbl);
-                let stream = Stream::from_tls_stream(addr, stream);
-                indices.streams.insert(stream.clone());
+                    let addr = atom!("TLS");
+                    let stream = Stream::from_tls_stream(addr, stream, &mut self.arena);
+                    indices.streams.insert(stream);
 
-                let stream = self.heap.to_unifiable(HeapCellValue::Stream(stream));
-                let stream_addr = self.store(self.deref(self[temp_v!(3)]));
-                self.bind(stream_addr.as_var().unwrap(), stream);
+                    self.heap.push(stream_as_cell!(stream));
+                    let stream_addr = self.store(self.deref(self.registers[3]));
+                    self.bind(stream_addr.as_var().unwrap(), stream_as_cell!(stream));
+                } else {
+                    unreachable!();
+                }
             }
             &SystemClauseType::TLSAcceptClient => {
-                let pkcs12 = self.string_encoding_bytes(1, "octet");
-                let password = self.heap_pstr_iter(self[temp_v!(2)]).to_string();
-                let identity =
-                    match Identity::from_pkcs12(&pkcs12, &password) {
-                        Ok(identity) => identity,
-                        Err(_) => {
-                            return Err(self.open_permission_error(
-                                self[temp_v!(1)],
-                                "tls_server_negotiate",
-                                3,
-                            ));
-                        }
-                    };
-
-                let stream0 = self.get_stream_or_alias(
-                    self[temp_v!(3)],
-                    &indices.stream_aliases,
-                    "tls_server_negotiate",
-                    3,
-                )?;
-
-                let acceptor = TlsAcceptor::new(identity).unwrap();
-
-                let stream =
-                    match acceptor.accept(stream0) {
-                        Ok(tls_stream) => tls_stream,
-                        Err(_) => {
-                            return Err(self.open_permission_error(
-                                self[temp_v!(3)],
-                                "tls_server_negotiate",
-                                3,
-                            ));
-                        }
-                    };
-                let addr = clause_name!("TLS".to_string(), self.atom_tbl);
-                let stream = Stream::from_tls_stream(addr, stream);
-                indices.streams.insert(stream.clone());
-
-                let stream = self.heap.to_unifiable(HeapCellValue::Stream(stream));
-                let stream_addr = self.store(self.deref(self[temp_v!(4)]));
-                self.bind(stream_addr.as_var().unwrap(), stream);
-            }
-            &SystemClauseType::SetStreamPosition => {
-                let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
-                    &indices.stream_aliases,
-                    "set_stream_position",
-                    2,
-                )?;
+                let pkcs12 = self.string_encoding_bytes(self.registers[1], atom!("octet"));
+
+                if let Some(password) = self.value_to_str_like(self.registers[2]) {
+                    let identity =
+                        match Identity::from_pkcs12(&pkcs12, password.as_str()) {
+                            Ok(identity) => identity,
+                            Err(_) => {
+                                return Err(self.open_permission_error(
+                                    self.registers[1],
+                                    atom!("tls_server_negotiate"),
+                                    3,
+                                ));
+                            }
+                        };
 
-                if !stream.options().reposition {
-                    let stub = MachineError::functor_stub(clause_name!("set_stream_position"), 2);
+                    let stream0 = self.get_stream_or_alias(
+                        self.registers[3],
+                        &indices.stream_aliases,
+                        atom!("tls_server_negotiate"),
+                        3,
+                    )?;
+
+                    let acceptor = TlsAcceptor::new(identity).unwrap();
+
+                    let stream =
+                        match acceptor.accept(stream0) {
+                            Ok(tls_stream) => tls_stream,
+                            Err(_) => {
+                                return Err(self.open_permission_error(
+                                    self.registers[3],
+                                    atom!("tls_server_negotiate"),
+                                    3,
+                                ));
+                            }
+                        };
 
-                    let err = MachineError::permission_error(
-                        self.heap.h(),
-                        Permission::Reposition,
-                        "stream",
-                        vec![HeapCellValue::Stream(stream)],
-                    );
+                    let stream = Stream::from_tls_stream(atom!("TLS"), stream, &mut self.arena);
+                    indices.streams.insert(stream);
 
-                    return Err(self.error_form(err, stub));
+                    let stream_addr = self.store(self.deref(self.registers[4]));
+                    self.bind(stream_addr.as_var().unwrap(), stream_as_cell!(stream));
+                } else {
+                    unreachable!();
                 }
+            }
+            &SystemClauseType::SocketServerClose => {
+                let culprit = self.store(self.deref(self.registers[1]));
+
+                read_heap_cell!(culprit,
+                    (HeapCellValueTag::Cons, cons_ptr) => {
+                        match_untyped_arena_ptr!(cons_ptr,
+                            (ArenaHeaderTag::TcpListener, tcp_listener) => {
+                                unsafe {
+                                    // dropping closes the instance.
+                                    std::ptr::drop_in_place(&mut tcp_listener as *mut _);
+                                }
 
-                let position = self.store(self.deref(self[temp_v!(2)]));
-
-                let position = match Number::try_from((position, &self.heap)) {
-                    Ok(Number::Fixnum(n)) => n as u64,
-                    Ok(Number::Integer(n)) => {
-                        if let Some(n) = n.to_u64() {
-                            n
-                        } else {
-                            self.fail = true;
-                            return Ok(());
-                        }
+                                tcp_listener.set_tag(ArenaHeaderTag::Dropped);
+                                return return_from_clause!(self.last_call, self);
+                            }
+                            _ => {
+                            }
+                        );
                     }
                     _ => {
-                        unreachable!()
                     }
-                };
+                );
 
-                stream.set_position(position);
+                let err = self.type_error(ValidType::TcpListener, culprit);
+                let stub = functor_stub(atom!("socket_server_close"), 1);
+
+                return Err(self.error_form(err, stub));
             }
-            &SystemClauseType::StreamProperty => {
+            &SystemClauseType::SetStreamPosition => {
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "stream_property",
+                    atom!("set_stream_position"),
                     2,
                 )?;
 
-                let property = match self.store(self.deref(self[temp_v!(2)])) {
-                    Addr::Con(h) if self.heap.atom_at(h) => match &self.heap[h] {
-                        HeapCellValue::Atom(ref name, _) => match name.as_str() {
-                            "file_name" => {
-                                if let Some(file_name) = stream.file_name() {
-                                    HeapCellValue::Atom(file_name, None)
-                                } else {
-                                    self.fail = true;
-                                    return Ok(());
-                                }
-                            }
-                            "mode" => HeapCellValue::Atom(clause_name!(stream.mode()), None),
-                            "direction" => HeapCellValue::Atom(
-                                if stream.is_input_stream() && stream.is_output_stream() {
-                                    clause_name!("input_output")
-                                } else if stream.is_input_stream() {
-                                    clause_name!("input")
-                                } else {
-                                    clause_name!("output")
-                                },
-                                None,
-                            ),
-                            "alias" => {
-                                if let Some(alias) = &stream.options().alias {
-                                    HeapCellValue::Atom(alias.clone(), None)
-                                } else {
-                                    self.fail = true;
-                                    return Ok(());
-                                }
-                            }
-                            "position" => {
-                                if let Some((position, lines_read)) = stream.position() {
-                                    let h = self.heap.h();
-
-                                    let position_term = functor!(
-                                        "position_and_lines_read",
-                                        [integer(position), integer(lines_read)]
-                                    );
+                if !stream.options().reposition() {
+                    let stub = functor_stub(atom!("set_stream_position"), 2);
 
-                                    self.heap.extend(position_term.into_iter());
+                    let err = self.permission_error(
+                        Permission::Reposition,
+                        atom!("stream"),
+                        vec![stream_as_cell!(stream)],
+                    );
 
-                                    HeapCellValue::Addr(Addr::HeapCell(h))
-                                } else {
-                                    self.fail = true;
-                                    return Ok(());
-                                }
-                            }
-                            "end_of_stream" => {
-                                let end_of_stream_pos = stream.position_relative_to_end();
-                                HeapCellValue::Atom(clause_name!(end_of_stream_pos.as_str()), None)
-                            }
-                            "eof_action" => HeapCellValue::Atom(
-                                clause_name!(stream.options().eof_action.as_str()),
-                                None,
-                            ),
-                            "reposition" => HeapCellValue::Atom(
-                                clause_name!(if stream.options().reposition {
-                                    "true"
-                                } else {
-                                    "false"
-                                }),
-                                None,
-                            ),
-                            "type" => HeapCellValue::Atom(
-                                clause_name!(stream.options().stream_type.as_property_str()),
-                                None,
-                            ),
-                            _ => {
-                                unreachable!()
-                            }
-                        },
-                        _ => {
-                            unreachable!()
+                    return Err(self.error_form(err, stub));
+                }
+
+                let position = self.store(self.deref(self.registers[2]));
+
+                let position = match Number::try_from(position) {
+                    Ok(Number::Fixnum(n)) => n.get_num() as u64,
+                    Ok(Number::Integer(n)) => {
+                        if let Some(n) = n.to_u64() {
+                            n
+                        } else {
+                            self.fail = true;
+                            return Ok(());
                         }
-                    },
+                    }
                     _ => {
                         unreachable!()
                     }
                 };
 
-                let property = self.heap.to_unifiable(property);
-                (self.unify_fn)(self, self[temp_v!(3)], property);
+                stream.set_position(position);
             }
-            &SystemClauseType::StoreGlobalVar => {
-                let key = match self.store(self.deref(self[temp_v!(1)])) {
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
-                            atom.clone()
+            &SystemClauseType::StreamProperty => {
+                let mut stream = self.get_stream_or_alias(
+                    self.registers[1],
+                    &indices.stream_aliases,
+                    atom!("stream_property"),
+                    2,
+                )?;
+
+                let atom = cell_as_atom!(self.store(self.deref(self.registers[2])));
+
+                let property = match atom {
+                    atom!("file_name") => {
+                        atom_as_cell!(if let Some(file_name) = stream.file_name() {
+                            file_name
                         } else {
-                            unreachable!()
+                            self.fail = true;
+                            return Ok(());
+                        })
+                    }
+                    atom!("mode") => atom_as_cell!(stream.mode()),
+                    atom!("direction") =>
+                        atom_as_cell!(if stream.is_input_stream() && stream.is_output_stream() {
+                            atom!("input_output")
+                        } else if stream.is_input_stream() {
+                            atom!("input")
+                        } else {
+                            atom!("output")
+                        }),
+                    atom!("alias") => {
+                        atom_as_cell!(if let Some(alias) = stream.options().get_alias() {
+                            alias
+                        } else {
+                            self.fail = true;
+                            return Ok(());
+                        })
+                    }
+                    atom!("position") => {
+                        if let Some((position, lines_read)) = stream.position() {
+                            let h = self.heap.len();
+
+                            let position_term = functor!(
+                                atom!("position_and_lines_read"),
+                                [integer(position, &mut self.arena),
+                                 integer(lines_read, &mut self.arena)]
+                            );
+
+                            self.heap.extend(position_term.into_iter());
+                            str_loc_as_cell!(h)
+                        } else {
+                            self.fail = true;
+                            return Ok(());
                         }
                     }
+                    atom!("end_of_stream") => {
+                        let end_of_stream_pos = stream.position_relative_to_end();
+                        atom_as_cell!(end_of_stream_pos.as_atom())
+                    }
+                    atom!("eof_action") => {
+                        atom_as_cell!(stream.options().eof_action().as_atom())
+                    }
+                    atom!("reposition") =>
+                        atom_as_cell!(if stream.options().reposition() {
+                            atom!("true")
+                        } else {
+                            atom!("false")
+                        }),
+                    atom!("type") => {
+                        atom_as_cell!(stream.options().stream_type().as_property_atom())
+                    }
                     _ => {
                         unreachable!()
                     }
                 };
 
-                let value = self[temp_v!(2)];
+                unify!(self, property, self.registers[3]);
+            }
+            &SystemClauseType::StoreGlobalVar => {
+                let key = cell_as_atom!(self.store(self.deref(self.registers[1])));
+
+                let value = self.registers[2];
                 let mut ball = Ball::new();
 
-                ball.boundary = self.heap.h();
+                ball.boundary = self.heap.len();
 
                 copy_term(
                     CopyBallTerm::new(&mut self.stack, &mut self.heap, &mut ball.stub),
@@ -4613,74 +4238,101 @@ impl MachineState {
                 indices.global_variables.insert(key, (ball, None));
             }
             &SystemClauseType::StoreBacktrackableGlobalVar => {
-                let (key_h, key) = match self.store(self.deref(self[temp_v!(1)])) {
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
-                            (h, atom.clone())
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    _ => {
-                        unreachable!()
-                    }
-                };
-
-                let new_value = self.store(self.deref(self[temp_v!(2)]));
+                let key = cell_as_atom!(self.store(self.deref(self.registers[1])));
+                let new_value = self.store(self.deref(self.registers[2]));
 
                 match indices.global_variables.get_mut(&key) {
                     Some((_, ref mut loc)) => match loc {
                         Some(ref mut value) => {
-                            let old_value_loc = self.heap.push(HeapCellValue::Addr(*value));
-                            self.trail(TrailRef::BlackboardOffset(key_h, old_value_loc));
+                            self.trail(TrailRef::BlackboardOffset(key, *value));
                             *value = new_value;
                         }
                         loc @ None => {
-                            self.trail(TrailRef::BlackboardEntry(key_h));
+                            self.trail(TrailRef::BlackboardEntry(key));
                             *loc = Some(new_value);
                         }
                     },
                     None => {
-                        self.trail(TrailRef::BlackboardEntry(key_h));
+                        self.trail(TrailRef::BlackboardEntry(key));
                         indices
                             .global_variables
                             .insert(key, (Ball::new(), Some(new_value)));
                     }
                 }
             }
-            &SystemClauseType::Succeed => {}
             &SystemClauseType::TermAttributedVariables => {
-                let seen_vars = self.attr_vars_of_term(self[temp_v!(1)]);
-                let outcome = Addr::HeapCell(self.heap.to_list(seen_vars.into_iter()));
+                if self.registers[1].is_constant() {
+                    self.unify_atom(atom!("[]"), self.store(self.deref(self.registers[2])));
+                    return return_from_clause!(self.last_call, self);
+                }
+
+                let seen_vars = self.attr_vars_of_term(self.registers[1]);
+                let outcome = heap_loc_as_cell!(
+                    iter_to_heap_list(&mut self.heap, seen_vars.into_iter())
+                );
 
-                (self.unify_fn)(self, self[temp_v!(2)], outcome);
+                unify_fn!(self, self.registers[2], outcome);
             }
+            &SystemClauseType::Succeed => {}
             &SystemClauseType::TermVariables => {
-                let a1 = self[temp_v!(1)];
+                let a1 = self.registers[1];
+                let a2 = self.registers[2];
+
+                let stored_v = self.store(self.deref(a1));
+
+                if stored_v.is_constant() {
+                    self.unify_atom(atom!("[]"), self.store(self.deref(a2)));
+                    return return_from_clause!(self.last_call, self);
+                }
+
                 let mut seen_set = IndexSet::new();
-                let mut seen_vars = vec![];
 
-                for addr in self.acyclic_pre_order_iter(a1) {
-                    if addr.is_ref() && !seen_set.contains(&addr) {
-                        seen_vars.push(addr);
-                        seen_set.insert(addr);
+                {
+                    let mut iter = stackless_preorder_iter(&mut self.heap, stored_v);
+
+                    while let Some(addr) = iter.next() {
+                        let addr = unmark_cell_bits!(addr);
+
+                        if addr.is_var() {
+                            seen_set.insert(addr);
+                        }
                     }
                 }
 
-                let outcome = Addr::HeapCell(self.heap.to_list(seen_vars.into_iter()));
-                (self.unify_fn)(self, self[temp_v!(2)], outcome);
+                let outcome = heap_loc_as_cell!(
+                    filtered_iter_to_heap_list(
+                        &mut self.heap,
+                        seen_set.into_iter().rev(),
+                        |heap, value| {
+                            heap_bound_store(
+                                heap,
+                                heap_bound_deref(heap, value),
+                            ).is_var()
+                        },
+                    )
+                );
+
+                unify_fn!(self, a2, outcome);
+            }
+            &SystemClauseType::TermVariablesUnderMaxDepth => {
+                       // Term, MaxDepth, VarList
+                       let max_depth = cell_as_fixnum!(
+                           self.store(self.deref(self.registers[2]))
+                       ).get_num() as usize;
+
+                       self.term_variables_under_max_depth(self.registers[1], max_depth, self.registers[3]);
             }
             &SystemClauseType::TruncateLiftedHeapTo => {
-                match self.store(self.deref(self[temp_v!(1)])) {
-                    Addr::Usize(lh_offset) => self.lifted_heap.truncate(lh_offset),
-                    _ => self.fail = true,
-                }
+                let a1 = self.store(self.deref(self.registers[1]));
+                let lh_offset = cell_as_fixnum!(a1).get_num() as usize;
+
+                self.lifted_heap.truncate(lh_offset);
             }
             &SystemClauseType::UnifyWithOccursCheck => {
-                let a1 = self[temp_v!(1)];
-                let a2 = self[temp_v!(2)];
+                let a1 = self.registers[1];
+                let a2 = self.registers[2];
 
-                self.unify_with_occurs_check(a1, a2);
+                unify_with_occurs_check!(self, a1, a2);
             }
             &SystemClauseType::UnwindEnvironments => {
                 let mut e = self.e;
@@ -4694,66 +4346,52 @@ impl MachineState {
                         return Ok(());
                     }
 
-                    cp = self.stack.index_and_frame(e).prelude.cp;
-                    e = self.stack.index_and_frame(e).prelude.e;
+                    let and_frame = self.stack.index_and_frame(e);
+
+                    cp = and_frame.prelude.cp;
+                    e = and_frame.prelude.e;
                 }
             }
             &SystemClauseType::UnwindStack => {
                 self.unwind_stack();
             }
+            /*
             &SystemClauseType::Variant => {
                 self.fail = self.structural_eq_test();
             }
+            */
             &SystemClauseType::WAMInstructions => {
-                let module_name = atom_from!(self, self.store(self.deref(self[temp_v!(1)])));
-
-                let name = self[temp_v!(2)];
-                let arity = self[temp_v!(3)];
+                let module_name = cell_as_atom!(self.store(self.deref(self.registers[1])));
 
-                let name = match self.store(self.deref(name)) {
-                    Addr::Con(h) if self.heap.atom_at(h) => {
-                        if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
-                            atom.clone()
-                        } else {
-                            unreachable!()
-                        }
-                    }
-                    _ => {
-                        unreachable!()
-                    }
-                };
+                let name = self.registers[2];
+                let arity = self.registers[3];
 
+                let name = cell_as_atom!(self.store(self.deref(name)));
                 let arity = self.store(self.deref(arity));
 
-                let arity = match Number::try_from((arity, &self.heap)) {
-                    Ok(Number::Fixnum(n)) => Integer::from(n),
-                    Ok(Number::Integer(n)) => Integer::from(n.as_ref()),
+                let arity = match Number::try_from(arity) {
+                    Ok(Number::Fixnum(n)) => n.get_num() as usize,
+                    Ok(Number::Integer(n)) => n.to_usize().unwrap(),
                     _ => {
                         unreachable!()
                     }
                 };
 
-                let key = (name.clone(), arity.to_usize().unwrap());
+                let key = (name, arity);
 
-                let first_idx = match module_name.as_str() {
-                    "user" => indices.code_dir.get(&key),
+                let first_idx = match module_name {
+                    atom!("user") => indices.code_dir.get(&key),
                     _ => match indices.modules.get(&module_name) {
                         Some(module) => module.code_dir.get(&key),
                         None => {
-                            let stub = MachineError::functor_stub(key.0, key.1);
-                            let h = self.heap.h();
-
-                            let err = MachineError::session_error(
-                                h,
+                            let stub = functor_stub(key.0, key.1);
+                            let err = self.session_error(
                                 SessionError::from(CompilationError::InvalidModuleResolution(
                                     module_name,
                                 )),
                             );
 
-                            let err = self.error_form(err, stub);
-
-                            self.throw_exception(err);
-                            return Ok(());
+                            return Err(self.error_form(err, stub));
                         }
                     },
                 };
@@ -4767,34 +4405,39 @@ impl MachineState {
                         }
                     }
                     _ => {
-                        let arity = arity.to_usize().unwrap();
-                        let stub = MachineError::functor_stub(name.clone(), arity);
-                        let h = self.heap.h();
-
-                        let err = MachineError::existence_error(
-                            h,
+                        let stub = functor_stub(name, arity);
+                        let err = self.existence_error(
                             ExistenceError::Procedure(name, arity),
                         );
 
-                        let err = self.error_form(err, stub);
-
-                        self.throw_exception(err);
-                        return Ok(());
+                        return Err(self.error_form(err, stub));
                     }
                 };
 
-                let mut h = self.heap.h();
+                let mut h = self.heap.len();
+
                 let mut functors = vec![];
                 let mut functor_list = vec![];
 
                 walk_code(&code_repo.code, first_idx, |instr| {
                     let old_len = functors.len();
-                    instr.enqueue_functors(h, &mut functors);
+                    instr.enqueue_functors(h, &mut self.arena, &mut functors);
                     let new_len = functors.len();
 
                     for index in old_len..new_len {
-                        functor_list.push(Addr::HeapCell(h));
-                        h += functors[index].len();
+                        let functor_len = functors[index].len();
+
+                        match functor_len {
+                            0 => {}
+                            1 => {
+                                functor_list.push(heap_loc_as_cell!(h));
+                                h += functor_len;
+                            }
+                            _ => {
+                                functor_list.push(str_loc_as_cell!(h));
+                                h += functor_len;
+                            }
+                        }
                     }
                 });
 
@@ -4802,64 +4445,69 @@ impl MachineState {
                     self.heap.extend(functor.into_iter());
                 }
 
-                let listing = Addr::HeapCell(self.heap.to_list(functor_list.into_iter()));
-                let listing_var = self[temp_v!(4)];
+                let listing = heap_loc_as_cell!(
+                    iter_to_heap_list(&mut self.heap, functor_list.into_iter())
+                );
+
+                let listing_var = self.registers[4];
 
-                (self.unify_fn)(self, listing, listing_var);
+                unify!(self, listing, listing_var);
             }
             &SystemClauseType::WriteTerm => {
                 let mut stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "write_term",
+                    atom!("write_term"),
                     3,
                 )?;
 
                 self.check_stream_properties(
-                    &mut stream,
+                    stream,
                     StreamType::Text,
                     None, // input
-                    clause_name!("write_term"),
+                    atom!("write_term"),
                     3,
                 )?;
 
                 let opt_err = if !stream.is_output_stream() {
-                    Some("stream") // 8.14.2.3 g)
-                } else if stream.options().stream_type == StreamType::Binary {
-                    Some("binary_stream") // 8.14.2.3 h)
+                    Some(atom!("stream")) // 8.14.2.3 g)
+                } else if stream.options().stream_type() == StreamType::Binary {
+                    Some(atom!("binary_stream")) // 8.14.2.3 h)
                 } else {
                     None
                 };
 
-                if let Some(err_string) = opt_err {
+                if let Some(err_atom) = opt_err {
                     return Err(self.stream_permission_error(
                         Permission::OutputStream,
-                        err_string,
+                        err_atom,
                         stream,
-                        clause_name!("write_term"),
+                        atom!("write_term"),
                         3,
                     ));
                 }
 
-                let addr = self[temp_v!(2)];
-
                 let printer = match self.write_term(&indices.op_dir)? {
+                    Some(printer) => printer,
                     None => {
-                        self.fail = true;
+                        // this next line is executed by
+                        // MachineState::write_term in this case. it's
+                        // commented here because rustc can't prove
+                        // that it's no longer borrowed.
+
+                        // self.fail = true;
                         return Ok(());
                     }
-                    Some(printer) => printer,
                 };
 
-                let output = printer.print(addr);
+                let output = printer.print();
 
                 match write!(&mut stream, "{}", output.result()) {
                     Ok(_) => {}
                     Err(_) => {
-                        let stub = MachineError::functor_stub(clause_name!("open"), 4);
-                        let err = MachineError::existence_error(
-                            self.heap.h(),
-                            ExistenceError::Stream(self[temp_v!(1)]),
+                        let stub = functor_stub(atom!("open"), 4);
+                        let err = self.existence_error(
+                            ExistenceError::Stream(self.registers[1]),
                         );
 
                         return Err(self.error_form(err, stub));
@@ -4869,20 +4517,23 @@ impl MachineState {
                 stream.flush().unwrap();
             }
             &SystemClauseType::WriteTermToChars => {
-                let addr = self[temp_v!(2)];
-
                 let printer = match self.write_term(&indices.op_dir)? {
                     None => {
-                        self.fail = true;
+                        // this next line is executed by
+                        // MachineState::write_term in this case. it's
+                        // commented here because rustc can't prove
+                        // that it's no longer borrowed.
+
+                        // self.fail = true;
                         return Ok(());
                     }
                     Some(printer) => printer,
                 };
 
-                let result = printer.print(addr).result();
-                let chars = self.heap.put_complete_string(&result);
+                let result = printer.print().result();
+                let chars = put_complete_string(&mut self.heap, &result, &mut self.atom_tbl);
 
-                let result_addr = self.store(self.deref(self[temp_v!(1)]));
+                let result_addr = self.store(self.deref(self.registers[1]));
 
                 if let Some(var) = result_addr.as_var() {
                     self.bind(var, chars);
@@ -4892,14 +4543,14 @@ impl MachineState {
             }
             &SystemClauseType::ScryerPrologVersion => {
                 use git_version::git_version;
-                let version = self[temp_v!(1)];
+
                 let buffer = git_version!(cargo_prefix = "cargo:", fallback = "unknown");
-                let chars = buffer.chars().map(|c| Addr::Char(c));
-                let result = Addr::HeapCell(self.heap.to_list(chars));
-                (self.unify_fn)(self, version, result);
+                let buffer_atom = self.atom_tbl.build_with(buffer);
+
+                self.unify_complete_string(buffer_atom, self.store(self.deref(self.registers[1])));
             }
             &SystemClauseType::CryptoRandomByte => {
-                let arg = self[temp_v!(1)];
+                let arg = self.registers[1];
                 let mut bytes: [u8; 1] = [0];
 
                 match rng().fill(&mut bytes) {
@@ -4913,149 +4564,163 @@ impl MachineState {
                     }
                 }
 
-                let byte = self
-                    .heap
-                    .to_unifiable(HeapCellValue::Integer(Rc::new(Integer::from(bytes[0]))));
-
-                (self.unify_fn)(self, arg, byte);
+                let byte = Fixnum::build_with(bytes[0] as i64);
+                self.unify_fixnum(byte, arg);
             }
             &SystemClauseType::CryptoDataHash => {
-                let encoding = self.atom_argument_to_string(2);
-                let bytes = self.string_encoding_bytes(1, &encoding);
+                let encoding = cell_as_atom!(self.registers[2]);
+                let bytes = self.string_encoding_bytes(self.registers[1], encoding);
 
-                let algorithm = self.atom_argument_to_string(4);
+                let algorithm = cell_as_atom!(self.registers[4]);
 
-                let ints_list = match algorithm.as_str() {
-                    "sha3_224" => {
+                let ints_list = match algorithm {
+                    atom!("sha3_224") => {
                         let mut context = Sha3_224::new();
                         context.input(&bytes);
-                        Addr::HeapCell(
-                            self.heap.to_list(
+
+                        heap_loc_as_cell!(
+                            iter_to_heap_list(
+                                &mut self.heap,
                                 context
                                     .result()
                                     .as_ref()
                                     .iter()
-                                    .map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))),
-                            ),
+                                    .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))),
+                            )
                         )
                     }
-                    "sha3_256" => {
+                    atom!("sha3_256") => {
                         let mut context = Sha3_256::new();
                         context.input(&bytes);
-                        Addr::HeapCell(
-                            self.heap.to_list(
+                        heap_loc_as_cell!(
+                            iter_to_heap_list(
+                                &mut self.heap,
                                 context
                                     .result()
                                     .as_ref()
                                     .iter()
-                                    .map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))),
-                            ),
+                                    .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))),
+                            )
                         )
                     }
-                    "sha3_384" => {
+                    atom!("sha3_384") => {
                         let mut context = Sha3_384::new();
                         context.input(&bytes);
-                        Addr::HeapCell(
-                            self.heap.to_list(
+
+                        heap_loc_as_cell!(
+                            iter_to_heap_list(
+                                &mut self.heap,
                                 context
                                     .result()
                                     .as_ref()
                                     .iter()
-                                    .map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))),
-                            ),
+                                    .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))),
+                            )
                         )
                     }
-                    "sha3_512" => {
+                    atom!("sha3_512") => {
                         let mut context = Sha3_512::new();
                         context.input(&bytes);
-                        Addr::HeapCell(
-                            self.heap.to_list(
+
+                        heap_loc_as_cell!(
+                            iter_to_heap_list(
+                                &mut self.heap,
                                 context
                                     .result()
                                     .as_ref()
                                     .iter()
-                                    .map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))),
-                            ),
+                                    .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))),
+                            )
                         )
                     }
-                    "blake2s256" => {
+                    atom!("blake2s256") => {
                         let mut context = Blake2s::new();
                         context.input(&bytes);
-                        Addr::HeapCell(
-                            self.heap.to_list(
+
+                        heap_loc_as_cell!(
+                            iter_to_heap_list(
+                                &mut self.heap,
                                 context
                                     .result()
                                     .as_ref()
                                     .iter()
-                                    .map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))),
-                            ),
+                                    .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))),
+                            )
                         )
                     }
-                    "blake2b512" => {
+                    atom!("blake2b512") => {
                         let mut context = Blake2b::new();
                         context.input(&bytes);
-                        Addr::HeapCell(
-                            self.heap.to_list(
+
+                        heap_loc_as_cell!(
+                            iter_to_heap_list(
+                                &mut self.heap,
                                 context
                                     .result()
                                     .as_ref()
                                     .iter()
-                                    .map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))),
-                            ),
+                                    .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))),
+                            )
                         )
                     }
-                    "ripemd160" => {
+                    atom!("ripemd160") => {
                         let mut context = Ripemd160::new();
                         context.input(&bytes);
-                        Addr::HeapCell(
-                            self.heap.to_list(
+
+                        heap_loc_as_cell!(
+                            iter_to_heap_list(
+                                &mut self.heap,
                                 context
                                     .result()
                                     .as_ref()
                                     .iter()
-                                    .map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))),
-                            ),
+                                    .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))),
+                            )
                         )
                     }
                     _ => {
                         let ints = digest::digest(
-                            match algorithm.as_str() {
-                                "sha256" => &digest::SHA256,
-                                "sha384" => &digest::SHA384,
-                                "sha512" => &digest::SHA512,
-                                "sha512_256" => &digest::SHA512_256,
+                            match algorithm {
+                                atom!("sha256") => &digest::SHA256,
+                                atom!("sha384") => &digest::SHA384,
+                                atom!("sha512") => &digest::SHA512,
+                                atom!("sha512_256") => &digest::SHA512_256,
                                 _ => {
                                     unreachable!()
                                 }
                             },
                             &bytes,
                         );
-                        Addr::HeapCell(
-                            self.heap.to_list(
+
+                        heap_loc_as_cell!(
+                            iter_to_heap_list(
+                                &mut self.heap,
                                 ints.as_ref()
                                     .iter()
-                                    .map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))),
-                            ),
+                                    .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))),
+                            )
                         )
                     }
                 };
 
-                (self.unify_fn)(self, self[temp_v!(3)], ints_list);
+                unify!(self, self.registers[3], ints_list);
             }
             &SystemClauseType::CryptoDataHKDF => {
-                let encoding = self.atom_argument_to_string(2);
-                let data = self.string_encoding_bytes(1, &encoding);
-                let stub1 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 4);
-                let salt = self.integers_to_bytevec(temp_v!(3), stub1);
-                let stub2 = MachineError::functor_stub(clause_name!("crypto_data_hkdf"), 4);
-                let info = self.integers_to_bytevec(temp_v!(4), stub2);
+                let encoding = cell_as_atom!(self.registers[2]);
+                let data = self.string_encoding_bytes(self.registers[1], encoding);
 
-                let algorithm = self.atom_argument_to_string(5);
+                let stub1_gen = || functor_stub(atom!("crypto_data_hkdf"), 4);
+                let salt = self.integers_to_bytevec(self.registers[3], stub1_gen);
 
-                let length = self.store(self.deref(self[temp_v!(6)]));
+                let stub2_gen = || functor_stub(atom!("crypto_data_hkdf"), 4);
+                let info = self.integers_to_bytevec(self.registers[4], stub2_gen);
 
-                let length = match Number::try_from((length, &self.heap)) {
-                    Ok(Number::Fixnum(n)) => usize::try_from(n).unwrap(),
+                let algorithm = cell_as_atom!(self.registers[5]);
+
+                let length = self.store(self.deref(self.registers[6]));
+
+                let length = match Number::try_from(length) {
+                    Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).unwrap(),
                     Ok(Number::Integer(n)) => match n.to_usize() {
                         Some(u) => u,
                         _ => {
@@ -5069,18 +4734,21 @@ impl MachineState {
                 };
 
                 let ints_list = {
-                    let digest_alg = match algorithm.as_str() {
-                        "sha256" => hkdf::HKDF_SHA256,
-                        "sha384" => hkdf::HKDF_SHA384,
-                        "sha512" => hkdf::HKDF_SHA512,
+                    let digest_alg = match algorithm {
+                        atom!("sha256") => hkdf::HKDF_SHA256,
+                        atom!("sha384") => hkdf::HKDF_SHA384,
+                        atom!("sha512") => hkdf::HKDF_SHA512,
                         _ => {
                             self.fail = true;
                             return Ok(());
                         }
                     };
+
                     let salt = hkdf::Salt::new(digest_alg, &salt);
                     let mut bytes: Vec<u8> = Vec::new();
+
                     bytes.resize(length, 0);
+
                     match salt.extract(&data).expand(&[&info[..]], MyKey(length)) {
                         Ok(r) => {
                             r.fill(&mut bytes).unwrap();
@@ -5091,27 +4759,28 @@ impl MachineState {
                         }
                     }
 
-                    Addr::HeapCell(
-                        self.heap.to_list(
+                    heap_loc_as_cell!(
+                        iter_to_heap_list(
+                            &mut self.heap,
                             bytes
                                 .iter()
-                                .map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))),
-                        ),
+                                .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))),
+                        )
                     )
                 };
 
-                (self.unify_fn)(self, self[temp_v!(7)], ints_list);
+                unify!(self, self.registers[7], ints_list);
             }
             &SystemClauseType::CryptoPasswordHash => {
-                let stub1 = MachineError::functor_stub(clause_name!("crypto_password_hash"), 3);
-                let data = self.integers_to_bytevec(temp_v!(1), stub1);
-                let stub2 = MachineError::functor_stub(clause_name!("crypto_password_hash"), 3);
-                let salt = self.integers_to_bytevec(temp_v!(2), stub2);
+                let stub1_gen = || functor_stub(atom!("crypto_password_hash"), 3);
+                let data = self.integers_to_bytevec(self.registers[1], stub1_gen);
+                let stub2_gen = || functor_stub(atom!("crypto_password_hash"), 3);
+                let salt = self.integers_to_bytevec(self.registers[2], stub2_gen);
 
-                let iterations = self.store(self.deref(self[temp_v!(3)]));
+                let iterations = self.store(self.deref(self.registers[3]));
 
-                let iterations = match Number::try_from((iterations, &self.heap)) {
-                    Ok(Number::Fixnum(n)) => u64::try_from(n).unwrap(),
+                let iterations = match Number::try_from(iterations) {
+                    Ok(Number::Fixnum(n)) => u64::try_from(n.get_num()).unwrap(),
                     Ok(Number::Integer(n)) => match n.to_u64() {
                         Some(i) => i,
                         None => {
@@ -5126,6 +4795,7 @@ impl MachineState {
 
                 let ints_list = {
                     let mut bytes = [0u8; digest::SHA512_OUTPUT_LEN];
+
                     pbkdf2::derive(
                         pbkdf2::PBKDF2_HMAC_SHA512,
                         NonZeroU32::new(iterations as u32).unwrap(),
@@ -5134,31 +4804,36 @@ impl MachineState {
                         &mut bytes,
                     );
 
-                    Addr::HeapCell(
-                        self.heap.to_list(
+                    heap_loc_as_cell!(
+                        iter_to_heap_list(
+                            &mut self.heap,
                             bytes
                                 .iter()
-                                .map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))),
-                        ),
+                                .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))),
+                        )
                     )
                 };
 
-                (self.unify_fn)(self, self[temp_v!(4)], ints_list);
+                unify!(self, self.registers[4], ints_list);
             }
             &SystemClauseType::CryptoDataEncrypt => {
-                let encoding = self.atom_argument_to_string(3);
-                let data = self.string_encoding_bytes(1, &encoding);
-                let aad = self.string_encoding_bytes(2, &encoding);
-                let stub2 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 7);
-                let key = self.integers_to_bytevec(temp_v!(4), stub2);
-                let stub3 = MachineError::functor_stub(clause_name!("crypto_data_encrypt"), 7);
-                let iv = self.integers_to_bytevec(temp_v!(5), stub3);
+                let encoding = cell_as_atom!(self.registers[3]);
+
+                let data = self.string_encoding_bytes(self.registers[1], encoding);
+                let aad = self.string_encoding_bytes(self.registers[2], encoding);
+
+                let stub2_gen = || functor_stub(atom!("crypto_data_encrypt"), 7);
+                let key = self.integers_to_bytevec(self.registers[4], stub2_gen);
+
+                let stub3_gen = || functor_stub(atom!("crypto_data_encrypt"), 7);
+                let iv = self.integers_to_bytevec(self.registers[5], stub3_gen);
 
                 let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap();
                 let nonce = aead::Nonce::try_assume_unique_for_key(&iv).unwrap();
                 let key = aead::LessSafeKey::new(unbound_key);
 
-                let mut in_out = data.clone();
+                let mut in_out = data;
+
                 let tag = match key.seal_in_place_separate_tag(
                     nonce,
                     aead::Aad::from(aad),
@@ -5171,36 +4846,39 @@ impl MachineState {
                     }
                 };
 
-                let tag_list = Addr::HeapCell(
-                    self.heap.to_list(
+                let tag_list = heap_loc_as_cell!(
+                    iter_to_heap_list(
+                        &mut self.heap,
                         tag.as_ref()
                             .iter()
-                            .map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))),
-                    ),
+                            .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))),
+                    )
                 );
 
                 let complete_string = {
                     let buffer = String::from_iter(in_out.iter().map(|b| *b as char));
-                    self.heap.put_complete_string(&buffer)
+                    put_complete_string(&mut self.heap, &buffer, &mut self.atom_tbl)
                 };
 
-                (self.unify_fn)(self, self[temp_v!(6)], tag_list);
-                (self.unify_fn)(self, self[temp_v!(7)], complete_string);
+                unify!(self, self.registers[6], tag_list);
+                unify!(self, self.registers[7], complete_string);
             }
             &SystemClauseType::CryptoDataDecrypt => {
-                let data = self.string_encoding_bytes(1, "octet");
-                let encoding = self.atom_argument_to_string(5);
-                let aad = self.string_encoding_bytes(2, &encoding);
-                let stub1 = MachineError::functor_stub(clause_name!("crypto_data_decrypt"), 7);
-                let key = self.integers_to_bytevec(temp_v!(3), stub1);
-                let stub2 = MachineError::functor_stub(clause_name!("crypto_data_decrypt"), 7);
-                let iv = self.integers_to_bytevec(temp_v!(4), stub2);
+                let data = self.string_encoding_bytes(self.registers[1], atom!("octet"));
+                let encoding = cell_as_atom!(self.registers[5]);
+
+                let aad = self.string_encoding_bytes(self.registers[2], encoding);
+                let stub1_gen = || functor_stub(atom!("crypto_data_decrypt"), 7);
+
+                let key = self.integers_to_bytevec(self.registers[3], stub1_gen);
+                let stub2_gen = || functor_stub(atom!("crypto_data_decrypt"), 7);
+                let iv = self.integers_to_bytevec(self.registers[4], stub2_gen);
 
                 let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap();
                 let nonce = aead::Nonce::try_assume_unique_for_key(&iv).unwrap();
                 let key = aead::LessSafeKey::new(unbound_key);
 
-                let mut in_out = data.clone();
+                let mut in_out = data;
 
                 let complete_string = {
                     let decrypted_data =
@@ -5212,9 +4890,9 @@ impl MachineState {
                             }
                         };
 
-                    let buffer = match encoding.as_str() {
-                        "octet" => String::from_iter(decrypted_data.iter().map(|b| *b as char)),
-                        "utf8" => match String::from_utf8(decrypted_data.to_vec()) {
+                    let buffer = match encoding {
+                        atom!("octet") => String::from_iter(decrypted_data.iter().map(|b| *b as char)),
+                        atom!("utf8") => match String::from_utf8(decrypted_data.to_vec()) {
                             Ok(str) => str,
                             _ => {
                                 self.fail = true;
@@ -5226,67 +4904,80 @@ impl MachineState {
                         }
                     };
 
-                    self.heap.put_complete_string(&buffer)
+                    put_complete_string(&mut self.heap, &buffer, &mut self.atom_tbl)
                 };
 
-                (self.unify_fn)(self, self[temp_v!(6)], complete_string);
+                unify!(self, self.registers[6], complete_string);
             }
             &SystemClauseType::CryptoCurveScalarMult => {
-                let curve = self.atom_argument_to_string(1);
-                let curve_id = match curve.as_str() {
-                    "secp112r1" => Nid::SECP112R1,
-                    "secp256k1" => Nid::SECP256K1,
+                let curve = cell_as_atom!(self.registers[1]);
+
+                let curve_id = match curve {
+                    atom!("secp112r1") => Nid::SECP112R1,
+                    atom!("secp256k1") => Nid::SECP256K1,
                     _ => {
                         unreachable!()
                     }
                 };
 
-                let scalar = self.store(self.deref(self[temp_v!(2)]));
+                let scalar = self.store(self.deref(self.registers[2]));
 
-                let scalar = match Number::try_from((scalar, &self.heap)) {
-                    Ok(Number::Fixnum(n)) => Integer::from(n),
-                    Ok(Number::Integer(n)) => Integer::from(&*n.clone()),
+                let scalar = match Number::try_from(scalar) {
+                    Ok(Number::Fixnum(n)) => Integer::from(n.get_num()),
+                    Ok(Number::Integer(n)) => Integer::from(&*n),
                     _ => {
                         unreachable!()
                     }
                 };
 
-                let stub = MachineError::functor_stub(clause_name!("crypto_curve_scalar_mult"), 5);
-                let qbytes = self.integers_to_bytevec(temp_v!(3), stub);
+                let stub_gen = || functor_stub(atom!("crypto_curve_scalar_mult"), 5);
+                let qbytes = self.integers_to_bytevec(self.registers[3], stub_gen);
 
                 let mut bnctx = BigNumContext::new().unwrap();
                 let group = EcGroup::from_curve_name(curve_id).unwrap();
                 let mut point = EcPoint::from_bytes(&group, &qbytes, &mut bnctx).unwrap();
                 let scalar_bn = BigNum::from_dec_str(&scalar.to_string()).unwrap();
                 let mut result = EcPoint::new(&group).unwrap();
+
                 result.mul(&group, &mut point, &scalar_bn, &mut bnctx).ok();
 
                 let mut rx = BigNum::new().unwrap();
                 let mut ry = BigNum::new().unwrap();
+
                 result
                     .affine_coordinates_gfp(&group, &mut rx, &mut ry, &mut bnctx)
                     .ok();
-                let sx = self
-                    .heap
-                    .put_complete_string(&rx.to_dec_str().unwrap().to_string());
-                let sy = self
-                    .heap
-                    .put_complete_string(&ry.to_dec_str().unwrap().to_string());
 
-                (self.unify_fn)(self, self[temp_v!(4)], sx);
-                (self.unify_fn)(self, self[temp_v!(5)], sy);
+                let sx = put_complete_string(
+                    &mut self.heap,
+                    &rx.to_dec_str().unwrap(),
+                    &mut self.atom_tbl,
+                );
+
+                let sy = put_complete_string(
+                    &mut self.heap,
+                    &ry.to_dec_str().unwrap(),
+                    &mut self.atom_tbl,
+                );
+
+                unify!(self, self.registers[4], sx);
+                unify!(self, self.registers[5], sy);
             }
             &SystemClauseType::Ed25519NewKeyPair => {
                 let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(rng()).unwrap();
                 let complete_string = {
                     let buffer = String::from_iter(pkcs8_bytes.as_ref().iter().map(|b| *b as char));
-                    self.heap.put_complete_string(&buffer)
+                    put_complete_string(
+                        &mut self.heap,
+                        &buffer,
+                        &mut self.atom_tbl,
+                    )
                 };
 
-                (self.unify_fn)(self, self[temp_v!(1)], complete_string);
+                unify!(self, self.registers[1], complete_string)
             }
             &SystemClauseType::Ed25519KeyPairPublicKey => {
-                let bytes = self.string_encoding_bytes(1, "octet");
+                let bytes = self.string_encoding_bytes(self.registers[1], atom!("octet"));
 
                 let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&bytes) {
                     Ok(kp) => kp,
@@ -5300,15 +4991,20 @@ impl MachineState {
                     let buffer = String::from_iter(
                         key_pair.public_key().as_ref().iter().map(|b| *b as char),
                     );
-                    self.heap.put_complete_string(&buffer)
+
+                    put_complete_string(
+                        &mut self.heap,
+                        &buffer,
+                        &mut self.atom_tbl,
+                    )
                 };
 
-                (self.unify_fn)(self, self[temp_v!(2)], complete_string);
+                unify!(self, self.registers[2], complete_string);
             }
             &SystemClauseType::Ed25519Sign => {
-                let key = self.string_encoding_bytes(1, "octet");
-                let encoding = self.atom_argument_to_string(3);
-                let data = self.string_encoding_bytes(2, &encoding);
+                let key = self.string_encoding_bytes(self.registers[1], atom!("octet"));
+                let encoding = cell_as_atom!(self.registers[3]);
+                let data = self.string_encoding_bytes(self.registers[2], encoding);
 
                 let key_pair = match signature::Ed25519KeyPair::from_pkcs8(&key) {
                     Ok(kp) => kp,
@@ -5320,24 +5016,26 @@ impl MachineState {
 
                 let sig = key_pair.sign(&data);
 
-                let sig_list = Addr::HeapCell(
-                    self.heap.to_list(
+                let sig_list = heap_loc_as_cell!(
+                    iter_to_heap_list(
+                        &mut self.heap,
                         sig.as_ref()
                             .iter()
-                            .map(|b| HeapCellValue::from(Addr::Fixnum(*b as isize))),
-                    ),
+                            .map(|b| fixnum_as_cell!(Fixnum::build_with(*b as i64))),
+                    )
                 );
 
-                (self.unify_fn)(self, self[temp_v!(4)], sig_list);
+                unify!(self, self.registers[4], sig_list);
             }
             &SystemClauseType::Ed25519Verify => {
-                let key = self.string_encoding_bytes(1, "octet");
-                let encoding = self.atom_argument_to_string(3);
-                let data = self.string_encoding_bytes(2, &encoding);
-                let stub = MachineError::functor_stub(clause_name!("ed25519_verify"), 5);
-                let signature = self.integers_to_bytevec(temp_v!(4), stub);
+                let key = self.string_encoding_bytes(self.registers[1], atom!("octet"));
+                let encoding = cell_as_atom!(self.registers[3]);
+                let data = self.string_encoding_bytes(self.registers[2], encoding);
+                let stub_gen = || functor_stub(atom!("ed25519_verify"), 5);
+                let signature = self.integers_to_bytevec(self.registers[4], stub_gen);
 
                 let peer_public_key = signature::UnparsedPublicKey::new(&signature::ED25519, &key);
+
                 match peer_public_key.verify(&data, &signature) {
                     Ok(_) => {}
                     _ => {
@@ -5347,73 +5045,112 @@ impl MachineState {
                 }
             }
             &SystemClauseType::Curve25519ScalarMult => {
-                let stub1 = MachineError::functor_stub(clause_name!("curve25519_scalar_mult"), 3);
-                let scalar_bytes = self.integers_to_bytevec(temp_v!(1), stub1);
+                let stub1_gen = || functor_stub(atom!("curve25519_scalar_mult"), 3);
+                let scalar_bytes = self.integers_to_bytevec(self.registers[1], stub1_gen);
                 let scalar = Scalar(<[u8; 32]>::try_from(&scalar_bytes[..]).unwrap());
 
-                let stub2 = MachineError::functor_stub(clause_name!("curve25519_scalar_mult"), 3);
-                let point_bytes = self.integers_to_bytevec(temp_v!(2), stub2);
+                let stub2_gen = || functor_stub(atom!("curve25519_scalar_mult"), 3);
+                let point_bytes = self.integers_to_bytevec(self.registers[2], stub2_gen);
                 let point = GroupElement(<[u8; 32]>::try_from(&point_bytes[..]).unwrap());
 
                 let result = scalarmult(&scalar, &point).unwrap();
 
                 let string = String::from_iter(result[..].iter().map(|b| *b as char));
-                let cstr = self.heap.put_complete_string(&string);
-                (self.unify_fn)(self, self[temp_v!(3)], cstr);
+                let cstr = put_complete_string(&mut self.heap, &string, &mut self.atom_tbl);
+
+                unify!(self, self.registers[3], cstr);
             }
             &SystemClauseType::FirstNonOctet => {
-                for c in self.heap_pstr_iter(self[temp_v!(1)]).to_string().chars() {
-                    if c as u32 > 255 {
-                        let chars = clause_name!(String::from(c.to_string()), self.atom_tbl);
-                        let non_octet = self.heap.to_unifiable(HeapCellValue::Atom(chars, None));
-                        (self.unify_fn)(self, self[temp_v!(2)], non_octet);
-                        return return_from_clause!(self.last_call, self);
+                let addr = self.store(self.deref(self.registers[1]));
+
+                if let Some(string) = self.value_to_str_like(addr) {
+                    for c in string.as_str().chars() {
+                        if c as u32 > 255 {
+                            let non_octet = self.atom_tbl.build_with(&c.to_string());
+                            self.unify_atom(non_octet, self.registers[2]);
+                            return return_from_clause!(self.last_call, self);
+                        }
                     }
                 }
+
                 self.fail = true;
                 return Ok(());
             }
             &SystemClauseType::LoadHTML => {
-                let string = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
-                let doc = select::document::Document::from_read(string.as_bytes()).unwrap();
-                let result = self.html_node_to_term(indices, doc.nth(0).unwrap());
+                if let Some(string) = self.value_to_str_like(self.registers[1]) {
+                    let doc = select::document::Document::from_read(string.as_str().as_bytes())
+                        .unwrap();
 
-                (self.unify_fn)(self, self[temp_v!(2)], result);
+                    let result = self.html_node_to_term(indices, doc.nth(0).unwrap());
+                    unify!(self, self.registers[2], result);
+                } else {
+                    self.fail = true;
+                    return Ok(());
+                }
             }
             &SystemClauseType::LoadXML => {
-                let string = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
-                match roxmltree::Document::parse(&string) {
-                    Ok(doc) => {
-                        let result = self.xml_node_to_term(indices, doc.root_element());
-                        (self.unify_fn)(self, self[temp_v!(2)], result);
-                    }
-                    _ => {
-                        self.fail = true;
-                        return Ok(());
+                if let Some(string) = self.value_to_str_like(self.registers[1]) {
+                    match roxmltree::Document::parse(string.as_str()) {
+                        Ok(doc) => {
+                            let result = self.xml_node_to_term(indices, doc.root_element());
+                            unify!(self, self.registers[2], result);
+                        }
+                        _ => {
+                            self.fail = true;
+                            return Ok(());
+                        }
                     }
+                } else {
+                    self.fail = true;
+                    return Ok(());
                 }
             }
             &SystemClauseType::GetEnv => {
-                let key = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
-                match env::var(key) {
-                    Ok(value) => {
-                        let cstr = self.heap.put_complete_string(&value);
-                        (self.unify_fn)(self, self[temp_v!(2)], cstr);
-                    }
-                    _ => {
-                        self.fail = true;
-                        return Ok(());
+                if let Some(key) = self.value_to_str_like(self.registers[1]) {
+                    match env::var(key.as_str()) {
+                        Ok(value) => {
+                            let cstr = put_complete_string(
+                                &mut self.heap,
+                                &value,
+                                &mut self.atom_tbl,
+                            );
+
+                            unify!(self, self.registers[2], cstr);
+                        }
+                        _ => {
+                            self.fail = true;
+                            return Ok(());
+                        }
                     }
+                } else {
+                    self.fail = true;
+                    return Ok(());
                 }
             }
             &SystemClauseType::SetEnv => {
-                let key = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
-                let value = self.heap_pstr_iter(self[temp_v!(2)]).to_string();
-                env::set_var(key, value);
+                let key = self.value_to_str_like(self.registers[1]).unwrap();
+                let value = self.value_to_str_like(self.registers[2]).unwrap();
+
+                env::set_var(key.as_str(), value.as_str());
             }
             &SystemClauseType::UnsetEnv => {
-                let key = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
-                env::remove_var(key);
+                let key = self.value_to_str_like(self.registers[1]).unwrap();
+                env::remove_var(key.as_str());
+            }
+            &SystemClauseType::PID => {
+                let pid = process::id();
+
+                match fixnum!(Number, pid as i64, &mut self.arena) {
+                    Number::Fixnum(pid) => {
+                        self.unify_fixnum(pid, self.registers[1]);
+                    }
+                    Number::Integer(pid) => {
+                        self.unify_big_int(pid, self.registers[1]);
+                    }
+                    _ => {
+                        unreachable!();
+                    }
+                }
             }
             &SystemClauseType::Shell => {
                 // shell executes a command in a system shell
@@ -5426,8 +5163,8 @@ impl MachineState {
                         Ok(status) => {
                             match status.code() {
                                 Some(code) => {
-                                    let addr = machine.heap.put_constant(Constant::Integer(Rc::new(Integer::from(code))));
-                                    (machine.unify_fn)(machine, machine[temp_v!(2)], addr);
+                                    let code = integer_as_cell!(Number::arena_from(code, &mut machine.arena));
+                                    unify!(machine, code, machine.registers[2]);
                                 }
                                 _ => {
                                     machine.fail = true;
@@ -5440,12 +5177,13 @@ impl MachineState {
                     }
                 }
 
-                let command = self.heap_pstr_iter(self[temp_v!(1)]).to_string();
+                let command = self.value_to_str_like(self.store(self.deref(self.registers[1]))).unwrap();
+
                 match env::var("SHELL") {
                     Ok(value) => {
                         let command = process::Command::new(&value)
                             .arg("-c")
-                            .arg(command)
+                            .arg(command.as_str())
                             .status();
                         command_result(self, command);
                     }
@@ -5454,7 +5192,7 @@ impl MachineState {
                             Ok(value) => {
                                 let command = process::Command::new(&value)
                                     .arg("/C")
-                                    .arg(command)
+                                    .arg(command.as_str())
                                     .status();
                                 command_result(self, command);
                             }
@@ -5465,39 +5203,38 @@ impl MachineState {
                     }
                 };
             }
-            &SystemClauseType::PID => {
-                let a1 = self[temp_v!(1)];
-                let pid = process::id();
-                let addr = self.heap.put_constant(Constant::Integer(Rc::new(Integer::from(pid))));
-                (self.unify_fn)(self, a1, addr);
-            }
             &SystemClauseType::CharsBase64 => {
-                let padding = self.atom_argument_to_string(3);
-                let charset = self.atom_argument_to_string(4);
+                let padding = cell_as_atom!(self.registers[3]);
+                let charset = cell_as_atom!(self.registers[4]);
 
-                let config = if padding == "true" {
-                    if charset == "standard" {
+                let config = if padding == atom!("true") {
+                    if charset == atom!("standard") {
                         base64::STANDARD
                     } else {
                         base64::URL_SAFE
                     }
                 } else {
-                    if charset == "standard" {
+                    if charset == atom!("standard") {
                         base64::STANDARD_NO_PAD
                     } else {
                         base64::URL_SAFE_NO_PAD
                     }
                 };
 
-                if self.store(self.deref(self[temp_v!(1)])).is_ref() {
-                    let b64 = self.heap_pstr_iter(self[temp_v!(2)]).to_string();
-                    let bytes = base64::decode_config(b64, config);
+                if self.store(self.deref(self.registers[1])).is_var() {
+                    let b64 = self.value_to_str_like(self.registers[2]).unwrap();
+                    let bytes = base64::decode_config(b64.as_str(), config);
 
                     match bytes {
                         Ok(bs) => {
                             let string = String::from_iter(bs.iter().map(|b| *b as char));
-                            let cstr = self.heap.put_complete_string(&string);
-                            (self.unify_fn)(self, self[temp_v!(1)], cstr);
+                            let cstr = put_complete_string(
+                                &mut self.heap,
+                                &string,
+                                &mut self.atom_tbl,
+                            );
+
+                            unify!(self, self.registers[1], cstr);
                         }
                         _ => {
                             self.fail = true;
@@ -5506,28 +5243,40 @@ impl MachineState {
                     }
                 } else {
                     let mut bytes = vec![];
-                    for c in self.heap_pstr_iter(self[temp_v!(1)]).to_string().chars() {
+                    for c in self.value_to_str_like(self.registers[1]).unwrap().as_str().chars() {
+                        if c as u32 > 255 {
+                            let stub = functor_stub(atom!("chars_base64"), 3);
+
+                            let err = self.type_error(
+                                ValidType::Byte,
+                                char_as_cell!(c),
+                            );
+
+                            return Err(self.error_form(err, stub));
+                        }
+
                         bytes.push(c as u8);
                     }
+
                     let b64 = base64::encode_config(bytes, config);
+                    let cstr = put_complete_string(
+                        &mut self.heap,
+                        &b64,
+                        &mut self.atom_tbl,
+                    );
 
-                    let cstr = self.heap.put_complete_string(&b64);
-                    (self.unify_fn)(self, self[temp_v!(2)], cstr);
+                    unify!(self, self.registers[2], cstr);
                 }
             }
             &SystemClauseType::LoadLibraryAsStream => {
-                let library_name = atom_from!(self, self.store(self.deref(self[temp_v!(1)])));
+                let library_name = cell_as_atom!(self.store(self.deref(self.registers[1])));
 
-                use crate::LIBRARIES;
+                use crate::machine::LIBRARIES;
 
                 match LIBRARIES.borrow().get(library_name.as_str()) {
                     Some(library) => {
-                        let var_ref = Ref::HeapCell(
-                            self.heap
-                                .push(HeapCellValue::Stream(Stream::from(*library))),
-                        );
-
-                        self.bind(var_ref, self[temp_v!(2)]);
+                        let lib_stream = Stream::from_static_string(library, &mut self.arena);
+                        unify!(self, stream_as_cell!(lib_stream), self.registers[2]);
 
                         let mut path_buf = machine::current_dir();
 
@@ -5535,35 +5284,31 @@ impl MachineState {
                         path_buf.push(library_name.as_str());
 
                         let library_path_str = path_buf.to_str().unwrap();
-                        let library_path =
-                            clause_name!(library_path_str.to_string(), self.atom_tbl);
+                        let library_path = self.atom_tbl.build_with(library_path_str);
 
-                        let library_path_ref =
-                            Ref::HeapCell(self.heap.push(HeapCellValue::Atom(library_path, None)));
-
-                        self.bind(library_path_ref, self[temp_v!(3)]);
+                        self.unify_atom(library_path, self.registers[3]);
                     }
                     None => {
-                        return Err(self.error_form(
-                            MachineError::existence_error(
-                                self.heap.h(),
-                                ExistenceError::ModuleSource(ModuleSource::Library(library_name)),
-                            ),
-                            MachineError::functor_stub(clause_name!("load"), 1),
-                        ));
+                        let stub = functor_stub(atom!("load"), 1);
+                        let err = self.existence_error(
+                            ExistenceError::ModuleSource(ModuleSource::Library(library_name))
+                        );
+
+                        return Err(self.error_form(err, stub));
                     }
                 }
             }
             &SystemClauseType::DevourWhitespace => {
                 let stream = self.get_stream_or_alias(
-                    self[temp_v!(1)],
+                    self.registers[1],
                     &indices.stream_aliases,
-                    "$devour_whitespace",
+                    atom!("$devour_whitespace"),
                     1,
                 )?;
 
-                match self.devour_whitespace(stream, self.atom_tbl.clone()) {
-                    Ok(false) => {} // not at EOF.
+                match self.devour_whitespace(stream) {
+                    Ok(false) => { // not at EOF.
+                    }
                     _ => {
                         self.fail = true;
                         return Ok(());
@@ -5572,25 +5317,13 @@ impl MachineState {
             }
             &SystemClauseType::IsSTOEnabled => {
                 if self.unify_fn as usize == MachineState::unify_with_occurs_check as usize {
-                    let value = self
-                        .heap
-                        .to_unifiable(HeapCellValue::Atom(clause_name!("true"), None));
-
-                    (self.unify_fn)(self, self[temp_v!(1)], value);
+                    self.unify_atom(atom!("true"), self.registers[1]);
                 } else if self.unify_fn as usize
                     == MachineState::unify_with_occurs_check_with_error as usize
                 {
-                    let value = self
-                        .heap
-                        .to_unifiable(HeapCellValue::Atom(clause_name!("error"), None));
-
-                    (self.unify_fn)(self, self[temp_v!(1)], value);
+                    self.unify_atom(atom!("error"), self.registers[1]);
                 } else {
-                    let value = self
-                        .heap
-                        .to_unifiable(HeapCellValue::Atom(clause_name!("false"), None));
-
-                    (self.unify_fn)(self, self[temp_v!(1)], value);
+                    self.unify_atom(atom!("false"), self.registers[1]);
                 }
             }
             &SystemClauseType::SetSTOAsUnify => {
@@ -5616,9 +5349,13 @@ impl MachineState {
 
                 if path.is_dir() {
                     if let Some(path) = path.to_str() {
-                        let path_string = self.heap.put_complete_string(path);
+                        let path_string = put_complete_string(
+                            &mut self.heap,
+                            path,
+                            &mut self.atom_tbl,
+                        );
 
-                        self.unify(self[temp_v!(1)], path_string);
+                        unify!(self, self.registers[1], path_string);
                         return return_from_clause!(self.last_call, self);
                     }
                 }
@@ -5629,66 +5366,51 @@ impl MachineState {
                 self.fail = false;
             }
             &SystemClauseType::PopCount => {
-                let number  = self.store(self.deref(self[temp_v!(1)]));
-                let count = match Number::try_from((number, &self.heap)) {
-                    Ok(Number::Fixnum(n)) => Integer::from(n.count_ones()),
-                    Ok(Number::Integer(n)) => Integer::from((&*n).count_ones().unwrap()),
+                let number = self.store(self.deref(self.registers[1]));
+                let pop_count = integer_as_cell!(match Number::try_from(number) {
+                    Ok(Number::Fixnum(n)) => {
+                        Number::Fixnum(Fixnum::build_with(n.get_num().count_ones() as i64))
+                    }
+                    Ok(Number::Integer(n)) => {
+                        Number::arena_from(n.count_ones().unwrap(), &mut self.arena)
+                    }
                     _ => {
                         unreachable!()
                     }
-                };
+                });
 
-                let pop_count = self.heap.to_unifiable(HeapCellValue::Integer(Rc::new(count)));
-                (self.unify_fn)(self, self[temp_v!(2)], pop_count);
+                unify!(self, self.registers[2], pop_count);
             }
         };
 
         return_from_clause!(self.last_call, self)
     }
 
-    pub(super) fn systemtime_to_timestamp(&mut self, system_time: SystemTime) -> Addr {
+    pub(super) fn systemtime_to_timestamp(&mut self, system_time: SystemTime) -> Atom {
         let datetime: DateTime<Local> = system_time.into();
 
         let mut fstr = "[".to_string();
-        let specifiers = vec![
+        const SPECIFIERS: [&'static str; 19] = [
             "Y", "m", "d", "H", "M", "S", "y", "b", "B", "a", "A", "w", "u", "U", "W", "j", "D",
             "x", "v",
         ];
-        for spec in specifiers {
+
+        for spec in SPECIFIERS {
             fstr.push_str(&format!("'{}'=\"%{}\", ", spec, spec).to_string());
         }
+
         fstr.push_str("finis].");
         let s = datetime.format(&fstr).to_string();
-        self.heap.put_complete_string(&s)
-    }
 
-    pub(super) fn atom_argument_to_string(&mut self, atom_arg: usize) -> String {
-        match self.store(self.deref(self[temp_v!(atom_arg)])) {
-            Addr::Con(h) if self.heap.atom_at(h) => {
-                if let HeapCellValue::Atom(ref atom, _) = &self.heap[h] {
-                    atom.as_str().to_string()
-                } else {
-                    unreachable!()
-                }
-            }
-            _ => {
-                unreachable!()
-            }
-        }
+        self.atom_tbl.build_with(&s)
     }
 
-    pub(super) fn string_encoding_bytes(&mut self, data_arg: usize, encoding: &str) -> Vec<u8> {
-        let data = self.heap_pstr_iter(self[temp_v!(data_arg)]).to_string();
+    pub(super) fn string_encoding_bytes(&mut self, data_arg: HeapCellValue, encoding: Atom) -> Vec<u8> {
+        let data = self.value_to_str_like(data_arg).unwrap();
 
         match encoding {
-            "utf8" => data.into_bytes(),
-            "octet" => {
-                let mut buf = vec![];
-                for c in data.chars() {
-                    buf.push(c as u8);
-                }
-                buf
-            }
+            atom!("utf8") => data.as_str().bytes().collect(),
+            atom!("octet") => data.as_str().chars().map(|c| c as u8).collect(),
             _ => {
                 unreachable!()
             }
@@ -5699,43 +5421,53 @@ impl MachineState {
         &mut self,
         indices: &mut IndexStore,
         node: roxmltree::Node,
-    ) -> Addr {
+    ) -> HeapCellValue {
         if node.is_text() {
-            let string = String::from(node.text().unwrap());
-            self.heap.put_complete_string(&string)
+            put_complete_string(
+                &mut self.heap,
+                node.text().unwrap(),
+                &mut self.atom_tbl,
+            )
         } else {
             let mut avec = Vec::new();
-            for attr in node.attributes() {
-                let chars = clause_name!(String::from(attr.name()), self.atom_tbl);
-                let name = self.heap.to_unifiable(HeapCellValue::Atom(chars, None));
 
-                let value = self.heap.put_complete_string(&attr.value());
+            for attr in node.attributes() {
+                let name = self.atom_tbl.build_with(attr.name());
+                let value = put_complete_string(
+                    &mut self.heap,
+                    &attr.value(),
+                    &mut self.atom_tbl,
+                );
 
-                avec.push(HeapCellValue::Addr(Addr::HeapCell(self.heap.h())));
+                avec.push(heap_loc_as_cell!(self.heap.len()));
 
-                self.heap
-                    .push(HeapCellValue::NamedStr(2, clause_name!("="), None));
-                self.heap.push(HeapCellValue::Addr(name));
-                self.heap.push(HeapCellValue::Addr(value));
+                self.heap.push(atom_as_cell!(atom!("="), 2));
+                self.heap.push(atom_as_cell!(name));
+                self.heap.push(value);
             }
-            let attrs = Addr::HeapCell(self.heap.to_list(avec.into_iter()));
+
+            let attrs = heap_loc_as_cell!(
+                iter_to_heap_list(&mut self.heap, avec.into_iter())
+            );
 
             let mut cvec = Vec::new();
+
             for child in node.children() {
                 cvec.push(self.xml_node_to_term(indices, child));
             }
-            let children = Addr::HeapCell(self.heap.to_list(cvec.into_iter()));
 
-            let chars = clause_name!(String::from(node.tag_name().name()), self.atom_tbl);
-            let tag = self.heap.to_unifiable(HeapCellValue::Atom(chars, None));
+            let children = heap_loc_as_cell!(
+                iter_to_heap_list(&mut self.heap, cvec.into_iter())
+            );
+
+            let tag = self.atom_tbl.build_with(node.tag_name().name());
 
-            let result = Addr::HeapCell(self.heap.h());
+            let result = heap_loc_as_cell!(self.heap.len());
 
-            self.heap
-                .push(HeapCellValue::NamedStr(3, clause_name!("element"), None));
-            self.heap.push(HeapCellValue::Addr(tag));
-            self.heap.push(HeapCellValue::Addr(attrs));
-            self.heap.push(HeapCellValue::Addr(children));
+            self.heap.push(atom_as_cell!(atom!("element"), 3));
+            self.heap.push(atom_as_cell!(tag));
+            self.heap.push(attrs);
+            self.heap.push(children);
 
             result
         }
@@ -5745,45 +5477,54 @@ impl MachineState {
         &mut self,
         indices: &mut IndexStore,
         node: select::node::Node,
-    ) -> Addr {
+    ) -> HeapCellValue {
         match node.name() {
             None => {
-                let string = String::from(node.text());
-                self.heap.put_complete_string(&string)
+                put_complete_string(
+                    &mut self.heap,
+                    &node.text(),
+                    &mut self.atom_tbl,
+                )
             }
             Some(name) => {
                 let mut avec = Vec::new();
-                for attr in node.attrs() {
-                    let chars = clause_name!(String::from(attr.0), self.atom_tbl);
-                    let name = self.heap.to_unifiable(HeapCellValue::Atom(chars, None));
 
-                    let value = self.heap.put_complete_string(&String::from(attr.1));
+                for attr in node.attrs() {
+                    let name = self.atom_tbl.build_with(attr.0);
+                    let value = put_complete_string(
+                        &mut self.heap,
+                        &attr.1,
+                        &mut self.atom_tbl,
+                    );
 
-                    avec.push(HeapCellValue::Addr(Addr::HeapCell(self.heap.h())));
+                    avec.push(heap_loc_as_cell!(self.heap.len()));
 
-                    self.heap
-                        .push(HeapCellValue::NamedStr(2, clause_name!("="), None));
-                    self.heap.push(HeapCellValue::Addr(name));
-                    self.heap.push(HeapCellValue::Addr(value));
+                    self.heap.push(atom_as_cell!(atom!("="), 2));
+                    self.heap.push(atom_as_cell!(name));
+                    self.heap.push(value);
                 }
-                let attrs = Addr::HeapCell(self.heap.to_list(avec.into_iter()));
+
+                let attrs = heap_loc_as_cell!(
+                    iter_to_heap_list(&mut self.heap, avec.into_iter())
+                );
 
                 let mut cvec = Vec::new();
+
                 for child in node.children() {
                     cvec.push(self.html_node_to_term(indices, child));
                 }
-                let children = Addr::HeapCell(self.heap.to_list(cvec.into_iter()));
 
-                let chars = clause_name!(String::from(name), self.atom_tbl);
-                let tag = self.heap.to_unifiable(HeapCellValue::Atom(chars, None));
+                let children = heap_loc_as_cell!(
+                    iter_to_heap_list(&mut self.heap, cvec.into_iter())
+                );
 
-                let result = Addr::HeapCell(self.heap.h());
+                let tag = self.atom_tbl.build_with(name);
+                let result = heap_loc_as_cell!(self.heap.len());
 
-                self.heap
-                    .push(HeapCellValue::NamedStr(3, clause_name!("element"), None));
-                self.heap.push(HeapCellValue::Addr(tag));
-                self.heap.push(HeapCellValue::Addr(attrs));
-                self.heap.push(HeapCellValue::Addr(children));
+                self.heap.push(atom_as_cell!(atom!("element"), 3));
+                self.heap.push(atom_as_cell!(tag));
+                self.heap.push(attrs);
+                self.heap.push(children);
 
                 result
             }
@@ -5808,3 +5549,4 @@ impl hkdf::KeyType for MyKey<usize> {
         self.0
     }
 }
+
index e55a54996ffc1f0170cc7e488de58a5bcef3de27..a1d38271033e1b997a61a2998672d98fe309aa20 100644 (file)
@@ -1,48 +1,53 @@
-use prolog_parser::ast::*;
-use prolog_parser::parser::*;
-
-use crate::machine::machine_errors::CompilationError;
+use crate::forms::*;
 use crate::machine::*;
+use crate::machine::load_state::*;
+use crate::machine::loader::*;
+use crate::machine::machine_errors::*;
+use crate::parser::ast::*;
+use crate::parser::parser::*;
+
+use crate::predicate_queue;
 
 use indexmap::IndexSet;
 
 use std::collections::VecDeque;
 use std::fmt;
 
-pub(crate) trait TermStream: Sized {
-    type Evacuable;
+pub struct LoadStatePayload<TS> {
+    pub term_stream: TS,
+    pub(super) compilation_target: CompilationTarget,
+    pub(super) retraction_info: RetractionInfo,
+    pub(super) module_op_exports: ModuleOpExports,
+    pub(super) non_counted_bt_preds: IndexSet<PredicateKey>,
+    pub(super) predicates: PredicateQueue,
+    pub(super) clause_clauses: Vec<(Term, Term)>,
+}
 
+pub trait TermStream: Sized {
     fn next(&mut self, op_dir: &CompositeOpDir) -> Result<Term, CompilationError>;
     fn eof(&mut self) -> Result<bool, CompilationError>;
     fn listing_src(&self) -> &ListingSource;
-    fn evacuate<'a>(loader: Loader<'a, Self>) -> Result<Self::Evacuable, SessionError>;
 }
 
 #[derive(Debug)]
-pub(super) struct BootstrappingTermStream<'a> {
+pub struct BootstrappingTermStream<'a> {
     listing_src: ListingSource,
-    parser: Parser<'a, Stream>,
+    pub(super) parser: Parser<'a, Stream>,
 }
 
 impl<'a> BootstrappingTermStream<'a> {
     #[inline]
-    pub(super) fn from_prolog_stream(
-        stream: &'a mut PrologStream,
-        atom_tbl: TabledData<Atom>,
-        flags: MachineFlags,
+    pub(super) fn from_char_reader(
+        stream: Stream,
+        machine_st: &'a mut MachineState,
         listing_src: ListingSource,
     ) -> Self {
-        let parser = Parser::new(stream, atom_tbl, flags);
-        Self {
-            parser,
-            listing_src,
-        }
+        let parser = Parser::new(stream, machine_st);
+        Self { parser, listing_src }
     }
 }
 
 impl<'a> TermStream for BootstrappingTermStream<'a> {
-    type Evacuable = CompilationTarget;
-
     #[inline]
     fn next(&mut self, op_dir: &CompositeOpDir) -> Result<Term, CompilationError> {
         self.parser.reset();
@@ -61,24 +66,9 @@ impl<'a> TermStream for BootstrappingTermStream<'a> {
     fn listing_src(&self) -> &ListingSource {
         &self.listing_src
     }
-
-    fn evacuate(mut loader: Loader<Self>) -> Result<Self::Evacuable, SessionError> {
-        if !loader.predicates.is_empty() {
-            loader.compile_and_submit()?;
-        }
-
-        loader
-            .load_state
-            .retraction_info
-            .reset(loader.load_state.wam.code_repo.code.len());
-
-        loader.load_state.remove_module_op_exports();
-
-        Ok(loader.load_state.compilation_target.take())
-    }
 }
 
-pub(crate) struct LiveTermStream {
+pub struct LiveTermStream {
     pub(super) term_queue: VecDeque<Term>,
     pub(super) listing_src: ListingSource,
 }
@@ -93,28 +83,18 @@ impl LiveTermStream {
     }
 }
 
-pub(crate) struct LoadStatePayload {
-    pub(super) term_stream: LiveTermStream,
-    pub(super) compilation_target: CompilationTarget,
-    pub(super) retraction_info: RetractionInfo,
-    pub(super) module_op_exports: Vec<(OpDecl, Option<(usize, Specifier)>)>,
-    pub(super) non_counted_bt_preds: IndexSet<PredicateKey>,
-    pub(super) predicates: PredicateQueue,
-    pub(super) clause_clauses: Vec<(Term, Term)>,
-}
-
-impl fmt::Debug for LoadStatePayload {
+impl<TS> fmt::Debug for LoadStatePayload<TS> {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         write!(fmt, "LoadStatePayload")
     }
 }
 
-impl LoadStatePayload {
-    pub(super) fn new(wam: &Machine) -> Self {
+impl<TS> LoadStatePayload<TS> {
+    pub(super) fn new(code_repo_len: usize, term_stream: TS) -> Self {
         Self {
-            term_stream: LiveTermStream::new(ListingSource::User),
+            term_stream,
             compilation_target: CompilationTarget::default(),
-            retraction_info: RetractionInfo::new(wam.code_repo.code.len()),
+            retraction_info: RetractionInfo::new(code_repo_len),
             module_op_exports: vec![],
             non_counted_bt_preds: IndexSet::new(),
             predicates: predicate_queue![],
@@ -124,8 +104,6 @@ impl LoadStatePayload {
 }
 
 impl TermStream for LiveTermStream {
-    type Evacuable = LoadStatePayload;
-
     #[inline]
     fn next(&mut self, _: &CompositeOpDir) -> Result<Term, CompilationError> {
         Ok(self.term_queue.pop_front().unwrap())
@@ -140,9 +118,4 @@ impl TermStream for LiveTermStream {
     fn listing_src(&self) -> &ListingSource {
         &self.listing_src
     }
-
-    #[inline]
-    fn evacuate(loader: Loader<Self>) -> Result<LoadStatePayload, SessionError> {
-        Ok(loader.to_load_state_payload())
-    }
 }
index b8b6d802a111937129e64cc6a0a897b04b93f7a7..1b459928194e439e22f4d2f9cabb3d2dc6f75079 100644 (file)
@@ -13,53 +13,421 @@ macro_rules! count_tt {
     ($($a:tt $even:tt)*) => { count_tt!($($a)*) << 1 };
 }
 
-macro_rules! functor {
-    ($name:expr, $fixity:expr, [$($dt:ident($($value:expr),*)),+], [$($aux:ident),*]) => ({
-        {
-            #[allow(unused_variables, unused_mut)]
-            let mut addendum = Heap::new();
-            let arity = count_tt!($($dt) +);
-            let aux_lens = [$($aux.len()),*];
+macro_rules! char_as_cell {
+    ($c: expr) => {
+        HeapCellValue::build_with(HeapCellValueTag::Char, $c as u64)
+    };
+}
 
-            let mut result =
-                vec![ HeapCellValue::NamedStr(arity, clause_name!($name), Some($fixity)),
-                      $(functor_term!( $dt($($value),*), arity, aux_lens, addendum ),)+ ];
+macro_rules! fixnum_as_cell {
+    ($n: expr) => {
+        HeapCellValue::from_bytes($n.into_bytes()) //HeapCellValueTag::Fixnum, $n.get_num() as u64)
+    };
+}
 
-            $(
-                result.extend($aux.into_iter());
-            )*
+macro_rules! cell_as_fixnum {
+    ($cell:expr) => {
+        Fixnum::from_bytes($cell.into_bytes())
+    };
+}
 
-            result.extend(addendum.into_iter());
-            result
+macro_rules! integer_as_cell {
+    ($n: expr) => {{
+        match $n {
+            Number::Float(_) => unreachable!(),
+            Number::Fixnum(n) => fixnum_as_cell!(n),
+            Number::Rational(r) => typed_arena_ptr_as_cell!(r),
+            Number::Integer(n) => typed_arena_ptr_as_cell!(n),
         }
+    }};
+}
+
+macro_rules! empty_list_as_cell {
+    () => {
+        // the empty list atom has the fixed index of 8 (8 >> 3 == 1 in the condensed atom representation).
+        atom_as_cell!(atom!("[]"))
+    };
+}
+
+macro_rules! atom_as_cell {
+    ($atom:expr) => {
+        HeapCellValue::from_bytes(
+            AtomCell::build_with($atom.flat_index(), 0, HeapCellValueTag::Atom).into_bytes(),
+        )
+    };
+    ($atom:expr, $arity:expr) => {
+        HeapCellValue::from_bytes(
+            AtomCell::build_with($atom.flat_index(), $arity as u16, HeapCellValueTag::Atom)
+                .into_bytes(),
+        )
+    };
+}
+
+macro_rules! cell_as_ossified_op_dir {
+    ($cell:expr) => {{
+        let ptr_u64 = cell_as_untyped_arena_ptr!($cell);
+        TypedArenaPtr::new(ptr_u64.payload_offset() as *mut OssifiedOpDir)
+    }};
+}
+
+macro_rules! cell_as_string {
+    ($cell:expr) => {
+        PartialString::from(cell_as_atom!($cell))
+    };
+}
+
+macro_rules! cell_as_atom {
+    ($cell:expr) => {{
+        let cell = AtomCell::from_bytes($cell.into_bytes());
+        let name = cell.get_index() << 3;
+
+        Atom::from(name as usize)
+    }};
+}
+
+macro_rules! cell_as_atom_cell {
+    ($cell:expr) => {
+        AtomCell::from_bytes($cell.into_bytes())
+    };
+}
+
+macro_rules! cell_as_f64_ptr {
+    ($cell:expr) => {{
+        let ptr_u64 = ConsPtr::from_bytes($cell.into_bytes());
+        F64Ptr(TypedArenaPtr::new(
+            ptr_u64.as_ptr() as *mut OrderedFloat<f64>
+        ))
+    }};
+}
+
+macro_rules! cell_as_untyped_arena_ptr {
+    ($cell:expr) => {
+        UntypedArenaPtr::from(u64::from($cell) as *const ArenaHeader)
+    };
+}
+
+macro_rules! pstr_as_cell {
+    ($atom:expr) => {
+        HeapCellValue::from_bytes(
+            AtomCell::build_with($atom.flat_index(), 0, HeapCellValueTag::PStr).into_bytes(),
+        )
+    };
+}
+
+macro_rules! pstr_loc_as_cell {
+    ($h:expr) => {
+        HeapCellValue::build_with(HeapCellValueTag::PStrLoc, $h as u64)
+    };
+}
+
+macro_rules! pstr_offset_as_cell {
+    ($h:expr) => {
+        HeapCellValue::build_with(HeapCellValueTag::PStrOffset, $h as u64)
+    };
+}
+
+macro_rules! list_loc_as_cell {
+    ($h:expr) => {
+        HeapCellValue::build_with(HeapCellValueTag::Lis, $h as u64)
+    };
+}
+
+macro_rules! str_loc_as_cell {
+    ($h:expr) => {
+        HeapCellValue::build_with(HeapCellValueTag::Str, $h as u64)
+    };
+}
+
+macro_rules! stack_loc {
+    (OrFrame, $b:expr, $idx:expr) => ({
+        $b + prelude_size::<OrFrame>() + $idx * std::mem::size_of::<HeapCellValue>()
     });
-    ($name:expr, $fixity:expr, [$($dt:ident($($value:expr),*)),+]) => ({
-        {
-            #[allow(unused_variables, unused_mut)]
-            let mut addendum = Heap::new();
-            let arity = count_tt!($($dt) +);
+    (AndFrame, $e:expr, $idx:expr) => ({
+        $e + prelude_size::<AndFrame>() + ($idx  - 1) * std::mem::size_of::<HeapCellValue>()
+    });
+}
 
-            let mut result =
-                vec![ HeapCellValue::NamedStr(arity, clause_name!($name), Some($fixity)),
-                      $(functor_term!( $dt($($value),*), arity, [], addendum ),)+ ];
+macro_rules! stack_loc_as_cell {
+    (OrFrame, $b:expr, $idx:expr) => {
+        stack_loc_as_cell!(stack_loc!(OrFrame, $b, $idx))
+    };
+    (AndFrame, $b:expr, $idx:expr) => {
+        stack_loc_as_cell!(stack_loc!(AndFrame, $b, $idx))
+    };
+    ($h:expr) => {
+        HeapCellValue::build_with(HeapCellValueTag::StackVar, $h as u64)
+    };
+}
 
-            result.extend(addendum.into_iter());
-            result
+#[macro_export]
+macro_rules! heap_loc_as_cell {
+    ($h:expr) => {
+        HeapCellValue::build_with(HeapCellValueTag::Var, $h as u64)
+    };
+}
+
+macro_rules! attr_var_as_cell {
+    ($h:expr) => {
+        HeapCellValue::build_with(HeapCellValueTag::AttrVar, $h as u64)
+    };
+}
+
+#[allow(unused)]
+macro_rules! attr_var_loc_as_cell {
+    ($h:expr) => {
+        HeapCellValue::build_with(HeapCellValueTag::AttrVar, $h as u64)
+    };
+}
+
+macro_rules! typed_arena_ptr_as_cell {
+    ($ptr:expr) => {
+        untyped_arena_ptr_as_cell!($ptr.header_ptr())
+    };
+}
+
+macro_rules! untyped_arena_ptr_as_cell {
+    ($ptr:expr) => {
+        HeapCellValue::from_bytes(unsafe { std::mem::transmute($ptr) })
+    };
+}
+
+macro_rules! atom_as_cstr_cell {
+    ($atom:expr) => {{
+        let offset = $atom.flat_index();
+
+        HeapCellValue::from_bytes(
+            AtomCell::build_with(offset as u64, 0, HeapCellValueTag::CStr).into_bytes(),
+        )
+    }};
+}
+
+macro_rules! string_as_cstr_cell {
+    ($ptr:expr) => {{
+        let atom: Atom = $ptr.into();
+        let offset = atom.flat_index();
+
+        HeapCellValue::from_bytes(
+            AtomCell::build_with(offset as u64, 0, HeapCellValueTag::CStr).into_bytes(),
+        )
+    }};
+}
+
+macro_rules! string_as_pstr_cell {
+    ($ptr:expr) => {{
+        let atom: Atom = $ptr.into();
+        let offset = atom.flat_index();
+
+        HeapCellValue::from_bytes(
+            AtomCell::build_with(offset as u64, 0, HeapCellValueTag::PStr).into_bytes(),
+        )
+    }};
+}
+
+macro_rules! stream_as_cell {
+    ($ptr:expr) => {
+        untyped_arena_ptr_as_cell!($ptr.as_ptr())
+    };
+}
+
+macro_rules! cell_as_stream {
+    ($cell:expr) => {{
+        let ptr = cell_as_untyped_arena_ptr!($cell);
+        Stream::from_tag(ptr.get_tag(), ptr.payload_offset())
+    }};
+}
+
+macro_rules! cell_as_load_state_payload {
+    ($cell:expr) => { unsafe {
+        let ptr = cell_as_untyped_arena_ptr!($cell);
+        let ptr = std::mem::transmute::<_, *mut LiveLoadState>(ptr.payload_offset());
+
+        TypedArenaPtr::new(ptr)
+    }};
+}
+
+macro_rules! match_untyped_arena_ptr_pat_body {
+    ($ptr:ident, Integer, $n:ident, $code:expr) => {{
+        let payload_ptr = unsafe { std::mem::transmute::<_, *mut Integer>($ptr.payload_offset()) };
+        let $n = TypedArenaPtr::new(payload_ptr);
+        #[allow(unused_braces)]
+        $code
+    }};
+    ($ptr:ident, F64, $n:ident, $code:expr) => {{
+        let payload_ptr =
+            unsafe { std::mem::transmute::<_, *mut OrderedFloat<f64>>($ptr.payload_offset()) };
+        let $n = TypedArenaPtr::new(payload_ptr);
+        #[allow(unused_braces)]
+        $code
+    }};
+    ($ptr:ident, Rational, $n:ident, $code:expr) => {{
+        let payload_ptr = unsafe { std::mem::transmute::<_, *mut Rational>($ptr.payload_offset()) };
+        let $n = TypedArenaPtr::new(payload_ptr);
+        #[allow(unused_braces)]
+        $code
+    }};
+    ($cell:ident, OssifiedOpDir, $n:ident, $code:expr) => {{
+        let $n = cell_as_ossified_op_dir!($cell);
+        #[allow(unused_braces)]
+        $code
+    }};
+    ($cell:ident, LiveLoadState, $n:ident, $code:expr) => {{
+        let $n = cell_as_load_state_payload!($cell);
+        #[allow(unused_braces)]
+        $code
+    }};
+    ($ptr:ident, Stream, $s:ident, $code:expr) => {{
+        let $s = Stream::from_tag($ptr.get_tag(), $ptr.payload_offset());
+        #[allow(unused_braces)]
+        $code
+    }};
+    ($ptr:ident, TcpListener, $listener:ident, $code:expr) => {{
+        let payload_ptr = unsafe { std::mem::transmute::<_, *mut TcpListener>($ptr.payload_offset()) };
+        #[allow(unused_mut)]
+        let mut $listener = TypedArenaPtr::new(payload_ptr);
+        #[allow(unused_braces)]
+        $code
+    }};
+    ($ptr:ident, $($tags:tt)|+, $s:ident, $code:expr) => {{
+        let $s = Stream::from_tag($ptr.get_tag(), $ptr.payload_offset());
+        #[allow(unused_braces)]
+        $code
+    }};
+}
+
+macro_rules! match_untyped_arena_ptr_pat {
+    (Stream) => {
+        ArenaHeaderTag::InputFileStream
+            | ArenaHeaderTag::OutputFileStream
+            | ArenaHeaderTag::NamedTcpStream
+            | ArenaHeaderTag::NamedTlsStream
+            | ArenaHeaderTag::ReadlineStream
+            | ArenaHeaderTag::StaticStringStream
+            | ArenaHeaderTag::ByteStream
+            | ArenaHeaderTag::NullStream
+            | ArenaHeaderTag::StandardOutputStream
+            | ArenaHeaderTag::StandardErrorStream
+    };
+    ($tag:ident) => {
+        ArenaHeaderTag::$tag
+    };
+}
+
+macro_rules! match_untyped_arena_ptr {
+    ($ptr:expr, $( ($(ArenaHeaderTag::$tag:tt)|+, $n:ident) => $code:block $(,)?)+ $(_ => $misc_code:expr $(,)?)?) => ({
+        let ptr_id = $ptr;
+
+        match ptr_id.get_tag() {
+            $($(match_untyped_arena_ptr_pat!($tag) => {
+                match_untyped_arena_ptr_pat_body!(ptr_id, $tag, $n, $code)
+            })+)+
+            $(_ => $misc_code)?
+        }
+    });
+}
+
+macro_rules! read_heap_cell_pat_body {
+    ($cell:ident, Cons, $n:ident, $code:expr) => ({
+        let $n = cell_as_untyped_arena_ptr!($cell);
+        #[allow(unused_braces)]
+        $code
+    });
+    ($cell:ident, F64, $n:ident, $code:expr) => ({
+        let $n = cell_as_f64_ptr!($cell);
+        #[allow(unused_braces)]
+        $code
+    });
+    ($cell:ident, Atom, ($name:ident, $arity:ident), $code:expr) => ({
+        let ($name, $arity) = cell_as_atom_cell!($cell).get_name_and_arity();
+        #[allow(unused_braces)]
+        $code
+    });
+    ($cell:ident, PStr, $atom:ident, $code:expr) => ({
+        let $atom = cell_as_atom!($cell);
+        #[allow(unused_braces)]
+        $code
+    });
+    ($cell:ident, CStr, $atom:ident, $code:expr) => ({
+        let $atom = cell_as_atom!($cell);
+        #[allow(unused_braces)]
+        $code
+    });
+    ($cell:ident, CStr | PStr, $atom:ident, $code:expr) => ({
+        let $atom = cell_as_atom!($cell);
+        #[allow(unused_braces)]
+        $code
+    });
+    ($cell:ident, PStr | CStr, $atom:ident, $code:expr) => ({
+        let $atom = cell_as_atom!($cell);
+        #[allow(unused_braces)]
+        $code
+    });
+    ($cell:ident, Fixnum, $value:ident, $code:expr) => ({
+        let $value = Fixnum::from_bytes($cell.into_bytes());
+        #[allow(unused_braces)]
+        $code
+    });
+    ($cell:ident, Char, $value:ident, $code:expr) => ({
+        let $value = unsafe { char::from_u32_unchecked($cell.get_value() as u32) };
+        #[allow(unused_braces)]
+        $code
+    });
+    ($cell:ident, $($tags:tt)|+, $value:ident, $code:expr) => ({
+        let $value = $cell.get_value() as usize;
+        #[allow(unused_braces)]
+        $code
+    });
+}
+
+macro_rules! read_heap_cell_pat {
+    (($(HeapCellValueTag::$tag:tt)|+, $n:tt)) => {
+        $(HeapCellValueTag::$tag)|+
+    };
+    (($(HeapCellValueTag::$tag:tt)|+)) => {
+        $(HeapCellValueTag::$tag)|+
+    };
+    (_) => { _ };
+}
+
+macro_rules! read_heap_cell_pat_expander {
+    ($cell_id:ident, ($(HeapCellValueTag::$tag:tt)|+, $n:tt), $code:block) => ({
+        read_heap_cell_pat_body!($cell_id, $($tag)|+, $n, $code)
+    });
+    ($cell_id:ident, ($(HeapCellValueTag::$tag:tt)|+), $code:block) => ({
+        $code
+    });
+    ($cell_id:ident, _, $code:block) => ({
+        $code
+    });
+}
+
+macro_rules! read_heap_cell {
+    ($cell:expr, $($pat:tt $(if $guard_expr:expr)? => $code:block $(,)?)+) => ({
+        let cell_id = $cell;
+
+        match cell_id.get_tag() {
+            $(read_heap_cell_pat!($pat) $(if $guard_expr)? => {
+                read_heap_cell_pat_expander!(cell_id, $pat, $code)
+            })+
         }
     });
+}
+
+macro_rules! functor {
     ($name:expr, [$($dt:ident($($value:expr),*)),+], [$($aux:ident),*]) => ({
         {
             #[allow(unused_variables, unused_mut)]
             let mut addendum = Heap::new();
-            let arity = count_tt!($($dt) +);
-            let aux_lens = [$($aux.len()),*];
+            let arity: usize = count_tt!($($dt) +);
+
+            #[allow(unused_variables)]
+            let aux_lens: [usize; count_tt!($($aux) *)] = [$($aux.len()),*];
 
             let mut result =
-                vec![ HeapCellValue::NamedStr(arity, clause_name!($name), None),
+                vec![ atom_as_cell!($name, arity as u16),
                       $(functor_term!( $dt($($value),*), arity, aux_lens, addendum ),)+ ];
 
             $(
-                result.extend($aux.into_iter());
+                result.extend($aux.iter());
             )*
 
             result.extend(addendum.into_iter());
@@ -68,143 +436,126 @@ macro_rules! functor {
     });
     ($name:expr, [$($dt:ident($($value:expr),*)),+]) => ({
         {
-            use crate::machine::heap::*;
+            let arity: usize = count_tt!($($dt) +);
 
-            let arity = count_tt!($($dt) +);
             #[allow(unused_variables, unused_mut)]
             let mut addendum = Heap::new();
 
             let mut result =
-                vec![ HeapCellValue::NamedStr(arity, clause_name!($name), None),
+                vec![ atom_as_cell!($name, arity as u16),
                       $(functor_term!( $dt($($value),*), arity, [], addendum ),)+ ];
 
             result.extend(addendum.into_iter());
             result
         }
     });
-    ($name:expr, $fixity:expr) => (
-        vec![ HeapCellValue::Atom(clause_name!($name), Some($fixity)) ]
-    );
-    (clause_name($name:expr)) => (
-        vec![ HeapCellValue::Atom($name, None) ]
-    );
-    ($name:expr) => (
-        vec![ HeapCellValue::Atom(clause_name!($name), None) ]
-    );
+    ($name:expr) => ({
+        vec![ atom_as_cell!($name) ]
+    });
 }
 
 macro_rules! functor_term {
-    (aux(0), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
-        HeapCellValue::Addr(Addr::HeapCell($arity + 1))
+    (str(0), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
+        str_loc_as_cell!($arity + 1)
     });
-    (aux($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
+    (str($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
         let len: usize = $aux_lens[0 .. $e].iter().sum();
-        HeapCellValue::Addr(Addr::HeapCell($arity + 1 + len))
+        str_loc_as_cell!($arity + 1 + len)
     });
-    (aux($h:expr, 0), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
-        HeapCellValue::Addr(Addr::HeapCell($arity + $h + 1))
+    (str($h:expr, 0), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
+        str_loc_as_cell!($arity + $h + 1)
     });
-    (aux($h:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
+    (str($h:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
         let len: usize = $aux_lens[0 .. $e].iter().sum();
-        HeapCellValue::Addr(Addr::HeapCell($arity + $h + 1 + len))
+        str_loc_as_cell!($arity + $h + 1 + len)
     });
-    (addr($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
-        HeapCellValue::Addr($e)
+    (literal($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
+        HeapCellValue::from($e)
     );
-    (constant($h:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
-        from_constant!($e, $h, $arity, $aux_lens, $addendum)
+    (integer($e:expr, $arena:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
+        HeapCellValue::arena_from(Number::arena_from($e, $arena), $arena)
     );
-    (constant($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
-        from_constant!($e, 0, $arity, $aux_lens, $addendum)
-    );
-    (number($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
-        $e.into()
-    );
-    (integer($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
-        HeapCellValue::Integer(Rc::new(Integer::from($e)))
+    (fixnum($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
+        fixnum_as_cell!(Fixnum::build_with($e as i64))
     );
     (indexing_code_ptr($h:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
         let stub =
             match $e {
-                IndexingCodePtr::DynamicExternal(o) => functor!("dynamic_external", [integer(o)]),
-                IndexingCodePtr::External(o) => functor!("external", [integer(o)]),
-                IndexingCodePtr::Internal(o) => functor!("internal", [integer(o)]),
-                IndexingCodePtr::Fail => vec![HeapCellValue::Atom(clause_name!("fail"), None)],
+                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"))]
+                },
             };
 
         let len: usize = $aux_lens.iter().sum();
-        let h = len + $arity + 1 + $addendum.h() + $h;
+        let h = len + $arity + 1 + $addendum.len() + $h;
 
         $addendum.extend(stub.into_iter());
 
-        HeapCellValue::Addr(Addr::HeapCell(h))
+        str_loc_as_cell!(h)
     });
-    (clause_name($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
-        HeapCellValue::Atom($e, None)
+    (number($arena:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
+        HeapCellValue::from(($e, $arena))
     );
     (atom($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
-        HeapCellValue::Atom(clause_name!($e), None)
-    );
-    (value($e:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => (
-        $e
+        atom_as_cell!($e)
     );
     (string($h:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => ({
         let len: usize = $aux_lens.iter().sum();
-        let h = len + $arity + 1 + $addendum.h() + $h;
+        let h = len + $arity + 1 + $addendum.len() + $h;
 
-        $addendum.put_complete_string(&$e);
+        let cell = string_as_pstr_cell!($e);
 
-        HeapCellValue::Addr(Addr::PStrLocation(h, 0))
+        $addendum.push(cell);
+        $addendum.push(empty_list_as_cell!());
+
+        heap_loc_as_cell!(h)
     });
     (boolean($e:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => ({
         if $e {
-            functor_term!(atom("true"), $arity, $aux_lens, $addendum)
+            functor_term!(atom(atom!("true")), $arity, $aux_lens, $addendum)
         } else {
-            functor_term!(atom("false"), $arity, $aux_lens, $addendum)
+            functor_term!(atom(atom!("false")), $arity, $aux_lens, $addendum)
         }
     });
-    ($e:expr, $arity:expr, $aux_lens:expr, $addendum:ident) => (
+    (cell($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
         $e
     );
 }
 
-macro_rules! from_constant {
-    ($e:expr, $over_h:expr, $arity:expr, $aux_lens:expr, $addendum:ident) => ({
-        match $e {
-            &Constant::Atom(ref name, ref op) => {
-                HeapCellValue::Atom(name.clone(), op.clone())
-            }
-            &Constant::Char(c) => {
-                HeapCellValue::Addr(Addr::Char(c))
-            }
-            &Constant::Fixnum(n) => {
-                HeapCellValue::Addr(Addr::Fixnum(n))
-            }
-            &Constant::Integer(ref n) => {
-                HeapCellValue::Integer(n.clone())
-            }
-            &Constant::Rational(ref r) => {
-                HeapCellValue::Rational(r.clone())
-            }
-            &Constant::Float(f) => {
-                HeapCellValue::Addr(Addr::Float(f))
-            }
-            &Constant::String(ref s) => {
-                let len: usize = $aux_lens.iter().sum();
-                let h = len + $arity + 1 + $addendum.h() + $over_h;
+macro_rules! ar_reg {
+    ($r: expr) => {
+        ArithmeticTerm::Reg($r)
+    };
+}
 
-                $addendum.put_complete_string(&s);
+macro_rules! unmark_cell_bits {
+    ($e:expr) => {{
+        let mut result = $e;
 
-                HeapCellValue::Addr(Addr::PStrLocation(h, 0))
-            }
-            &Constant::Usize(u) => {
-                HeapCellValue::Addr(Addr::Usize(u))
-            }
-            &Constant::EmptyList => {
-                HeapCellValue::Addr(Addr::EmptyList)
-            }
+        result.set_mark_bit(false);
+        result.set_forwarding_bit(false);
+
+        result
+    }};
+}
+
+macro_rules! index_store {
+    ($code_dir:expr, $op_dir:expr, $modules:expr) => {
+        IndexStore {
+            code_dir: $code_dir,
+            extensible_predicates: ExtensiblePredicates::new(),
+            local_extensible_predicates: LocalExtensiblePredicates::new(),
+            global_variables: GlobalVarDir::new(),
+            meta_predicates: MetaPredicateDir::new(),
+            modules: $modules,
+            op_dir: $op_dir,
+            streams: StreamDir::new(),
+            stream_aliases: StreamAliasDir::new(),
         }
-    })
+    };
 }
 
 macro_rules! is_atom {
@@ -358,22 +709,6 @@ macro_rules! dir_entry {
     };
 }
 
-macro_rules! index_store {
-    ($code_dir:expr, $op_dir:expr, $modules:expr) => {
-        IndexStore {
-            code_dir: $code_dir,
-            extensible_predicates: ExtensiblePredicates::new(),
-            local_extensible_predicates: LocalExtensiblePredicates::new(),
-            global_variables: GlobalVarDir::new(),
-            meta_predicates: MetaPredicateDir::new(),
-            modules: $modules,
-            op_dir: $op_dir,
-            streams: StreamDir::new(),
-            stream_aliases: StreamAliasDir::new(),
-        }
-    };
-}
-
 macro_rules! put_constant {
     ($lvl:expr, $cons:expr, $r:expr) => {
         QueryInstruction::PutConstant($lvl, $cons, $r)
@@ -408,43 +743,58 @@ macro_rules! discard_result {
     };
 }
 */
-macro_rules! ar_reg {
-    ($r: expr) => {
-        ArithmeticTerm::Reg($r)
-    };
-}
 
-macro_rules! atom_from {
-    ($self:expr, $e:expr) => {
+macro_rules! try_or_fail {
+    ($s:expr, $e:expr) => {{
         match $e {
-            Addr::Con(h) if $self.heap.atom_at(h) => {
-                match &$self.heap[h] {
-                    HeapCellValue::Atom(ref atom, _) => {
-                        atom.clone()
-                    }
-                    _ => {
-                        unreachable!()
-                    }
-                }
-            }
-            Addr::Char(c) => {
-                clause_name!(c.to_string(), $self.atom_tbl)
-            }
-            _ => {
-                unreachable!()
+            Ok(val) => val,
+            Err(msg) => {
+                $s.throw_exception(msg);
+                return;
             }
         }
-    }
+    }};
 }
 
-macro_rules! try_or_fail {
+macro_rules! try_or_fail_gen {
     ($s:expr, $e:expr) => {{
         match $e {
             Ok(val) => val,
-            Err(msg) => {
-                $s.throw_exception(msg);
+            Err(msg_fn) => {
+                let e = msg_fn($s);
+                $s.throw_exception(e);
                 return;
             }
         }
     }};
 }
+
+macro_rules! unify {
+    ($machine_st:expr, $($value:expr),*) => {{
+        $($machine_st.pdl.push($value);)*
+        $machine_st.unify()
+    }};
+}
+
+macro_rules! unify_fn {
+    ($machine_st:expr, $($value:expr),*) => {{
+        $($machine_st.pdl.push($value);)*
+        ($machine_st.unify_fn)($machine_st)
+    }};
+}
+
+macro_rules! unify_with_occurs_check {
+    ($machine_st:expr, $($value:expr),*) => {{
+        $($machine_st.pdl.push($value);)*
+        $machine_st.unify_with_occurs_check()
+    }};
+}
+
+macro_rules! compare_term_test {
+    ($machine_st:expr, $e1:expr, $e2:expr) => {{
+        $machine_st.pdl.push($e2);
+        $machine_st.pdl.push($e1);
+
+        $machine_st.compare_term_test()
+    }};
+}
diff --git a/src/parser/ast.rs b/src/parser/ast.rs
new file mode 100644 (file)
index 0000000..1bbde9c
--- /dev/null
@@ -0,0 +1,631 @@
+use crate::arena::*;
+use crate::atom_table::*;
+use crate::parser::char_reader::*;
+use crate::types::HeapCellValueTag;
+
+use std::cell::Cell;
+use std::fmt;
+use std::hash::Hash;
+use std::io::{Error as IOError};
+use std::ops::Neg;
+use std::rc::Rc;
+use std::vec::Vec;
+
+use rug::{Integer, Rational};
+
+use indexmap::IndexMap;
+use modular_bitfield::error::OutOfBounds;
+use modular_bitfield::prelude::*;
+
+pub type Specifier = u32;
+
+pub const MAX_ARITY: usize = 1023;
+
+pub const XFX: u32 = 0x0001;
+pub const XFY: u32 = 0x0002;
+pub const YFX: u32 = 0x0004;
+pub const XF: u32 = 0x0010;
+pub const YF: u32 = 0x0020;
+pub const FX: u32 = 0x0040;
+pub const FY: u32 = 0x0080;
+pub const DELIMITER: u32 = 0x0100;
+pub const TERM: u32 = 0x1000;
+pub const LTERM: u32 = 0x3000;
+
+pub const NEGATIVE_SIGN: u32 = 0x0200;
+
+#[macro_export]
+macro_rules! fixnum {
+    ($wrapper:tt, $n:expr, $arena:expr) => {
+        Fixnum::build_with_checked($n)
+            .map(<$wrapper>::Fixnum)
+            .unwrap_or_else(|_| <$wrapper>::Integer(arena_alloc!(Integer::from($n), $arena)))
+    };
+}
+
+macro_rules! is_term {
+    ($x:expr) => {
+        ($x as u32 & $crate::parser::ast::TERM) != 0
+    };
+}
+
+macro_rules! is_lterm {
+    ($x:expr) => {
+        ($x as u32 & $crate::parser::ast::LTERM) != 0
+    };
+}
+
+macro_rules! is_op {
+    ($x:expr) => {
+        $x as u32
+            & ($crate::parser::ast::XF
+                | $crate::parser::ast::YF
+                | $crate::parser::ast::FX
+                | $crate::parser::ast::FY
+                | $crate::parser::ast::XFX
+                | $crate::parser::ast::XFY
+                | $crate::parser::ast::YFX)
+            != 0
+    };
+}
+
+macro_rules! is_negate {
+    ($x:expr) => {
+        ($x as u32 & $crate::parser::ast::NEGATIVE_SIGN) != 0
+    };
+}
+
+#[macro_export]
+macro_rules! is_prefix {
+    ($x:expr) => {
+        $x as u32 & ($crate::parser::ast::FX | $crate::parser::ast::FY) != 0
+    };
+}
+
+#[macro_export]
+macro_rules! is_postfix {
+    ($x:expr) => {
+        $x as u32 & ($crate::parser::ast::XF | $crate::parser::ast::YF) != 0
+    };
+}
+
+#[macro_export]
+macro_rules! is_infix {
+    ($x:expr) => {
+        ($x as u32
+            & ($crate::parser::ast::XFX | $crate::parser::ast::XFY | $crate::parser::ast::YFX))
+            != 0
+    };
+}
+
+#[macro_export]
+macro_rules! is_xfx {
+    ($x:expr) => {
+        ($x as u32 & $crate::parser::ast::XFX) != 0
+    };
+}
+
+#[macro_export]
+macro_rules! is_xfy {
+    ($x:expr) => {
+        ($x as u32 & $crate::parser::ast::XFY) != 0
+    };
+}
+
+#[macro_export]
+macro_rules! is_yfx {
+    ($x:expr) => {
+        ($x as u32 & $crate::parser::ast::YFX) != 0
+    };
+}
+
+#[macro_export]
+macro_rules! is_yf {
+    ($x:expr) => {
+        ($x as u32 & $crate::parser::ast::YF) != 0
+    };
+}
+
+#[macro_export]
+macro_rules! is_xf {
+    ($x:expr) => {
+        ($x as u32 & $crate::parser::ast::XF) != 0
+    };
+}
+
+#[macro_export]
+macro_rules! is_fx {
+    ($x:expr) => {
+        ($x as u32 & $crate::parser::ast::FX) != 0
+    };
+}
+
+#[macro_export]
+macro_rules! is_fy {
+    ($x:expr) => {
+        ($x as u32 & $crate::parser::ast::FY) != 0
+    };
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum RegType {
+    Perm(usize),
+    Temp(usize),
+}
+
+impl Default for RegType {
+    fn default() -> Self {
+        RegType::Temp(0)
+    }
+}
+
+impl RegType {
+    pub fn reg_num(self) -> usize {
+        match self {
+            RegType::Perm(reg_num) | RegType::Temp(reg_num) => reg_num,
+        }
+    }
+
+    pub fn is_perm(self) -> bool {
+        matches!(self, RegType::Perm(_))
+    }
+}
+
+impl fmt::Display for RegType {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            RegType::Perm(val) => write!(f, "Y{}", val),
+            RegType::Temp(val) => write!(f, "X{}", val),
+        }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum VarReg {
+    ArgAndNorm(RegType, usize),
+    Norm(RegType),
+}
+
+impl VarReg {
+    pub fn norm(self) -> RegType {
+        match self {
+            VarReg::ArgAndNorm(reg, _) | VarReg::Norm(reg) => reg,
+        }
+    }
+}
+
+impl fmt::Display for VarReg {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            VarReg::Norm(RegType::Perm(reg)) => write!(f, "Y{}", reg),
+            VarReg::Norm(RegType::Temp(reg)) => write!(f, "X{}", reg),
+            VarReg::ArgAndNorm(RegType::Perm(reg), arg) => write!(f, "Y{} A{}", reg, arg),
+            VarReg::ArgAndNorm(RegType::Temp(reg), arg) => write!(f, "X{} A{}", reg, arg),
+        }
+    }
+}
+
+impl Default for VarReg {
+    fn default() -> Self {
+        VarReg::Norm(RegType::default())
+    }
+}
+
+#[macro_export]
+macro_rules! temp_v {
+    ($x:expr) => {
+        $crate::parser::ast::RegType::Temp($x)
+    };
+}
+
+#[macro_export]
+macro_rules! perm_v {
+    ($x:expr) => {
+        $crate::parser::ast::RegType::Perm($x)
+    };
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum GenContext {
+    Head,
+    Mid(usize),
+    Last(usize), // Mid & Last: chunk_num
+}
+
+impl GenContext {
+    pub fn chunk_num(self) -> usize {
+        match self {
+            GenContext::Head => 0,
+            GenContext::Mid(cn) | GenContext::Last(cn) => cn,
+        }
+    }
+}
+
+#[bitfield]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
+pub struct OpDesc {
+    prec: B11,
+    spec: B8,
+    #[allow(unused)] padding: B13,
+}
+
+impl OpDesc {
+    #[inline]
+    pub fn build_with(prec: u16, spec: u8) -> Self {
+        OpDesc::new().with_spec(spec).with_prec(prec)
+    }
+
+    #[inline]
+    pub fn get(self) -> (u16, u8) {
+        (self.prec(), self.spec())
+    }
+
+    pub fn set(&mut self, prec: u16, spec: u8) {
+        self.set_prec(prec);
+        self.set_spec(spec);
+    }
+
+    #[inline]
+    pub fn get_prec(self) -> u16 {
+        self.prec()
+    }
+
+    #[inline]
+    pub fn get_spec(self) -> u8 {
+        self.spec()
+    }
+
+    #[inline]
+    pub fn arity(self) -> usize {
+        if self.spec() as u32 & (XFX | XFY | YFX) == 0 {
+            1
+        } else {
+            2
+        }
+    }
+}
+
+// name and fixity -> operator type and precedence.
+pub type OpDir = IndexMap<(Atom, Fixity), OpDesc>;
+
+#[derive(Debug, Clone, Copy)]
+pub struct MachineFlags {
+    pub double_quotes: DoubleQuotes,
+}
+
+impl Default for MachineFlags {
+    fn default() -> Self {
+        MachineFlags {
+            double_quotes: DoubleQuotes::default(),
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum DoubleQuotes {
+    Atom,
+    Chars,
+    Codes,
+}
+
+impl DoubleQuotes {
+    pub fn is_chars(self) -> bool {
+        matches!(self, DoubleQuotes::Chars)
+    }
+
+    pub fn is_atom(self) -> bool {
+        matches!(self, DoubleQuotes::Atom)
+    }
+
+    pub fn is_codes(self) -> bool {
+        matches!(self, DoubleQuotes::Codes)
+    }
+}
+
+impl Default for DoubleQuotes {
+    fn default() -> Self {
+        DoubleQuotes::Chars
+    }
+}
+
+pub fn default_op_dir() -> OpDir {
+    let mut op_dir = OpDir::new();
+
+    op_dir.insert(
+        (atom!(":-"), Fixity::In),
+        OpDesc::build_with(1200, XFX as u8),
+    );
+    op_dir.insert(
+        (atom!(":-"), Fixity::Pre),
+        OpDesc::build_with(1200, FX as u8),
+    );
+    op_dir.insert(
+        (atom!("?-"), Fixity::Pre),
+        OpDesc::build_with(1200, FX as u8),
+    );
+    op_dir.insert(
+        (atom!(","), Fixity::In),
+        OpDesc::build_with(1000, XFY as u8),
+    );
+
+    op_dir
+}
+
+#[derive(Debug, Clone)]
+pub enum ArithmeticError {
+    NonEvaluableFunctor(Literal, usize),
+    UninstantiatedVar,
+}
+
+#[derive(Debug)]
+pub enum ParserError {
+    BackQuotedString(usize, usize),
+    UnexpectedChar(char, usize, usize),
+    UnexpectedEOF,
+    IO(IOError),
+    IncompleteReduction(usize, usize),
+    InvalidSingleQuotedCharacter(char),
+    MissingQuote(usize, usize),
+    NonPrologChar(usize, usize),
+    ParseBigInt(usize, usize),
+    LexicalError(lexical::Error),
+    Utf8Error(usize, usize),
+}
+
+impl ParserError {
+    pub fn line_and_col_num(&self) -> Option<(usize, usize)> {
+        match self {
+            &ParserError::BackQuotedString(line_num, col_num)
+            | &ParserError::UnexpectedChar(_, line_num, col_num)
+            | &ParserError::IncompleteReduction(line_num, col_num)
+            | &ParserError::MissingQuote(line_num, col_num)
+            | &ParserError::NonPrologChar(line_num, col_num)
+            | &ParserError::ParseBigInt(line_num, col_num)
+            | &ParserError::Utf8Error(line_num, col_num) => Some((line_num, col_num)),
+            _ => None,
+        }
+    }
+
+    pub fn as_atom(&self) -> Atom {
+        match self {
+            ParserError::BackQuotedString(..) => atom!("back_quoted_string"),
+            ParserError::UnexpectedChar(..) => atom!("unexpected_char"),
+            ParserError::UnexpectedEOF => atom!("unexpected_end_of_file"),
+            ParserError::IncompleteReduction(..) => atom!("incomplete_reduction"),
+            ParserError::InvalidSingleQuotedCharacter(..) => atom!("invalid_single_quoted_character"),
+            ParserError::IO(_) => atom!("input_output_error"),
+            ParserError::LexicalError(_) => atom!("lexical_error"), // TODO: ?
+            ParserError::MissingQuote(..) => atom!("missing_quote"),
+            ParserError::NonPrologChar(..) => atom!("non_prolog_character"),
+            ParserError::ParseBigInt(..) => atom!("cannot_parse_big_int"),
+            ParserError::Utf8Error(..) => atom!("utf8_conversion_error"),
+        }
+    }
+}
+
+impl From<lexical::Error> for ParserError {
+    fn from(e: lexical::Error) -> ParserError {
+        ParserError::LexicalError(e)
+    }
+}
+
+impl From<IOError> for ParserError {
+    fn from(e: IOError) -> ParserError {
+        ParserError::IO(e)
+    }
+}
+
+impl From<&IOError> for ParserError {
+    fn from(error: &IOError) -> ParserError {
+        if error.get_ref().filter(|e| e.is::<BadUtf8Error>()).is_some() {
+            ParserError::Utf8Error(0, 0)
+        } else {
+            ParserError::IO(error.kind().into())
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct CompositeOpDir<'a, 'b> {
+    pub primary_op_dir: Option<&'b OpDir>,
+    pub secondary_op_dir: &'a OpDir,
+}
+
+impl<'a, 'b> CompositeOpDir<'a, 'b> {
+    #[inline]
+    pub fn new(secondary_op_dir: &'a OpDir, primary_op_dir: Option<&'b OpDir>) -> Self {
+        CompositeOpDir {
+            primary_op_dir,
+            secondary_op_dir,
+        }
+    }
+
+    #[inline]
+    pub(crate) fn get(&self, name: Atom, fixity: Fixity) -> Option<OpDesc> {
+        let entry = if let Some(ref primary_op_dir) = &self.primary_op_dir {
+            primary_op_dir.get(&(name, fixity))
+        } else {
+            None
+        };
+
+        entry
+            .or_else(move || self.secondary_op_dir.get(&(name, fixity)))
+            .cloned()
+    }
+}
+
+#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
+pub enum Fixity {
+    In,
+    Post,
+    Pre,
+}
+
+#[bitfield]
+#[repr(u64)]
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct Fixnum {
+    num: B57,
+    #[allow(unused)] m: bool,
+    #[allow(unused)] tag: B6,
+}
+
+impl Fixnum {
+    #[inline]
+    pub fn build_with(num: i64) -> Self {
+        Fixnum::new()
+            .with_num(u64::from_ne_bytes(num.to_ne_bytes()) & ((1 << 57) - 1))
+            .with_tag(HeapCellValueTag::Fixnum as u8)
+            .with_m(false)
+        //num as u64).with__m(false)
+    }
+
+    #[inline]
+    pub fn build_with_checked(num: i64) -> Result<Self, OutOfBounds> {
+        const UPPER_BOUND: i64 = (1 << 56) - 1;
+        const LOWER_BOUND: i64 = -(1 << 56);
+
+        if LOWER_BOUND <= num && num <= UPPER_BOUND {
+            Ok(Fixnum::new()
+                .with_m(false)
+                .with_tag(HeapCellValueTag::Fixnum as u8)
+                .with_num(u64::from_ne_bytes(num.to_ne_bytes()) & ((1 << 57) - 1))) //num as u64 & ((1 << 57) - 1)))
+        } else {
+            Err(OutOfBounds {})
+        }
+    }
+
+    #[inline]
+    pub fn get_num(self) -> i64 {
+        let n = self.num() as i64;
+        let (n, overflowed) = (n << 7).overflowing_shr(7); // sign-extend the 57-bit signed fixnum.
+        debug_assert_eq!(overflowed, false);
+        n
+    }
+}
+
+impl Neg for Fixnum {
+    type Output = Self;
+
+    #[inline]
+    fn neg(self) -> Self::Output {
+        Fixnum::build_with(-self.get_num())
+    }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum Literal {
+    Atom(Atom),
+    Char(char),
+    Fixnum(Fixnum),
+    Integer(TypedArenaPtr<Integer>),
+    Rational(TypedArenaPtr<Rational>),
+    Float(F64Ptr),
+    String(Atom),
+}
+
+impl fmt::Display for Literal {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Literal::Atom(ref atom) => {
+                //                if atom.as_str().chars().any(|c| "`.$'\" ".contains(c)) {
+                //                    write!(f, "'{}'", atom)
+                //                } else {
+                write!(f, "{}", atom.flat_index())
+                //                }
+            }
+            Literal::Char(c) => write!(f, "'{}'", *c as u32),
+            Literal::Fixnum(n) => write!(f, "{}", n.get_num()),
+            Literal::Integer(ref n) => write!(f, "{}", n),
+            Literal::Rational(ref n) => write!(f, "{}", n),
+            Literal::Float(ref n) => write!(f, "{}", *n),
+            Literal::String(ref s) => write!(f, "\"{}\"", s.as_str()),
+            //          Literal::Usize(integer) => write!(f, "u{}", integer),
+        }
+    }
+}
+
+impl Literal {
+    pub fn to_atom(&self, atom_tbl: &mut AtomTable) -> Option<Atom> {
+        match self {
+            Literal::Atom(atom) => Some(atom.defrock_brackets(atom_tbl)),
+            _ => None,
+        }
+    }
+}
+
+#[derive(Debug, Clone)]
+pub enum Term {
+    AnonVar,
+    Clause(Cell<RegType>, Atom, Vec<Term>),
+    Cons(Cell<RegType>, Box<Term>, Box<Term>),
+    Literal(Cell<RegType>, Literal),
+    PartialString(Cell<RegType>, Atom, Option<Box<Term>>),
+    Var(Cell<VarReg>, Rc<String>),
+}
+
+impl Term {
+    pub fn into_literal(self) -> Option<Literal> {
+        match self {
+            Term::Literal(_, c) => Some(c),
+            _ => None,
+        }
+    }
+
+    pub fn first_arg(&self) -> Option<&Term> {
+        match self {
+            Term::Clause(_, _, ref terms) => terms.first(),
+            _ => None,
+        }
+    }
+
+    pub fn set_name(&mut self, new_name: Atom) {
+        match self {
+            Term::Literal(_, Literal::Atom(ref mut atom)) | Term::Clause(_, ref mut atom, ..) => {
+                *atom = new_name;
+            }
+            _ => {}
+        }
+    }
+
+    pub fn name(&self) -> Option<Atom> {
+        match self {
+            &Term::Literal(_, Literal::Atom(ref atom)) | &Term::Clause(_, ref atom, ..) => {
+                Some(*atom)
+            }
+            _ => None,
+        }
+    }
+
+    pub fn arity(&self) -> usize {
+        match self {
+            Term::Clause(_, _, ref child_terms, ..) => child_terms.len(),
+            _ => 0,
+        }
+    }
+}
+
+fn unfold_by_str_once(term: &mut Term, s: Atom) -> Option<(Term, Term)> {
+    if let Term::Clause(_, ref name, ref mut subterms) = term {
+        if name == &s && subterms.len() == 2 {
+            let snd = subterms.pop().unwrap();
+            let fst = subterms.pop().unwrap();
+
+            return Some((fst, snd));
+        }
+    }
+
+    None
+}
+
+pub fn unfold_by_str(mut term: Term, s: Atom) -> Vec<Term> {
+    let mut terms = vec![];
+
+    while let Some((fst, snd)) = unfold_by_str_once(&mut term, s) {
+        terms.push(fst);
+        term = snd;
+    }
+
+    terms.push(term);
+    terms
+}
diff --git a/src/parser/char_reader.rs b/src/parser/char_reader.rs
new file mode 100644 (file)
index 0000000..9c23371
--- /dev/null
@@ -0,0 +1,747 @@
+/*
+ * CharReader is a not entirely redundant flattening/chimera of std's
+ * BufReader and unicode_reader's CodePoints, introduced to allow
+ * peekable buffered UTF-8 codepoints and access to the underlying
+ * reader.
+ *
+ * Unlike CodePoints, it doesn't make the reader inaccessible by
+ * wrapping it a Bytes struct.
+ *
+ * Unlike BufReader, its buffer is peekable as a char.
+ */
+
+use smallvec::*;
+
+use std::error::Error;
+use std::fmt;
+use std::io;
+use std::io::{ErrorKind, IoSliceMut, Read};
+use std::str;
+
+pub struct CharReader<R> {
+    inner: R,
+    buf: SmallVec<[u8;4]>,
+    pos: usize,
+}
+
+/// An error raised when parsing a UTF-8 byte stream fails.
+#[derive(Debug)]
+pub struct BadUtf8Error {
+    /// The bytes that could not be parsed as a code point.
+    pub bytes: Vec<u8>,
+}
+
+impl Error for BadUtf8Error {
+    fn description(&self) -> &str {
+        "BadUtf8Error"
+    }
+}
+
+impl fmt::Display for BadUtf8Error {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "Bad UTF-8: {:?}", self.bytes)
+    }
+}
+
+impl<R> CharReader<R> {
+    pub fn new(inner: R) -> CharReader<R> {
+        Self {
+            inner,
+            buf: SmallVec::new(),
+            pos: 0,
+        }
+    }
+
+    #[inline]
+    pub fn inner(&self) -> &R {
+        &self.inner
+    }
+
+    #[inline]
+    pub fn inner_mut(&mut self) -> &mut R {
+        &mut self.inner
+    }
+}
+
+pub trait CharRead {
+    fn read_char(&mut self) -> Option<io::Result<char>> {
+        match self.peek_char() {
+            Some(Ok(c)) => {
+                self.consume(c.len_utf8());
+                Some(Ok(c))
+            }
+            result => result
+        }
+    }
+
+    fn peek_char(&mut self) -> Option<io::Result<char>>;
+    fn put_back_char(&mut self, c: char);
+    fn consume(&mut self, nread: usize);
+}
+
+impl<R> CharReader<R> {
+    pub fn get_ref(&self) -> &R {
+        &self.inner
+    }
+
+    pub fn get_mut(&mut self) -> &mut R {
+        &mut self.inner
+    }
+
+    pub fn buffer(&self) -> &[u8] {
+        &self.buf[self.pos..]
+    }
+
+    pub fn into_inner(self) -> R {
+        self.inner
+    }
+
+    fn reset_buffer(&mut self) {
+        self.buf.clear();
+        self.pos = 0;
+    }
+}
+
+impl<R: Read> CharReader<R> {
+    fn refresh_buffer(&mut self) -> io::Result<&[u8]> {
+        // If we've reached the end of our internal buffer then we need to fetch
+        // some more data from the underlying reader.
+        // Branch using `>=` instead of the more correct `==`
+        // to tell the compiler that the pos..cap slice is always valid.
+        if self.pos >= self.buf.len() {
+            debug_assert!(self.pos == self.buf.len());
+
+            self.buf.clear();
+
+            let mut word = [0u8;4];
+            let nread = self.inner.read(&mut word)?;
+
+            self.buf.extend_from_slice(&word[..nread]);
+            self.pos = 0;
+        }
+
+        Ok(&self.buf[self.pos..])
+    }
+}
+
+impl<R: Read> CharRead for CharReader<R> {
+    fn peek_char(&mut self) -> Option<io::Result<char>> {
+        match self.refresh_buffer() {
+            Ok(_buf) => {}
+            Err(e) => return Some(Err(e)),
+        }
+
+        loop {
+            let buf = &self.buf[self.pos..];
+
+            if !buf.is_empty() {
+                let e = match str::from_utf8(buf) {
+                    Ok(s) => {
+                        let mut chars = s.chars();
+                        let c = chars.next().unwrap();
+
+                        return Some(Ok(c));
+                    }
+                    Err(e) => {
+                        e
+                    }
+                };
+
+                if buf.len() - e.valid_up_to() >= 4 {
+                    // If we have 4 bytes that still don't make up
+                    // a valid code point, then we have garbage.
+
+                    // We have bad data in the buffer. Remove
+                    // leading bytes until either the buffer is
+                    // empty, or we have a valid code point.
+
+                    let mut split_point = 1;
+                    let mut badbytes = vec![];
+
+                    loop {
+                        let (bad, rest) = buf.split_at(split_point);
+
+                        if rest.is_empty() || str::from_utf8(rest).is_ok() {
+                            badbytes.extend_from_slice(bad);
+                            break;
+                        }
+
+                        split_point += 1;
+                    }
+
+                    // Raise the error. If we still have data in
+                    // the buffer, it will be returned on the next
+                    // loop.
+
+                    return Some(Err(io::Error::new(io::ErrorKind::InvalidData,
+                                                   BadUtf8Error { bytes: badbytes })));
+                } else {
+                    if self.pos >= self.buf.len() {
+                        return None;
+                    } else if self.buf.len() - self.pos >= 4 {
+                        return match str::from_utf8(&self.buf[..e.valid_up_to()]) {
+                            Ok(s) => {
+                                let mut chars = s.chars();
+                                let c = chars.next().unwrap();
+
+                                Some(Ok(c))
+                            }
+                            Err(e) => {
+                                let badbytes = self.buf[..e.valid_up_to()].to_vec();
+
+                                Some(Err(io::Error::new(io::ErrorKind::InvalidData,
+                                                        BadUtf8Error { bytes: badbytes })))
+                            }
+                        };
+                    } else {
+                        let buf_len = self.buf.len();
+
+                        for (c, idx) in (self.pos..buf_len).enumerate() {
+                            self.buf[c] = self.buf[idx];
+                        }
+
+                        self.buf.truncate(buf_len - self.pos);
+
+                        let buf_len = self.buf.len();
+
+                        let mut word = [0u8;4];
+                        let word_slice = &mut word[buf_len..4];
+
+                        match self.inner.read(word_slice) {
+                            Err(e) => return Some(Err(e)),
+                            Ok(nread) => {
+                                self.buf.extend_from_slice(&word_slice[0..nread]);
+                            }
+                        }
+
+                        self.pos = 0;
+                    }
+                }
+            } else {
+                return None;
+            }
+        }
+    }
+
+    #[inline(always)]
+    fn put_back_char(&mut self, c: char) {
+        let src_len = self.buf.len() - self.pos;
+        debug_assert!(src_len <= 4);
+
+        let c_len = c.len_utf8();
+        let mut shifted_slice = [0u8; 4];
+
+        shifted_slice[0..src_len].copy_from_slice(&self.buf[self.pos .. self.buf.len()]);
+
+        self.buf.resize(c_len, 0);
+        self.buf.extend_from_slice(&shifted_slice[0..src_len]);
+        self.pos = 0;
+
+        c.encode_utf8(&mut self.buf[0..c_len]);
+    }
+
+    #[inline(always)]
+    fn consume(&mut self, nread: usize) {
+        self.pos += nread;
+    }
+}
+
+/*
+impl<R: Seek> BufReader<R> {
+    /// Seeks relative to the current position. If the new position lies within the buffer,
+    /// the buffer will not be flushed, allowing for more efficient seeks.
+    /// This method does not return the location of the underlying reader, so the caller
+    /// must track this information themselves if it is required.
+    #[stable(feature = "bufreader_seek_relative", since = "1.53.0")]
+    pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> {
+        let pos = self.pos as u64;
+        if offset < 0 {
+            if let Some(new_pos) = pos.checked_sub((-offset) as u64) {
+                self.pos = new_pos as usize;
+                return Ok(());
+            }
+        } else {
+            if let Some(new_pos) = pos.checked_add(offset as u64) {
+                if new_pos <= self.cap as u64 {
+                    self.pos = new_pos as usize;
+                    return Ok(());
+                }
+            }
+        }
+        self.seek(SeekFrom::Current(offset)).map(drop)
+    }
+}
+*/
+
+impl<R: Read> Read for CharReader<R> {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        // // If we don't have any buffered data and we're doing a massive read
+        // // (larger than our internal buffer), bypass our internal buffer
+        // // entirely.
+        // if self.pos == self.cap && buf.len() >= self.buf.len() {
+        //     self.discard_buffer();
+        //     return self.inner.read(buf);
+        // }
+
+        let mut inner_buf = self.refresh_buffer()?;
+        let nread = inner_buf.read(buf)?;
+
+        // let nread = {
+        //     let mut rem = self.fill_buf()?;
+        //     rem.read(buf)?
+        // };
+
+        self.consume(nread);
+        Ok(nread)
+    }
+
+    // Small read_exacts from a BufReader are extremely common when used with a deserializer.
+    // The default implementation calls read in a loop, which results in surprisingly poor code
+    // generation for the common path where the buffer has enough bytes to fill the passed-in
+    // buffer.
+    fn read_exact(&mut self, mut buf: &mut [u8]) -> io::Result<()> {
+        if self.buffer().len() >= buf.len() {
+            buf.copy_from_slice(&self.buffer()[..buf.len()]);
+            self.consume(buf.len());
+            return Ok(());
+        }
+
+        while !buf.is_empty() {
+            match self.read(buf) {
+                Ok(0) => break,
+                Ok(n) => {
+                    let tmp = buf;
+                    buf = &mut tmp[n..];
+                }
+                Err(e) if e.kind() == ErrorKind::Interrupted => {}
+                Err(e) => return Err(e),
+            }
+        }
+
+        if !buf.is_empty() {
+            Err(io::Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
+        } else {
+            Ok(())
+        }
+    }
+
+    fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
+
+        if self.pos == self.buf.len() && total_len >= self.buf.len() {
+            self.reset_buffer(); // self.discard_buffer();
+            return self.inner.read_vectored(bufs);
+        }
+
+        let nread = {
+            self.refresh_buffer()?;
+            (&self.buf[self.pos..]).read_vectored(bufs)?
+        };
+
+        self.consume(nread);
+        Ok(nread)
+    }
+}
+
+/*
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<R: Read> BufRead for BufReader<R> {
+    fn fill_buf(&mut self) -> io::Result<&[u8]> {
+        // If we've reached the end of our internal buffer then we need to fetch
+        // some more data from the underlying reader.
+        // Branch using `>=` instead of the more correct `==`
+        // to tell the compiler that the pos..cap slice is always valid.
+        if self.pos >= self.cap {
+            debug_assert!(self.pos == self.cap);
+            self.cap = self.inner.read(&mut self.buf)?;
+            self.pos = 0;
+        }
+        Ok(&self.buf[self.pos..self.cap])
+    }
+
+    fn consume(&mut self, amt: usize) {
+        self.pos = cmp::min(self.pos + amt, self.cap);
+    }
+}
+*/
+
+impl<R> fmt::Debug for CharReader<R>
+where
+    R: fmt::Debug,
+{
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt.debug_struct("CharReader")
+            .field("reader", &self.inner)
+            .field("buf", &format_args!("{}/{}", self.buf.capacity() - self.pos, self.buf.len()))
+            .finish()
+    }
+}
+
+/*
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<R: Seek> Seek for BufReader<R> {
+    /// Seek to an offset, in bytes, in the underlying reader.
+    ///
+    /// The position used for seeking with [`SeekFrom::Current`]`(_)` is the
+    /// position the underlying reader would be at if the `BufReader<R>` had no
+    /// internal buffer.
+    ///
+    /// Seeking always discards the internal buffer, even if the seek position
+    /// would otherwise fall within it. This guarantees that calling
+    /// [`BufReader::into_inner()`] immediately after a seek yields the underlying reader
+    /// at the same position.
+    ///
+    /// To seek without discarding the internal buffer, use [`BufReader::seek_relative`].
+    ///
+    /// See [`std::io::Seek`] for more details.
+    ///
+    /// Note: In the edge case where you're seeking with [`SeekFrom::Current`]`(n)`
+    /// where `n` minus the internal buffer length overflows an `i64`, two
+    /// seeks will be performed instead of one. If the second seek returns
+    /// [`Err`], the underlying reader will be left at the same position it would
+    /// have if you called `seek` with [`SeekFrom::Current`]`(0)`.
+    ///
+    /// [`std::io::Seek`]: Seek
+    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
+        let result: u64;
+        if let SeekFrom::Current(n) = pos {
+            let remainder = (self.cap - self.pos) as i64;
+            // it should be safe to assume that remainder fits within an i64 as the alternative
+            // means we managed to allocate 8 exbibytes and that's absurd.
+            // But it's not out of the realm of possibility for some weird underlying reader to
+            // support seeking by i64::MIN so we need to handle underflow when subtracting
+            // remainder.
+            if let Some(offset) = n.checked_sub(remainder) {
+                result = self.inner.seek(SeekFrom::Current(offset))?;
+            } else {
+                // seek backwards by our remainder, and then by the offset
+                self.inner.seek(SeekFrom::Current(-remainder))?;
+                self.discard_buffer();
+                result = self.inner.seek(SeekFrom::Current(n))?;
+            }
+        } else {
+            // Seeking with Start/End doesn't care about our buffer length.
+            result = self.inner.seek(pos)?;
+        }
+        self.discard_buffer();
+        Ok(result)
+    }
+
+    /// Returns the current seek position from the start of the stream.
+    ///
+    /// The value returned is equivalent to `self.seek(SeekFrom::Current(0))`
+    /// but does not flush the internal buffer. Due to this optimization the
+    /// function does not guarantee that calling `.into_inner()` immediately
+    /// afterwards will yield the underlying reader at the same position. Use
+    /// [`BufReader::seek`] instead if you require that guarantee.
+    ///
+    /// # Panics
+    ///
+    /// This function will panic if the position of the inner reader is smaller
+    /// than the amount of buffered data. That can happen if the inner reader
+    /// has an incorrect implementation of [`Seek::stream_position`], or if the
+    /// position has gone out of sync due to calling [`Seek::seek`] directly on
+    /// the underlying reader.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use std::{
+    ///     io::{self, BufRead, BufReader, Seek},
+    ///     fs::File,
+    /// };
+    ///
+    /// fn main() -> io::Result<()> {
+    ///     let mut f = BufReader::new(File::open("foo.txt")?);
+    ///
+    ///     let before = f.stream_position()?;
+    ///     f.read_line(&mut String::new())?;
+    ///     let after = f.stream_position()?;
+    ///
+    ///     println!("The first line was {} bytes long", after - before);
+    ///     Ok(())
+    /// }
+    /// ```
+    fn stream_position(&mut self) -> io::Result<u64> {
+        let remainder = (self.cap - self.pos) as u64;
+        self.inner.stream_position().map(|pos| {
+            pos.checked_sub(remainder).expect(
+                "overflow when subtracting remaining buffer size from inner stream position",
+            )
+        })
+    }
+}
+*/
+/*
+impl<T> SizeHint for CharReader<T> {
+    fn lower_bound(&self) -> usize {
+        self.buffer().len()
+    }
+}
+*/
+
+#[cfg(test)]
+mod tests {
+    use crate::parser::char_reader::*;
+    use std::io::Cursor;
+
+    #[test]
+    fn plain_string() {
+        let mut read_string = CharReader::new(Cursor::new("a string"));
+
+        for c in "a string".chars() {
+            assert_eq!(read_string.peek_char().unwrap().ok(), Some(c));
+            assert_eq!(read_string.read_char().unwrap().ok(), Some(c));
+        }
+
+        assert!(read_string.read_char().is_none());
+    }
+
+    #[test]
+    fn greek_string() {
+        let mut read_string = CharReader::new(Cursor::new("λέξη"));
+
+        for c in "λέξη".chars() {
+            assert_eq!(read_string.peek_char().unwrap().ok(), Some(c));
+            assert_eq!(read_string.read_char().unwrap().ok(), Some(c));
+        }
+
+        assert!(read_string.read_char().is_none());
+    }
+
+    #[test]
+    fn russian_string() {
+        let mut read_string = CharReader::new(Cursor::new("слово"));
+
+        for c in "слово".chars() {
+            assert_eq!(read_string.peek_char().unwrap().ok(), Some(c));
+            assert_eq!(read_string.read_char().unwrap().ok(), Some(c));
+        }
+
+        assert!(read_string.read_char().is_none());
+    }
+
+    #[test]
+    fn greek_lorem_ipsum() {
+        let lorem_ipsum = "Λορεμ ιπσθμ δολορ σιτ αμετ, οφφενδιτ
+    εφφιcιενδι σιτ ει, ηαρθμ λεγερε qθαερενδθμ ιθσ νε. Ηασ νο εροσ
+    σιγνιφερθμqθε, σεδ ετ μθτατ jθστο, ει cθμ ελιγενδι σcριπτορεμ
+    ρεπρεηενδθντ. Εοσ ατ αμετ μαλισ ελειφενδ. Ιν cθμ εριπθιτ
+    νομινατι. Θσθ ιν cετεροσ μαιορθμ, μθνερε ατομορθμ ινcιδεριντ θτ
+    ηασ. Αν ηασ λιβρισ πραεσεντ πατριοqθε, ηινc θτιναμ πριμισ νε
+    cθμ. Cθ μοδο ερρεμ σcριβεντθρ cθμ. Ει vισ δεcορε μαλορθμ
+    σεντεντιαε, σεδ νο λιβερ εvερτι μεντιτθμ. Προ φαcερ vολθτπατ
+    σαπιεντεμ ιν. Cθ εροσ περσεqθερισ πρι, εα ποσσιτ cετεροσ δθο. Πρι
+    εα μαλισ μθνερε.
+
+    Qθισ jθστο μαλορθμ cθ qθο. Νεc ατ οδιο σολετ μαιεστατισ, νε
+    φορενσιβθσ σαδιπσcινγ ιθσ, αν qθι ειθσ βρθτε σαπιεντεμ. Cομμθνε
+    περcιπιτθρ ιθσ αδ, μθνερε δολορθμ ιμπεδιτ ηισ νε. Νεc ετιαμ
+    προπριαε vιτθπερατα ιν. Σονετ νεμορε ιθσ cθ, ιν αφφερτ ινερμισ
+    cοτιδιεqθε vισ.
+
+    Ηασ ιδ νονθμυ δοcτθσ cοτιδιεqθε. Σινγθλισ πηιλοσοπηια εξ δθο. Εστ
+    νο ιραcθνδια cονσεqθθντθρ. Τε διcτασ επιcθρει εφφιcιαντθρ δθο, εοσ
+    νε νθλλα νομιναvι. Εθμ cθ ελιτρ λιβεραvισσε, σιτ περσεqθερισ
+    cομπλεcτιτθρ εξ, πονδερθμ σιμιλιqθε ηασ νο.
+
+    Σολθμ ποσσιμ λαβιτθρ εξ ηισ, ει δομινγ εξπετενδισ vελ, διαμ μινιμ
+    σcριπσεριτ ει περ. Αθδιαμ οcθρρερετ προ εξ, δομινγ vολθπταρια ετ
+    qθο. Cονσθλ σανcτθσ αccθμσαν νο ιθσ, αδ εαμ αλβθcιθσ
+    ηονεστατισ. Ετ vιξ φαcιλισ qθαλισqθε ερροριβθσ, ηισ εθ πθρτο
+    ασσεντιορ. Ιθσ βονορθμ ηονεστατισ σcριπσεριτ ατ, ιν ναμ εσσε μοvετ
+    γραεcο. Αθγθε cονσεcτετθερ εστ ατ.
+
+    Αδ ταλε σθασ μθνερε σεδ, vισ φεθγαιτ αντιοπαμ ιδ. Προ εθ ινερμισ
+    σαλθτατθσ, σαεπε qθαεστιο θρβανιτασ cθ περ. Ιν μαλορθμ σαλθτατθσ
+    δετερρθισσετ περ, νε παρτεμ vολθτπατ ινστρθcτιορ vιξ. Νο vισ
+    δεμοcριτθμ εφφιcιαντθρ, επιcθρει αδολεσcενσ εστ cθ, ιδ vιξ
+    λθcιλιθσ αδιπισcινγ. Σεα τε cλιτα ιραcθνδια. Σεα αν σιμθλ
+    εσσεντ. Vοcιβθσ ελειφενδ cονσεqθθντθρ περ αδ, αν ναμ πονδερθμ
+    vολθπταρια.
+
+    Λιβερ ερθδιτι αccθσαμθσ θτ ναμ. Σιτ αντιοπαμ γθβεργρεν νε. Αμετ
+    ανcιλλαε ετ qθι, μεα σολθμ λαθδεμ εα. Εθ μελ παρτεμ οβλιqθε
+    πηαεδρθμ. Εξ μελ jθστο αccομμοδαρε, νε νολθισσε σινγθλισ σενσιβθσ
+    cθμ, vισ εθ τιμεαμ αδιπισcινγ.
+
+    Τε νολθισσε vολθπτατθμ εστ. Ασσθμ νομιναvι πρι νε, ει νοστρθμ
+    επιcθρει μεα. Σεδ cθ ελιτ δεσερθντ, γραεcε ερροριβθσ προ θτ, περ
+    νε εθισμοδ vολθπταρια. Νο εθμ διcατ ποσσιμ, νεc πρινcιπεσ
+    cονcεπταμ νε. Εθ αππαρεατ ιντελλεγατ σεα. Μελ θτ ελιτ λαθδεμ, θσθ
+    δολορεμ cομπλεcτιτθρ ετ, νε μεα δολορεσ μολεστιαε.
+
+    Θσθ λεγενδοσ vολθπτατιβθσ cθ. Qθο νε αδηθc ρεφερρεντθρ, αλια
+    μεδιοcρεμ δθο νε, σεδ ερρεμ δολορθμ αccομμοδαρε νε. Ετιαμ εqθιδεμ
+    δετερρθισσετ cθ μει, ετ εροσ cετεροσ σεα, εξ vιξ ενιμ cασε
+    δετραξιτ. Σεδ σολθτα λιβρισ ειρμοδ τε, νοvθμ ποπθλο νε εθμ. Σθμμο
+    αδμοδθμ δεσερθντ εστ εξ, εστ διcαμ εqθιδεμ cθ.
+
+    Ιλλθμ cορπορα ινvιδθντ εαμ ετ. Σεδ μαλισ ταcιματεσ εvερτιτθρ εα,
+    μαζιμ νθλλαμ vοcιβθσ μεα ει. Μεα ορνατθσ λθπτατθμ αδιπισcινγ
+    αδ. Μεα αφφερτ νοστερ ατ, ναμ αν σολεατ ερροριβθσ. Εξ σεα αεqθε
+    μθνερε cετερο, εοσ ηινc ελειφενδ δεμοcριτθμ.";
+
+        let mut lorem_ipsum_reader = CharReader::new(Cursor::new(lorem_ipsum));
+
+        for c in lorem_ipsum.chars() {
+            assert_eq!(lorem_ipsum_reader.peek_char().unwrap().ok(), Some(c));
+            assert_eq!(lorem_ipsum_reader.read_char().unwrap().ok(), Some(c));
+        }
+
+        assert!(lorem_ipsum_reader.read_char().is_none());
+    }
+
+    #[test]
+    fn armenian_lorem_ipsum() {
+        let lorem_ipsum = "լոռեմ իպսում դոլոռ սիթ ամեթ, նովում գռաեծո
+        սեա եա, աբհոռռեանթ դիսպութանդո եի քուի. իդ քուոդ ինդոծթում
+        եսթ, մեա թե ծոմմոդո ծոռպոռա. եթ ծոնսուլ ադիպիսծինգ ռեֆոռմիդանս
+        պեռ, ինեռմիս ֆեուգաիթ նո քուո, թալե սալե պռո եա. եթ նիբհ
+        աուգուե վոլումուս դուո, նե ծում եխեռծի սալութաթուս գլոռիաթուռ,
+        ծու թաթիոն պռաեսենթ մեդիոծռեմ վիս.
+
+        վիխ եռոս ռեֆեռռենթուռ եու. պեռսիուս վիթուպեռաթոռիբուս ութ սեա,
+        վիդե ինվիդունթ պռոբաթուս նո քուո. մեի եռոս մելիուս նոմինավի
+        իդ, ութ պռո քուաս քուաեսթիո. եթ նաթում պեթենթիում սուավիթաթե
+        հիս. քուի ծոնսթիթութո մեդիոծռիթաթեմ թե. ծեթեռո դեթռածթո
+        ծոնծեպթամ սեա եթ. դիսսենթիեթ ելոքուենթիամ թհեոպհռասթուս նեծ
+        աթ, աթ ֆածեթե եռիպուիթ վիխ.
+
+        ասսուեվեռիթ սծռիպսեռիթ եսթ եթ, վիդիթ դեբեթ եվեռթի եխ
+        եսթ. աութեմ լաուդեմ պոսիդոնիում մեի եի. ռեբում դիծամ ծեթեռոս
+        եում ծու. նիհիլ եխպեթենդա ասսուեվեռիթ ուսու ան. ւիսի թաթիոն
+        դելենիթ նո իուս, սեդ եխ իդքուե սիգնիֆեռումքուե, բռութե զռիլ
+        ալբուծիուս ան պռի.
+
+        մովեթ իռիուռե սալութանդի պեռ նո, եի ոմնիս աֆֆեռթ պեռսեքուեռիս
+        իուս, եթ պռաեսենթ մալուիսսեթ եսթ. եսթ պռոբո գուբեռգռեն եթ, հաս
+        ին դիամ նումքուամ. ֆեուգաիթ ինվենիռե ռեպուդիանդաե աթ սեդ,
+        իուվառեթ ծոնսուլաթու եֆֆիծիանթուռ ուսու եի. ութ մեա ածծումսան
+        նոմինավի թինծիդունթ, մեի դիծթա ածծումսան ութ. վիմ ոմնիում
+        ելիգենդի սծռիպթոռեմ եու.
+
+        իդ վիս եռռոռ ալիքուիպ ելոքուենթիամ, ադ դելենիթի պեռծիպիթ
+        դեֆինիթիոնես իուս. վիմ իուդիծո դեմոծռիթում ծոմպռեհենսամ թե,
+        ութ նիհիլ լոբոռթիս վոլուպթաթիբուս վել, դիծունթ մենթիթում
+        ֆածիլիսիս եի եում. եսսե սալե մինիմ եոս նե. ագամ ոմնեսքուե ծում
+        ին.
+
+        իուվառեթ իուդիծաբիթ ծում աթ, ուսու նիբհ աթքուի դոմինգ եխ. եի
+        քուի սանծթուս սենսիբուս, նամ ուբիքուե ապպեթեռե պռոդեսսեթ
+        եու. ուսու եթ աուգուե ծոնվենիռե սծռիբենթուռ. ան ոմնիում վեռեառ
+        ութռոքուե դուո, եսթ եի լիբեռ մեդիոծռեմ եխպլիծառի, ոմնիս
+        աուդիռե թե պռի. վիմ մունեռե սոլեաթ ծու, եռոս ինվենիռե
+        դիսպութաթիոնի եի քուո, ան ալթեռա պութենթ լաբոռես պռո. անթիոպամ
+        դեմոծռիթում պեռ ին.
+
+        նե քուի ծիբո ելիթռ. նեծ նե լիբեռ վոլուպթուա. նիսլ ծոմմունե
+        եխպեթենդիս նամ եխ, իուդիծո պլածեռաթ պեռծիպիթուռ մել նո, եթ
+        պառթեմ պութանթ քուի. վիմ թինծիդունթ ածծոմմոդառե աթ, նե նամ
+        վիդիթ իռիուռե, պռո եա ելիգենդի պոսթուլանթ ծոնսթիթութո.
+
+        մել ութ ոդիո նուլլամ եխպլիծառի. պռոպռիաե թինծիդունթ
+        դելիծաթիսսիմի եամ ան, մոդո քուոդսի ապեռիռի եու եսթ, պեռ աթ
+        լաբոռես սենսեռիթ. վիմ ծոնգուե ռեպուդիանդաե եի, նեծ ագամ
+        դիծունթ դելիծաթիսսիմի աթ. պոսսիթ լիբեռավիսսե եոս եու.
+
+        աթ ալիա դեբեթ ելաբոռառեթ քուո, ին ալիի ածծումսան ծոնսթիթուամ
+        հաս, մել թոթա ոմիթթանթուռ ինսթռուծթիոռ նո. պեռ նե ծաուսաե
+        սապիենթեմ, պաուլո ոմնեսքուե եի քուո, եխ ոռաթիո պհիլոսոպհիա
+        սիթ. իգնոթա ծաուսաե աթ ուսու, եխ քուո դիծթաս քուոդսի
+        ռեպուդիառե. ծոռպոռա պռոդեսսեթ ռեֆեռռենթուռ եոս եխ.
+
+        եու եթիամ ելեիֆենդ մել, սալե սծռիպսեռիթ հիս եու. պոռռո
+        ադոլեսծենս մեի եա. ին մեա զռիլ պռոբաթուս սալութաթուս. եոս ադ
+        մինիմ թեմպոռիբուս. սեա նե եթիամ.";
+
+        let mut lorem_ipsum_reader = CharReader::new(Cursor::new(lorem_ipsum));
+
+        for c in lorem_ipsum.chars() {
+            assert_eq!(lorem_ipsum_reader.peek_char().unwrap().ok(), Some(c));
+            assert_eq!(lorem_ipsum_reader.read_char().unwrap().ok(), Some(c));
+        }
+
+        assert!(lorem_ipsum_reader.read_char().is_none());
+    }
+
+    #[test]
+    fn russian_lorem_ipsum() {
+        let lorem_ipsum = "Лорем ипсум долор сит амет, атяуи дицам еи
+        сит, ид сеа фацилис елаборарет. Меа еу яуас алияуид, те яуи
+        саперет аппеллантур. Ех иус диам дицта волуптариа, еу пер
+        бруте омиттам аццусата. Хис сапиентем губергрен те, яуидам
+        луптатум персеяуерис ад ест.
+
+        Ан алияуип перицулис нам, нец апериам цотидиеяуе волуптатибус
+        но. Солум тритани пер ех, меи не одио тритани рецусабо, цу при
+        веро мелиоре импердиет. Ин граеци индоцтум салутатус нец, диам
+        сцаевола пертинациа про те. Ут сеа дебитис лаборамус
+        диссентиас, еи цум яуот лобортис.
+
+        Децоре сингулис вим не. Еос не риденс оффициис, еу нонумы
+        лабитур еррорибус хас, вел омнис цонституто посидониум но. Вел
+        персиус фастидии репрехендунт ид. Натум иллум ипсум сит ад, еа
+        еам новум латине. Еос нолуиссе патриояуе елояуентиам те.
+
+        Стет малис яуаерендум хас ад, прима цотидиеяуе мел ан,
+        трацтатос десеруиссе нам ех. Ин малорум сусципиантур вим, ех
+        меа граецо тритани адолесценс. Промпта цонцлусионемяуе нам еи,
+        дуо ин лаборе алтерум цотидиеяуе. Но елитр промпта сплендиде
+        еум, аеяуе ассуеверит цонституам яуи ид. Ад тале еррор
+        интеллегебат хас, ерудити граецис хас не, пер ут лабитур
+        еуисмод. Те при суммо путант. Про утинам цоммуне урбанитас еа.
+
+        Идяуе репрехендунт еи нам, алии толлит легере нам не, хис еа
+        виси адверсариум цонцлусионемяуе. Хас ассум омиттам луцилиус
+        ет, вих цонсул малорум фастидии не, сенсибус ассуеверит дуо
+        ут. Дуо алиа видит цетеро ат, еа аппареат пертинах вел. Пер
+        цонституто инцидеринт ин, убияуе риденс сенсерит цум цу. Про
+        ет цетерос темпорибус, те вел пурто суммо, дуо мунере вертерем
+        урбанитас ад. Сит оптион елецтрам форенсибус но. Еи татион
+        сапиентем ест, лаборе сцрипта сингулис но вим, усу еу елигенди
+        персецути.
+
+        Иус ан елецтрам цонтентионес. Меи атяуи нонумес ут, вел амет
+        репрехендунт ан, вис еу яуаестио патриояуе. Про синт легере
+        детрацто ад. Постеа долорем евертитур при ет, вим номинави
+        принципес ирацундиа ех. Доцтус интеллегебат но нам. Фацете
+        оффициис нецесситатибус цу меа.
+
+        Промпта симилияуе вис ин. Пер бонорум перицулис аргументум
+        ад. Еу дицат фацилис губергрен нам, еффициенди цомпрехенсам
+        хас еу. Инани нонумы усу но, ад цонцептам репудиандае
+        про. Тота нуллам делицата еа яуо, усу дуис дебет путент еи.
+
+        Вис апериам доценди елояуентиам еа. Ех яуот детрацто
+        елояуентиам цум, ерос малис дицерет вис ин. Еа цум модус
+        еяуидем, дебет нуллам ан меи. Алтерум омиттам про ет.
+
+        Яуи ех латине алияуам, ан меи одио нуллам. Ид хас омнис ребум
+        либрис. Ет убияуе путант дебитис про, ех хис медиоцрем
+        партиендо, но елит елецтрам дуо. Еу меа сонет номинави
+        цотидиеяуе. Нам фалли новум минимум еу, перфецто ратионибус
+        цонституто ад меа.
+
+        Нобис детрацто еам ид, при еу ассум пертинах, те етиам
+        проприае салутанди яуо. Легимус сусципиантур ет хас, сед
+        поссит дефинитионес еа. Ест не патриояуе омиттантур
+        интеллегебат, еу яуо дебет цонцлудатуряуе. Еум ад мнесарчум
+        дефинитионем, елитр лаборамус перципитур про не, хас феугаит
+        фастидии луцилиус ид. Фастидии интеллегат ех.";
+
+        let mut lorem_ipsum_reader = CharReader::new(Cursor::new(lorem_ipsum));
+
+        for c in lorem_ipsum.chars() {
+            assert_eq!(lorem_ipsum_reader.peek_char().unwrap().ok(), Some(c));
+            assert_eq!(lorem_ipsum_reader.read_char().unwrap().ok(), Some(c));
+
+            lorem_ipsum_reader.put_back_char(c);
+
+            assert_eq!(lorem_ipsum_reader.peek_char().unwrap().ok(), Some(c));
+            assert_eq!(lorem_ipsum_reader.read_char().unwrap().ok(), Some(c));
+        }
+
+        assert!(lorem_ipsum_reader.read_char().is_none());
+    }
+}
similarity index 53%
rename from crates/prolog_parser/src/lexer.rs
rename to src/parser/lexer.rs
index 8be50d36a53615cfc38803c8c15eaf3e0ce210f9..d248766656d20460a9e6f923c78eb1917449714e 100644 (file)
@@ -1,20 +1,24 @@
-use crate::rug::Integer;
 use lexical::parse_lossy;
 use ordered_float::*;
 
-use crate::ast::*;
-use crate::tabled_rc::*;
+use crate::atom_table::*;
+pub use crate::machine::machine_state::*;
+use crate::parser::ast::*;
+use crate::parser::char_reader::*;
+use crate::parser::rug::Integer;
 
 use std::convert::TryFrom;
 use std::fmt;
-use std::io::Read;
-use std::rc::Rc;
 
 macro_rules! is_not_eof {
-    ($c:expr) => {
+    ($parser:expr, $c:expr) => {
         match $c {
+            Ok('\u{0}') => {
+                $parser.consume('\u{0}'.len_utf8());
+                return Ok(true);
+            }
             Ok(c) => c,
-            Err($crate::ast::ParserError::UnexpectedEOF) => return Ok(true),
+            Err($crate::parser::ast::ParserError::UnexpectedEOF) => return Ok(true),
             Err(e) => return Err(e),
         }
     };
@@ -26,17 +30,17 @@ macro_rules! consume_chars_with {
             match $e {
                 Ok(Some(c)) => $token.push(c),
                 Ok(None) => continue,
-                Err($crate::ast::ParserError::UnexpectedChar(..)) => break,
+                Err($crate::parser::ast::ParserError::UnexpectedChar(..)) => break,
                 Err(e) => return Err(e),
             }
         }
     };
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, PartialEq)]
 pub enum Token {
-    Constant(Constant),
-    Var(Rc<Atom>),
+    Literal(Literal),
+    Var(String),
     Open,              // '('
     OpenCT,            // '('
     Close,             // ')'
@@ -60,94 +64,93 @@ impl Token {
     }
 }
 
-pub struct Lexer<'a, R: Read> {
-    pub(crate) atom_tbl: TabledData<Atom>,
-    pub(crate) reader: &'a mut ParsingStream<R>,
-    pub(crate) flags: MachineFlags,
+pub struct Lexer<'a, R> {
+    pub(crate) reader: R,
+    pub(crate) machine_st: &'a mut MachineState,
     pub(crate) line_num: usize,
     pub(crate) col_num: usize,
 }
 
-impl<'a, R: Read + fmt::Debug> fmt::Debug for Lexer<'a, R> {
+impl<'a, R: fmt::Debug> fmt::Debug for Lexer<'a, R> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("Lexer")
-            .field("atom_tbl", &self.atom_tbl)
-            .field("reader", &"&'a mut ParsingStream<R>") // Hacky solution.
+            .field("reader", &"&'a mut R") // Hacky solution.
             .field("line_num", &self.line_num)
             .field("col_num", &self.col_num)
             .finish()
     }
 }
 
-impl<'a, R: Read> Lexer<'a, R> {
-    pub fn new(
-        atom_tbl: TabledData<Atom>,
-        flags: MachineFlags,
-        src: &'a mut ParsingStream<R>,
-    ) -> Self {
+impl<'a, R: CharRead> Lexer<'a, R> {
+    pub fn new(src: R, machine_st: &'a mut MachineState) -> Self {
         Lexer {
-            atom_tbl,
-            flags,
             reader: src,
+            machine_st,
             line_num: 0,
             col_num: 0,
         }
     }
 
-    fn return_char(&mut self, c: char) {
-        if new_line_char!(c) {
-            self.line_num -= 1;
-            self.col_num = 0;
+    pub fn lookahead_char(&mut self) -> Result<char, ParserError> {
+        match self.reader.peek_char() {
+            Some(Ok(c)) => Ok(c),
+            _ => Err(ParserError::UnexpectedEOF)
         }
+    }
 
-        self.reader.put_back(Ok(c));
+    pub fn read_char(&mut self) -> Result<char, ParserError> {
+        match self.reader.read_char() {
+            Some(Ok(c)) => Ok(c),
+            _ => Err(ParserError::UnexpectedEOF)
+        }
     }
 
-    fn skip_char(&mut self) -> Result<char, ParserError> {
-        if let Some(Ok(c)) = self.reader.next() {
-            self.col_num += 1;
+    #[inline(always)]
+    fn return_char(&mut self, c: char) {
+        self.reader.put_back_char(c);
+    }
 
-            if new_line_char!(c) {
-                self.line_num += 1;
-                self.col_num = 0;
-            }
+    fn skip_char(&mut self, c: char) {
+        self.reader.consume(c.len_utf8());
 
-            Ok(c)
+        if new_line_char!(c) {
+            self.line_num += 1;
+            self.col_num = 0;
         } else {
-            Err(ParserError::UnexpectedEOF)
+            self.col_num += 1;
         }
     }
 
     pub fn eof(&mut self) -> Result<bool, ParserError> {
-        if self.reader.peek().is_none() {
+        if self.reader.peek_char().is_none() {
             return Ok(true);
         }
 
-        let mut c = is_not_eof!(self.lookahead_char());
+        let mut c = is_not_eof!(self.reader, self.lookahead_char());
 
         while layout_char!(c) {
-            self.skip_char()?;
+            self.skip_char(c);
 
-            if self.reader.peek().is_none() {
+            if self.reader.peek_char().is_none() {
                 return Ok(true);
             }
 
-            c = is_not_eof!(self.lookahead_char());
+            c = is_not_eof!(self.reader, self.lookahead_char());
         }
 
         Ok(false)
     }
 
-    pub fn lookahead_char(&mut self) -> Result<char, ParserError> {
-        match self.reader.peek() {
-            Some(&Ok(c)) => Ok(c),
-            _ => Err(ParserError::UnexpectedEOF),
-        }
-    }
-
     fn single_line_comment(&mut self) -> Result<(), ParserError> {
         loop {
-            if self.reader.peek().is_none() || new_line_char!(self.skip_char()?) {
+            if self.reader.peek_char().is_none() {
+                break;
+            }
+
+            let c = self.lookahead_char()?;
+            self.skip_char(c);
+
+            if new_line_char!(c) {
                 break;
             }
         }
@@ -156,65 +159,80 @@ impl<'a, R: Read> Lexer<'a, R> {
     }
 
     fn bracketed_comment(&mut self) -> Result<bool, ParserError> {
-        // we have already checked that the current lookahead_char is comment_1_char, just skip it
-        let c = self.skip_char()?;
+        // we have already checked that the current lookahead_char is
+        // comment_1_char, just skip it
+        self.skip_char('/');
 
-        if comment_2_char!(self.lookahead_char()?) {
-            self.skip_char()?;
+        let c = self.lookahead_char()?;
+
+        if comment_2_char!(c) {
+            self.skip_char(c);
 
             // Keep reading until we find characters '*' and '/'
-            // Deliberately skip checks for prolog_char to allow comments to contain any characters,
-            // including so-called "extended characters", without having to explicitly add them to a character class.
+            // Deliberately skip checks for prolog_char to allow
+            // comments to contain any characters, including so-called
+            // "extended characters", without having to explicitly add
+            // them to a character class.
+
             let mut c = self.lookahead_char()?;
+
             loop {
                 while !comment_2_char!(c) {
-                    self.skip_char()?;
+                    self.skip_char(c);
                     c = self.lookahead_char()?;
                 }
 
-                self.skip_char()?;
-
+                self.skip_char(c);
                 c = self.lookahead_char()?;
+
                 if comment_1_char!(c) {
                     break;
                 }
             }
 
             if prolog_char!(c) {
-                self.skip_char()?;
+                self.skip_char(c);
                 Ok(true)
             } else {
                 Err(ParserError::NonPrologChar(self.line_num, self.col_num))
             }
         } else {
-            self.return_char(c);
+            self.return_char('/');
             Ok(false)
         }
     }
 
     fn get_back_quoted_char(&mut self) -> Result<char, ParserError> {
-        if back_quote_char!(self.lookahead_char()?) {
-            let c = self.skip_char()?;
+        let c = self.lookahead_char()?;
 
-            if !back_quote_char!(self.lookahead_char()?) {
+        if back_quote_char!(c) {
+            self.skip_char(c);
+            let c2 = self.lookahead_char()?;
+
+            if !back_quote_char!(c2) {
                 self.return_char(c);
                 Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num))
             } else {
-                self.skip_char()
+                self.skip_char(c2);
+                Ok(c2)
             }
-        } else if single_quote_char!(self.lookahead_char()?) {
-            self.skip_char()
+        } else if single_quote_char!(c) {
+            self.skip_char(c);
+            self.read_char()
         } else {
             self.get_non_quote_char()
         }
     }
 
     fn get_back_quoted_item(&mut self) -> Result<Option<char>, ParserError> {
-        if backslash_char!(self.lookahead_char()?) {
-            let c = self.skip_char()?;
+        let c = self.lookahead_char()?;
 
-            if new_line_char!(self.lookahead_char()?) {
-                self.skip_char()?;
+        if backslash_char!(c) {
+            self.skip_char(c);
+            let c2 = self.lookahead_char()?;
+
+            if new_line_char!(c2) {
+                self.skip_char(c2);
                 Ok(None)
             } else {
                 self.return_char(c);
@@ -229,13 +247,15 @@ impl<'a, R: Read> Lexer<'a, R> {
         let c = self.lookahead_char()?;
 
         if back_quote_char!(c) {
-            self.skip_char()?;
+            self.skip_char(c);
 
-            let mut token = String::new();
+            let mut token = String::with_capacity(16);
             consume_chars_with!(token, self.get_back_quoted_item());
 
-            if back_quote_char!(self.lookahead_char()?) {
-                self.skip_char()?;
+            let c = self.lookahead_char()?;
+
+            if back_quote_char!(c) {
+                self.skip_char(c);
                 Ok(token)
             } else {
                 Err(ParserError::MissingQuote(self.line_num, self.col_num))
@@ -246,11 +266,14 @@ impl<'a, R: Read> Lexer<'a, R> {
     }
 
     fn get_single_quoted_item(&mut self) -> Result<Option<char>, ParserError> {
-        if backslash_char!(self.lookahead_char()?) {
-            let c = self.skip_char()?;
+        let c = self.lookahead_char()?;
+
+        if backslash_char!(c) {
+            self.skip_char(c);
+            let c2 = self.lookahead_char()?;
 
-            if new_line_char!(self.lookahead_char()?) {
-                self.skip_char()?;
+            if new_line_char!(c2) {
+                self.skip_char(c2);
                 return Ok(None);
             } else {
                 self.return_char(c);
@@ -264,27 +287,34 @@ impl<'a, R: Read> Lexer<'a, R> {
         let c = self.lookahead_char()?;
 
         if single_quote_char!(c) {
-            self.skip_char()?;
+            self.skip_char(c);
+            let c2 = self.lookahead_char()?;
 
-            if !single_quote_char!(self.lookahead_char()?) {
+            if !single_quote_char!(c2) {
                 self.return_char(c);
                 Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num))
             } else {
-                self.skip_char()
+                self.skip_char(c2);
+                Ok(c2)
             }
         } else if double_quote_char!(c) || back_quote_char!(c) {
-            self.skip_char()
+            self.skip_char(c);
+            Ok(c)
         } else {
             self.get_non_quote_char()
         }
     }
 
     fn get_double_quoted_item(&mut self) -> Result<Option<char>, ParserError> {
-        if backslash_char!(self.lookahead_char()?) {
-            let c = self.skip_char()?;
+        let c = self.lookahead_char()?;
+
+        if backslash_char!(c) {
+            self.skip_char(c);
 
-            if new_line_char!(self.lookahead_char()?) {
-                self.skip_char()?;
+            let c2 = self.lookahead_char()?;
+
+            if new_line_char!(c2) {
+                self.skip_char(c2);
                 return Ok(None);
             } else {
                 self.return_char(c);
@@ -295,26 +325,31 @@ impl<'a, R: Read> Lexer<'a, R> {
     }
 
     fn get_double_quoted_char(&mut self) -> Result<char, ParserError> {
-        if double_quote_char!(self.lookahead_char()?) {
-            let c = self.skip_char()?;
+        let c = self.lookahead_char()?;
 
-            if !double_quote_char!(self.lookahead_char()?) {
+        if double_quote_char!(c) {
+            self.skip_char(c);
+            let c2 = self.lookahead_char()?;
+
+            if !double_quote_char!(c2) {
                 self.return_char(c);
                 Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num))
             } else {
-                self.skip_char()
+                self.skip_char(c2);
+                Ok(c2)
             }
-        } else if single_quote_char!(self.lookahead_char()?) {
-            self.skip_char()
-        } else if back_quote_char!(self.lookahead_char()?) {
-            self.skip_char()
+        } else if single_quote_char!(c) || back_quote_char!(c) {
+            self.skip_char(c);
+            Ok(c)
         } else {
             self.get_non_quote_char()
         }
     }
 
     fn get_control_escape_sequence(&mut self) -> Result<char, ParserError> {
-        let escaped = match self.lookahead_char()? {
+        let c = self.lookahead_char()?;
+
+        let escaped = match c {
             'a' => '\u{07}', // UTF-8 alert
             'b' => '\u{08}', // UTF-8 backspace
             'v' => '\u{0b}', // UTF-8 vertical tab
@@ -325,16 +360,16 @@ impl<'a, R: Read> Lexer<'a, R> {
             c => return Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num)),
         };
 
-        self.skip_char()?;
-        return Ok(escaped);
+        self.skip_char(c);
+        Ok(escaped)
     }
 
     fn get_octal_escape_sequence(&mut self) -> Result<char, ParserError> {
         self.escape_sequence_to_char(|c| octal_digit_char!(c), 8)
     }
 
-    fn get_hexadecimal_escape_sequence(&mut self) -> Result<char, ParserError> {
-        self.skip_char()?;
+    fn get_hexadecimal_escape_sequence(&mut self, start: char) -> Result<char, ParserError> {
+        self.skip_char(start);
         let c = self.lookahead_char()?;
 
         if hexadecimal_digit_char!(c) {
@@ -350,12 +385,12 @@ impl<'a, R: Read> Lexer<'a, R> {
         radix: u32,
     ) -> Result<char, ParserError> {
         let mut c = self.lookahead_char()?;
-        let mut token = String::new();
+        let mut token = String::with_capacity(16);
 
         loop {
             token.push(c);
 
-            self.skip_char()?;
+            self.skip_char(c);
             c = self.lookahead_char()?;
 
             if !accept_char(c) {
@@ -364,7 +399,7 @@ impl<'a, R: Read> Lexer<'a, R> {
         }
 
         if backslash_char!(c) {
-            self.skip_char()?;
+            self.skip_char(c);
             u32::from_str_radix(&token, radix).map_or_else(
                 |_| Err(ParserError::ParseBigInt(self.line_num, self.col_num)),
                 |n| {
@@ -374,8 +409,8 @@ impl<'a, R: Read> Lexer<'a, R> {
             )
         } else {
             // on failure, restore the token characters and backslash.
-            self.reader.put_back_all(token.chars().map(Ok));
-            self.reader.put_back(Ok('\\'));
+            // self.reader.put_back_all(token.chars().map(Ok));
+            // self.reader.put_back(Ok('\\'));
 
             Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num))
         }
@@ -385,158 +420,212 @@ impl<'a, R: Read> Lexer<'a, R> {
         let c = self.lookahead_char()?;
 
         if graphic_char!(c) || alpha_numeric_char!(c) || solo_char!(c) || space_char!(c) {
-            self.skip_char()
+            self.skip_char(c);
+            Ok(c)
         } else {
             if !backslash_char!(c) {
                 return Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num));
             }
 
-            self.skip_char()?;
-
+            self.skip_char(c);
             let c = self.lookahead_char()?;
 
             if meta_char!(c) {
-                self.skip_char()
+                self.skip_char(c);
+                Ok(c)
             } else if octal_digit_char!(c) {
                 self.get_octal_escape_sequence()
             } else if symbolic_hexadecimal_char!(c) {
-                self.get_hexadecimal_escape_sequence()
+                self.get_hexadecimal_escape_sequence(c)
             } else {
                 self.get_control_escape_sequence()
             }
         }
     }
 
-    fn char_code_list_token(&mut self) -> Result<String, ParserError> {
-        let mut token = String::new();
+    fn char_code_list_token(&mut self, start: char) -> Result<String, ParserError> {
+        let mut token = String::with_capacity(16);
 
-        self.skip_char()?;
+        self.skip_char(start);
         consume_chars_with!(token, self.get_double_quoted_item());
 
-        if double_quote_char!(self.lookahead_char()?) {
-            self.skip_char()?;
+        let c = self.lookahead_char()?;
+
+        if double_quote_char!(c) {
+            self.skip_char(c);
             Ok(token)
         } else {
             Err(ParserError::MissingQuote(self.line_num, self.col_num))
         }
     }
 
-    fn hexadecimal_constant(&mut self) -> Result<Token, ParserError> {
-        self.skip_char()?;
+    fn hexadecimal_constant(&mut self, start: char) -> Result<Token, ParserError> {
+        self.skip_char(start);
+        let mut c = self.lookahead_char()?;
 
-        if hexadecimal_digit_char!(self.lookahead_char()?) {
-            let mut token = String::new();
+        if hexadecimal_digit_char!(c) {
+            let mut token = String::with_capacity(16);
 
-            while hexadecimal_digit_char!(self.lookahead_char()?) {
-                token.push(self.skip_char()?);
+            loop {
+                if hexadecimal_digit_char!(c) {
+                    self.skip_char(c);
+                    token.push(c);
+                    c = self.lookahead_char()?;
+                } else {
+                    break;
+                }
             }
 
-            isize::from_str_radix(&token, 16)
-                .map(|n| Token::Constant(Constant::Fixnum(n)))
+            i64::from_str_radix(&token, 16)
+                .map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
                 .or_else(|_| {
                     Integer::from_str_radix(&token, 16)
-                        .map(|n| Token::Constant(Constant::Integer(Rc::new(n))))
+                        .map(|n| Token::Literal(Literal::Integer(
+                            arena_alloc!(n, &mut self.machine_st.arena)
+                        )))
                         .map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
                 })
         } else {
-            self.return_char('x');
+            self.return_char(start);
             Err(ParserError::ParseBigInt(self.line_num, self.col_num))
         }
     }
 
-    fn octal_constant(&mut self) -> Result<Token, ParserError> {
-        self.skip_char()?;
+    fn octal_constant(&mut self, start: char) -> Result<Token, ParserError> {
+        self.skip_char(start);
+        let mut c = self.lookahead_char()?;
 
-        if octal_digit_char!(self.lookahead_char()?) {
-            let mut token = String::new();
+        if octal_digit_char!(c) {
+            let mut token = String::with_capacity(16);
 
-            while octal_digit_char!(self.lookahead_char()?) {
-                token.push(self.skip_char()?);
+            loop {
+                if octal_digit_char!(c) {
+                    self.skip_char(c);
+                    token.push(c);
+                    c = self.lookahead_char()?;
+                } else {
+                    break;
+                }
             }
 
-            isize::from_str_radix(&token, 8)
-                .map(|n| Token::Constant(Constant::Fixnum(n)))
+            i64::from_str_radix(&token, 8)
+                .map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
                 .or_else(|_| {
                     Integer::from_str_radix(&token, 8)
-                        .map(|n| Token::Constant(Constant::Integer(Rc::new(n))))
+                        .map(|n| Token::Literal(Literal::Integer(
+                            arena_alloc!(n, &mut self.machine_st.arena)
+                        )))
                         .map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
                 })
         } else {
-            self.return_char('o');
+            self.return_char(start);
             Err(ParserError::ParseBigInt(self.line_num, self.col_num))
         }
     }
 
-    fn binary_constant(&mut self) -> Result<Token, ParserError> {
-        self.skip_char()?;
+    fn binary_constant(&mut self, start: char) -> Result<Token, ParserError> {
+        self.skip_char(start);
+        let mut c = self.lookahead_char()?;
 
-        if binary_digit_char!(self.lookahead_char()?) {
-            let mut token = String::new();
+        if binary_digit_char!(c) {
+            let mut token = String::with_capacity(16);
 
-            while binary_digit_char!(self.lookahead_char()?) {
-                token.push(self.skip_char()?);
+            loop {
+                if binary_digit_char!(c) {
+                    self.skip_char(c);
+                    token.push(c);
+                    c = self.lookahead_char()?;
+                } else {
+                    break;
+                }
             }
 
-            isize::from_str_radix(&token, 2)
-                .map(|n| Token::Constant(Constant::Fixnum(n)))
+            i64::from_str_radix(&token, 2)
+                .map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
                 .or_else(|_| {
                     Integer::from_str_radix(&token, 2)
-                        .map(|n| Token::Constant(Constant::Integer(Rc::new(n))))
+                        .map(|n| Token::Literal(Literal::Integer(
+                            arena_alloc!(n, &mut self.machine_st.arena)
+                        )))
                         .map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
                 })
         } else {
-            self.return_char('b');
+            self.return_char(start);
             Err(ParserError::ParseBigInt(self.line_num, self.col_num))
         }
     }
 
     fn variable_token(&mut self) -> Result<Token, ParserError> {
-        let mut s = String::new();
-        s.push(self.skip_char()?);
+        let mut s = String::with_capacity(16);
+        s.push(self.read_char()?);
 
-        while alpha_numeric_char!(self.lookahead_char()?) {
-            s.push(self.skip_char()?);
+        loop {
+            let c = self.lookahead_char()?;
+
+            if alpha_numeric_char!(c) {
+                self.skip_char(c);
+                s.push(c);
+            } else {
+                break;
+            }
         }
 
-        Ok(Token::Var(rc_atom!(s)))
+        Ok(Token::Var(s))
     }
 
     fn name_token(&mut self, c: char) -> Result<Token, ParserError> {
-        let mut token = String::new();
+        let mut token = String::with_capacity(16);
 
         if small_letter_char!(c) {
-            token.push(self.skip_char()?);
+            self.skip_char(c);
+            token.push(c);
+
+            loop {
+                let c = self.lookahead_char()?;
 
-            while alpha_numeric_char!(self.lookahead_char()?) {
-                token.push(self.skip_char()?);
+                if alpha_numeric_char!(c) {
+                    self.skip_char(c);
+                    token.push(c);
+                } else {
+                    break;
+                }
             }
         } else if graphic_token_char!(c) {
-            token.push(self.skip_char()?);
+            self.skip_char(c);
+            token.push(c);
+
+            loop {
+                let c = self.lookahead_char()?;
 
-            while graphic_token_char!(self.lookahead_char()?) {
-                token.push(self.skip_char()?);
+                if graphic_token_char!(c) {
+                    self.skip_char(c);
+                    token.push(c);
+                } else {
+                    break;
+                }
             }
         } else if cut_char!(c) {
-            token.push(self.skip_char()?);
+            self.skip_char(c);
+            token.push(c);
         } else if semicolon_char!(c) {
-            token.push(self.skip_char()?);
+            self.skip_char(c);
+            token.push(c);
         } else if single_quote_char!(c) {
-            self.skip_char()?;
-
+            self.skip_char(c);
             consume_chars_with!(token, self.get_single_quoted_item());
 
-            if single_quote_char!(self.lookahead_char()?) {
-                self.skip_char()?;
+            let c = self.lookahead_char()?;
+
+            if single_quote_char!(c) {
+                self.skip_char(c);
 
                 if !token.is_empty() && token.chars().nth(1).is_none() {
                     if let Some(c) = token.chars().next() {
-                        return Ok(Token::Constant(Constant::Char(c)));
+                        return Ok(Token::Literal(Literal::Char(c)));
                     }
                 }
             } else {
-                return Err(ParserError::InvalidSingleQuotedCharacter(
-                    self.lookahead_char()?,
-                ));
+                return Err(ParserError::InvalidSingleQuotedCharacter(c));
             }
         } else {
             match self.get_back_quoted_string() {
@@ -546,24 +635,29 @@ impl<'a, R: Read> Lexer<'a, R> {
         }
 
         if token.as_str() == "[]" {
-            Ok(Token::Constant(Constant::EmptyList))
+            Ok(Token::Literal(Literal::Atom(atom!("[]"))))
         } else {
-            Ok(Token::Constant(atom!(token, self.atom_tbl)))
+            Ok(Token::Literal(Literal::Atom(
+                self.machine_st.atom_tbl.build_with(&token),
+            )))
         }
     }
 
-    fn vacate_with_float(&mut self, mut token: String) -> Token {
+    fn vacate_with_float(&mut self, mut token: String) -> Result<Token, ParserError> {
         self.return_char(token.pop().unwrap());
 
-        let result = OrderedFloat(parse_lossy::<f64, _>(token.as_bytes()).unwrap());
-        Token::Constant(Constant::Float(result))
+        let result = OrderedFloat(parse_lossy::<f64, _>(token.as_bytes())?);
+        Ok(Token::Literal(Literal::Float(arena_alloc!(
+            result,
+            &mut self.machine_st.arena
+        ))))
     }
 
     fn skip_underscore_in_number(&mut self) -> Result<char, ParserError> {
         let mut c = self.lookahead_char()?;
 
         if c == '_' {
-            self.skip_char()?;
+            self.skip_char(c);
             self.scan_for_layout()?;
             c = self.lookahead_char()?;
 
@@ -577,112 +671,140 @@ impl<'a, R: Read> Lexer<'a, R> {
         }
     }
 
-    pub fn number_token(&mut self) -> Result<Token, ParserError> {
-        let mut token = String::new();
+    pub fn number_token(&mut self, leading_c: char) -> Result<Token, ParserError> {
+        let mut token = String::with_capacity(16);
 
-        token.push(self.skip_char()?);
+        self.skip_char(leading_c);
+        token.push(leading_c);
         let mut c = self.skip_underscore_in_number()?;
 
         while decimal_digit_char!(c) {
             token.push(c);
-            self.skip_char()?;
+            self.skip_char(c);
             c = self.skip_underscore_in_number()?;
         }
 
         if decimal_point_char!(c) {
-            self.skip_char()?;
+            self.skip_char(c);
 
-            if self.reader.peek().is_none() {
+            if self.reader.peek_char().is_none() {
                 self.return_char('.');
 
-                isize::from_str_radix(&token, 10)
-                    .map(|n| Token::Constant(Constant::Fixnum(n)))
+                i64::from_str_radix(&token, 10)
+                    .map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
                     .or_else(|_| {
                         token
                             .parse::<Integer>()
-                            .map(|n| Token::Constant(Constant::Integer(Rc::new(n))))
+                            .map(|n| {
+                                Token::Literal(Literal::Integer(arena_alloc!(n, &mut self.machine_st.arena)))
+                            })
                             .map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
                     })
             } else if decimal_digit_char!(self.lookahead_char()?) {
                 token.push('.');
-                token.push(self.skip_char()?);
+                token.push(self.read_char()?);
 
                 let mut c = self.lookahead_char()?;
 
                 while decimal_digit_char!(c) {
                     token.push(c);
-                    self.skip_char()?;
+                    self.skip_char(c);
                     c = self.lookahead_char()?;
                 }
 
-                if exponent_char!(self.lookahead_char()?) {
-                    token.push(self.skip_char()?);
+                if exponent_char!(c) {
+                    self.skip_char(c);
+                    token.push(c);
 
                     let c = match self.lookahead_char() {
-                        Err(_) => return Ok(self.vacate_with_float(token)),
+                        Err(_) => return Ok(self.vacate_with_float(token)?),
                         Ok(c) => c,
                     };
 
                     if !sign_char!(c) && !decimal_digit_char!(c) {
-                        return Ok(self.vacate_with_float(token));
+                        return Ok(self.vacate_with_float(token)?);
                     }
 
                     if sign_char!(c) {
-                        token.push(self.skip_char()?);
+                        self.skip_char(c);
+                        token.push(c);
 
                         let c = match self.lookahead_char() {
                             Err(_) => {
                                 self.return_char(token.pop().unwrap());
-                                return Ok(self.vacate_with_float(token));
+                                return Ok(self.vacate_with_float(token)?);
                             }
                             Ok(c) => c,
                         };
 
                         if !decimal_digit_char!(c) {
                             self.return_char(token.pop().unwrap());
-                            return Ok(self.vacate_with_float(token));
+                            return Ok(self.vacate_with_float(token)?);
                         }
                     }
 
-                    if decimal_digit_char!(self.lookahead_char()?) {
-                        token.push(self.skip_char()?);
+                    let mut c = self.lookahead_char()?;
+
+                    if decimal_digit_char!(c) {
+                        self.skip_char(c);
+                        token.push(c);
 
-                        while decimal_digit_char!(self.lookahead_char()?) {
-                            token.push(self.skip_char()?);
+                        loop {
+                            c = self.lookahead_char()?;
+
+                            if decimal_digit_char!(c) {
+                                self.skip_char(c);
+                                token.push(c);
+                            } else {
+                                break;
+                            }
                         }
 
-                        let n = OrderedFloat(parse_lossy::<f64, _>(token.as_bytes()).unwrap());
-                        Ok(Token::Constant(Constant::Float(n)))
+                        let n = OrderedFloat(parse_lossy::<f64, _>(token.as_bytes())?);
+                        Ok(Token::Literal(Literal::Float(arena_alloc!(
+                            n,
+                            &mut self.machine_st.arena
+                        ))))
                     } else {
-                        return Ok(self.vacate_with_float(token));
+                        return Ok(self.vacate_with_float(token)?);
                     }
                 } else {
-                    let n = OrderedFloat(parse_lossy::<f64, _>(token.as_bytes()).unwrap());
-                    Ok(Token::Constant(Constant::Float(n)))
+                    let n = OrderedFloat(parse_lossy::<f64, _>(token.as_bytes())?);
+                    Ok(Token::Literal(Literal::Float(arena_alloc!(
+                        n,
+                        &mut self.machine_st.arena
+                    ))))
                 }
             } else {
                 self.return_char('.');
 
-                isize::from_str_radix(&token, 10)
-                    .map(|n| Token::Constant(Constant::Fixnum(n)))
+                i64::from_str_radix(&token, 10)
+                    .map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
                     .or_else(|_| {
                         token
                             .parse::<Integer>()
-                            .map(|n| Token::Constant(Constant::Integer(Rc::new(n))))
+                            .map(|n| {
+                                Token::Literal(Literal::Integer(arena_alloc!(n, &mut self.machine_st.arena)))
+                            })
                             .map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
                     })
             }
         } else {
             if token.starts_with('0') && token.len() == 1 {
                 if c == 'x' {
-                    self.hexadecimal_constant().or_else(|e| {
+                    self.hexadecimal_constant(c).or_else(|e| {
                         if let ParserError::ParseBigInt(..) = e {
-                            isize::from_str_radix(&token, 10)
-                                .map(|n| Token::Constant(Constant::Fixnum(n)))
+                            i64::from_str_radix(&token, 10)
+                                .map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
                                 .or_else(|_| {
                                     token
                                         .parse::<Integer>()
-                                        .map(|n| Token::Constant(Constant::Integer(Rc::new(n))))
+                                        .map(|n| {
+                                            Token::Literal(Literal::Integer(arena_alloc!(
+                                                n,
+                                                &mut self.machine_st.arena
+                                            )))
+                                        })
                                         .map_err(|_| {
                                             ParserError::ParseBigInt(self.line_num, self.col_num)
                                         })
@@ -692,14 +814,19 @@ impl<'a, R: Read> Lexer<'a, R> {
                         }
                     })
                 } else if c == 'o' {
-                    self.octal_constant().or_else(|e| {
+                    self.octal_constant(c).or_else(|e| {
                         if let ParserError::ParseBigInt(..) = e {
-                            isize::from_str_radix(&token, 10)
-                                .map(|n| Token::Constant(Constant::Fixnum(n)))
+                            i64::from_str_radix(&token, 10)
+                                .map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
                                 .or_else(|_| {
                                     token
                                         .parse::<Integer>()
-                                        .map(|n| Token::Constant(Constant::Integer(Rc::new(n))))
+                                        .map(|n| {
+                                            Token::Literal(Literal::Integer(arena_alloc!(
+                                                n,
+                                                &mut self.machine_st.arena
+                                            )))
+                                        })
                                         .map_err(|_| {
                                             ParserError::ParseBigInt(self.line_num, self.col_num)
                                         })
@@ -709,14 +836,19 @@ impl<'a, R: Read> Lexer<'a, R> {
                         }
                     })
                 } else if c == 'b' {
-                    self.binary_constant().or_else(|e| {
+                    self.binary_constant(c).or_else(|e| {
                         if let ParserError::ParseBigInt(..) = e {
-                            isize::from_str_radix(&token, 10)
-                                .map(|n| Token::Constant(Constant::Fixnum(n)))
+                            i64::from_str_radix(&token, 10)
+                                .map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
                                 .or_else(|_| {
                                     token
                                         .parse::<Integer>()
-                                        .map(|n| Token::Constant(Constant::Integer(Rc::new(n))))
+                                        .map(|n| {
+                                            Token::Literal(Literal::Integer(arena_alloc!(
+                                                n,
+                                                &mut self.machine_st.arena
+                                            )))
+                                        })
                                         .map_err(|_| {
                                             ParserError::ParseBigInt(self.line_num, self.col_num)
                                         })
@@ -726,54 +858,68 @@ impl<'a, R: Read> Lexer<'a, R> {
                         }
                     })
                 } else if single_quote_char!(c) {
-                    self.skip_char()?;
+                    self.skip_char(c);
+                    let c = self.lookahead_char()?;
 
-                    if backslash_char!(self.lookahead_char()?) {
-                        self.skip_char()?;
+                    if backslash_char!(c) {
+                        self.skip_char(c);
+                        let c = self.lookahead_char()?;
 
-                        if new_line_char!(self.lookahead_char()?) {
-                            self.return_char('\\');
+                        if new_line_char!(c) {
+                            self.skip_char(c);
                             self.return_char('\'');
 
-                            return Ok(Token::Constant(Constant::Fixnum(0)));
+                            return Ok(Token::Literal(Literal::Fixnum(Fixnum::build_with(0))));
                         } else {
                             self.return_char('\\');
                         }
                     }
 
                     self.get_single_quoted_char()
-                        .map(|c| Token::Constant(Constant::Fixnum(c as isize)))
+                        .map(|c| Token::Literal(Literal::Fixnum(Fixnum::build_with(c as i64))))
                         .or_else(|_| {
                             self.return_char(c);
 
-                            isize::from_str_radix(&token, 10)
-                                .map(|n| Token::Constant(Constant::Fixnum(n)))
+                            i64::from_str_radix(&token, 10)
+                                .map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
                                 .or_else(|_| {
                                     token
                                         .parse::<Integer>()
-                                        .map(|n| Token::Constant(Constant::Integer(Rc::new(n))))
+                                        .map(|n| {
+                                            Token::Literal(Literal::Integer(arena_alloc!(
+                                                n,
+                                                &mut self.machine_st.arena
+                                            )))
+                                        })
                                         .map_err(|_| {
                                             ParserError::ParseBigInt(self.line_num, self.col_num)
                                         })
                                 })
                         })
                 } else {
-                    isize::from_str_radix(&token, 10)
-                        .map(|n| Token::Constant(Constant::Fixnum(n)))
+                    i64::from_str_radix(&token, 10)
+                        .map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
                         .or_else(|_| {
                             token
                                 .parse::<Integer>()
-                                .map(|n| Token::Constant(Constant::Integer(Rc::new(n))))
+                                .map(|n| {
+                                    Token::Literal(Literal::Integer(arena_alloc!(
+                                        n,
+                                        &mut self.machine_st.arena
+                                    )))
+                                })
                                 .map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
                         })
                 }
             } else {
-                isize::from_str_radix(&token, 10)
-                    .map(|n| Token::Constant(Constant::Fixnum(n)))
+                i64::from_str_radix(&token, 10)
+                    .map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
                     .or_else(|_| {
                         token
                             .parse::<Integer>()
-                            .map(|n| Token::Constant(Constant::Integer(Rc::new(n))))
+                            .map(|n| {
+                                Token::Literal(Literal::Integer(arena_alloc!(n, &mut self.machine_st.arena)))
+                            })
                             .map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
                     })
             }
@@ -789,7 +935,7 @@ impl<'a, R: Read> Lexer<'a, R> {
 
             match cr {
                 Ok(c) if layout_char!(c) => {
-                    self.skip_char()?;
+                    self.skip_char(c);
                     layout_inserted = true;
                 }
                 Ok(c) if end_line_comment_char!(c) => {
@@ -825,17 +971,17 @@ impl<'a, R: Read> Lexer<'a, R> {
                 }
 
                 if c == ',' {
-                    self.skip_char()?;
+                    self.skip_char(c);
                     return Ok(Token::Comma);
                 }
 
                 if c == ')' {
-                    self.skip_char()?;
+                    self.skip_char(c);
                     return Ok(Token::Close);
                 }
 
                 if c == '(' {
-                    self.skip_char()?;
+                    self.skip_char(c);
                     return Ok(if layout_inserted {
                         Token::Open
                     } else {
@@ -844,12 +990,12 @@ impl<'a, R: Read> Lexer<'a, R> {
                 }
 
                 if c == '.' {
-                    self.skip_char()?;
+                    self.skip_char(c);
 
                     match self.lookahead_char() {
                         Ok(c) if layout_char!(c) || c == '%' => {
                             if new_line_char!(c) {
-                                self.skip_char()?;
+                                self.skip_char(c);
                             }
 
                             return Ok(Token::End);
@@ -861,47 +1007,48 @@ impl<'a, R: Read> Lexer<'a, R> {
                             self.return_char('.');
                         }
                     };
+
+                    return self.name_token(c);
                 }
 
                 if decimal_digit_char!(c) {
-                    return self.number_token();
+                    return self.number_token(c);
                 }
 
                 if c == ']' {
-                    self.skip_char()?;
+                    self.skip_char(c);
                     return Ok(Token::CloseList);
                 }
 
                 if c == '[' {
-                    self.skip_char()?;
+                    self.skip_char(c);
                     return Ok(Token::OpenList);
                 }
 
                 if c == '|' {
-                    self.skip_char()?;
+                    self.skip_char(c);
                     return Ok(Token::HeadTailSeparator);
                 }
 
                 if c == '{' {
-                    self.skip_char()?;
+                    self.skip_char(c);
                     return Ok(Token::OpenCurly);
                 }
 
                 if c == '}' {
-                    self.skip_char()?;
+                    self.skip_char(c);
                     return Ok(Token::CloseCurly);
                 }
 
                 if c == '"' {
-                    let s = self.char_code_list_token()?;
+                    let s = self.char_code_list_token(c)?;
+                    let atom = self.machine_st.atom_tbl.build_with(&s);
 
-                    if let DoubleQuotes::Atom = self.flags.double_quotes {
-                        let s = clause_name!(s, self.atom_tbl);
-                        return Ok(Token::Constant(Constant::Atom(s, None)));
+                    return if let DoubleQuotes::Atom = self.machine_st.flags.double_quotes {
+                        Ok(Token::Literal(Literal::Atom(atom)))
                     } else {
-                        let s = Rc::new(s);
-                        return Ok(Token::Constant(Constant::String(s)));
-                    }
+                        Ok(Token::Literal(Literal::String(atom)))
+                    };
                 }
 
                 self.name_token(c)
similarity index 100%
rename from crates/prolog_parser/src/macros.rs
rename to src/parser/macros.rs
index f8ec9072dbaadfde623dc746fc0ed474cb25ae2a..6e37f3204315b6b0d50b503bfbe5a47b0d5b079c 100644 (file)
@@ -54,9 +54,9 @@ macro_rules! back_quote_char {
 }
 
 #[macro_export]
-macro_rules! binary_digit_char {
+macro_rules! octet_char {
     ($c: expr) => {
-        $c >= '0' && $c <= '1'
+        ('\u{0000}'..='\u{00FF}').contains(&$c)
     };
 }
 
@@ -172,9 +172,9 @@ macro_rules! octal_digit_char {
 }
 
 #[macro_export]
-macro_rules! octet_char {
+macro_rules! binary_digit_char {
     ($c: expr) => {
-        ('\u{0000}'..='\u{00FF}').contains(&$c)
+        $c >= '0' && $c <= '1'
     };
 }
 
similarity index 57%
rename from crates/prolog_parser/src/lib.rs
rename to src/parser/mod.rs
index 2c37cea680f727c0c5c29a07f9513751f3c85239..8835c9efcd7e852c8c2ef16ad2c47a7c77105ff9 100644 (file)
@@ -1,15 +1,17 @@
 #[cfg(feature = "num-rug-adapter")]
 use num_rug_adapter as rug;
 #[cfg(feature = "rug")]
-use rug;
+pub use rug;
 
-#[macro_use]
-pub mod tabled_rc;
+// #[macro_use]
+// extern crate lazy_static;
+// #[macro_use]
+// extern crate static_assertions;
+
+pub mod char_reader;
 #[macro_use]
 pub mod ast;
 #[macro_use]
 pub mod macros;
-pub mod parser;
-pub mod put_back_n;
-
 pub mod lexer;
+pub mod parser;
similarity index 68%
rename from crates/prolog_parser/src/parser.rs
rename to src/parser/parser.rs
index 43ee244cd9517c8a913eec9d4e48fa5e1aca9f79..2d6334c008af66664d291f33c6b9ce30e85c4b37 100644 (file)
@@ -1,14 +1,15 @@
-use crate::ast::*;
-use crate::lexer::*;
-use crate::tabled_rc::*;
+use crate::arena::*;
+use crate::atom_table::*;
+use crate::parser::ast::*;
+use crate::parser::char_reader::*;
+use crate::parser::lexer::*;
 
 use ordered_float::OrderedFloat;
 
-use crate::rug::ops::NegAssign;
+use rug::ops::NegAssign;
 
 use std::cell::Cell;
-use std::io::Read;
-use std::mem::swap;
+use std::mem;
 use std::rc::Rc;
 
 #[derive(Debug, Clone, Copy, PartialEq)]
@@ -50,69 +51,114 @@ struct TokenDesc {
     spec: u32,
 }
 
-pub fn get_clause_spec(
-    name: ClauseName,
-    arity: usize,
-    op_dir: &CompositeOpDir,
-) -> Option<SharedOpDesc> {
-    match arity {
-        1 => {
-            /* This is a clause with an operator principal functor. Prefix operators
-            are supposed over post.
-             */
-            if let Some(OpDirValue(cell)) = op_dir.get(name.clone(), Fixity::Pre) {
-                return Some(cell.clone());
+fn is_partial_string(
+    head: Term,
+    mut tail: Term,
+    atom_tbl: &mut AtomTable,
+) -> Result<(Atom, Option<Box<Term>>), Term> {
+    let mut string = match &head {
+        Term::Literal(_, Literal::Atom(atom)) => {
+            if let Some(c) = atom.as_char() {
+                c.to_string()
+            } else {
+                return Err(Term::Cons(Cell::default(), Box::new(head), Box::new(tail)));
             }
+        }
+        Term::Literal(_, Literal::Char(c)) => c.to_string(),
+        _ => {
+            return Err(Term::Cons(Cell::default(), Box::new(head), Box::new(tail)));
+        }
+    };
+
+    let mut orig_tail = Box::new(tail);
+    let mut tail_ref = &mut orig_tail;
+
+    loop {
+        match &mut **tail_ref {
+            Term::Cons(_, prev, succ) => {
+                match prev.as_ref() {
+                    Term::Literal(_, Literal::Atom(atom)) => {
+                        if let Some(c) = atom.as_char() {
+                            string.push(c);
+                        } else {
+                            return Err(Term::Cons(Cell::default(), Box::new(head), orig_tail));
+                        }
+                    }
+                    Term::Literal(_, Literal::Char(c)) => {
+                        string.push(*c);
+                    }
+                    _ => {
+                        return Err(Term::Cons(Cell::default(), Box::new(head), orig_tail));
+                    }
+                }
 
-            if let Some(OpDirValue(cell)) = op_dir.get(name, Fixity::Post) {
-                return Some(cell.clone());
+                tail_ref = succ;
             }
-        }
-        2 => {
-            if let Some(OpDirValue(cell)) = op_dir.get(name, Fixity::In) {
-                return Some(cell.clone());
+            tail_ref => {
+                tail = mem::replace(tail_ref, Term::AnonVar);
+                break;
             }
         }
-        _ => {}
-    };
+    }
 
-    None
+    match &tail {
+        Term::AnonVar | Term::Var(..) => {
+            let pstr_atom = atom_tbl.build_with(&string);
+            Ok((pstr_atom, Some(Box::new(tail))))
+        }
+        Term::Literal(_, Literal::Atom(atom!("[]"))) => {
+            let pstr_atom = atom_tbl.build_with(&string);
+            Ok((pstr_atom, None))
+        }
+        Term::Literal(_, Literal::String(tail)) => {
+            string += tail.as_str();
+            let pstr_atom = atom_tbl.build_with(&string);
+            Ok((pstr_atom, None))
+        }
+        _ => {
+            let pstr_atom = atom_tbl.build_with(&string);
+            Ok((pstr_atom, Some(Box::new(tail))))
+        }
+    }
 }
 
-pub fn get_op_desc(name: ClauseName, op_dir: &CompositeOpDir) -> Option<OpDesc> {
-    let mut op_desc = OpDesc {
+pub fn get_op_desc(
+    name: Atom,
+    op_dir: &CompositeOpDir,
+) -> Option<CompositeOpDesc> {
+    let mut op_desc = CompositeOpDesc {
         pre: 0,
         inf: 0,
         post: 0,
         spec: 0,
     };
 
-    if let Some(OpDirValue(cell)) = op_dir.get(name.clone(), Fixity::Pre) {
+    if let Some(cell) = op_dir.get(name, Fixity::Pre) {
         let (pri, spec) = cell.get();
 
         if pri > 0 {
-            op_desc.pre = pri;
-            op_desc.spec |= spec;
-        } else if name.as_str() == "-" {
+            op_desc.pre = pri as usize;
+            op_desc.spec |= spec as u32;
+        } else if name == atom!("-") {
             op_desc.spec |= NEGATIVE_SIGN;
         }
     }
 
-    if let Some(OpDirValue(cell)) = op_dir.get(name.clone(), Fixity::Post) {
+    if let Some(cell) = op_dir.get(name, Fixity::Post) {
         let (pri, spec) = cell.get();
 
         if pri > 0 {
-            op_desc.post = pri;
-            op_desc.spec |= spec;
+            op_desc.post = pri as usize;
+            op_desc.spec |= spec as u32;
         }
     }
 
-    if let Some(OpDirValue(cell)) = op_dir.get(name.clone(), Fixity::In) {
+    if let Some(cell) = op_dir.get(name, Fixity::In) {
         let (pri, spec) = cell.get();
 
         if pri > 0 {
-            op_desc.inf = pri;
-            op_desc.spec |= spec;
+            op_desc.inf = pri as usize;
+            op_desc.spec |= spec as u32;
         }
     }
 
@@ -123,6 +169,31 @@ pub fn get_op_desc(name: ClauseName, op_dir: &CompositeOpDir) -> Option<OpDesc>
     }
 }
 
+pub fn get_clause_spec(name: Atom, arity: usize, op_dir: &CompositeOpDir) -> Option<OpDesc> {
+    match arity {
+        1 => {
+            /* This is a clause with an operator principal functor. Prefix operators
+            are supposed over post.
+             */
+            if let Some(cell) = op_dir.get(name, Fixity::Pre) {
+                return Some(cell);
+            }
+
+            if let Some(cell) = op_dir.get(name, Fixity::Post) {
+                return Some(cell);
+            }
+        }
+        2 => {
+            if let Some(cell) = op_dir.get(name, Fixity::In) {
+                return Some(cell);
+            }
+        }
+        _ => {}
+    };
+
+    None
+}
+
 fn affirm_xfx(priority: usize, d2: TokenDesc, d3: TokenDesc, d1: TokenDesc) -> bool {
     d2.priority <= priority
         && is_term!(d3.spec)
@@ -164,23 +235,8 @@ fn affirm_fx(priority: usize, d1: TokenDesc, d2: TokenDesc) -> bool {
     d2.priority <= priority && is_term!(d1.spec) && d1.priority < d2.priority
 }
 
-fn sep_to_atom(tt: TokenType) -> Option<ClauseName> {
-    match tt {
-        TokenType::Open | TokenType::OpenCT => Some(clause_name!("(")),
-        TokenType::Close => Some(clause_name!(")")),
-        TokenType::OpenList => Some(clause_name!("[")),
-        TokenType::CloseList => Some(clause_name!("]")),
-        TokenType::OpenCurly => Some(clause_name!("{")),
-        TokenType::CloseCurly => Some(clause_name!("}")),
-        TokenType::HeadTailSeparator => Some(clause_name!("|")),
-        TokenType::Comma => Some(clause_name!(",")),
-        TokenType::End => Some(clause_name!(".")),
-        _ => None,
-    }
-}
-
 #[derive(Debug, Clone, Copy)]
-pub struct OpDesc {
+pub struct CompositeOpDesc {
     pub pre: usize,
     pub inf: usize,
     pub post: usize,
@@ -188,14 +244,14 @@ pub struct OpDesc {
 }
 
 #[derive(Debug)]
-pub struct Parser<'a, R: Read> {
-    lexer: Lexer<'a, R>,
+pub struct Parser<'a, R> {
+    pub lexer: Lexer<'a, R>,
     tokens: Vec<Token>,
     stack: Vec<TokenDesc>,
     terms: Vec<Term>,
 }
 
-fn read_tokens<R: Read>(lexer: &mut Lexer<R>) -> Result<Vec<Token>, ParserError> {
+fn read_tokens<R: CharRead>(lexer: &mut Lexer<R>) -> Result<Vec<Token>, ParserError> {
     let mut tokens = vec![];
 
     loop {
@@ -209,7 +265,10 @@ fn read_tokens<R: Read>(lexer: &mut Lexer<R>) -> Result<Vec<Token>, ParserError>
                 }
             }
             Err(ParserError::UnexpectedEOF) if !tokens.is_empty() => {
-                return Err(ParserError::IncompleteReduction(lexer.line_num, lexer.col_num));
+                return Err(ParserError::IncompleteReduction(
+                    lexer.line_num,
+                    lexer.col_num,
+                ));
             }
             Err(e) => {
                 return Err(e);
@@ -222,20 +281,46 @@ fn read_tokens<R: Read>(lexer: &mut Lexer<R>) -> Result<Vec<Token>, ParserError>
     Ok(tokens)
 }
 
-impl<'a, R: Read> Parser<'a, R> {
-    pub fn new(
-        stream: &'a mut ParsingStream<R>,
-        atom_tbl: TabledData<Atom>,
-        flags: MachineFlags,
-    ) -> Self {
+fn atomize_term(atom_tbl: &mut AtomTable, term: &Term) -> Option<Atom> {
+    match term {
+        Term::Literal(_, ref c) => atomize_constant(atom_tbl, *c),
+        _ => None,
+    }
+}
+
+fn atomize_constant(atom_tbl: &mut AtomTable, c: Literal) -> Option<Atom> {
+    match c {
+        Literal::Atom(ref name) => Some(*name),
+        Literal::Char(c) => Some(atom_tbl.build_with(&c.to_string())),
+        _ => None,
+    }
+}
+
+impl<'a, R: CharRead> Parser<'a, R> {
+    pub fn new(stream: R, machine_st: &'a mut MachineState) -> Self {
         Parser {
-            lexer: Lexer::new(atom_tbl, flags, stream),
+            lexer: Lexer::new(stream, machine_st),
             tokens: vec![],
             stack: Vec::new(),
             terms: Vec::new(),
         }
     }
 
+    fn sep_to_atom(&mut self, tt: TokenType) -> Option<Atom> {
+        match tt {
+            TokenType::Open | TokenType::OpenCT => Some(atom!("(")),
+            TokenType::Close => Some(atom!(")")),
+            TokenType::OpenList => Some(atom!("[")),
+            TokenType::CloseList => Some(atom!("]")),
+            TokenType::OpenCurly => Some(atom!("{")),
+            TokenType::CloseCurly => Some(atom!("}")),
+            TokenType::HeadTailSeparator => Some(atom!("|")),
+            TokenType::Comma => Some(atom!(",")),
+            TokenType::End => Some(atom!(".")),
+            _ => None,
+        }
+    }
+
     #[inline]
     pub fn line_num(&self) -> usize {
         self.lexer.line_num
@@ -246,25 +331,12 @@ impl<'a, R: Read> Parser<'a, R> {
         self.lexer.col_num
     }
 
-    #[inline]
-    pub fn get_atom_tbl(&self) -> TabledData<Atom> {
-        self.lexer.atom_tbl.clone()
-    }
-
-    #[inline]
-    pub fn set_atom_tbl(&mut self, atom_tbl: TabledData<Atom>) {
-        self.lexer.atom_tbl = atom_tbl;
-    }
-
-    fn get_term_name(&mut self, td: TokenDesc) -> Option<(ClauseName, Option<SharedOpDesc>)> {
+    fn get_term_name(&mut self, td: TokenDesc) -> Option<Atom> {
         match td.tt {
-            TokenType::HeadTailSeparator => Some((
-                clause_name!("|"),
-                Some(SharedOpDesc::new(td.priority, td.spec)),
-            )),
-            TokenType::Comma => Some((clause_name!(","), Some(SharedOpDesc::new(1000, XFY)))),
+            TokenType::HeadTailSeparator => Some(atom!("|")),
+            TokenType::Comma => Some(atom!(",")),
             TokenType::Term => match self.terms.pop() {
-                Some(Term::Constant(_, Constant::Atom(atom, spec))) => Some((atom, spec)),
+                Some(Term::Literal(_, Literal::Atom(atom))) => Some(atom),
                 Some(term) => {
                     self.terms.push(term);
                     None
@@ -277,14 +349,9 @@ impl<'a, R: Read> Parser<'a, R> {
 
     fn push_binary_op(&mut self, td: TokenDesc, spec: Specifier) {
         if let Some(arg2) = self.terms.pop() {
-            if let Some((name, shared_op_desc)) = self.get_term_name(td) {
+            if let Some(name) = self.get_term_name(td) {
                 if let Some(arg1) = self.terms.pop() {
-                    let term = Term::Clause(
-                        Cell::default(),
-                        name,
-                        vec![Box::new(arg1), Box::new(arg2)],
-                        shared_op_desc,
-                    );
+                    let term = Term::Clause(Cell::default(), name, vec![arg1, arg2]);
 
                     self.terms.push(term);
                     self.stack.push(TokenDesc {
@@ -301,12 +368,11 @@ impl<'a, R: Read> Parser<'a, R> {
         if let Some(mut arg1) = self.terms.pop() {
             if let Some(mut name) = self.terms.pop() {
                 if is_postfix!(assoc) {
-                    swap(&mut arg1, &mut name);
+                    mem::swap(&mut arg1, &mut name);
                 }
 
-                if let Term::Constant(_, Constant::Atom(name, shared_op_desc)) = name {
-                    let term =
-                        Term::Clause(Cell::default(), name, vec![Box::new(arg1)], shared_op_desc);
+                if let Term::Literal(_, Literal::Atom(name)) = name {
+                    let term = Term::Clause(Cell::default(), name, vec![arg1]);
 
                     self.terms.push(term);
                     self.stack.push(TokenDesc {
@@ -319,17 +385,8 @@ impl<'a, R: Read> Parser<'a, R> {
         }
     }
 
-    fn promote_atom_op(
-        &mut self,
-        atom: ClauseName,
-        priority: usize,
-        assoc: u32,
-        op_dir_val: Option<&OpDirValue>,
-    ) {
-        let spec = op_dir_val.map(|op_dir_val| op_dir_val.shared_op_desc());
-
-        self.terms
-            .push(Term::Constant(Cell::default(), Constant::Atom(atom, spec)));
+    fn promote_atom_op(&mut self, atom: Atom, priority: usize, assoc: u32) {
+        self.terms.push(Term::Literal(Cell::default(), Literal::Atom(atom)));
         self.stack.push(TokenDesc {
             tt: TokenType::Term,
             priority,
@@ -339,15 +396,15 @@ impl<'a, R: Read> Parser<'a, R> {
 
     fn shift(&mut self, token: Token, priority: usize, spec: Specifier) {
         let tt = match token {
-            Token::Constant(Constant::String(s)) if self.lexer.flags.double_quotes.is_codes() => {
-                let mut list = Term::Constant(Cell::default(), Constant::EmptyList);
+            Token::Literal(Literal::String(s)) if self.lexer.machine_st.flags.double_quotes.is_codes() => {
+                let mut list = Term::Literal(Cell::default(), Literal::Atom(atom!("[]")));
 
-                for c in s.chars().rev() {
+                for c in s.as_str().chars().rev() {
                     list = Term::Cons(
                         Cell::default(),
-                        Box::new(Term::Constant(
+                        Box::new(Term::Literal(
                             Cell::default(),
-                            Constant::Fixnum(c as isize),
+                            Literal::Fixnum(Fixnum::build_with(c as i64)),
                         )),
                         Box::new(list),
                     );
@@ -356,15 +413,19 @@ impl<'a, R: Read> Parser<'a, R> {
                 self.terms.push(list);
                 TokenType::Term
             }
-            Token::Constant(c) => {
-                self.terms.push(Term::Constant(Cell::default(), c));
+            Token::Literal(Literal::String(s)) if self.lexer.machine_st.flags.double_quotes.is_chars() => {
+                self.terms.push(Term::PartialString(Cell::default(), s, None));
+                TokenType::Term
+            }
+            Token::Literal(c) => {
+                self.terms.push(Term::Literal(Cell::default(), c));
                 TokenType::Term
             }
             Token::Var(v) => {
                 if v.trim() == "_" {
                     self.terms.push(Term::AnonVar);
                 } else {
-                    self.terms.push(Term::Var(Cell::default(), v));
+                    self.terms.push(Term::Var(Cell::default(), Rc::new(v)));
                 }
 
                 TokenType::Term
@@ -457,7 +518,7 @@ impl<'a, R: Read> Parser<'a, R> {
         None
     }
 
-    fn reduce_term(&mut self, op_dir: &CompositeOpDir) -> bool {
+    fn reduce_term(&mut self) -> bool {
         if self.stack.is_empty() {
             return false;
         }
@@ -489,22 +550,40 @@ impl<'a, R: Read> Parser<'a, R> {
         let idx = self.terms.len() - arity;
 
         if TokenType::Term == self.stack[stack_len].tt {
-            if self.atomize_term(&self.terms[idx - 1]).is_some() {
+            if atomize_term(&mut self.lexer.machine_st.atom_tbl, &self.terms[idx - 1]).is_some() {
                 self.stack.truncate(stack_len + 1);
 
-                let mut subterms: Vec<_> = self.terms.drain(idx..).map(Box::new).collect();
+                let mut subterms: Vec<_> = self.terms.drain(idx..).collect();
 
-                if let Some(name) = self.terms.pop().and_then(|t| self.atomize_term(&t)) {
+                if let Some(name) = self
+                    .terms
+                    .pop()
+                    .and_then(|t| atomize_term(&mut self.lexer.machine_st.atom_tbl, &t))
+                {
                     // reduce the '.' functor to a cons cell if it applies.
-                    if name.as_str() == "." && subterms.len() == 2 {
+                    if name == atom!(".") && subterms.len() == 2 {
                         let tail = subterms.pop().unwrap();
                         let head = subterms.pop().unwrap();
 
-                        self.terms.push(Term::Cons(Cell::default(), head, tail));
+                        self.terms.push(
+                            match is_partial_string(head, tail, &mut self.lexer.machine_st.atom_tbl) {
+                                Ok((string_buf, tail_opt)) => {
+                                    Term::PartialString(Cell::default(), string_buf, tail_opt)
+                                }
+                                Err(term) => term,
+                            },
+                        );
+
+                        /*
+                        self.terms.push(Term::Cons(
+                            Cell::default(),
+                            Box::new(head),
+                            Box::new(tail),
+                        ));
+                        */
                     } else {
-                        let spec = get_clause_spec(name.clone(), subterms.len(), op_dir);
                         self.terms
-                            .push(Term::Clause(Cell::default(), name, subterms, spec));
+                            .push(Term::Clause(Cell::default(), name, subterms));
                     }
 
                     if let Some(&mut TokenDesc {
@@ -544,8 +623,8 @@ impl<'a, R: Read> Parser<'a, R> {
                  * an operator, so expand the
                  * terms it compacted out again. */
                 match (term.name(), term.arity()) {
-                    (Some(name), 2) if name.as_str() == "," => {
-                        let terms = unfold_by_str(term, ",");
+                    (Some(name), 2) if name == atom!(",") => {
+                        let terms = unfold_by_str(term, name); // notice: name == "," here.
                         let arity = terms.len() - 1;
 
                         self.terms.extend(terms.into_iter());
@@ -603,8 +682,7 @@ impl<'a, R: Read> Parser<'a, R> {
                 td.tt = TokenType::Term;
                 td.priority = 0;
 
-                self.terms
-                    .push(Term::Constant(Cell::default(), Constant::EmptyList));
+                self.terms.push(Term::Literal(Cell::default(), Literal::Atom(atom!("[]"))));
                 return Ok(true);
             }
         }
@@ -621,7 +699,7 @@ impl<'a, R: Read> Parser<'a, R> {
         let list_len = self.stack.len() - 2 * arity;
 
         let end_term = if self.stack[idx].tt != TokenType::HeadTailSeparator {
-            Term::Constant(Cell::default(), Constant::EmptyList)
+            Term::Literal(Cell::default(), Literal::Atom(atom!("[]")))
         } else {
             let term = match self.terms.pop() {
                 Some(term) => term,
@@ -662,7 +740,18 @@ impl<'a, R: Read> Parser<'a, R> {
             priority: 0,
             spec: TERM,
         });
-        self.terms.push(list);
+
+        self.terms.push(match list {
+            Term::Cons(_, head, tail) => {
+                match is_partial_string(*head, *tail, &mut self.lexer.machine_st.atom_tbl) {
+                    Ok((string_buf, tail_opt)) => {
+                        Term::PartialString(Cell::default(), string_buf, tail_opt)
+                    }
+                    Err(term) => term,
+                }
+            }
+            term => term,
+        });
 
         Ok(true)
     }
@@ -678,7 +767,11 @@ impl<'a, R: Read> Parser<'a, R> {
                 td.priority = 0;
                 td.spec = TERM;
 
-                let term = Term::Constant(Cell::default(), atom!("{}", self.lexer.atom_tbl));
+                let term = Term::Literal(
+                    Cell::default(),
+                    Literal::Atom(atom!("{}")),
+                );
+
                 self.terms.push(term);
                 return Ok(true);
             }
@@ -710,9 +803,8 @@ impl<'a, R: Read> Parser<'a, R> {
 
                         self.terms.push(Term::Clause(
                             Cell::default(),
-                            clause_name!("{}"),
-                            vec![Box::new(term)],
-                            None,
+                            atom!("{}"),
+                            vec![term],
                         ));
 
                         return Ok(true);
@@ -744,27 +836,28 @@ impl<'a, R: Read> Parser<'a, R> {
                     return false;
                 }
 
-                if let Some(atom) = sep_to_atom(self.stack[idx].tt) {
+                if let Some(atom) = self.sep_to_atom(self.stack[idx].tt) {
                     self.terms
-                        .push(Term::Constant(Cell::default(), Constant::Atom(atom, None)));
+                        .push(Term::Literal(Cell::default(), Literal::Atom(atom)));
                 }
 
                 self.stack[idx].spec = TERM;
                 self.stack[idx].tt = TokenType::Term;
                 self.stack[idx].priority = 0;
+
                 true
             }
             _ => false,
         }
     }
 
-    fn shift_op(&mut self, name: ClauseName, op_dir: &CompositeOpDir) -> Result<bool, ParserError> {
-        if let Some(OpDesc {
+    fn shift_op(&mut self, name: Atom, op_dir: &CompositeOpDir) -> Result<bool, ParserError> {
+        if let Some(CompositeOpDesc {
             pre,
             inf,
             post,
             spec,
-        }) = get_op_desc(name.clone(), op_dir)
+        }) = get_op_desc(name, op_dir)
         {
             if (pre > 0 && inf + post > 0) || is_negate!(spec) {
                 match self.tokens.last().ok_or(ParserError::UnexpectedEOF)? {
@@ -775,15 +868,9 @@ impl<'a, R: Read> Parser<'a, R> {
                         // or post == 0.
                         self.reduce_op(inf + post);
 
-                        let fixity = if inf > 0 { Fixity::In } else { Fixity::Post };
-                        let op_dir_val = op_dir.get(name.clone(), fixity);
+                        // let fixity = if inf > 0 { Fixity::In } else { Fixity::Post };
 
-                        self.promote_atom_op(
-                            name,
-                            inf + post,
-                            spec & (XFX | XFY | YFX | YF | XF),
-                            op_dir_val,
-                        );
+                        self.promote_atom_op(name, inf + post, spec & (XFX | XFY | YFX | YF | XF));
                     }
                     _ => {
                         self.reduce_op(inf + post);
@@ -791,49 +878,22 @@ impl<'a, R: Read> Parser<'a, R> {
                         if let Some(TokenDesc { spec: pspec, .. }) = self.stack.last().cloned() {
                             // rterm.c: 412
                             if is_term!(pspec) {
-                                let fixity = if inf > 0 { Fixity::In } else { Fixity::Post };
-                                let op_dir_val = op_dir.get(name.clone(), fixity);
-
                                 self.promote_atom_op(
                                     name,
                                     inf + post,
                                     spec & (XFX | XFY | YFX | XF | YF),
-                                    op_dir_val,
                                 );
                             } else {
-                                let op_dir_val = op_dir.get(name.clone(), Fixity::Pre);
-                                self.promote_atom_op(
-                                    name,
-                                    pre,
-                                    spec & (FX | FY | NEGATIVE_SIGN),
-                                    op_dir_val,
-                                );
+                                self.promote_atom_op(name, pre, spec & (FX | FY | NEGATIVE_SIGN));
                             }
                         } else {
-                            let op_dir_val = op_dir.get(name.clone(), Fixity::Pre);
-                            self.promote_atom_op(
-                                name,
-                                pre,
-                                spec & (FX | FY | NEGATIVE_SIGN),
-                                op_dir_val,
-                            );
+                            self.promote_atom_op(name, pre, spec & (FX | FY | NEGATIVE_SIGN));
                         }
                     }
                 }
             } else {
-                let op_dir_val = op_dir.get(
-                    name.clone(),
-                    if pre + inf == 0 {
-                        Fixity::Post
-                    } else if post + pre == 0 {
-                        Fixity::In
-                    } else {
-                        Fixity::Pre
-                    },
-                );
-
                 self.reduce_op(pre + inf + post); // only one non-zero priority among these.
-                self.promote_atom_op(name, pre + inf + post, spec, op_dir_val);
+                self.promote_atom_op(name, pre + inf + post, spec);
             }
 
             Ok(true)
@@ -843,38 +903,23 @@ impl<'a, R: Read> Parser<'a, R> {
         }
     }
 
-    fn atomize_term(&self, term: &Term) -> Option<ClauseName> {
-        match term {
-            Term::Constant(_, ref c) => self.atomize_constant(c),
-            _ => None,
-        }
-    }
-
-    fn atomize_constant(&self, c: &Constant) -> Option<ClauseName> {
-        match c {
-            Constant::Atom(ref name, _) => Some(name.clone()),
-            Constant::Char(c) => Some(clause_name!(c.to_string(), self.lexer.atom_tbl)),
-            Constant::EmptyList => Some(clause_name!(c.to_string(), self.lexer.atom_tbl)),
-            _ => None,
-        }
-    }
-
-    fn negate_number<N, Negator, ToConstant>(&mut self, n: N, negator: Negator, constr: ToConstant)
+    fn negate_number<N, Negator, ToLiteral>(&mut self, n: N, negator: Negator, constr: ToLiteral)
     where
         Negator: Fn(N) -> N,
-        ToConstant: Fn(N) -> Constant,
+        ToLiteral: Fn(N, &mut Arena) -> Literal,
     {
         if let Some(desc) = self.stack.last().cloned() {
             if let Some(term) = self.terms.last().cloned() {
                 match term {
-                    Term::Constant(_, Constant::Atom(ref name, _))
-                        if name.as_str() == "-"
-                            && (is_prefix!(desc.spec) || is_negate!(desc.spec)) =>
+                    Term::Literal(_, Literal::Atom(name))
+                        if name == atom!("-") && (is_prefix!(desc.spec) || is_negate!(desc.spec)) =>
                     {
                         self.stack.pop();
                         self.terms.pop();
 
-                        self.shift(Token::Constant(constr(negator(n))), 0, TERM);
+                        let literal = constr(negator(n), &mut self.lexer.machine_st.arena);
+                        self.shift(Token::Literal(literal), 0, TERM);
+
                         return;
                     }
                     _ => {}
@@ -882,43 +927,45 @@ impl<'a, R: Read> Parser<'a, R> {
             }
         }
 
-        self.shift(Token::Constant(constr(n)), 0, TERM);
+        let literal = constr(n, &mut self.lexer.machine_st.arena);
+        self.shift(Token::Literal(literal), 0, TERM);
     }
 
     fn shift_token(&mut self, token: Token, op_dir: &CompositeOpDir) -> Result<(), ParserError> {
-        fn negate_rc<T: NegAssign>(mut t: Rc<T>) -> Rc<T> {
-            if let Some(t) = Rc::get_mut(&mut t) {
-                t.neg_assign();
-            };
-
+        fn negate_rc<T: NegAssign>(mut t: TypedArenaPtr<T>) -> TypedArenaPtr<T> {
+            (&mut t).neg_assign();
             t
         }
 
         match token {
-            Token::Constant(Constant::Fixnum(n)) => self.negate_number(n, |n| -n, Constant::Fixnum),
-            Token::Constant(Constant::Integer(n)) => {
-                self.negate_number(n, negate_rc, Constant::Integer)
+            Token::Literal(Literal::Fixnum(n)) => {
+                self.negate_number(n, |n| -n, |n, _| Literal::Fixnum(n))
             }
-            Token::Constant(Constant::Rational(n)) => {
-                self.negate_number(n, negate_rc, Constant::Rational)
+            Token::Literal(Literal::Integer(n)) => {
+                self.negate_number(n, negate_rc, |n, _| Literal::Integer(n))
             }
-            Token::Constant(Constant::Float(n)) => {
-                self.negate_number(n, |n| OrderedFloat(-n.into_inner()), Constant::Float)
+            Token::Literal(Literal::Rational(n)) => {
+                self.negate_number(n, negate_rc, |r, _| Literal::Rational(r))
             }
-            Token::Constant(c) => {
-                if let Some(name) = self.atomize_constant(&c) {
+            Token::Literal(Literal::Float(n)) => self.negate_number(
+                **n,
+                |n| OrderedFloat(-n.into_inner()),
+                |n, arena| Literal::Float(arena_alloc!(n, arena)),
+            ),
+            Token::Literal(c) => {
+                if let Some(name) = atomize_constant(&mut self.lexer.machine_st.atom_tbl, c) {
                     if !self.shift_op(name, op_dir)? {
-                        self.shift(Token::Constant(c), 0, TERM);
+                        self.shift(Token::Literal(c), 0, TERM);
                     }
                 } else {
-                    self.shift(Token::Constant(c), 0, TERM);
+                    self.shift(Token::Literal(c), 0, TERM);
                 }
             }
             Token::Var(v) => self.shift(Token::Var(v), 0, TERM),
             Token::Open => self.shift(Token::Open, 1300, DELIMITER),
             Token::OpenCT => self.shift(Token::OpenCT, 1300, DELIMITER),
             Token::Close => {
-                if !self.reduce_term(op_dir) {
+                if !self.reduce_term() {
                     if !self.reduce_brackets() {
                         return Err(ParserError::IncompleteReduction(
                             self.lexer.line_num,
@@ -949,8 +996,10 @@ impl<'a, R: Read> Parser<'a, R> {
                 /* '|' as an operator must have priority > 1000 and can only be infix.
                  * See: http://www.complang.tuwien.ac.at/ulrich/iso-prolog/dtc2#Res_A78
                  */
-                let (priority, spec) = get_op_desc(clause_name!("|"), op_dir)
-                    .map(|OpDesc { inf, spec, .. }| (inf, spec))
+                let bar_atom = atom!("|");
+
+                let (priority, spec) = get_op_desc(bar_atom, op_dir)
+                    .map(|CompositeOpDesc { inf, spec, .. }| (inf, spec))
                     .unwrap_or((1000, DELIMITER));
 
                 self.reduce_op(priority);
@@ -990,7 +1039,7 @@ impl<'a, R: Read> Parser<'a, R> {
     }
 
     #[inline]
-    pub fn num_lines_read(&self) -> usize {
+    pub fn lines_read(&self) -> usize {
         self.lexer.line_num
     }
 
diff --git a/src/raw_block.rs b/src/raw_block.rs
new file mode 100644 (file)
index 0000000..d39c65c
--- /dev/null
@@ -0,0 +1,105 @@
+use core::marker::PhantomData;
+
+use std::alloc;
+use std::ptr;
+
+pub trait RawBlockTraits {
+    fn init_size() -> usize;
+    fn align() -> usize;
+}
+
+#[derive(Debug)]
+pub struct RawBlock<T: RawBlockTraits> {
+    pub base: *const u8,
+    pub top: *const u8,
+    pub ptr: *mut u8,
+    _marker: PhantomData<T>,
+}
+
+impl<T: RawBlockTraits> RawBlock<T> {
+    #[inline]
+    fn empty_block() -> Self {
+        RawBlock {
+            base: ptr::null(),
+            top: ptr::null(),
+            ptr: ptr::null_mut(),
+            _marker: PhantomData,
+        }
+    }
+
+    pub fn new() -> Self {
+        let mut block = Self::empty_block();
+
+        unsafe {
+            block.grow();
+        }
+
+        block
+    }
+
+    unsafe fn init_at_size(&mut self, cap: usize) {
+        let layout = alloc::Layout::from_size_align_unchecked(cap, T::align());
+
+        self.base = alloc::alloc(layout) as *const _;
+        self.top = (self.base as usize + cap) as *const _;
+        self.ptr = self.base as *mut _;
+    }
+
+    pub unsafe fn grow(&mut self) {
+        if self.base.is_null() {
+            self.init_at_size(T::init_size());
+        } else {
+            let size = self.size();
+            let layout = alloc::Layout::from_size_align_unchecked(size, T::align());
+
+            self.base = alloc::realloc(self.base as *mut _, layout, size * 2) as *const _;
+            self.top = (self.base as usize + size * 2) as *const _;
+            self.ptr = (self.base as usize + size) as *mut _;
+        }
+    }
+
+    /*
+    #[inline]
+    pub fn take(&mut self) -> Self {
+        mem::replace(self, Self::empty_block())
+    }
+    */
+
+    #[inline]
+    pub fn size(&self) -> usize {
+        self.top as usize - self.base as usize
+    }
+
+    #[inline(always)]
+    fn free_space(&self) -> usize {
+        debug_assert!(
+            self.ptr as *const _ >= self.base,
+            "self.ptr = {:?} < {:?} = self.base",
+            self.ptr,
+            self.base
+        );
+
+        self.top as usize - self.ptr as usize
+    }
+
+    pub unsafe fn alloc(&mut self, size: usize) -> *mut u8 {
+        if self.free_space() >= size {
+            let ptr = self.ptr;
+            self.ptr = (self.ptr as usize + size) as *mut _;
+            ptr
+        } else {
+            ptr::null_mut()
+        }
+    }
+
+    pub fn deallocate(&mut self) {
+        unsafe {
+            let layout = alloc::Layout::from_size_align_unchecked(self.size(), T::align());
+            alloc::dealloc(self.base as *mut _, layout);
+
+            self.top = ptr::null();
+            self.base = ptr::null();
+            self.ptr = ptr::null_mut();
+        }
+    }
+}
index 366962cb007864dd5377dcb2069b0fd8bc5a7b74..b4c4dbcc8770563cc6ec44f97ff3bdbc3f9abdc5 100644 (file)
-use prolog_parser::ast::*;
-use prolog_parser::parser::*;
-use prolog_parser::tabled_rc::TabledData;
+use crate::parser::ast::*;
+use crate::parser::parser::*;
 
+use crate::arena::*;
+use crate::atom_table::*;
 use crate::forms::*;
 use crate::iterators::*;
+use crate::machine::heap::*;
 use crate::machine::machine_indices::*;
 use crate::machine::machine_state::MachineState;
-use crate::machine::streams::Stream;
+use crate::machine::streams::*;
+use crate::parser::char_reader::*;
+use crate::types::*;
+
+use rustyline::error::ReadlineError;
+use rustyline::{Cmd, Config, Editor, KeyEvent};
 
 use std::collections::VecDeque;
+use std::io::{Cursor, Error, ErrorKind, Read};
 
 type SubtermDeque = VecDeque<(usize, usize)>;
 
-pub(crate) type PrologStream = ParsingStream<Stream>;
+// pub(crate) type PrologStream = ParsingStream<Stream>;
 
-pub mod readline {
-    use crate::machine::streams::Stream;
-    use rustyline::error::ReadlineError;
-    use rustyline::{Cmd, Config, Editor, KeyEvent};
-    use std::io::{Cursor, Error, ErrorKind, Read};
+impl MachineState {
+    pub(crate) fn devour_whitespace(
+        &mut self,
+        mut inner: Stream,
+    ) -> Result<bool, ParserError> {
+        let mut parser = Parser::new(inner, self);
 
-    static mut PROMPT: bool = false;
+        parser.devour_whitespace()?;
+        inner.add_lines_read(parser.lines_read());
 
-    const HISTORY_FILE: &'static str = ".scryer_history";
+        parser.eof()
+    }
 
-    pub(crate) fn set_prompt(value: bool) {
-        unsafe {
-            PROMPT = value;
-        }
+    pub(crate) fn read(
+        &mut self,
+        mut inner: Stream,
+        op_dir: &OpDir,
+    ) -> Result<TermWriteResult, ParserError> {
+        let (term, num_lines_read) = {
+            let prior_num_lines_read = inner.lines_read();
+            let mut parser = Parser::new(inner, self);
+
+            parser.add_lines_read(prior_num_lines_read);
+
+            let term = parser.read_term(&CompositeOpDir::new(op_dir, None))?;
+            (term, parser.lines_read() - prior_num_lines_read)
+        };
+
+        inner.add_lines_read(num_lines_read);
+        Ok(write_term_to_heap(&term, &mut self.heap, &mut self.atom_tbl))
     }
+}
 
-    #[inline]
-    fn get_prompt() -> &'static str {
-        unsafe {
-            if PROMPT {
-                "?- "
-            } else {
-                ""
-            }
-        }
+static mut PROMPT: bool = false;
+
+const HISTORY_FILE: &'static str = ".scryer_history";
+
+pub(crate) fn set_prompt(value: bool) {
+    unsafe {
+        PROMPT = value;
     }
+}
 
-    #[derive(Debug)]
-    pub struct ReadlineStream {
-        rl: Editor<()>,
-        pending_input: Cursor<String>,
+#[inline]
+fn get_prompt() -> &'static str {
+    unsafe {
+        if PROMPT {
+            "?- "
+        } else {
+            ""
+        }
     }
+}
 
-    impl ReadlineStream {
-        #[inline]
-        pub(crate) fn new(pending_input: String) -> Self {
-            let config = Config::builder().check_cursor_position(true).build();
-
-            let mut rl = Editor::<()>::with_config(config); //Editor::<()>::new();
-            if let Some(mut path) = dirs_next::home_dir() {
-                path.push(HISTORY_FILE);
-                if path.exists() {
-                    if rl.load_history(&path).is_err() {
-                        println!("Warning: loading history failed");
-                    }
-                }
-            }
+#[inline]
+pub fn input_stream(arena: &mut Arena) -> Stream {
+    let input_stream = ReadlineStream::new("");
+    Stream::from_readline_stream(input_stream, arena)
+}
+
+#[derive(Debug)]
+pub struct ReadlineStream {
+    rl: Editor<()>,
+    pending_input: Cursor<String>,
+}
 
-            rl.bind_sequence(KeyEvent::from('\t'), Cmd::Insert(1, "\t".to_string()));
-            ReadlineStream {
-                rl,
-                pending_input: Cursor::new(pending_input),
+impl ReadlineStream {
+    #[inline]
+    pub fn new(pending_input: &str) -> Self {
+        let config = Config::builder().check_cursor_position(true).build();
+        let mut rl = Editor::<()>::with_config(config);
+
+        if let Some(mut path) = dirs_next::home_dir() {
+            path.push(HISTORY_FILE);
+            if path.exists() && rl.load_history(&path).is_err() {
+                println!("Warning: loading history failed");
             }
         }
 
-        #[inline]
-        pub(crate) fn input_stream(pending_input: String) -> Stream {
-            Stream::from(Self::new(pending_input))
+        rl.bind_sequence(KeyEvent::from('\t'), Cmd::Insert(1, "\t".to_string()));
+
+        ReadlineStream {
+            rl,
+            pending_input: Cursor::new(pending_input.to_owned()),
         }
+    }
 
-        fn call_readline(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
-            match self.rl.readline(get_prompt()) {
-                Ok(text) => {
-                    *self.pending_input.get_mut() = text;
-                    self.pending_input.set_position(0);
-
-                    unsafe {
-                        if PROMPT {
-                            self.rl.history_mut().add(self.pending_input.get_ref());
-                            self.save_history();
-                            PROMPT = false;
-                        }
+    fn call_readline(&mut self) -> std::io::Result<usize> {
+        match self.rl.readline(get_prompt()) {
+            Ok(text) => {
+                *self.pending_input.get_mut() = text;
+                self.pending_input.set_position(0);
+
+                unsafe {
+                    if PROMPT {
+                        self.rl.history_mut().add(self.pending_input.get_ref());
+                        self.save_history();
+                        PROMPT = false;
                     }
+                }
 
-                    if self.pending_input.get_ref().chars().last() != Some('\n') {
-                        *self.pending_input.get_mut() += "\n";
-                    }
+                if self.pending_input.get_ref().chars().last() != Some('\n') {
+                    *self.pending_input.get_mut() += "\n";
+                }
+
+                Ok(self.pending_input.get_ref().len())
+            }
+            Err(ReadlineError::Eof) => Ok(0),
+            Err(e) => Err(Error::new(ErrorKind::InvalidInput, e)),
+        }
+    }
 
-                    self.pending_input.read(buf)
+    fn save_history(&mut self) {
+        if let Some(mut path) = dirs_next::home_dir() {
+            path.push(HISTORY_FILE);
+            if path.exists() {
+                if self.rl.append_history(&path).is_err() {
+                    println!("Warning: couldn't append history (existing file)");
                 }
-                Err(ReadlineError::Eof) => Ok(0),
-                Err(e) => Err(Error::new(ErrorKind::InvalidInput, e)),
+            } else if self.rl.save_history(&path).is_err() {
+                println!("Warning: couldn't save history (new file)");
             }
         }
+    }
 
-        fn save_history(&mut self) {
-            if let Some(mut path) = dirs_next::home_dir() {
-                path.push(HISTORY_FILE);
-                if path.exists() {
-                    if self.rl.append_history(&path).is_err() {
-                        println!("Warning: couldn't append history (existing file)");
+    pub(crate) fn peek_byte(&mut self) -> std::io::Result<u8> {
+        loop {
+            match self.pending_input.get_ref().bytes().next() {
+                Some(0) => {
+                    return Ok(0);
+                }
+                Some(b) => {
+                    return Ok(b);
+                }
+                None => match self.call_readline() {
+                    Err(e) => {
+                        return Err(e);
                     }
-                } else {
-                    if self.rl.save_history(&path).is_err() {
-                        println!("Warning: couldn't save history (new file)");
+                    Ok(0) => {
+                        self.pending_input.get_mut().push('\u{0}');
+                        return Ok(0);
                     }
-                }
+                    _ => {
+                        set_prompt(false);
+                    }
+                },
             }
         }
+    }
+}
 
-        pub(crate) fn peek_byte(&mut self) -> std::io::Result<u8> {
-            set_prompt(false);
-
-            loop {
-                match self.pending_input.get_ref().bytes().next() {
-                    Some(b) => {
-                        return Ok(b);
-                    }
-                    None => match self.call_readline(&mut []) {
-                        Err(e) => {
-                            return Err(e);
-                        }
-                        Ok(0) => {
-                            return Err(Error::new(ErrorKind::UnexpectedEof, "end of file"));
-                        }
-                        _ => {}
-                    },
-                }
+impl Read for ReadlineStream {
+    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+        match self.pending_input.read(buf) {
+            Ok(0) => {
+                self.call_readline()?;
+                self.pending_input.read(buf)
             }
+            result => result
         }
+    }
+}
 
-        pub(crate) fn peek_char(&mut self) -> std::io::Result<char> {
-            set_prompt(false);
+impl CharRead for ReadlineStream {
+    fn peek_char(&mut self) -> Option<std::io::Result<char>> {
+        loop {
+            let pos = self.pending_input.position() as usize;
 
-            loop {
-                match self.pending_input.get_ref().chars().next() {
-                    Some(c) => {
-                        return Ok(c);
-                    }
-                    None => match self.call_readline(&mut []) {
+            match self.pending_input.get_ref()[pos ..].chars().next() {
+                Some('\u{0}') => {
+                    return Some(Ok('\u{0}'));
+                }
+                Some(c) => {
+                    return Some(Ok(c));
+                }
+                None => {
+                    match self.call_readline() {
                         Err(e) => {
-                            return Err(e);
+                            return Some(Err(e));
                         }
                         Ok(0) => {
-                            return Err(Error::new(ErrorKind::UnexpectedEof, "end of file"));
+                            self.pending_input.get_mut().push('\u{0}');
+                            return Some(Ok('\u{0}'));
                         }
-                        _ => {}
-                    },
+                        _ => {
+                            set_prompt(false);
+                        }
+                    }
                 }
             }
         }
     }
 
-    impl Read for ReadlineStream {
-        fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
-            match self.pending_input.read(buf) {
-                Ok(0) => self.call_readline(buf),
-                result => result,
-            }
-        }
+    fn consume(&mut self, nread: usize) {
+        let offset = self.pending_input.position() as usize;
+        self.pending_input.set_position((offset + nread) as u64);
     }
 
-    #[inline]
-    pub fn input_stream() -> Stream {
-        let input_stream = ReadlineStream::input_stream(String::from(""));
-        Stream::from(input_stream)
-    }
-}
-
-impl MachineState {
-    pub(crate) fn devour_whitespace(
-        &mut self,
-        mut inner: Stream,
-        atom_tbl: TabledData<Atom>,
-    ) -> Result<bool, ParserError> {
-        let mut stream = parsing_stream(inner.clone())?;
-        let mut parser = Parser::new(&mut stream, atom_tbl, self.flags);
-
-        parser.devour_whitespace()?;
-
-        inner.add_lines_read(parser.num_lines_read());
-
-        let result = parser.eof();
-        let buf = stream.take_buf();
-
-        inner.pause_stream(buf)?;
-
-        result
-    }
-
-    pub(crate) fn read(
-        &mut self,
-        mut inner: Stream,
-        atom_tbl: TabledData<Atom>,
-        op_dir: &OpDir,
-    ) -> Result<TermWriteResult, ParserError> {
-        let mut stream = parsing_stream(inner.clone())?;
-
-        let (term, num_lines_read) = {
-            let prior_num_lines_read = inner.lines_read();
-            let mut parser = Parser::new(&mut stream, atom_tbl, self.flags);
-
-            parser.add_lines_read(prior_num_lines_read);
-
-            let term = parser.read_term(&CompositeOpDir::new(op_dir, None))?;
-            (term, parser.num_lines_read() - prior_num_lines_read)
-        };
-
-        inner.add_lines_read(num_lines_read);
-
-        // 'pausing' the stream saves the pending top buffer
-        // created by the parsing stream, which was created in this
-        // scope and is about to be destroyed in it.
-
-        let buf = stream.take_buf();
-        inner.pause_stream(buf)?;
-
-        Ok(write_term_to_heap(&term, self))
+    fn put_back_char(&mut self, c: char) {
+        let offset = self.pending_input.position() as usize;
+        self.pending_input.set_position((offset - c.len_utf8()) as u64);
     }
 }
 
 #[inline]
-pub(crate) fn write_term_to_heap(term: &Term, machine_st: &mut MachineState) -> TermWriteResult {
-    let term_writer = TermWriter::new(machine_st);
+pub(crate) fn write_term_to_heap(
+    term: &Term,
+    heap: &mut Heap,
+    atom_tbl: &mut AtomTable,
+) -> TermWriteResult {
+    let term_writer = TermWriter::new(heap, atom_tbl);
     term_writer.write_term_to_heap(term)
 }
 
 #[derive(Debug)]
-struct TermWriter<'a> {
-    machine_st: &'a mut MachineState,
+struct TermWriter<'a, 'b> {
+    heap: &'a mut Heap,
+    atom_tbl: &'b mut AtomTable,
     queue: SubtermDeque,
     var_dict: HeapVarDict,
 }
 
 #[derive(Debug)]
-pub(crate) struct TermWriteResult {
-    pub(crate) heap_loc: usize,
-    pub(crate) var_dict: HeapVarDict,
+pub struct TermWriteResult {
+    pub heap_loc: usize,
+    pub var_dict: HeapVarDict,
 }
 
-impl<'a> TermWriter<'a> {
+impl<'a, 'b> TermWriter<'a, 'b> {
     #[inline]
-    fn new(machine_st: &'a mut MachineState) -> Self {
+    fn new(heap: &'a mut Heap, atom_tbl: &'b mut AtomTable) -> Self {
         TermWriter {
-            machine_st,
+            heap,
+            atom_tbl,
             queue: SubtermDeque::new(),
             var_dict: HeapVarDict::new(),
         }
@@ -257,7 +264,7 @@ impl<'a> TermWriter<'a> {
     #[inline]
     fn modify_head_of_queue(&mut self, term: &TermRef<'a>, h: usize) {
         if let Some((arity, site_h)) = self.queue.pop_front() {
-            self.machine_st.heap[site_h] = HeapCellValue::Addr(self.term_as_addr(term, h));
+            self.heap[site_h] = self.term_as_addr(term, h);
 
             if arity > 1 {
                 self.queue.push_front((arity - 1, site_h + 1));
@@ -267,64 +274,82 @@ impl<'a> TermWriter<'a> {
 
     #[inline]
     fn push_stub_addr(&mut self) {
-        let h = self.machine_st.heap.h();
-        self.machine_st
-            .heap
-            .push(HeapCellValue::Addr(Addr::HeapCell(h)));
+        let h = self.heap.len();
+        self.heap.push(heap_loc_as_cell!(h));
     }
 
-    fn term_as_addr(&mut self, term: &TermRef<'a>, h: usize) -> Addr {
+    fn term_as_addr(&mut self, term: &TermRef<'a>, h: usize) -> HeapCellValue {
         match term {
-            &TermRef::AnonVar(_) | &TermRef::Var(..) => Addr::HeapCell(h),
-            &TermRef::Cons(..) => Addr::HeapCell(h),
-            &TermRef::Constant(_, _, c) => self.machine_st.heap.put_constant(c.clone()),
-            &TermRef::Clause(..) => Addr::Str(h),
-            &TermRef::PartialString(..) => Addr::PStrLocation(h, 0),
+            &TermRef::Cons(..) => list_loc_as_cell!(h),
+            &TermRef::AnonVar(_) | &TermRef::Var(..) => heap_loc_as_cell!(h),
+            &TermRef::PartialString(_, _, ref src, None) =>
+                if src.as_str().is_empty() {
+                    empty_list_as_cell!()
+                } else if self.heap[h].get_tag() == HeapCellValueTag::CStr {
+                    heap_loc_as_cell!(h)
+                } else {
+                    pstr_loc_as_cell!(h)
+                },
+            &TermRef::PartialString(..) => pstr_loc_as_cell!(h),
+            &TermRef::Literal(_, _, literal) => HeapCellValue::from(*literal),
+            &TermRef::Clause(..) => str_loc_as_cell!(h),
         }
     }
 
     fn write_term_to_heap(mut self, term: &'a Term) -> TermWriteResult {
-        let heap_loc = self.machine_st.heap.h();
+        let heap_loc = self.heap.len();
 
         for term in breadth_first_iter(term, true) {
-            let h = self.machine_st.heap.h();
+            let h = self.heap.len();
 
             match &term {
-                &TermRef::Cons(lvl, ..) => {
+                &TermRef::Cons(Level::Root, ..) => {
                     self.queue.push_back((2, h + 1));
-                    self.machine_st
-                        .heap
-                        .push(HeapCellValue::Addr(Addr::Lis(h + 1)));
+                    self.heap.push(list_loc_as_cell!(h + 1));
 
                     self.push_stub_addr();
                     self.push_stub_addr();
 
-                    if let Level::Root = lvl {
-                        continue;
-                    }
+                    continue;
                 }
-                &TermRef::Clause(lvl, _, ref ct, subterms) => {
-                    self.queue.push_back((subterms.len(), h + 1));
-                    let named = HeapCellValue::NamedStr(subterms.len(), ct.name(), ct.spec());
+                &TermRef::Cons(..) => {
+                    self.queue.push_back((2, h));
+
+                    self.push_stub_addr();
+                    self.push_stub_addr();
+                }
+                &TermRef::Clause(Level::Root, _, ref ct, subterms) => {
+                    self.heap.push(str_loc_as_cell!(heap_loc + 1));
+
+                    self.queue.push_back((subterms.len(), h + 2));
+                    let named = atom_as_cell!(ct.name(), subterms.len());
 
-                    self.machine_st.heap.push(named);
+                    self.heap.push(named);
 
                     for _ in 0..subterms.len() {
                         self.push_stub_addr();
                     }
 
-                    if let Level::Root = lvl {
-                        continue;
+                    continue;
+                }
+                &TermRef::Clause(_, _, ref ct, subterms) => {
+                    self.queue.push_back((subterms.len(), h + 1));
+                    let named = atom_as_cell!(ct.name(), subterms.len());
+
+                    self.heap.push(named);
+
+                    for _ in 0..subterms.len() {
+                        self.push_stub_addr();
                     }
                 }
-                &TermRef::AnonVar(Level::Root) | &TermRef::Constant(Level::Root, ..) => {
+                &TermRef::AnonVar(Level::Root) | &TermRef::Literal(Level::Root, ..) => {
                     let addr = self.term_as_addr(&term, h);
-                    self.machine_st.heap.push(HeapCellValue::Addr(addr));
+                    self.heap.push(addr);
                 }
                 &TermRef::Var(Level::Root, _, ref var) => {
                     let addr = self.term_as_addr(&term, h);
-                    self.var_dict.insert(var.clone(), Addr::HeapCell(h));
-                    self.machine_st.heap.push(HeapCellValue::Addr(addr));
+                    self.var_dict.insert(var.clone(), heap_loc_as_cell!(h));
+                    self.heap.push(addr);
                 }
                 &TermRef::AnonVar(_) => {
                     if let Some((arity, site_h)) = self.queue.pop_front() {
@@ -335,25 +360,28 @@ impl<'a> TermWriter<'a> {
 
                     continue;
                 }
-                &TermRef::PartialString(lvl, _, ref pstr, tail) => {
+                &TermRef::PartialString(lvl, _, ref src, tail) => {
                     if tail.is_some() {
-                        self.machine_st.heap.allocate_pstr(&pstr);
+                        allocate_pstr(self.heap, src.as_str(), self.atom_tbl);
                     } else {
-                        self.machine_st.heap.put_complete_string(&pstr);
+                        put_complete_string(self.heap, src.as_str(), self.atom_tbl);
                     }
 
-                    if let Level::Root = lvl {
-                    } else if tail.is_some() {
-                        let h = self.machine_st.heap.h();
+                    if tail.is_some() {
+                        let h = self.heap.len();
                         self.queue.push_back((1, h - 1));
+
+                        if let Level::Root = lvl {
+                            continue;
+                        }
                     }
                 }
                 &TermRef::Var(_, _, ref var) => {
                     if let Some((arity, site_h)) = self.queue.pop_front() {
                         if let Some(addr) = self.var_dict.get(var).cloned() {
-                            self.machine_st.heap[site_h] = HeapCellValue::Addr(addr);
+                            self.heap[site_h] = addr;
                         } else {
-                            self.var_dict.insert(var.clone(), Addr::HeapCell(site_h));
+                            self.var_dict.insert(var.clone(), heap_loc_as_cell!(site_h));
                         }
 
                         if arity > 1 {
index 78e988e5dcaf25c65f2a9905d2efa00779d625ec..0d4ac6d6ec66cb4b8cf53aac372d8461c5a56488 100644 (file)
@@ -1,37 +1,39 @@
-use prolog_parser::ast::*;
+use crate::parser::ast::*;
 
+use crate::atom_table::*;
 use crate::clause_types::*;
 use crate::forms::*;
 use crate::instructions::*;
 use crate::iterators::*;
+use crate::types::*;
 
 pub(crate) trait CompilationTarget<'a> {
     type Iterator: Iterator<Item = TermRef<'a>>;
 
-    fn iter(_: &'a Term) -> Self::Iterator;
+    fn iter(term: &'a Term) -> Self::Iterator;
 
-    fn to_constant(_: Level, _: Constant, _: RegType) -> Self;
-    fn to_list(_: Level, _: RegType) -> Self;
-    fn to_structure(_: ClauseType, _: usize, _: RegType) -> Self;
+    fn to_constant(lvl: Level, literal: Literal, r: RegType) -> Self;
+    fn to_list(lvl: Level, r: RegType) -> Self;
+    fn to_structure(ct: ClauseType, arity: usize, r: RegType) -> Self;
 
-    fn to_void(_: usize) -> Self;
+    fn to_void(num_subterms: usize) -> Self;
     fn is_void_instr(&self) -> bool;
 
-    fn to_pstr(lvl: Level, string: String, r: RegType, has_tail: bool) -> Self;
+    fn to_pstr(lvl: Level, string: Atom, r: RegType, has_tail: bool) -> Self;
 
     fn incr_void_instr(&mut self);
 
-    fn constant_subterm(_: Constant) -> Self;
+    fn constant_subterm(literal: Literal) -> Self;
 
-    fn argument_to_variable(_: RegType, _: usize) -> Self;
-    fn argument_to_value(_: RegType, _: usize) -> Self;
+    fn argument_to_variable(r: RegType, r: usize) -> Self;
+    fn argument_to_value(r: RegType, val: usize) -> Self;
 
-    fn move_to_register(_: RegType, _: usize) -> Self;
+    fn move_to_register(r: RegType, val: usize) -> Self;
 
-    fn subterm_to_variable(_: RegType) -> Self;
-    fn subterm_to_value(_: RegType) -> Self;
+    fn subterm_to_variable(r: RegType) -> Self;
+    fn subterm_to_value(r: RegType) -> Self;
 
-    fn clause_arg_to_instr(_: RegType) -> Self;
+    fn clause_arg_to_instr(r: RegType) -> Self;
 }
 
 impl<'a> CompilationTarget<'a> for FactInstruction {
@@ -41,8 +43,8 @@ impl<'a> CompilationTarget<'a> for FactInstruction {
         breadth_first_iter(term, false) // do not iterate over the root clause if one exists.
     }
 
-    fn to_constant(lvl: Level, constant: Constant, reg: RegType) -> Self {
-        FactInstruction::GetConstant(lvl, constant, reg)
+    fn to_constant(lvl: Level, constant: Literal, reg: RegType) -> Self {
+        FactInstruction::GetConstant(lvl, HeapCellValue::from(constant), reg)
     }
 
     fn to_structure(ct: ClauseType, arity: usize, reg: RegType) -> Self {
@@ -53,8 +55,8 @@ impl<'a> CompilationTarget<'a> for FactInstruction {
         FactInstruction::GetList(lvl, reg)
     }
 
-    fn to_void(subterms: usize) -> Self {
-        FactInstruction::UnifyVoid(subterms)
+    fn to_void(num_subterms: usize) -> Self {
+        FactInstruction::UnifyVoid(num_subterms)
     }
 
     fn is_void_instr(&self) -> bool {
@@ -64,7 +66,7 @@ impl<'a> CompilationTarget<'a> for FactInstruction {
         }
     }
 
-    fn to_pstr(lvl: Level, string: String, r: RegType, has_tail: bool) -> Self {
+    fn to_pstr(lvl: Level, string: Atom, r: RegType, has_tail: bool) -> Self {
         FactInstruction::GetPartialString(lvl, string, r, has_tail)
     }
 
@@ -75,8 +77,8 @@ impl<'a> CompilationTarget<'a> for FactInstruction {
         }
     }
 
-    fn constant_subterm(constant: Constant) -> Self {
-        FactInstruction::UnifyConstant(constant)
+    fn constant_subterm(constant: Literal) -> Self {
+        FactInstruction::UnifyConstant(HeapCellValue::from(constant))
     }
 
     fn argument_to_variable(arg: RegType, val: usize) -> Self {
@@ -115,15 +117,15 @@ impl<'a> CompilationTarget<'a> for QueryInstruction {
         QueryInstruction::PutStructure(ct, arity, r)
     }
 
-    fn to_constant(lvl: Level, constant: Constant, reg: RegType) -> Self {
-        QueryInstruction::PutConstant(lvl, constant, reg)
+    fn to_constant(lvl: Level, constant: Literal, reg: RegType) -> Self {
+        QueryInstruction::PutConstant(lvl, HeapCellValue::from(constant), reg)
     }
 
     fn to_list(lvl: Level, reg: RegType) -> Self {
         QueryInstruction::PutList(lvl, reg)
     }
 
-    fn to_pstr(lvl: Level, string: String, r: RegType, has_tail: bool) -> Self {
+    fn to_pstr(lvl: Level, string: Atom, r: RegType, has_tail: bool) -> Self {
         QueryInstruction::PutPartialString(lvl, string, r, has_tail)
     }
 
@@ -145,8 +147,8 @@ impl<'a> CompilationTarget<'a> for QueryInstruction {
         }
     }
 
-    fn constant_subterm(constant: Constant) -> Self {
-        QueryInstruction::SetConstant(constant)
+    fn constant_subterm(constant: Literal) -> Self {
+        QueryInstruction::SetConstant(HeapCellValue::from(constant))
     }
 
     fn argument_to_variable(arg: RegType, val: usize) -> Self {
index fdc5c18fcec9a366e8e15408e4f24364c9ddab8d..441d470a0e0a43b5394a5733721fcb671f176904 100644 (file)
@@ -4,6 +4,7 @@
 :- use_module(library(charsio)).
 :- use_module(library(files)).
 :- use_module(library(iso_ext)).
+:- use_module(library(lambda)).
 :- use_module(library(lists)).
 :- use_module(library(si)).
 
@@ -17,7 +18,7 @@ load_scryerrc :-
        append(HomeDir, "/.scryerrc", ScryerrcFile),
        (  file_exists(ScryerrcFile) ->
           atom_chars(ScryerrcFileAtom, ScryerrcFile),
-          catch(consult(ScryerrcFileAtom), E, print_exception(E))
+          catch(use_module(ScryerrcFileAtom), E, print_exception(E))
        ;  true
        )
     ;  true
@@ -166,7 +167,7 @@ instruction_match(Term, VarList) :-
     ;  Term = end_of_file ->
        halt
     ;
-       submit_query_and_print_results(Term, VarList)
+    submit_query_and_print_results(Term, VarList)
     ).
 
 
@@ -193,10 +194,10 @@ submit_query_and_print_results(Term0, VarList) :-
 
 needs_bracketing(Value, Op) :-
     catch((functor(Value, F, _),
-              current_op(EqPrec, EqSpec, Op),
-              current_op(FPrec, _, F)),
-             _,
-             false),
+          current_op(EqPrec, EqSpec, Op),
+          current_op(FPrec, _, F)),
+         _,
+         false),
     (  EqPrec < FPrec ->
        true
     ;  FPrec > 0, F == Value, graphic_token_char(F) ->
@@ -210,15 +211,15 @@ needs_bracketing(Value, Op) :-
 write_goal(G, VarList, MaxDepth) :-
     (  G = (Var = Value) ->
        (  var(Value) ->
-             select((Var = _), VarList, NewVarList)
+         select((Var = _), VarList, NewVarList)
        ;  VarList = NewVarList
        ),
        write(Var),
        write(' = '),
        (  needs_bracketing(Value, (=)) ->
-             write('('),
-             write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]),
-             write(')')
+         write('('),
+         write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]),
+         write(')')
        ;  write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)])
        )
     ;  G == [] ->
@@ -229,20 +230,20 @@ write_goal(G, VarList, MaxDepth) :-
 write_last_goal(G, VarList, MaxDepth) :-
     (  G = (Var = Value) ->
        (  var(Value) ->
-             select((Var = _), VarList, NewVarList)
+         select((Var = _), VarList, NewVarList)
        ;  VarList = NewVarList
        ),
        write(Var),
        write(' = '),
        (  needs_bracketing(Value, (=)) ->
-             write('('),
-             write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]),
-             write(')')
+         write('('),
+         write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]),
+         write(')')
        ;  write_term(Value, [quoted(true), variable_names(NewVarList), max_depth(MaxDepth)]),
-             (  trailing_period_is_ambiguous(Value) ->
-                write(' ')
-             ;  true
-             )
+         (  trailing_period_is_ambiguous(Value) ->
+            write(' ')
+         ;  true
+         )
        )
     ;  G == [] ->
        write('true')
@@ -272,8 +273,13 @@ trailing_period_is_ambiguous(Value) :-
     ValueChars \== ['.'],
     graphic_token_char(Char).
 
+term_variables_under_max_depth(Term, MaxDepth, Vars) :-
+    '$term_variables_under_max_depth'(Term, MaxDepth, Vars).
+
 write_eqs_and_read_input(B, VarList) :-
-    term_variables(VarList, Vars0),
+    gather_query_vars(VarList, OrigVars),
+    % one layer of depth added for (=/2) functor
+    '$term_variables_under_max_depth'(OrigVars, 22, Vars0),
     '$term_attributed_variables'(VarList, AttrVars),
     '$project_atts':project_attributes(Vars0, AttrVars),
     copy_term(AttrVars, AttrVars, AttrGoals),
@@ -281,12 +287,13 @@ write_eqs_and_read_input(B, VarList) :-
     append([Vars0, AttrGoalVars, AttrVars], Vars),
     charsio:extend_var_list(Vars, VarList, NewVarList, fabricated),
     '$get_b_value'(B0),
-    gather_query_vars(VarList, OrigVars),
     gather_equations(NewVarList, OrigVars, Equations),
     append(Equations, AttrGoals, Goals),
-    term_variables(Equations, EquationVars),
-    append([AttrGoalVars, EquationVars], Vars1),
-    charsio:extend_var_list(Vars1, VarList, NewVarList0, fabricated),
+    % one layer of depth added for (=/2) functor
+    maplist(\Term^Vs^term_variables_under_max_depth(Term, 22, Vs), Equations, EquationVars),
+    append([AttrGoalVars | EquationVars], Vars1),
+    sort(Vars1, Vars2),
+    charsio:extend_var_list(Vars2, VarList, NewVarList0, fabricated),
     (   bb_get('$first_answer', true) ->
         write('   '),
         bb_put('$first_answer', false)
@@ -294,11 +301,11 @@ write_eqs_and_read_input(B, VarList) :-
     ),
     (  B0 == B ->
        (  Goals == [] ->
-             write('true.'), nl
+         write('true.'), nl
        ;  loader:thread_goals(Goals, ThreadedGoals, (',')),
-             write_eq(ThreadedGoals, NewVarList0, 20),
-             write('.'),
-             nl
+         write_eq(ThreadedGoals, NewVarList0, 20),
+         write('.'),
+         nl
        )
     ;  loader:thread_goals(Goals, ThreadedGoals, (',')),
        write_eq(ThreadedGoals, NewVarList0, 20),
@@ -340,7 +347,7 @@ gather_query_vars([_ = Var | Vars], QueryVars) :-
        QueryVars = [Var | QueryVars0],
        gather_query_vars(Vars, QueryVars0)
     ;
-       gather_query_vars(Vars, QueryVars)
+    gather_query_vars(Vars, QueryVars)
     ).
 gather_query_vars([], []).
 
@@ -358,8 +365,8 @@ select_all([OtherVar = OtherValue | Pairs], Var, Value, Vars, NewPairs) :-
        Vars = [OtherVar = OtherValue | Vars0],
        select_all(Pairs, Var, Value, Vars0, NewPairs)
     ;
-       NewPairs = [OtherVar = OtherValue | NewPairs0],
-       select_all(Pairs, Var, Value, Vars, NewPairs0)
+    NewPairs = [OtherVar = OtherValue | NewPairs0],
+    select_all(Pairs, Var, Value, Vars, NewPairs0)
     ).
 
 gather_equations([], _, []).
@@ -370,11 +377,11 @@ gather_equations([Var = Value | Pairs], OrigVarList, Goals) :-
           append([Var = Value | VarEqs], Goals0, Goals),
           gather_equations(NewPairs, OrigVarList, Goals0)
        ;
-          gather_equations(Pairs, OrigVarList, Goals)
+       gather_equations(Pairs, OrigVarList, Goals)
        )
     ;
-       Goals = [Var = Value | Goals0],
-       gather_equations(Pairs, OrigVarList, Goals0)
+    Goals = [Var = Value | Goals0],
+    gather_equations(Pairs, OrigVarList, Goals0)
     ).
 
 print_exception(E) :-
diff --git a/src/types.rs b/src/types.rs
new file mode 100644 (file)
index 0000000..57c999e
--- /dev/null
@@ -0,0 +1,739 @@
+use crate::arena::*;
+use crate::atom_table::*;
+use crate::forms::*;
+use crate::machine::machine_indices::*;
+use crate::machine::partial_string::PartialString;
+use crate::parser::ast::Fixnum;
+
+use modular_bitfield::prelude::*;
+
+use std::cmp::Ordering;
+use std::convert::TryFrom;
+use std::fmt;
+use std::mem;
+use std::ops::{Add, Sub, SubAssign};
+
+#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+#[bits = 6]
+pub enum HeapCellValueTag {
+    // non-constants / tags with adjoining forwarding bits.
+    Cons = 0b00,
+    F64 = 0b01,
+    Str = 0b000010,
+    Lis = 0b000011,
+    Var = 0b000110,
+    StackVar = 0b000111,
+    AttrVar = 0b010011,
+    PStrLoc = 0b111111,
+    PStrOffset = 0b001110,
+    // constants.
+    Fixnum = 0b010010,
+    Char = 0b011011,
+    Atom = 0b001010,
+    PStr = 0b001011,
+    CStr = 0b010110, // a complete string.
+}
+
+#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+#[bits = 6]
+pub enum HeapCellValueView {
+    // non-constants / tags with adjoining forwarding bits.
+    Cons = 0b00,
+    F64 = 0b01,
+    Str = 0b000010,
+    Lis = 0b000011,
+    Var = 0b000110,
+    StackVar = 0b000111,
+    AttrVar = 0b010011,
+    PStrLoc = 0b111111,
+    PStrOffset = 0b001110,
+    // constants.
+    Fixnum = 0b010010,
+    Char = 0b011011,
+    Atom = 0b001010,
+    PStr = 0b001011,
+    CStr = 0b010110,
+    // trail elements.
+    TrailedHeapVar = 0b011110,
+    TrailedStackVar = 0b011111,
+    TrailedAttrVarHeapLink = 0b101110,
+    TrailedAttrVarListLink = 0b100010,
+    TrailedAttachedValue = 0b101010,
+    TrailedBlackboardEntry = 0b100110,
+    TrailedBlackboardOffset = 0b100111,
+}
+
+#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+#[bits = 2]
+pub enum ConsPtrMaskTag {
+    Cons = 0b00,
+    F64 = 0b01,
+}
+
+#[bitfield]
+#[repr(u64)]
+#[derive(Copy, Clone, Debug)]
+pub struct ConsPtr {
+    ptr: B61,
+    m: bool,
+    tag: ConsPtrMaskTag,
+}
+
+impl ConsPtr {
+    #[inline(always)]
+    pub fn build_with(ptr: *const ArenaHeader, tag: ConsPtrMaskTag) -> Self {
+        ConsPtr::new()
+            .with_ptr(ptr as *const u8 as u64)
+            .with_m(false)
+            .with_tag(tag)
+    }
+
+    #[inline]
+    pub fn as_ptr(self) -> *mut u8 {
+        self.ptr() as *mut _
+    }
+}
+
+#[derive(BitfieldSpecifier, Copy, Clone, Debug)]
+#[bits = 6]
+pub(crate) enum RefTag {
+    HeapCell = 0b0110,
+    StackCell = 0b111,
+    AttrVar = 0b10011,
+}
+
+#[bitfield]
+#[repr(u64)]
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct Ref {
+    val: B56,
+    #[allow(unused)] m: bool,
+    #[allow(unused)] f: bool,
+    tag: RefTag,
+}
+
+impl Ord for Ref {
+    fn cmp(&self, rhs: &Ref) -> Ordering {
+        match self.get_tag() {
+            RefTag::HeapCell | RefTag::AttrVar => {
+                match rhs.get_tag() {
+                    RefTag::StackCell => Ordering::Less,
+                    _ => self.get_value().cmp(&rhs.get_value()),
+                }
+            }
+            RefTag::StackCell => {
+                match rhs.get_tag() {
+                    RefTag::StackCell =>
+                        self.get_value().cmp(&rhs.get_value()),
+                    _ =>
+                        Ordering::Greater,
+                }
+            }
+        }
+    }
+}
+
+impl PartialOrd for Ref {
+    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
+        Some(self.cmp(rhs))
+    }
+}
+
+impl Ref {
+    #[inline(always)]
+    pub(crate) fn build_with(tag: RefTag, value: u64) -> Self {
+        Ref::new().with_tag(tag).with_val(value)
+    }
+
+    #[inline(always)]
+    pub(crate) fn get_tag(self) -> RefTag {
+        self.tag()
+    }
+
+    #[inline(always)]
+    pub(crate) fn get_value(self) -> u64 {
+        self.val()
+    }
+
+    #[inline(always)]
+    pub(crate) fn as_heap_cell_value(self) -> HeapCellValue {
+        HeapCellValue::from_bytes(self.into_bytes())
+    }
+
+    #[inline(always)]
+    pub(crate) fn heap_cell(h: usize) -> Self {
+        Ref::build_with(RefTag::HeapCell, h as u64)
+    }
+
+    #[inline(always)]
+    pub(crate) fn stack_cell(h: usize) -> Self {
+        Ref::build_with(RefTag::StackCell, h as u64)
+    }
+
+    #[inline(always)]
+    pub(crate) fn attr_var(h: usize) -> Self {
+        Ref::build_with(RefTag::AttrVar, h as u64)
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum TrailRef {
+    Ref(Ref),
+    AttrVarHeapLink(usize),
+    AttrVarListLink(usize, usize),
+    BlackboardEntry(Atom),
+    BlackboardOffset(Atom, HeapCellValue), // key atom, key value
+}
+
+#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+#[bits = 6]
+pub(crate) enum TrailEntryTag {
+    TrailedHeapVar = 0b011110,
+    TrailedStackVar = 0b011111,
+    TrailedAttrVar = 0b101110,
+    TrailedAttrVarHeapLink = 0b100010,
+    TrailedAttrVarListLink = 0b100011,
+    TrailedAttachedValue = 0b101010,
+    TrailedBlackboardEntry = 0b100110,
+    TrailedBlackboardOffset = 0b100111,
+}
+
+#[bitfield]
+#[derive(Copy, Clone, Debug)]
+#[repr(u64)]
+pub(crate) struct TrailEntry {
+    val: B56,
+    #[allow(unused)] f: bool,
+    #[allow(unused)] m: bool,
+    #[allow(unused)] tag: TrailEntryTag,
+}
+
+impl TrailEntry {
+    #[inline(always)]
+    pub(crate) fn build_with(tag: TrailEntryTag, value: u64) -> Self {
+        TrailEntry::new()
+            .with_tag(tag)
+            .with_m(false)
+            .with_f(false)
+            .with_val(value)
+    }
+
+    #[inline(always)]
+    pub(crate) fn get_tag(self) -> TrailEntryTag {
+        match self.tag_or_err() {
+           Ok(tag) => tag,
+           Err(_) => TrailEntryTag::TrailedAttachedValue,
+       }
+    }
+
+    #[inline]
+    pub(crate) fn get_value(self) -> u64 {
+        self.val()
+    }
+}
+
+#[repr(u64)]
+#[bitfield]
+#[derive(Copy, Clone, Hash, PartialEq, Eq)]
+pub struct HeapCellValue {
+    val: B56,
+    f: bool,
+    m: bool,
+    tag: HeapCellValueTag,
+}
+
+impl fmt::Debug for HeapCellValue {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result {
+        match self.get_tag() {
+            tag @ (HeapCellValueTag::Cons | HeapCellValueTag::F64) => {
+                let cons_ptr = ConsPtr::from_bytes(self.into_bytes());
+
+                f.debug_struct("HeapCellValue")
+                    .field("tag", &tag)
+                    .field("ptr", &cons_ptr.ptr())
+                    .field("m", &cons_ptr.m())
+                    .finish()
+            }
+            HeapCellValueTag::Atom => {
+                let (name, arity) = cell_as_atom_cell!(self)
+                     .get_name_and_arity();
+
+                f.debug_struct("HeapCellValue")
+                    .field("tag", &HeapCellValueTag::Atom)
+                    .field("name", &name.as_str())
+                    .field("arity", &arity)
+                    .field("m", &self.m())
+                    .field("f", &self.f())
+                    .finish()
+            }
+            HeapCellValueTag::PStr => {
+                let (name, _) = cell_as_atom_cell!(self)
+                     .get_name_and_arity();
+
+                f.debug_struct("HeapCellValue")
+                    .field("tag", &HeapCellValueTag::PStr)
+                    .field("contents", &name.as_str())
+                    .field("m", &self.m())
+                    .field("f", &self.f())
+                    .finish()
+            }
+            tag => {
+                f.debug_struct("HeapCellValue")
+                    .field("tag", &tag)
+                    .field("value", &self.get_value())
+                    .field("m", &self.get_mark_bit())
+                    .field("f", &self.get_forwarding_bit())
+                    .finish()
+            }
+        }
+    }
+}
+
+impl<T> From<TypedArenaPtr<T>> for HeapCellValue {
+    #[inline]
+    fn from(arena_ptr: TypedArenaPtr<T>) -> HeapCellValue {
+        HeapCellValue::from(arena_ptr.header_ptr() as u64)
+    }
+}
+
+impl From<F64Ptr> for HeapCellValue {
+    #[inline]
+    fn from(f64_ptr: F64Ptr) -> HeapCellValue {
+        HeapCellValue::from_bytes(
+            ConsPtr::from(f64_ptr.as_ptr() as u64)
+                .with_tag(ConsPtrMaskTag::F64)
+                .with_m(false)
+                .into_bytes(),
+        )
+    }
+}
+
+impl From<ConsPtr> for HeapCellValue {
+    #[inline(always)]
+    fn from(cons_ptr: ConsPtr) -> HeapCellValue {
+        HeapCellValue::from_bytes(
+            ConsPtr::from(cons_ptr.as_ptr() as u64)
+                .with_tag(ConsPtrMaskTag::Cons)
+                .with_m(false)
+                .into_bytes(),
+        )
+    }
+}
+
+impl<'a> From<(Number, &mut Arena)> for HeapCellValue {
+    #[inline(always)]
+    fn from((n, arena): (Number, &mut Arena)) -> HeapCellValue {
+        match n {
+            Number::Float(n) => HeapCellValue::from(arena_alloc!(n, arena)),
+            Number::Integer(n) => HeapCellValue::from(n),
+            Number::Rational(n) => HeapCellValue::from(n),
+            Number::Fixnum(n) => fixnum_as_cell!(n),
+        }
+    }
+}
+
+impl HeapCellValue {
+    #[inline(always)]
+    pub fn build_with(tag: HeapCellValueTag, value: u64) -> Self {
+        HeapCellValue::new()
+            .with_tag(tag)
+            .with_val(value)
+            .with_m(false)
+            .with_f(false)
+    }
+
+    #[inline]
+    pub fn is_string_terminator(self, heap: &[HeapCellValue]) -> bool {
+        read_heap_cell!(self,
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                name == atom!("[]") && arity == 0
+            }
+            (HeapCellValueTag::CStr) => {
+                true
+            }
+            (HeapCellValueTag::PStrOffset, pstr_offset) => {
+                heap[pstr_offset].get_tag() == HeapCellValueTag::CStr
+            }
+            _ => {
+                false
+            }
+        )
+    }
+
+    #[inline(always)]
+    pub fn is_forwarded(self) -> bool {
+        self.get_forwarding_bit().unwrap_or(false)
+    }
+
+    #[inline]
+    pub fn is_ref(self) -> bool {
+        match self.get_tag() {
+            HeapCellValueTag::Str | HeapCellValueTag::Lis | HeapCellValueTag::Var |
+            HeapCellValueTag::StackVar | HeapCellValueTag::AttrVar | HeapCellValueTag::PStrLoc |
+            HeapCellValueTag::PStrOffset => true,
+            _ => false,
+        }
+    }
+
+    #[inline]
+    pub fn as_char(self) -> Option<char> {
+        read_heap_cell!(self,
+            (HeapCellValueTag::Char, c) => {
+                Some(c)
+            }
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                if arity > 0 {
+                    return None;
+                }
+
+                name.as_char()
+            }
+            _ => {
+                None
+            }
+        )
+    }
+
+    #[inline]
+    pub fn is_constant(self) -> bool {
+        match self.get_tag() {
+            HeapCellValueTag::Cons | HeapCellValueTag::F64 | HeapCellValueTag::Fixnum |
+            HeapCellValueTag::Char | HeapCellValueTag::CStr => {
+                true
+            }
+            HeapCellValueTag::Atom => {
+                cell_as_atom_cell!(self).get_arity() == 0
+            }
+            _ => {
+                false
+            }
+        }
+    }
+
+    #[inline(always)]
+    pub fn is_stack_var(self) -> bool {
+        self.get_tag() == HeapCellValueTag::StackVar
+    }
+
+    #[inline]
+    pub fn is_compound(self) -> bool {
+        match self.get_tag() {
+           HeapCellValueTag::Str
+            | HeapCellValueTag::Lis
+            | HeapCellValueTag::CStr
+            | HeapCellValueTag::PStr
+            | HeapCellValueTag::PStrLoc
+            | HeapCellValueTag::PStrOffset => {
+               true
+           }
+           HeapCellValueTag::Atom => {
+               cell_as_atom_cell!(self).get_arity() > 0
+           }
+           _ => { false }
+        }
+    }
+
+    #[inline]
+    pub fn is_var(self) -> bool {
+        read_heap_cell!(self,
+            (HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
+                true
+            }
+            _ => {
+                false
+            }
+        )
+    }
+
+    #[inline]
+    pub(crate) fn as_var(self) -> Option<Ref> {
+        read_heap_cell!(self,
+            (HeapCellValueTag::Var, h) => {
+                Some(Ref::heap_cell(h))
+            }
+            (HeapCellValueTag::AttrVar, h) => {
+                Some(Ref::attr_var(h))
+            }
+            (HeapCellValueTag::StackVar, s) => {
+                Some(Ref::stack_cell(s))
+            }
+            _ => {
+                None
+            }
+        )
+    }
+
+    #[inline]
+    pub fn get_value(self) -> usize {
+        self.val() as usize
+    }
+
+    #[inline]
+    pub fn set_value(&mut self, val: usize) {
+        self.set_val(val as u64);
+    }
+
+    #[inline]
+    pub fn get_tag(self) -> HeapCellValueTag {
+        match self.tag_or_err() {
+            Ok(tag) => tag,
+            Err(_) => match ConsPtr::from_bytes(self.into_bytes()).tag() {
+                ConsPtrMaskTag::Cons => HeapCellValueTag::Cons,
+                ConsPtrMaskTag::F64 => HeapCellValueTag::F64,
+            },
+        }
+    }
+
+    #[inline]
+    pub fn to_atom(self) -> Option<Atom> {
+        match self.tag() {
+            HeapCellValueTag::Atom => Some(Atom::from((self.val() << 3) as usize)),
+            _ => None,
+        }
+    }
+
+    #[inline]
+    pub fn to_pstr(self) -> Option<PartialString> {
+        match self.tag() {
+            HeapCellValueTag::PStr => {
+                Some(PartialString::from(Atom::from((self.val() as usize) << 3)))
+            }
+            _ => None,
+        }
+    }
+
+    #[inline]
+    pub fn to_fixnum(self) -> Option<Fixnum> {
+        match self.get_tag() {
+            HeapCellValueTag::Fixnum => Some(Fixnum::from_bytes(self.into_bytes())),
+            _ => None,
+        }
+    }
+
+    #[inline]
+    pub fn to_untyped_arena_ptr(self) -> Option<UntypedArenaPtr> {
+        match self.tag() {
+            HeapCellValueTag::Cons => Some(UntypedArenaPtr::from_bytes(self.into_bytes())),
+            _ => None,
+        }
+    }
+
+    #[inline]
+    pub fn get_forwarding_bit(self) -> Option<bool> {
+        match self.get_tag() {
+            HeapCellValueTag::Cons        // the list of non-forwardable cell tags.
+                | HeapCellValueTag::F64
+                // | HeapCellValueTag::Atom
+                // | HeapCellValueTag::PStr
+                | HeapCellValueTag::Fixnum
+                | HeapCellValueTag::Char => None,
+            _ => Some(self.f()),
+        }
+    }
+
+    #[inline]
+    pub fn set_forwarding_bit(&mut self, f: bool) {
+        match self.get_tag() {
+            HeapCellValueTag::Cons        // the list of non-forwardable cell tags.
+                | HeapCellValueTag::F64
+                // | HeapCellValueTag::Atom
+                // | HeapCellValueTag::PStr
+                | HeapCellValueTag::Fixnum
+                | HeapCellValueTag::Char => {}
+            _ => self.set_f(f),
+        }
+    }
+
+    #[inline]
+    pub fn get_mark_bit(self) -> bool {
+        match self.get_tag() {
+            HeapCellValueTag::Cons | HeapCellValueTag::F64 => {
+                ConsPtr::from_bytes(self.into_bytes()).m()
+            }
+            _ => self.m(),
+        }
+    }
+
+    #[inline]
+    pub fn set_mark_bit(&mut self, m: bool) {
+        match self.get_tag() {
+            HeapCellValueTag::Cons | HeapCellValueTag::F64 => {
+                let value = ConsPtr::from_bytes(self.into_bytes()).with_m(m);
+                *self = HeapCellValue::from_bytes(value.into_bytes());
+            }
+            _ => self.set_m(m),
+        }
+    }
+
+    pub fn order_category(self) -> Option<TermOrderCategory> {
+        match Number::try_from(self).ok() {
+            Some(Number::Integer(_)) | Some(Number::Fixnum(_)) | Some(Number::Rational(_)) => {
+                Some(TermOrderCategory::Integer)
+            }
+            Some(Number::Float(_)) => Some(TermOrderCategory::FloatingPoint),
+            None => match self.get_tag() {
+                HeapCellValueTag::Var | HeapCellValueTag::StackVar | HeapCellValueTag::AttrVar => {
+                    Some(TermOrderCategory::Variable)
+                }
+                HeapCellValueTag::Char => Some(TermOrderCategory::Atom),
+                HeapCellValueTag::Atom => {
+                    Some(if cell_as_atom_cell!(self).get_arity() > 0 {
+                        TermOrderCategory::Compound
+                    } else {
+                        TermOrderCategory::Atom
+                    })
+                }
+                HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc |
+               HeapCellValueTag::CStr | HeapCellValueTag::Str => {
+                    Some(TermOrderCategory::Compound)
+                }
+                _ => {
+                    None
+                }
+            },
+        }
+    }
+
+    #[inline(always)]
+    pub fn is_protected(self, e: usize) -> bool {
+        read_heap_cell!(self,
+            (HeapCellValueTag::StackVar, s) => {
+                s < e
+            }
+            _ => {
+                true
+            }
+        )
+    }
+}
+
+const_assert!(mem::size_of::<HeapCellValue>() == 8);
+
+#[bitfield]
+#[repr(u64)]
+#[derive(Copy, Clone, Debug)]
+pub struct UntypedArenaPtr {
+    ptr: B61,
+    m: bool,
+    #[allow(unused)] padding: B2,
+}
+
+const_assert!(mem::size_of::<UntypedArenaPtr>() == 8);
+
+impl From<*const ArenaHeader> for UntypedArenaPtr {
+    #[inline]
+    fn from(ptr: *const ArenaHeader) -> UntypedArenaPtr {
+        unsafe { mem::transmute(ptr) }
+    }
+}
+
+impl From<UntypedArenaPtr> for *const ArenaHeader {
+    #[inline]
+    fn from(ptr: UntypedArenaPtr) -> *const ArenaHeader {
+        unsafe { mem::transmute(ptr) }
+    }
+}
+
+impl UntypedArenaPtr {
+    #[inline]
+    pub fn set_mark_bit(&mut self, m: bool) {
+        self.set_m(m);
+    }
+
+    #[inline]
+    pub fn get_ptr(self) -> *const u8 {
+        self.ptr() as *const u8
+    }
+
+    #[inline]
+    pub fn get_tag(self) -> ArenaHeaderTag {
+        unsafe {
+            let header = *(self.ptr() as *const ArenaHeader);
+            header.get_tag()
+        }
+    }
+
+    #[inline]
+    pub fn payload_offset(self) -> *const u8 {
+        unsafe {
+            self.get_ptr()
+                .offset(mem::size_of::<ArenaHeader>() as isize)
+        }
+    }
+
+    #[inline]
+    pub fn get_mark_bit(self) -> bool {
+        self.m()
+    }
+}
+
+impl Add<usize> for HeapCellValue {
+    type Output = HeapCellValue;
+
+    fn add(self, rhs: usize) -> Self::Output {
+        match self.get_tag() {
+            tag @ HeapCellValueTag::Str |
+            tag @ HeapCellValueTag::Lis |
+            tag @ HeapCellValueTag::PStrOffset |
+            tag @ HeapCellValueTag::PStrLoc |
+            tag @ HeapCellValueTag::Var |
+            tag @ HeapCellValueTag::AttrVar => {
+                HeapCellValue::build_with(tag, (self.get_value() + rhs) as u64)
+            }
+            _ => {
+                self
+            }
+        }
+    }
+}
+
+impl Sub<usize> for HeapCellValue {
+    type Output = HeapCellValue;
+
+    fn sub(self, rhs: usize) -> Self::Output {
+        match self.get_tag() {
+            tag @ HeapCellValueTag::Str |
+            tag @ HeapCellValueTag::Lis |
+            tag @ HeapCellValueTag::PStrOffset |
+            tag @ HeapCellValueTag::PStrLoc |
+            tag @ HeapCellValueTag::Var |
+            tag @ HeapCellValueTag::AttrVar => {
+                HeapCellValue::build_with(tag, (self.get_value() - rhs) as u64)
+            }
+            _ => {
+                self
+            }
+        }
+    }
+}
+
+impl SubAssign<usize> for HeapCellValue {
+    #[inline(always)]
+    fn sub_assign(&mut self, rhs: usize) {
+        *self = *self - rhs;
+    }
+}
+
+impl Sub<i64> for HeapCellValue {
+    type Output = HeapCellValue;
+
+    fn sub(self, rhs: i64) -> Self::Output {
+        if rhs < 0 {
+            match self.get_tag() {
+                tag @ HeapCellValueTag::Str |
+                tag @ HeapCellValueTag::Lis |
+                tag @ HeapCellValueTag::PStrOffset |
+                tag @ HeapCellValueTag::Var |
+                tag @ HeapCellValueTag::AttrVar => {
+                    HeapCellValue::build_with(tag, (self.get_value() + rhs.abs() as usize) as u64)
+                }
+                _ => {
+                    self
+                }
+            }
+        } else {
+            self.sub(rhs as usize)
+        }
+    }
+}
+
index 1c1fc3dcfa95fc54b2027752fbf19a023d1bb3a3..49e8c10bf9c4caafd241d2eb1d0a483366b13e11 100644 (file)
@@ -1,10 +1,17 @@
+use crate::arena::*;
+use crate::atom_table::*;
 use crate::clause_types::*;
 use crate::forms::*;
-use crate::indexing::IndexingCodePtr;
 use crate::instructions::*;
 use crate::machine::loader::CompilationTarget;
 use crate::machine::machine_errors::*;
 use crate::machine::machine_indices::*;
+use crate::machine::partial_string::*;
+use crate::machine::streams::*;
+use crate::parser::rug::{Integer, Rational};
+use crate::types::*;
+
+use ordered_float::OrderedFloat;
 
 use std::fmt;
 
@@ -13,7 +20,9 @@ impl fmt::Display for LocalCodePtr {
         match self {
             LocalCodePtr::DirEntry(p) => write!(f, "LocalCodePtr::DirEntry({})", p),
             LocalCodePtr::Halt => write!(f, "LocalCodePtr::Halt"),
-            LocalCodePtr::IndexingBuf(p, o, i) => write!(f, "LocalCodePtr::IndexingBuf({}, {}, {})", p, o, i),
+            LocalCodePtr::IndexingBuf(p, o, i) => {
+                write!(f, "LocalCodePtr::IndexingBuf({}, {}, {})", p, o, i)
+            }
         }
     }
 }
@@ -21,74 +30,50 @@ impl fmt::Display for LocalCodePtr {
 impl fmt::Display for REPLCodePtr {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
-            REPLCodePtr::AddDiscontiguousPredicate =>
-                write!(f, "REPLCodePtr::AddDiscontiguousPredicate"),
-            REPLCodePtr::AddDynamicPredicate =>
-                write!(f, "REPLCodePtr::AddDynamicPredicate"),
-            REPLCodePtr::AddMultifilePredicate =>
-                write!(f, "REPLCodePtr::AddMultifilePredicate"),
-            REPLCodePtr::AddGoalExpansionClause =>
-                write!(f, "REPLCodePtr::AddGoalExpansionClause"),
-            REPLCodePtr::AddTermExpansionClause =>
-                write!(f, "REPLCodePtr::AddTermExpansionClause"),
-            REPLCodePtr::AddInSituFilenameModule =>
-                write!(f, "REPLCodePtr::AddInSituFilenameModule"),
-            REPLCodePtr::AbolishClause =>
-                write!(f, "REPLCodePtr::AbolishClause"),
-            REPLCodePtr::Assertz =>
-                write!(f, "REPLCodePtr::Assertz"),
-            REPLCodePtr::Asserta =>
-                write!(f, "REPLCodePtr::Asserta"),
-            REPLCodePtr::Retract =>
-                write!(f, "REPLCodePtr::Retract"),
-            REPLCodePtr::ClauseToEvacuable =>
-                write!(f, "REPLCodePtr::ClauseToEvacuable"),
-            REPLCodePtr::ScopedClauseToEvacuable =>
-                write!(f, "REPLCodePtr::ScopedClauseToEvacuable"),
-            REPLCodePtr::ConcludeLoad =>
-                write!(f, "REPLCodePtr::ConcludeLoad"),
-               REPLCodePtr::DeclareModule =>
-                       write!(f, "REPLCodePtr::DeclareModule"),
-            REPLCodePtr::LoadCompiledLibrary =>
-                write!(f, "REPLCodePtr::LoadCompiledLibrary"),
-            REPLCodePtr::LoadContextSource =>
-                write!(f, "REPLCodePtr::LoadContextSource"),
-            REPLCodePtr::LoadContextFile =>
-                write!(f, "REPLCodePtr::LoadContextFile"),
-            REPLCodePtr::LoadContextDirectory =>
-                write!(f, "REPLCodePtr::LoadContextDirectory"),
-            REPLCodePtr::LoadContextModule =>
-                write!(f, "REPLCodePtr::LoadContextModule"),
-            REPLCodePtr::LoadContextStream =>
-                write!(f, "REPLCodePtr::LoadContextStream"),
-            REPLCodePtr::PopLoadContext =>
-                write!(f, "REPLCodePtr::PopLoadContext"),
-            REPLCodePtr::PopLoadStatePayload =>
-                write!(f, "REPLCodePtr::PopLoadStatePayload"),
-            REPLCodePtr::PushLoadContext =>
-                write!(f, "REPLCodePtr::PushLoadContext"),
-            REPLCodePtr::PushLoadStatePayload =>
-                write!(f, "REPLCodePtr::PushLoadStatePayload"),
-               REPLCodePtr::UseModule =>
-                       write!(f, "REPLCodePtr::UseModule"),
-            REPLCodePtr::MetaPredicateProperty =>
-                write!(f, "REPLCodePtr::MetaPredicateProperty"),
-            REPLCodePtr::BuiltInProperty =>
-                write!(f, "REPLCodePtr::BuiltInProperty"),
-            REPLCodePtr::DynamicProperty =>
-                write!(f, "REPLCodePtr::DynamicProperty"),
-            REPLCodePtr::MultifileProperty =>
-                write!(f, "REPLCodePtr::MultifileProperty"),
-            REPLCodePtr::DiscontiguousProperty =>
-                write!(f, "REPLCodePtr::DiscontiguousProperty"),
-            REPLCodePtr::IsConsistentWithTermQueue =>
-                write!(f, "REPLCodePtr::IsConsistentWithTermQueue"),
-            REPLCodePtr::FlushTermQueue =>
-                write!(f, "REPLCodePtr::FlushTermQueue"),
-            REPLCodePtr::RemoveModuleExports =>
-                write!(f, "REPLCodePtr::RemoveModuleExports"),
-            REPLCodePtr::AddNonCountedBacktracking =>
-                write!(f, "REPLCodePtr::AddNonCountedBacktracking"),
+            REPLCodePtr::AddDiscontiguousPredicate => {
+                write!(f, "REPLCodePtr::AddDiscontiguousPredicate")
+            }
+            REPLCodePtr::AddDynamicPredicate => write!(f, "REPLCodePtr::AddDynamicPredicate"),
+            REPLCodePtr::AddMultifilePredicate => write!(f, "REPLCodePtr::AddMultifilePredicate"),
+            REPLCodePtr::AddGoalExpansionClause => write!(f, "REPLCodePtr::AddGoalExpansionClause"),
+            REPLCodePtr::AddTermExpansionClause => write!(f, "REPLCodePtr::AddTermExpansionClause"),
+            REPLCodePtr::AddInSituFilenameModule => {
+                write!(f, "REPLCodePtr::AddInSituFilenameModule")
+            }
+            REPLCodePtr::AbolishClause => write!(f, "REPLCodePtr::AbolishClause"),
+            REPLCodePtr::Assertz => write!(f, "REPLCodePtr::Assertz"),
+            REPLCodePtr::Asserta => write!(f, "REPLCodePtr::Asserta"),
+            REPLCodePtr::Retract => write!(f, "REPLCodePtr::Retract"),
+            REPLCodePtr::ClauseToEvacuable => write!(f, "REPLCodePtr::ClauseToEvacuable"),
+            REPLCodePtr::ScopedClauseToEvacuable => {
+                write!(f, "REPLCodePtr::ScopedClauseToEvacuable")
+            }
+            REPLCodePtr::ConcludeLoad => write!(f, "REPLCodePtr::ConcludeLoad"),
+            REPLCodePtr::DeclareModule => write!(f, "REPLCodePtr::DeclareModule"),
+            REPLCodePtr::LoadCompiledLibrary => write!(f, "REPLCodePtr::LoadCompiledLibrary"),
+            REPLCodePtr::LoadContextSource => write!(f, "REPLCodePtr::LoadContextSource"),
+            REPLCodePtr::LoadContextFile => write!(f, "REPLCodePtr::LoadContextFile"),
+            REPLCodePtr::LoadContextDirectory => write!(f, "REPLCodePtr::LoadContextDirectory"),
+            REPLCodePtr::LoadContextModule => write!(f, "REPLCodePtr::LoadContextModule"),
+            REPLCodePtr::LoadContextStream => write!(f, "REPLCodePtr::LoadContextStream"),
+            REPLCodePtr::PopLoadContext => write!(f, "REPLCodePtr::PopLoadContext"),
+            REPLCodePtr::PopLoadStatePayload => write!(f, "REPLCodePtr::PopLoadStatePayload"),
+            REPLCodePtr::PushLoadContext => write!(f, "REPLCodePtr::PushLoadContext"),
+            REPLCodePtr::PushLoadStatePayload => write!(f, "REPLCodePtr::PushLoadStatePayload"),
+            REPLCodePtr::UseModule => write!(f, "REPLCodePtr::UseModule"),
+            REPLCodePtr::MetaPredicateProperty => write!(f, "REPLCodePtr::MetaPredicateProperty"),
+            REPLCodePtr::BuiltInProperty => write!(f, "REPLCodePtr::BuiltInProperty"),
+            REPLCodePtr::DynamicProperty => write!(f, "REPLCodePtr::DynamicProperty"),
+            REPLCodePtr::MultifileProperty => write!(f, "REPLCodePtr::MultifileProperty"),
+            REPLCodePtr::DiscontiguousProperty => write!(f, "REPLCodePtr::DiscontiguousProperty"),
+            REPLCodePtr::IsConsistentWithTermQueue => {
+                write!(f, "REPLCodePtr::IsConsistentWithTermQueue")
+            }
+            REPLCodePtr::FlushTermQueue => write!(f, "REPLCodePtr::FlushTermQueue"),
+            REPLCodePtr::RemoveModuleExports => write!(f, "REPLCodePtr::RemoveModuleExports"),
+            REPLCodePtr::AddNonCountedBacktracking => {
+                write!(f, "REPLCodePtr::AddNonCountedBacktracking")
+            }
         }
     }
 }
@@ -107,7 +92,7 @@ impl fmt::Display for CompilationTarget {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
             CompilationTarget::User => write!(f, "user"),
-            CompilationTarget::Module(ref module_name) => write!(f, "{}", module_name),
+            CompilationTarget::Module(ref module_name) => write!(f, "{}", module_name.as_str()),
         }
     }
 }
@@ -122,11 +107,17 @@ impl fmt::Display for FactInstruction {
                 write!(f, "get_list {}{}", lvl, r.reg_num())
             }
             &FactInstruction::GetPartialString(lvl, ref s, r, has_tail) => {
-                write!(f, "get_partial_string({}, {}, {}, {})",
-                       lvl, s, r, has_tail)
+                write!(
+                    f,
+                    "get_partial_string({}, {}, {}, {})",
+                    lvl,
+                    s.as_str(),
+                    r,
+                    has_tail
+                )
             }
             &FactInstruction::GetStructure(ref ct, ref arity, ref r) => {
-                write!(f, "get_structure {}/{}, {}", ct.name(), arity, r)
+                write!(f, "get_structure {}/{}, {}", ct.name().as_str(), arity, r)
             }
             &FactInstruction::GetValue(ref x, ref a) => {
                 write!(f, "get_value {}, A{}", x, a)
@@ -166,11 +157,17 @@ impl fmt::Display for QueryInstruction {
                 write!(f, "put_list {}{}", lvl, r.reg_num())
             }
             &QueryInstruction::PutPartialString(lvl, ref s, r, has_tail) => {
-                write!(f, "put_partial_string({}, {}, {}, {})",
-                       lvl, s, r, has_tail)
+                write!(
+                    f,
+                    "put_partial_string({}, {}, {}, {})",
+                    lvl,
+                    s.as_str(),
+                    r,
+                    has_tail
+                )
             }
             &QueryInstruction::PutStructure(ref ct, ref arity, ref r) => {
-                write!(f, "put_structure {}/{}, {}", ct.name(), arity, r)
+                write!(f, "put_structure {}/{}, {}", ct.name().as_str(), arity, r)
             }
             &QueryInstruction::PutUnsafeValue(y, a) => write!(f, "put_unsafe_value Y{}, A{}", y, a),
             &QueryInstruction::PutValue(ref x, ref a) => write!(f, "put_value {}, A{}", x, a),
@@ -214,12 +211,12 @@ impl fmt::Display for ClauseType {
             &ClauseType::System(SystemClauseType::SetCutPoint(r)) => {
                 write!(f, "$set_cp({})", r)
             }
-            &ClauseType::Named(ref name, _, ref idx) | &ClauseType::Op(ref name, _, ref idx) => {
+            &ClauseType::Named(ref name, _, ref idx) => {
                 let idx = idx.0.get();
-                write!(f, "{}/{}", name, idx)
+                write!(f, "{}/{}", name.as_str(), idx)
             }
             ref ct => {
-                write!(f, "{}", ct.name())
+                write!(f, "{}", ct.name().as_str())
             }
         }
     }
@@ -227,6 +224,52 @@ impl fmt::Display for ClauseType {
 
 impl fmt::Display for HeapCellValue {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        read_heap_cell!(*self,
+            (HeapCellValueTag::Atom, (name, arity)) => {
+                if arity == 0 {
+                    write!(f, "{}", name.as_str())
+                } else {
+                    write!(
+                        f,
+                        "{}/{}",
+                        name.as_str(),
+                        arity
+                    )
+                }
+            }
+            (HeapCellValueTag::PStr, pstr_atom) => {
+                let pstr = PartialString::from(pstr_atom);
+
+                write!(
+                    f,
+                    "pstr ( \"{}\", )",
+                    pstr.as_str_from(0)
+                )
+            }
+            (HeapCellValueTag::Cons, c) => {
+                match_untyped_arena_ptr!(c,
+                     (ArenaHeaderTag::Integer, n) => {
+                         write!(f, "{}", n)
+                     }
+                     (ArenaHeaderTag::Rational, r) => {
+                         write!(f, "{}", r)
+                     }
+                     (ArenaHeaderTag::F64, fl) => {
+                         write!(f, "{}", fl)
+                     }
+                     (ArenaHeaderTag::Stream, stream) => {
+                         write!(f, "$stream({})", stream.as_ptr() as usize)
+                     }
+                     _ => {
+                         write!(f, "")
+                     }
+                )
+            }
+            _ => {
+                unreachable!()
+            }
+        )
+        /*
         match self {
             &HeapCellValue::Addr(ref addr) => write!(f, "{}", addr),
             &HeapCellValue::Atom(ref atom, _) => write!(f, "{}", atom.as_str()),
@@ -260,9 +303,11 @@ impl fmt::Display for HeapCellValue {
                 write!(f, "$tcp_listener({})", tcp_listener.local_addr().unwrap())
             }
         }
+        */
     }
 }
 
+/*
 impl fmt::Display for DBRef {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
@@ -273,7 +318,9 @@ impl fmt::Display for DBRef {
         }
     }
 }
+*/
 
+/*
 impl fmt::Display for Addr {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
@@ -296,6 +343,7 @@ impl fmt::Display for Addr {
         }
     }
 }
+*/
 
 impl fmt::Display for ControlInstruction {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -353,29 +401,45 @@ impl fmt::Display for ChoiceInstruction {
             &ChoiceInstruction::DynamicElse(offset, Death::Finite(d), NextOrFail::Fail(i)) => {
                 write!(f, "dynamic_else {}, {}, fail({})", offset, d, i)
             }
-            &ChoiceInstruction::DynamicInternalElse(offset, Death::Infinity, NextOrFail::Next(i)) => {
+            &ChoiceInstruction::DynamicInternalElse(
+                offset,
+                Death::Infinity,
+                NextOrFail::Next(i),
+            ) => {
                 write!(f, "dynamic_internal_else {}, {}, {}", offset, "inf", i)
             }
-            &ChoiceInstruction::DynamicInternalElse(offset, Death::Infinity, NextOrFail::Fail(i)) => {
-                write!(f, "dynamic_internal_else {}, {}, fail({})", offset, "inf", i)
+            &ChoiceInstruction::DynamicInternalElse(
+                offset,
+                Death::Infinity,
+                NextOrFail::Fail(i),
+            ) => {
+                write!(
+                    f,
+                    "dynamic_internal_else {}, {}, fail({})",
+                    offset, "inf", i
+                )
             }
-            &ChoiceInstruction::DynamicInternalElse(offset, Death::Finite(d), NextOrFail::Next(i)) => {
+            &ChoiceInstruction::DynamicInternalElse(
+                offset,
+                Death::Finite(d),
+                NextOrFail::Next(i),
+            ) => {
                 write!(f, "dynamic_internal_else {}, {}, {}", offset, d, i)
             }
-            &ChoiceInstruction::DynamicInternalElse(offset, Death::Finite(d), NextOrFail::Fail(i)) => {
+            &ChoiceInstruction::DynamicInternalElse(
+                offset,
+                Death::Finite(d),
+                NextOrFail::Fail(i),
+            ) => {
                 write!(f, "dynamic_internal_else {}, {}, fail({})", offset, d, i)
             }
-            &ChoiceInstruction::TryMeElse(offset) =>
-                write!(f, "try_me_else {}", offset),
+            &ChoiceInstruction::TryMeElse(offset) => write!(f, "try_me_else {}", offset),
             &ChoiceInstruction::DefaultRetryMeElse(offset) => {
                 write!(f, "retry_me_else_by_default {}", offset)
             }
-            &ChoiceInstruction::RetryMeElse(offset) =>
-                write!(f, "retry_me_else {}", offset),
-            &ChoiceInstruction::DefaultTrustMe(_) =>
-                write!(f, "trust_me_by_default"),
-            &ChoiceInstruction::TrustMe(_) =>
-                write!(f, "trust_me"),
+            &ChoiceInstruction::RetryMeElse(offset) => write!(f, "retry_me_else {}", offset),
+            &ChoiceInstruction::DefaultTrustMe(_) => write!(f, "trust_me_by_default"),
+            &ChoiceInstruction::TrustMe(_) => write!(f, "trust_me"),
         }
     }
 }
@@ -434,8 +498,8 @@ impl fmt::Display for SessionError {
                 write!(
                     f,
                     "module {} does not contain claimed export {}/{}",
-                    module,
-                    key.0,
+                    module.as_str(),
+                    key.0.as_str(),
                     key.1,
                 )
             }
@@ -452,12 +516,23 @@ impl fmt::Display for SessionError {
                 write!(f, "queries cannot be defined as facts.")
             }
             &SessionError::ModuleCannotImportSelf(ref module_name) => {
-                write!(f, "modules ({}, in this case) cannot import themselves.",
-                       module_name)
+                write!(
+                    f,
+                    "modules ({}, in this case) cannot import themselves.",
+                    module_name.as_str()
+                )
             }
-            &SessionError::PredicateNotMultifileOrDiscontiguous(ref compilation_target, ref key) => {
-                write!(f, "module {} does not define {}/{} as multifile or discontiguous.",
-                       compilation_target.module_name(), key.0, key.1)
+            &SessionError::PredicateNotMultifileOrDiscontiguous(
+                ref compilation_target,
+                ref key,
+            ) => {
+                write!(
+                    f,
+                    "module {} does not define {}/{} as multifile or discontiguous.",
+                    compilation_target.module_name().as_str(),
+                    key.0.as_str(),
+                    key.1
+                )
             }
         }
     }
@@ -466,14 +541,19 @@ impl fmt::Display for SessionError {
 impl fmt::Display for ExistenceError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
-            &ExistenceError::Module(ref module_name) => {
-                write!(f, "the module {} does not exist", module_name)
+            &ExistenceError::Module(module_name) => {
+                write!(f, "the module {} does not exist", module_name.as_str())
             }
             &ExistenceError::ModuleSource(ref module_source) => {
                 write!(f, "the source/sink {} does not exist", module_source)
             }
-            &ExistenceError::Procedure(ref name, arity) => {
-                write!(f, "the procedure {}/{} does not exist", name, arity)
+            &ExistenceError::Procedure(name, arity) => {
+                write!(
+                    f,
+                    "the procedure {}/{} does not exist",
+                    name.as_str(),
+                    arity
+                )
             }
             &ExistenceError::SourceSink(ref addr) => {
                 write!(f, "the source/sink {} does not exist", addr)
@@ -489,10 +569,10 @@ impl fmt::Display for ModuleSource {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
             &ModuleSource::File(ref file) => {
-                write!(f, "at the file {}", file)
+                write!(f, "at the file {}", file.as_str())
             }
             &ModuleSource::Library(ref library) => {
-                write!(f, "at library({})", library)
+                write!(f, "at library({})", library.as_str())
             }
         }
     }
@@ -538,7 +618,9 @@ impl fmt::Display for Line {
                 Ok(())
             }
             &Line::IndexedChoice(ref indexed_choice_instr) => write!(f, "{}", indexed_choice_instr),
-            &Line::DynamicIndexedChoice(ref indexed_choice_instr) => write!(f, "{}", indexed_choice_instr),
+            &Line::DynamicIndexedChoice(ref indexed_choice_instr) => {
+                write!(f, "{}", indexed_choice_instr)
+            }
             &Line::Query(ref query_instr) => write!(f, "{}", query_instr),
         }
     }
@@ -547,10 +629,10 @@ impl fmt::Display for Line {
 impl fmt::Display for Number {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
-            &Number::Fixnum(n) => write!(f, "{}", n),
-            &Number::Float(fl) => write!(f, "{}", fl),
-            &Number::Integer(ref bi) => write!(f, "{}", bi),
-            &Number::Rational(ref r) => write!(f, "{}", r),
+            Number::Float(fl) => write!(f, "{}", fl),
+            Number::Integer(n) => write!(f, "{}", n),
+            Number::Rational(r) => write!(f, "{}", r),
+            Number::Fixnum(n) => write!(f, "{}", n.get_num()),
         }
     }
 }
index baa057d0777c91e57bc1d8c46458ea0e2fe6687d..93e7b7ca33a811c0b95784b3bc68b942a6d4ddc7 100644 (file)
@@ -31,21 +31,23 @@ impl Expectable for &[u8] {
 pub(crate) fn load_module_test<T: Expectable>(file: &str, expected: T) {
     use scryer_prolog::*;
 
-    let input = machine::Stream::from("");
-    let output = machine::Stream::from(String::new());
-    let error = machine::Stream::from(String::new());
+    // let input = machine::Stream::from("");
+    // let output = machine::Stream::from(String::new());
+    // let error = machine::Stream::from(String::new());
 
-    let mut wam = machine::Machine::new(input, output.clone(), error);
+    let mut wam = machine::Machine::new(); // input, output.clone(), error);
+    expected.assert_eq(wam.test_load_file(file).as_slice());
 
-    wam.load_file(
-        file.into(),
-        machine::Stream::from(
-            std::fs::read_to_string(AsRef::<std::path::Path>::as_ref(file)).unwrap(),
-        ),
-    );
-
-    let output = output.bytes().unwrap();
-    expected.assert_eq(output.as_slice());
+    // wam.load_file(
+    //     file.into(),
+    //     machine::Stream::from_owned_string(
+    //         std::fs::read_to_string(AsRef::<std::path::Path>::as_ref(file)).unwrap(),
+    //         &mut wam.machine_st.arena,
+    //     ),
+    // );
+    // 
+    // let output = output.bytes().unwrap();
+    // expected.assert_eq(output.as_slice());
 }
 
 pub const SCRYER_PROLOG: &str = "scryer-prolog";